From bc9e14a762823cbef9384e959ab2ca0fc1d650fc Mon Sep 17 00:00:00 2001 From: Louis Woods <louis.woods@ericsson.com> Date: Sun, 4 Nov 2018 01:07:38 -0800 Subject: [PATCH] Adds support for Multus (multiple interfaces) CNI plugin (#3166) * Adds support for Multus (multiple interfaces) CNI plugin Multus is a latin word for "Multi". As the name suggests, it acts as a Multi plugin in Kubernetes and provides multiple network interface support in a pod. Multus uses the concept of invoking delegates by grouping multiple plugins into delegates and invoking them in the sequential order of the CNI configuration file provided in json format. * Change CNI version (0.1.0->0.3.1) of Contiv to be compatible with Multus --- .gitlab-ci.yml | 15 ++++ README.md | 3 + Vagrantfile | 5 +- docs/multus.md | 73 +++++++++++++++++++ roles/download/defaults/main.yml | 12 +++ .../network_plugin/meta/main.yml | 5 ++ .../network_plugin/multus/tasks/main.yml | 11 +++ roles/kubespray-defaults/defaults/main.yaml | 1 + roles/network_plugin/contiv/defaults/main.yml | 2 +- roles/network_plugin/meta/main.yml | 5 ++ roles/network_plugin/multus/defaults/main.yml | 7 ++ .../multus/files/multus-clusterrole.yml | 16 ++++ .../files/multus-clusterrolebinding.yml | 13 ++++ .../multus/files/multus-crd.yml | 22 ++++++ .../multus/files/multus-serviceaccount.yml | 6 ++ roles/network_plugin/multus/tasks/main.yml | 19 +++++ .../multus/templates/multus-daemonset.yml.j2 | 54 ++++++++++++++ tests/files/gce_centos7-multus-calico.yml | 12 +++ tests/testcases/040_check-network-adv.yml | 65 +++++++++++++++++ 19 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 docs/multus.md create mode 100644 roles/kubernetes-apps/network_plugin/multus/tasks/main.yml create mode 100644 roles/network_plugin/multus/defaults/main.yml create mode 100644 roles/network_plugin/multus/files/multus-clusterrole.yml create mode 100644 roles/network_plugin/multus/files/multus-clusterrolebinding.yml create mode 100644 roles/network_plugin/multus/files/multus-crd.yml create mode 100644 roles/network_plugin/multus/files/multus-serviceaccount.yml create mode 100644 roles/network_plugin/multus/tasks/main.yml create mode 100644 roles/network_plugin/multus/templates/multus-daemonset.yml.j2 create mode 100644 tests/files/gce_centos7-multus-calico.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4fc458239..de4285c90 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -300,6 +300,10 @@ before_script: # stage: deploy-special MOVED_TO_GROUP_VARS: "true" +.centos7_multus_calico_variables: ¢os7_multus_calico_variables +# stage: deploy-part2 + MOVED_TO_GROUP_VARS: "true" + .coreos_alpha_weave_ha_variables: &coreos_alpha_weave_ha_variables # stage: deploy-special MOVED_TO_GROUP_VARS: "true" @@ -638,6 +642,17 @@ gce_centos7-kube-router: except: ['triggers'] only: ['master', /^pr-.*$/] +gce_centos7-multus-calico: + stage: deploy-part2 + <<: *job + <<: *gce + variables: + <<: *gce_variables + <<: *centos7_multus_calico_variables + when: manual + except: ['triggers'] + only: ['master', /^pr-.*$/] + gce_opensuse-canal: stage: deploy-part2 <<: *job diff --git a/README.md b/README.md index 2c7175936..f9d197184 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ Supported Components - [weave](https://github.com/weaveworks/weave) v2.4.1 - [kube-router](https://github.com/cloudnativelabs/kube-router) v0.2.1 - Application + - [multus](https://github.com/intel/multus-cni) v3.1 - [cephfs-provisioner](https://github.com/kubernetes-incubator/external-storage) v2.1.0-k8s1.11 - [cert-manager](https://github.com/jetstack/cert-manager) v0.5.0 - [coredns](https://github.com/coredns/coredns) v1.2.5 @@ -176,6 +177,8 @@ You can choose between 6 network plugins. (default: `calico`, except Vagrant use iptables for network policies, and BGP for ods L3 networking (with optionally BGP peering with out-of-cluster BGP peers). It can also optionally advertise routes to Kubernetes cluster Pods CIDRs, ClusterIPs, ExternalIPs and LoadBalancerIPs. +- [multus](docs/multus.md): Multus is a meta CNI plugin that provides multiple network interface support to pods. For each interface Multus delegates CNI calls to secondary CNI plugins such as Calico, macvlan, etc. + The choice is defined with the variable `kube_network_plugin`. There is also an option to leverage built-in cloud provider networking instead. See also [Network checker](docs/netcheck.md). diff --git a/Vagrantfile b/Vagrantfile index 1c0d6d7b9..de612516f 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -35,6 +35,8 @@ $forwarded_ports = {} $subnet = "172.17.8" $os = "ubuntu1804" $network_plugin = "flannel" +# Setting multi_networking to true will install Multus: https://github.com/intel/multus-cni +$multi_networking = false # The first three nodes are etcd servers $etcd_instances = $num_instances # The first two nodes are kube masters @@ -140,7 +142,8 @@ Vagrant.configure("2") do |config| "ip": ip, "local_release_dir" => $local_release_dir, "download_run_once": "False", - "kube_network_plugin": $network_plugin + "kube_network_plugin": $network_plugin, + "kube_network_plugin_multus": $multi_networking } config.vm.network :private_network, ip: ip diff --git a/docs/multus.md b/docs/multus.md new file mode 100644 index 000000000..2d46135f5 --- /dev/null +++ b/docs/multus.md @@ -0,0 +1,73 @@ +Multus +=========== + +Multus is a meta CNI plugin that provides multiple network interface support to +pods. For each interface, Multus delegates CNI calls to secondary CNI plugins +such as Calico, macvlan, etc. + +See [multus documentation](https://github.com/intel/multus-cni). + +## Multus installation + +Since Multus itself does not implement networking, it requires a master plugin, which is specified through the variable `kube_network_plugin`. To enable Multus an additional variable `kube_network_plugin_multus` must be set to `true`. For example, +``` +kube_network_plugin: calico +kube_network_plugin_multus: true +``` +will install Multus and Calico and configure Multus to use Calico as the primary network plugin. + +## Using Multus + +Once Multus is installed, you can create CNI configurations (as a CRD objects) for additional networks, in this case a macvlan CNI configuration is defined. You may replace the config field with any valid CNI configuration where the CNI binary is available on the nodes. + +``` +cat <<EOF | kubectl create -f - +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: macvlan-conf +spec: + config: '{ + "cniVersion": "0.3.0", + "type": "macvlan", + "master": "eth0", + "mode": "bridge", + "ipam": { + "type": "host-local", + "subnet": "192.168.1.0/24", + "rangeStart": "192.168.1.200", + "rangeEnd": "192.168.1.216", + "routes": [ + { "dst": "0.0.0.0/0" } + ], + "gateway": "192.168.1.1" + } + }' +EOF +``` + +You may then create a pod with and additional interface that connects to this network using annotations. The annotation correlates to the name in the NetworkAttachmentDefinition above. + +``` +cat <<EOF | kubectl create -f - +apiVersion: v1 +kind: Pod +metadata: + name: samplepod + annotations: + k8s.v1.cni.cncf.io/networks: macvlan-conf +spec: + containers: + - name: samplepod + command: ["/bin/bash", "-c", "sleep 2000000000000"] + image: dougbtv/centos-network +EOF +``` + +You may now inspect the pod and see that there is an additional interface configured: + +``` +$ kubectl exec -it samplepod -- ip a +``` + +For more details on how to use Multus, please visit https://github.com/intel/multus-cni diff --git a/roles/download/defaults/main.yml b/roles/download/defaults/main.yml index b375e07ff..aca850668 100644 --- a/roles/download/defaults/main.yml +++ b/roles/download/defaults/main.yml @@ -59,6 +59,7 @@ pod_infra_version: 3.1 contiv_version: 1.2.1 cilium_version: "v1.3.0" kube_router_version: "v0.2.1" +multus_version: "v3.1.autoconf" # Download URLs kubeadm_download_url: "https://storage.googleapis.com/kubernetes-release/release/{{ kubeadm_version }}/bin/linux/{{ image_arch }}/kubeadm" @@ -160,6 +161,8 @@ cilium_image_repo: "docker.io/cilium/cilium" cilium_image_tag: "{{ cilium_version }}" kube_router_image_repo: "cloudnativelabs/kube-router" kube_router_image_tag: "{{ kube_router_version }}" +multus_image_repo: "docker.io/nfvpe/multus" +multus_image_tag: "{{ multus_version }}" nginx_image_repo: nginx nginx_image_tag: 1.13 dnsmasq_version: 2.78 @@ -290,6 +293,15 @@ downloads: groups: - k8s-cluster + multus: + enabled: "{{ kube_network_plugin_multus }}" + container: true + repo: "{{ multus_image_repo }}" + tag: "{{ multus_image_tag }}" + sha256: "{{ multus_digest_checksum|default(None) }}" + groups: + - k8s-cluster + flannel: enabled: "{{ kube_network_plugin == 'flannel' or kube_network_plugin == 'canal' }}" container: true diff --git a/roles/kubernetes-apps/network_plugin/meta/main.yml b/roles/kubernetes-apps/network_plugin/meta/main.yml index c88dbf015..8d2a5be1b 100644 --- a/roles/kubernetes-apps/network_plugin/meta/main.yml +++ b/roles/kubernetes-apps/network_plugin/meta/main.yml @@ -34,3 +34,8 @@ dependencies: when: kube_network_plugin == 'kube-router' tags: - kube-router + + - role: kubernetes-apps/network_plugin/multus + when: kube_network_plugin_multus + tags: + - multus diff --git a/roles/kubernetes-apps/network_plugin/multus/tasks/main.yml b/roles/kubernetes-apps/network_plugin/multus/tasks/main.yml new file mode 100644 index 000000000..9d7669cc7 --- /dev/null +++ b/roles/kubernetes-apps/network_plugin/multus/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Multus | Start resources + kube: + name: "{{item.item.name}}" + namespace: "kube-system" + kubectl: "{{bin_dir}}/kubectl" + resource: "{{item.item.type}}" + filename: "{{kube_config_dir}}/{{item.item.file}}" + state: "latest" + with_items: "{{ multus_manifest_1.results }} + {{multus_manifest_2.results }}" + when: inventory_hostname == groups['kube-master'][0] and not item|skipped diff --git a/roles/kubespray-defaults/defaults/main.yaml b/roles/kubespray-defaults/defaults/main.yaml index 48bf477cb..1f2d7aa38 100644 --- a/roles/kubespray-defaults/defaults/main.yaml +++ b/roles/kubespray-defaults/defaults/main.yaml @@ -103,6 +103,7 @@ kube_users: # Choose network plugin (cilium, calico, weave or flannel) # Can also be set to 'cloud', which lets the cloud provider setup appropriate routing kube_network_plugin: calico +kube_network_plugin_multus: false # Determines if calico-rr group exists peer_with_calico_rr: "{{ 'calico-rr' in groups and groups['calico-rr']|length > 0 }}" diff --git a/roles/network_plugin/contiv/defaults/main.yml b/roles/network_plugin/contiv/defaults/main.yml index 5a3778937..82316357c 100644 --- a/roles/network_plugin/contiv/defaults/main.yml +++ b/roles/network_plugin/contiv/defaults/main.yml @@ -4,7 +4,7 @@ contiv_config_dir: "{{ kube_config_dir }}/contiv" contiv_etcd_conf_dir: "/etc/contiv/etcd" contiv_etcd_data_dir: "/var/lib/etcd/contiv-data" contiv_netmaster_port: 9999 -contiv_cni_version: 0.1.0 +contiv_cni_version: 0.3.1 # No need to download it by default, but must be defined contiv_etcd_image_repo: "{{ etcd_image_repo }}" diff --git a/roles/network_plugin/meta/main.yml b/roles/network_plugin/meta/main.yml index a0fae7207..120642371 100644 --- a/roles/network_plugin/meta/main.yml +++ b/roles/network_plugin/meta/main.yml @@ -37,3 +37,8 @@ dependencies: - role: network_plugin/cloud when: kube_network_plugin == 'cloud' + + - role: network_plugin/multus + when: kube_network_plugin_multus + tags: + - multus diff --git a/roles/network_plugin/multus/defaults/main.yml b/roles/network_plugin/multus/defaults/main.yml new file mode 100644 index 000000000..2fb723103 --- /dev/null +++ b/roles/network_plugin/multus/defaults/main.yml @@ -0,0 +1,7 @@ +--- +multus_conf_file: "auto" +multus_cni_conf_dir_host: "/etc/cni/net.d" +multus_cni_bin_dir_host: "/opt/cni/bin" +multus_cni_conf_dir: "{{ ('/host', multus_cni_conf_dir_host) | join }}" +multus_cni_bin_dir: "{{ ('/host', multus_cni_bin_dir_host) | join }}" +multus_kubeconfig_file_host: "{{ (multus_cni_conf_dir_host, '/multus.d/multus.kubeconfig') | join }}" diff --git a/roles/network_plugin/multus/files/multus-clusterrole.yml b/roles/network_plugin/multus/files/multus-clusterrole.yml new file mode 100644 index 000000000..337775be2 --- /dev/null +++ b/roles/network_plugin/multus/files/multus-clusterrole.yml @@ -0,0 +1,16 @@ +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: multus +rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +- nonResourceURLs: + - '*' + verbs: + - '*' diff --git a/roles/network_plugin/multus/files/multus-clusterrolebinding.yml b/roles/network_plugin/multus/files/multus-clusterrolebinding.yml new file mode 100644 index 000000000..5980330eb --- /dev/null +++ b/roles/network_plugin/multus/files/multus-clusterrolebinding.yml @@ -0,0 +1,13 @@ +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: multus +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: multus +subjects: +- kind: ServiceAccount + name: multus + namespace: kube-system diff --git a/roles/network_plugin/multus/files/multus-crd.yml b/roles/network_plugin/multus/files/multus-crd.yml new file mode 100644 index 000000000..eab4406e2 --- /dev/null +++ b/roles/network_plugin/multus/files/multus-crd.yml @@ -0,0 +1,22 @@ +--- +kind: CustomResourceDefinition +apiVersion: apiextensions.k8s.io/v1beta1 +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 diff --git a/roles/network_plugin/multus/files/multus-serviceaccount.yml b/roles/network_plugin/multus/files/multus-serviceaccount.yml new file mode 100644 index 000000000..62423082c --- /dev/null +++ b/roles/network_plugin/multus/files/multus-serviceaccount.yml @@ -0,0 +1,6 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: multus + namespace: kube-system diff --git a/roles/network_plugin/multus/tasks/main.yml b/roles/network_plugin/multus/tasks/main.yml new file mode 100644 index 000000000..7f603973d --- /dev/null +++ b/roles/network_plugin/multus/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- name: Multus | Copy manifest files + copy: + src: "{{ item.file }}" + dest: "{{ kube_config_dir }}" + with_items: + - {name: multus-crd, file: multus-crd.yml, type: customresourcedefinition} + - {name: multus-serviceaccount, file: multus-serviceaccount.yml, type: serviceaccount} + - {name: multus-clusterrole, file: multus-clusterrole.yml, type: clusterrole} + - {name: multus-clusterrolebinding, file: multus-clusterrolebinding.yml, type: clusterrolebinding} + register: multus_manifest_1 + +- name: Multus | Copy manifest templates + template: + src: "{{ item.file }}.j2" + dest: "{{ kube_config_dir }}/{{ item.file }}" + with_items: + - {name: multus-daemonset, file: multus-daemonset.yml, type: daemonset} + register: multus_manifest_2 diff --git a/roles/network_plugin/multus/templates/multus-daemonset.yml.j2 b/roles/network_plugin/multus/templates/multus-daemonset.yml.j2 new file mode 100644 index 000000000..11cf427d0 --- /dev/null +++ b/roles/network_plugin/multus/templates/multus-daemonset.yml.j2 @@ -0,0 +1,54 @@ +--- +kind: DaemonSet +apiVersion: extensions/v1beta1 +metadata: + name: kube-multus-ds-amd64 + namespace: kube-system + labels: + tier: node + app: multus +spec: + template: + metadata: + labels: + tier: node + app: multus + spec: + hostNetwork: true + nodeSelector: + beta.kubernetes.io/arch: amd64 + tolerations: + - key: node-role.kubernetes.io/master + operator: Exists + effect: NoSchedule + serviceAccountName: multus + containers: + - name: kube-multus + image: {{ multus_image_repo }}:{{ multus_image_tag }} + command: ["/entrypoint.sh"] + args: + - "--cni-conf-dir={{ multus_cni_conf_dir }}" + - "--cni-bin-dir={{ multus_cni_bin_dir }}" + - "--multus-conf-file={{ multus_conf_file }}" + - "--multus-kubeconfig-file-host={{ multus_kubeconfig_file_host }}" + resources: + requests: + cpu: "100m" + memory: "50Mi" + limits: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: true + volumeMounts: + - name: cni + mountPath: {{ multus_cni_conf_dir }} + - name: cnibin + mountPath: {{ multus_cni_bin_dir }} + volumes: + - name: cni + hostPath: + path: {{ multus_cni_conf_dir_host }} + - name: cnibin + hostPath: + path: {{ multus_cni_bin_dir_host }} diff --git a/tests/files/gce_centos7-multus-calico.yml b/tests/files/gce_centos7-multus-calico.yml new file mode 100644 index 000000000..57615e285 --- /dev/null +++ b/tests/files/gce_centos7-multus-calico.yml @@ -0,0 +1,12 @@ +# Instance settings +cloud_image_family: centos-7 +cloud_region: us-central1-c +cloud_machine_type: "n1-standard-1" +mode: default + +# Deployment settings +kube_network_plugin_multus: true +kube_network_plugin: calico +deploy_netchecker: true +kubedns_min_replicas: 1 +cloud_provider: gce diff --git a/tests/testcases/040_check-network-adv.yml b/tests/testcases/040_check-network-adv.yml index 709e8d8b4..819a7a485 100644 --- a/tests/testcases/040_check-network-adv.yml +++ b/tests/testcases/040_check-network-adv.yml @@ -80,3 +80,68 @@ run_once: true when: - agents.content == '{}' + + - name: Create macvlan network conf + # We cannot use only shell: below because Ansible will render the text + # with leading spaces, which means the shell will never find the string + # EOF at the beginning of a line. We can avoid Ansible's unhelpful + # heuristics by using the cmd parameter like this: + shell: + cmd: | + cat <<EOF | {{ bin_dir }}/kubectl create -f - + apiVersion: "k8s.cni.cncf.io/v1" + kind: NetworkAttachmentDefinition + metadata: + name: macvlan-conf + spec: + config: '{ + "cniVersion": "0.3.0", + "type": "macvlan", + "master": "eth0", + "mode": "bridge", + "ipam": { + "type": "host-local", + "subnet": "192.168.1.0/24", + "rangeStart": "192.168.1.200", + "rangeEnd": "192.168.1.216", + "routes": [ + { "dst": "0.0.0.0/0" } + ], + "gateway": "192.168.1.1" + } + }' + EOF + when: + - kube_network_plugin_multus|default(false) + + - name: Annotate pod with macvlan network + # We cannot use only shell: below because Ansible will render the text + # with leading spaces, which means the shell will never find the string + # EOF at the beginning of a line. We can avoid Ansible's unhelpful + # heuristics by using the cmd parameter like this: + shell: + cmd: | + cat <<EOF | {{ bin_dir }}/kubectl create -f - + apiVersion: v1 + kind: Pod + metadata: + name: samplepod + annotations: + k8s.v1.cni.cncf.io/networks: macvlan-conf + spec: + containers: + - name: samplepod + command: ["/bin/bash", "-c", "sleep 2000000000000"] + image: dougbtv/centos-network + EOF + when: + - kube_network_plugin_multus|default(false) + + - name: Check secondary macvlan interface + shell: "{{ bin_dir }}/kubectl exec samplepod -- ip addr show dev net1" + register: output + until: output.rc == 0 + retries: 90 + changed_when: false + when: + - kube_network_plugin_multus|default(false) -- GitLab