2 .. _container_orchestration_in_csit:
4 Container Orchestration in CSIT
5 ===============================
13 Linux Containers is an OS-level virtualization method for running
14 multiple isolated Linux systems (containers) on a compute host using a
15 single Linux kernel. Containers rely on Linux kernel cgroups
16 functionality for controlling usage of shared system resources (i.e.
17 CPU, memory, block I/O, network) and for namespace isolation. The latter
18 enables complete isolation of applications' view of operating
19 environment, including process trees, networking, user IDs and mounted
22 :abbr:`LXC (Linux Containers)` combine kernel's cgroups and support for isolated
23 namespaces to provide an isolated environment for applications. Docker
24 does use LXC as one of its execution drivers, enabling image management
25 and providing deployment services. More information in [lxc]_, [lxc-namespace]_
28 Linux containers can be of two kinds: privileged containers and
29 unprivileged containers.
31 Unprivileged Containers
32 ~~~~~~~~~~~~~~~~~~~~~~~
34 Running unprivileged containers is the safest way to run containers in a
35 production environment. From LXC 1.0 one can start a full system
36 container entirely as a user, allowing to map a range of UIDs on the
37 host into a namespace inside of which a user with UID 0 can exist again.
38 In other words an unprivileged container does mask the userid from the
39 host, making it impossible to gain a root access on the host even if a
40 user gets root in a container. With unprivileged containers, non-root
41 users can create containers and will appear in the container as the
42 root, but will appear as userid <non-zero> on the host. Unprivileged
43 containers are also better suited to supporting multi-tenancy operating
44 environments. More information in [lxc-security]_ and [stgraber]_.
49 Privileged containers do not mask UIDs, and container UID 0 is mapped to
50 the host UID 0. Security and isolation is controlled by a good
51 configuration of cgroup access, extensive AppArmor profile preventing
52 the known attacks as well as container capabilities and SELinux. Here a
53 list of applicable security control mechanisms:
55 - Capabilities - keep (whitelist) or drop (blacklist) Linux capabilities,
57 - Control groups - cgroups, resource bean counting, resource quotas, access
58 restrictions, [cgroup1]_, [cgroup2]_.
59 - AppArmor - apparmor profiles aim to prevent any of the known ways of
60 escaping a container or cause harm to the host, [apparmor]_.
61 - SELinux - Security Enhanced Linux is a Linux kernel security module
62 that provides similar function to AppArmor, supporting access control
63 security policies including United States Department of Defense–style
64 mandatory access controls. Mandatory access controls allow an
65 administrator of a system to define how applications and users can
66 access different resources such as files, devices, networks and inter-
67 process communication, [selinux]_.
68 - Seccomp - secure computing mode, enables filtering of system calls,
71 More information in [lxc-security]_ and [lxc-sec-features]_.
73 **Linux Containers in CSIT**
75 CSIT is using Privileged Containers as the ``sysfs`` is mounted with RW
76 access. Sysfs is required to be mounted as RW due to VPP accessing
77 :command:`/sys/bus/pci/drivers/uio_pci_generic/unbind`. This is not the case of
78 unprivileged containers where ``sysfs`` is mounted as read-only.
81 Orchestrating Container Lifecycle Events
82 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
84 Following Linux container lifecycle events need to be addressed by an
87 1. Acquire - acquiring/downloading existing container images via
88 :command:`docker pull` or :command:`lxc-create -t download`.
90 2. Build - building a container image from scratch or another
91 container image via :command:`docker build <dockerfile/composefile>` or
92 customizing LXC templates in
93 `https://github.com/lxc/lxc/tree/master/templates`_
95 3. (Re-)Create - creating a running instance of a container application
96 from anew, or re-creating one that failed. A.k.a. (re-)deploy via
97 :command:`docker run` or :command:`lxc-start`
99 4. Execute - execute system operations within the container by attaching to
100 running container. THis is done by :command:`lxc-attach` or
101 :command:`docker exec`
103 5. Distribute - distributing pre-built container images to the compute
104 nodes. Currently not implemented in CSIT.
107 Container Orchestration Systems Used in CSIT
108 --------------------------------------------
110 Current CSIT testing framework integrates following Linux container
111 orchestration mechanisms:
113 - LXC/Docker for complete VPP container lifecycle control.
114 - Combination of Kubernetes (container orchestration), Docker (container
115 images) and Ligato (container networking).
120 LXC is the well-known and heavily tested low-level Linux container
121 runtime [lxc-source]_, that provides a userspace interface for the Linux kernel
122 containment features. With a powerful API and simple tools, LXC enables
123 Linux users to easily create and manage system or application
124 containers. LXC uses following kernel features to contain processes:
126 - Kernel namespaces: ipc, uts, mount, pid, network and user.
127 - AppArmor and SELinux security profiles.
132 CSIT uses LXC runtime and LXC usertools to test VPP data plane performance in
133 a range of virtual networking topologies.
137 - Current CSIT restriction: only single instance of lxc runtime due to
138 the cgroup policies used in CSIT. There is plan to add the capability into
139 code to create cgroups per container instance to address this issue. This sort
140 of functionality is better supported in LXC 2.1 but can be done is current
143 - CSIT code is currently using cgroup to control the range of CPU cores the
144 LXC container runs on. VPP thread pinning is defined vpp startup.conf.
149 Docker builds on top of Linux kernel containment features, and
150 offers a high-level tool for wrapping the processes, maintaining and
151 executing them in containers [docker]_. Currently it using *runc* a CLI tool for
152 spawning and running containers according to the `OCI specification
153 <https://www.opencontainers.org/>`_
155 A Docker container image is a lightweight, stand-alone, executable
156 package of a piece of software that includes everything needed to run
157 it: code, runtime, system tools, system libraries, settings.
159 CSIT uses Docker to manage the maintenance and execution of
160 containerized applications used in CSIT performance tests.
162 - Data plane thread pinning to CPU cores - Docker CLI and/or Docker
163 configuration file controls the range of CPU cores the Docker image
164 must run on. VPP thread pinning defined vpp startup.conf.
169 Kubernetes [k8s-doc]_, or K8s, is a production-grade container orchestration
170 platform for automating the deployment, scaling and operating
171 application containers. Kubernetes groups containers that make up an
172 application into logical units, pods, for easy management and discovery.
173 K8s pod definitions including compute resource allocation is provided in
176 CSIT uses K8s and its infrastructure components like etcd to control all
177 phases of container based virtualized network topologies.
182 Ligato [ligato]_ is an open-source project developing a set of cloud-native
183 tools for orchestrating container networking. Ligato integrates with FD.io VPP
184 using goVPP [govpp]_ and vpp-agent [vpp-agent]_.
188 - Currently using a separate LF Jenkins job for building csit-centric
189 prod_vpp_agent docker images vs. dockerhub/ligato ones.
194 CSIT container orchestration is implemented in CSIT Level-1 keyword
195 Python libraries following the Builder design pattern. Builder design
196 pattern separates the construction of a complex object from its
197 representation, so that the same construction process can create
198 different representations e.g. LXC, Docker, other.
200 CSIT Robot Framework keywords are then responsible for higher level
201 lifecycle control of of the named container groups. One can have
202 multiple named groups, with 1..N containers in a group performing
203 different role/functionality e.g. NFs, Switch, Kafka bus, ETCD
204 datastore, etc. ContainerManager class acts as a Director and uses
205 ContainerEngine class that encapsulate container control.
207 Current CSIT implementation is illustrated using UML Class diagram:
216 +-----------------------------------------------------------------------+
217 | RF Keywords (high level lifecycle control) |
218 +-----------------------------------------------------------------------+
219 | Construct VNF containers on all DUTs |
220 | Acquire all '${group}' containers |
221 | Create all '${group}' containers |
222 | Install all '${group}' containers |
223 | Configure all '${group}' containers |
224 | Stop all '${group}' containers |
225 | Destroy all '${group}' containers |
226 +-----------------+-----------------------------------------------------+
230 +-----------------v-----------------+ +--------------------------+
231 | ContainerManager | | ContainerEngine |
232 +-----------------------------------+ +--------------------------+
233 | __init()__ | | __init(node)__ |
234 | construct_container() | | acquire(force) |
235 | construct_containers() | | create() |
236 | acquire_all_containers() | | stop() |
237 | create_all_containers() | 1 1 | destroy() |
238 | execute_on_container() <>-------| info() |
239 | execute_on_all_containers() | | execute(command) |
240 | install_vpp_in_all_containers() | | system_info() |
241 | configure_vpp_in_all_containers() | | install_supervisor() |
242 | stop_all_containers() | | install_vpp() |
243 | destroy_all_containers() | | restart_vpp() |
244 +-----------------------------------+ | create_vpp_exec_config() |
245 | create_vpp_startup_config|
246 | is_container_running() |
247 | is_container_present() |
248 | _configure_cgroup() |
249 +-------------^------------+
253 +----------+---------+
255 +------+-------+ +------+-------+
257 +--------------+ +--------------+
258 | (inherinted) | | (inherinted) |
259 +------+-------+ +------+-------+
261 +---------+---------+
265 +---------v---------+
267 +-------------------+
269 | __setattr__(a, v) |
270 +-------------------+
272 Sequentional diagram that illustrates the creation of a single container.
277 e = engine [Docker|LXC]
278 .. = kwargs (variable number of keyword argument)
280 +-------+ +------------------+ +-----------------+
281 | RF KW | | ContainerManager | | ContainerEngine |
282 +---+---+ +--------+---------+ +--------+--------+
284 | 1: new ContainerManager(e) | |
285 +-+---------------------------->+-+ |
286 |-| |-| 2: new ContainerEngine |
287 |-| |-+----------------------->+-+
291 |-| 3: construct_container(..) | |
292 |-+---------------------------->+-+ |
294 |-| |-+----------------------->+-+
295 |-| |-| |-| 5: new +-------------+
296 |-| |-| |-+-------->| Container A |
297 |-| |-| |-| +-------------+
298 |-| |-|<-----------------------+-|
301 |-| 6: acquire_all_containers() | |
302 |-+---------------------------->+-+ |
303 |-| |-| 7: acquire() |
304 |-| |-+----------------------->+-+
307 |-| |-| |-| | 8: is_container_present()
308 |-| |-| True/False |-|<-+
311 +---------------------------------------------------------------------------------------------+
312 | |-| ALT [isRunning & force] |-| |-|--+ |
313 | |-| |-| |-| | 8a: destroy() |
315 +---------------------------------------------------------------------------------------------+
319 |-| 9: create_all_containers() | |
320 |-+---------------------------->+-+ |
321 |-| |-| 10: create() |
322 |-| |-+----------------------->+-+
324 |-| |-| |-| | 11: wait('RUNNING')
328 +---------------------------------------------------------------------------------------------+
330 | |-| (install_vpp, configure_vpp) | | |
332 +---------------------------------------------------------------------------------------------+
334 |-| 12: destroy_all_containers() | |
335 |-+---------------------------->+-+ |
336 |-| |-| 13: destroy() |
337 |-| |-+----------------------->+-+
345 Container Data Structure
346 ~~~~~~~~~~~~~~~~~~~~~~~~
348 Container is represented in Python L1 library as a separate Class with instance
349 variables and no methods except overriden ``__getattr__`` and ``__setattr__``.
350 Instance variables are assigned to container dynamically during the
351 ``construct_container(**kwargs)`` call and are passed down from the RF keyword.
355 .. code-block:: robotframework
357 | Construct VNF containers on all DUTs
358 | | [Arguments] | ${technology} | ${image} | ${cpu_count}=${1} | ${count}=${1}
360 | | ${group}= | Set Variable | VNF
361 | | ${guest_dir}= | Set Variable | /mnt/host
362 | | ${host_dir}= | Set Variable | /tmp
363 | | ${skip_cpus}= | Evaluate | ${vpp_cpus}+${system_cpus}
364 | | Import Library | resources.libraries.python.ContainerUtils.ContainerManager
365 | | ... | engine=${technology} | WITH NAME | ${group}
366 | | ${duts}= | Get Matches | ${nodes} | DUT*
367 | | :FOR | ${dut} | IN | @{duts}
368 | | | {env}= | Create List | LC_ALL="en_US.UTF-8"
369 | | | ... | DEBIAN_FRONTEND=noninteractive | ETCDV3_ENDPOINTS=172.17.0.1:2379
370 | | | ${cpu_node}= | Get interfaces numa node | ${nodes['${dut}']}
371 | | | ... | ${dut1_if1} | ${dut1_if2}
372 | | | Run Keyword | ${group}.Construct containers
373 | | | ... | name=${dut}_${group}
374 | | | ... | node=${nodes['${dut}']}
375 | | | ... | host_dir=${host_dir}
376 | | | ... | guest_dir=${guest_dir}
377 | | | ... | image=${image}
378 | | | ... | cpu_count=${cpu_count}
379 | | | ... | cpu_skip=${skip_cpus}
380 | | | ... | smt_used=${False}
381 | | | ... | cpuset_mems=${cpu_node}
382 | | | ... | cpu_shared=${False}
383 | | | ... | env=${env}
385 Mandatory parameters to create standalone container are: ``node``, ``name``,
386 ``image`` [image-var]_, ``cpu_count``, ``cpu_skip``, ``smt_used``,
387 ``cpuset_mems``, ``cpu_shared``.
389 There is no parameters check functionality. Passing required arguments is in
390 coder responsibility. All the above parameters are required to calculate the
391 correct cpu placement. See documentation for the full reference.
396 Kubernetes is implemented as separate library ``KubernetesUtils.py``,
397 with a class with the same name. This utility provides an API for L2
398 Robot Keywords to control ``kubectl`` installed on each of DUTs. One
399 time initialization script, ``resources/libraries/bash/k8s_setup.sh``
400 does reset/init kubectl, applies Calico v2.6.3 and initializes the
401 ``csit`` namespace. CSIT namespace is required to not to interfere with
402 existing setups and it further simplifies apply/get/delete
403 Pod/ConfigMap operations on SUTs.
405 Kubernetes utility is based on YAML templates to avoid crafting the huge
406 YAML configuration files, what would lower the readability of code and
407 requires complicated algorithms. The templates can be found in
408 ``resources/templates/kubernetes`` and can be leveraged in the future
409 for other separate tasks.
411 Two types of YAML templates are defined:
413 - Static - do not change between deployments, that is infrastructure
414 containers like Kafka, Calico, ETCD.
416 - Dynamic - per test suite/case topology YAML files e.g. SFC_controller,
419 Making own python wrapper library of ``kubectl`` instead of using the
420 official Python package allows to control and deploy environment over
421 the SSH library without the need of using isolated driver running on
427 Ligato integration does require to compile the ``vpp-agent`` tool and build the
428 bundled Docker image. Compilation of ``vpp-agent`` depends on specific VPP. In
429 ``ligato/vpp-agent`` repository there are well prepared scripts for building the
430 Docker image. Building docker image is possible via series of commands:
434 git clone https://github.com/ligato/vpp-agent
435 cd vpp_agent/docker/dev_vpp_agent
436 sudo docker build -t dev_vpp_agent --build-arg AGENT_COMMIT=<agent commit id>\
437 --build-arg VPP_COMMIT=<vpp commit id> --no-cache .
443 CSIT requires Docker image to include the desired VPP version (per patch
444 testing, nightly testing, on demand testing).
446 The entire build process of building ``dev_vpp_agent`` image heavily depends
447 on internet connectivity and also takes a significant amount of time (~1-1.5h
448 based on internet bandwidth and allocated resources). The optimal solution would
449 be to build the image on jenkins slave, transfer the Docker image to DUTs and
450 execute separate suite of tests.
452 To adress the amount of time required to build ``dev_vpp_agent`` image, we can
453 pull existing specific version of ```dev_vpp_agent``` and exctract the
454 ```vpp-agent``` from it.
456 We created separate sets of Jenkins jobs, that will be executing following:
458 1. Clone latest CSIT and Ligato repositaries.
459 2. Pull specific version of ``dev_vpp_agent`` image from Dockerhub.
460 3. Extract VPP API (from ``.deb`` package) and copy into ``dev_vpp_agent``
462 4. Rebuild vpp-agent and extract outside image.
463 5. Build ``prod_vpp_image`` Docker image from ``dev_vpp_agent`` image.
464 6. Transfer ``prod_vpp_agent`` image to DUTs.
465 7. Execute subset of performance tests designed for Ligato testing.
469 +-----------------------------------------------+
470 | ubuntu:16.04 <-----| Base image on Dockerhub
471 +------------------------^----------------------+
474 +------------------------+----------------------+
475 | ligato/dev_vpp_agent <------| Pull this image from
476 +------------------------^----------------------+ | Dockerhub ligato/dev_vpp_agent:<version>
478 | Rebuild and extract agent.tar.gz from dev_vpp_agent
479 +------------------------+----------------------+
480 | prod_vpp_agent <------| Build by passing own
481 +-----------------------------------------------+ | vpp.tar.gz (from nexus
482 | or built by JJB) and
483 | agent.tar.gz extracted
484 | from ligato/dev_vpp_agent
487 Approximate size of vnf-agent docker images:
491 REPOSITORY TAG IMAGE ID CREATED SIZE
492 dev-vpp-agent latest 78c53bd57e2 6 weeks ago 9.79GB
493 prod_vpp_agent latest f68af5afe601 5 weeks ago 443MB
495 In CSIT we need to create separate performance suite under
496 ``tests/kubernetes/perf`` which contains modified Suite setup in comparison
497 to standard perf tests. This is due to reason that VPP will act as vswitch in
498 Docker image and not as standalone installed service.
503 Listed CSIT container networking test topologies are defined with DUT
504 containerized VPP switch forwarding packets between NF containers. Each
505 NF container runs their own instance of VPP in L2XC configuration.
507 Following container networking topologies are tested in CSIT |release|:
511 - eth-l2xcbase-eth-2memif-1lxc.
512 - eth-l2bdbasemaclrn-eth-2memif-1lxc.
516 - eth-l2xcbase-eth-2memif-1docker.
518 - Kubernetes/Ligato topologies:
520 - eth-1drcl2bdbasemaclrn-eth-2memif-1drcl2xc-1paral
521 - eth-1drcl2bdbasemaclrn-eth-2memif-2drcl2xc-1horiz
522 - eth-1drcl2bdbasemaclrn-eth-2memif-4drcl2xc-1horiz
523 - eth-1drcl2bdbasemaclrn-eth-4memif-2drcl2xc-1chain
524 - eth-1drcl2bdbasemaclrn-eth-8memif-4drcl2xc-1chain
525 - eth-1drcl2xcbase-eth-2memif-1drcl2xc-1paral
526 - eth-1drcl2xcbase-eth-2memif-2drcl2xc-1horiz
527 - eth-1drcl2xcbase-eth-2memif-4drcl2xc-1horiz
528 - eth-1drcl2xcbase-eth-4memif-2drcl2xc-1chain
529 - eth-1drcl2xcbase-eth-8memif-4drcl2xc-1chain
534 .. [lxc] `Linux Containers <https://linuxcontainers.org/>`_
535 .. [lxc-namespace] `Resource management: Linux kernel Namespaces and cgroups <https://www.cs.ucsb.edu/~rich/class/cs293b-cloud/papers/lxc-namespace.pdf>`_.
536 .. [stgraber] `LXC 1.0: Blog post series <https://stgraber.org/2013/12/20/lxc-1-0-blog-post-series/>`_.
537 .. [lxc-security] `Linux Containers Security <https://linuxcontainers.org/lxc/security/>`_.
538 .. [capabilities] `Linux manual - capabilities - overview of Linux capabilities http://man7.org/linux/man-pages/man7/capabilities.7.html`_.
539 .. [cgroup1] `Linux kernel documentation: cgroups <https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt>`_.
540 .. [cgroup2] `Linux kernel documentation: Control Group v2 <https://www.kernel.org/doc/Documentation/cgroup-v2.txt>`_.
541 .. [selinux] `SELinux Project Wiki <http://selinuxproject.org/page/Main_Page>`_.
542 .. [lxc-sec-features] `LXC 1.0: Security features <https://stgraber.org/2014/01/01/lxc-1-0-security-features/>`_.
543 .. [lxc-source] `Linux Containers source <https://github.com/lxc/lxc>`_.
544 .. [apparmor] `Ubuntu AppArmor <https://wiki.ubuntu.com/AppArmor>`_.
545 .. [seccomp] `SECure COMPuting with filters <https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt>`_.
546 .. [docker] `Docker <https://www.docker.com/what-docker>`_.
547 .. [k8s-doc] `Kubernetes documentation <https://kubernetes.io/docs/home/>`_.
548 .. [ligato] `Ligato <https://github.com/ligato>`_.
549 .. [govpp] `FD.io goVPP project <https://wiki.fd.io/view/GoVPP>`_.
550 .. [vpp-agent] `Ligato vpp-agent <https://github.com/ligato/vpp-agent>`_.
551 .. [image-var] Image parameter is required in initial commit version. There is plan to implement container build class to build Docker/LXC image.