Experimenting containerized VNFs with Kubernetes VIM

In the past, Tacker only supports creating virtual machine based VNF using Heat. This section covers how to deploy containerized VNF using Kubernetes VIM in Tacker.

Prepare Kubernetes VIM

To use Kubernetes type of VNF, firstly user must register Kubernetes VIM. Tacker supports Kubernetes authentication with two types: basic authentication (username and password) or Bearer token. User can secure the connection to Kubernetes cluster by providing SSL certificate. The following vim-config.yaml file provides necessary information to register a Kubernetes VIM.

auth_url: "https://192.168.11.110:6443"
username: "admin"
password: "admin"
project_name: "default"
ssl_ca_cert: None
type: "kubernetes"

More details about registering Kubernetes VIM, please refer [1]

Sample container TOSCA templates

1. One container per VDU example

Currently, because Kubernetes does not support multiple networks such as choosing networks, connection points for applications, therefore users only deploys their applications with default networks (Pod and Service networks). In this case, user need to provide only information about VDU to create a VNF in Tacker.

The following example shows TOSCA template of containerized VNF for pure Kubernetes environment with one container per VDU.

tosca-vnfd-containerized-two-containers.yaml

tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
description: A sample containerized VNF with two containers per VDU

metadata:
    template_name: sample-tosca-vnfd

topology_template:
  node_templates:
    VDU1:
      type: tosca.nodes.nfv.VDU.Tacker
      properties:
        namespace: default
        mapping_ports:
          - "80:80"
          - "88:88"
        service_type: NodePort
        vnfcs:
          front_end:
            num_cpus: 0.5
            mem_size: 512 MB
            image: nginx
            ports:
              - "80"
          rss_reader:
            num_cpus: 0.5
            mem_size: 512 MB
            image: nickchase/rss-php-nginx:v1
            ports:
              - "88"
  policies:
    - SP1:
        type: tosca.policies.tacker.Scaling
        targets: [VDU1]
        properties:
          min_instances: 1
          max_instances: 3
          target_cpu_utilization_percentage: 40
          default_instances: 1 # required parameter but ignored for cnf
          increment: 1         # required parameter but ignored for cnf

In “vnfcs”, there are 2 components: front_end and rss_reader. We model them as Containers [2] inside a Pod [3]. To provide recover ability of these containers, we put all of containers inside a Deployment [4] object, that can warrant the number of replica with auto-healing automatically.

The following table shows details about parameter of a Container. Config is translated to ConfigMap [5], when user want to update the VNF (config), equivalent ConfigMap will be updated.

+-----------------------------------------------------------------------------------------------+
|          vnfcs          |            Example            |               Description           |
+-----------------------------------------------------------------------------------------------+
|          name           |           front_end           |  Name of container                  |
+-----------------------------------------------------------------------------------------------+
|        num_cpus         |              0.5              |  Number of CPUs                     |
+-----------------------------------------------------------------------------------------------+
|        mem_size         |            512 MB             |  Memory size                        |
+-----------------------------------------------------------------------------------------------+
|         image           |             nginx             |  Image to launch container          |
+-----------------------------------------------------------------------------------------------+
|         ports           |            - "80"             |  Exposed ports in container         |
+-----------------------------------------------------------------------------------------------+
|        command          |      ['/bin/sh','echo']       |  Command when container was started |
+-----------------------------------------------------------------------------------------------+
|         args            |          ['hello']            |  Args of command                    |
+-----------------------------------------------------------------------------------------------+
|        config           |         param0: key1          |  Set variables                      |
|                         |         param1: key2          |                                     |
+-----------------------------------------------------------------------------------------------+

In Tacker, VDU is modeled as a Service [6] in Kubernetes. Because Pods can be easily replaced by others, when the number of replica increased, workload should be shared between Pods. To do this task, we model VDU as Service, it acts as a Load balancer for Pods. Currently, we support some parameters as the following table.

+--------------------------------------------------------------------------------------------------------------------------------+
|     VDU properties      |          Example          |            Description                                                   |
+--------------------------------------------------------------------------------------------------------------------------------+
|       namespace         |          default          | Namespace in Kubernetes where all objects are deployed                   |
+--------------------------------------------------------------------------------------------------------------------------------+
|     mapping_ports       |         - "443:443"       | Published ports and target ports (container ports) of Service Kubernetes |
|                         |         - "80:8080"       |                                                                          |
+--------------------------------------------------------------------------------------------------------------------------------+
|       labels            |      "app: webserver"     | Labels which is set for Kubernetes objects, it is used as Selector to    |
|                         |                           | Service can send requests to Pods                                        |
+--------------------------------------------------------------------------------------------------------------------------------+
|     service_type        |         ClusterIP         | Set service type for Service object.                                     |
|                         |                           |                                                                          |
+--------------------------------------------------------------------------------------------------------------------------------+
|         vnfcs           |                           | Vnfcs are modeled by Containers and Deployment object. User can limit    |
|                         |                           | resource, set image, publish container ports, set commands and variables |
+--------------------------------------------------------------------------------------------------------------------------------+

User can also set scaling policy for VDU by adding the following policy. These information is translated to Horizontal Pod Autoscaler in Kubernetes. In the current scope, we just support auto-scaling with CPU utilization, more metrics will be added in the future.

policies:
  - SP1:
      type: tosca.policies.tacker.Scaling
      targets: [VDU1]
      properties:
        min_instances: 1
        max_instances: 3
        target_cpu_utilization_percentage: 40
        default_instances: 1 # required parameter but ignored for cnf
        increment: 1         # required parameter but ignored for cnf

2. Two containers per VDU example

Similar to the above example, in this scenario, we define 2 containers in VDU1.

tosca-vnfd-containerized.yaml

tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
description: A sample containerized VNF with two containers per VDU

metadata:
    template_name: sample-tosca-vnfd

topology_template:
  node_templates:
    VDU1:
      type: tosca.nodes.nfv.VDU.Tacker
      properties:
        namespace: default
        mapping_ports:
          - "80:8080"
        labels:
          - "app: webserver"
        service_type: ClusterIP
        vnfcs:
          web_server:
              num_cpus: 0.5
              mem_size: 512 MB
              image: celebdor/kuryr-demo
              ports:
                - "8080"
              config: |
                param0: key1
                param1: key2

  policies:
    - SP1:
        type: tosca.policies.tacker.Scaling
        targets: [VDU1]
        properties:
          min_instances: 1
          max_instances: 3
          target_cpu_utilization_percentage: 40
          default_instances: 1 # required parameter but ignored for cnf
          increment: 1         # required parameter but ignored for cnf

Viewing a containerized VNF

Create sample containerized VNF

$ openstack vnf descriptor create --vnfd-file tosca-vnfd-containerized.yaml VNFD1
Created a new vnfd:
+-----------------+-------------------------------------------------------------------------------------------------------+
| Field           | Value                                                                                                 |
+-----------------+-------------------------------------------------------------------------------------------------------+
| created_at      | 2018-01-21 14:36:51.757044                                                                            |
| description     | A sample containerized VNF with one container per VDU                                                 |
| id              | fb4a0aa8-e410-4e73-abdc-d2808de155ef                                                                  |
| name            | VNFD1                                                                                                 |
| service_types   | vnfd                                                                                                  |
| template_source | onboarded                                                                                             |
| tenant_id       | 2d22508be9694091bb2f03ce27911416                                                                      |
| updated_at      |                                                                                                       |
+-----------------+-------------------------------------------------------------------------------------------------------+

$ openstack vnf create --vnfd-name VNFD1 --vim-name vim-kubernetes VNF1
Created a new vnf:
+----------------+-------------------------------------------------------------------------------------------------------+
| Field          | Value                                                                                                 |
+----------------+-------------------------------------------------------------------------------------------------------+
| created_at     | 2018-01-21 14:37:23.318018                                                                            |
| description    | A sample containerized VNF with one container per VDU                                                 |
| error_reason   |                                                                                                       |
| id             | 1faf776b-8d2b-4ee6-889d-e3b7c7310411                                                                  |
| instance_id    | default,svc-vdu1-05db44                                                                               |
| mgmt_ip_address|                                                                                                       |
| name           | VNF1                                                                                                  |
| placement_attr | {"vim_name": "vim-kubernetes"}                                                                        |
| status         | PENDING_CREATE                                                                                        |
| tenant_id      | 2d22508be9694091bb2f03ce27911416                                                                      |
| updated_at     |                                                                                                       |
| vim_id         | 791830a6-45fd-468a-bd85-e07fe24e5ce3                                                                  |
| vnfd_id        | fb4a0aa8-e410-4e73-abdc-d2808de155ef                                                                  |
+----------------+-------------------------------------------------------------------------------------------------------+

$ openstack vnf list
+--------------------------------------+------+----------------------------+--------+--------------------------------------+--------------------------------------+
| id                                   | name | mgmt_ip_address            | status | vim_id                               | vnfd_id                              |
+--------------------------------------+------+----------------------------+--------+--------------------------------------+--------------------------------------+
| 1faf776b-8d2b-4ee6-889d-e3b7c7310411 | VNF1 |                            | ACTIVE | 791830a6-45fd-468a-bd85-e07fe24e5ce3 | fb4a0aa8-e410-4e73-abdc-d2808de155ef |
+--------------------------------------+------+----------------------------+--------+--------------------------------------+--------------------------------------+

To test VNF is running in Kubernetes environment, we can check by running following commands

$ kubectl get svc
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes        ClusterIP   192.168.28.129   <none>        443/TCP   5h
svc-vdu1-05db44   ClusterIP   192.168.28.187   <none>        80/TCP    12m

$ kubectl get deployment
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
svc-vdu1-05db44   1         1         1            1           16m

$ kubectl get pod
NAME                               READY     STATUS    RESTARTS   AGE
svc-vdu1-05db44-7dcb6b955d-wkh7d   1/1       Running   0          18m

$ kubectl get hpa
NAME              REFERENCE                    TARGETS           MINPODS   MAXPODS   REPLICAS   AGE
svc-vdu1-05db44   Deployment/svc-vdu1-05db44   <unknown> / 40%   1         3         1          17m

$ kubectl get configmap
NAME              DATA      AGE
svc-vdu1-05db44   2         17m

User also can scale VNF manually, by running the following commands:

$ openstack vnf scale --scaling-policy-name SP1 --scaling-type out VNF1

$ kubectl get deployment
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
svc-vdu1-651815   2         2         2            1           3h

$ kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
svc-vdu1-651815-5b894b8bfb-b6mzq   2/2       Running   0          3h
svc-vdu1-651815-5b894b8bfb-b7f2c   2/2       Running   0          40s

In the same way, user also scale in VNF with scaling-type is ‘in’. The range of scaling manually is limited by ‘min_instances’ and ‘max_instances’ user provide in VNF template.

Multi-Interface for C-VNF

To use multi-interface for C-VNF, User should follow below procedure.

1. Checking kuryr.conf

After installation, user should check kuryr.conf configuration.

$ sudo cat /etc/kuryr/kuryr.conf | grep multi_vif_drivers
multi_vif_drivers = npwg_multiple_interfaces

2. Adding K8s CustomResourceDefinition

To use CustomResourceDefinition, user needs to add CRD. User can make a additional network using yaml file like below. Create yaml file like below and register it.

$ cat ./crdnetwork.yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: network-attachment-definitions.k8s.cni.cncf.io
spec:
  group: k8s.cni.cncf.io
  version: v1
  scope: Namespaced
  names:
    plural: network-attachment-definitions
    singular: network-attachment-definition
    kind: NetworkAttachmentDefinition
    shortNames:
    - net-attach-def
  validation:
    openAPIV3Schema:
      properties:
        spec:
          properties:
            config:
                 type: string

Register crdnetwork.yaml

$ kubectl create -f ~/crdnetwork.yaml

Get crd list

$ kubectl get crd

NAME                                             CREATED AT
kuryrnetpolicies.openstack.org                   2019-07-31T02:23:54Z
kuryrnets.openstack.org                          2019-07-31T02:23:54Z
network-attachment-definitions.k8s.cni.cncf.io   2019-07-31T02:23:55Z

3. Adding neutron subnet id information to k8s CRD

To use neutron subnet in kubernetes, user should register neutron subnet to CRD. At first, user should create subnet.yaml file.

$ cat ./kuryr-subnetname1.yaml

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: subnetname1
  annotations:
    openstack.org/kuryr-config: '{"subnetId": "$subnet_id"}'

After making a yaml file, user should create subnet with yaml file.

$ kubectl create -f ~/kuryr-subnetname1.yaml

After created, user can check subnet info like below.

$ kubectl get net-attach-def

NAME           AGE
k8s-multi-10   7d
k8s-multi-11   7d

Known Issues and Limitations

  • Does not support Volumes in Kubernetes

  • Horizontal Pod AutoScaler only support CPU utilization

  • Add support Kuryr-Kubernetes for making hybrid network in the future

References