Демистификация использования плагинов CNI

При настройке кластера Kubernetes установка сетевого плагина является обязательной для работы кластера. Чтобы не усложнять задачу, роль сетевого плагина заключается в настройке сетевого подключения, чтобы модули, работающие на разных узлах кластера, могли взаимодействовать друг с другом. В зависимости от плагина могут быть предоставлены различные сетевые решения: оверлей (vxlan, IP-in-IP) или без оверлея.

Чтобы упростить использование сетевого плагина, Kubernetes предоставляет контейнерный сетевой интерфейс (он же CNI), поэтому можно использовать любой сетевой плагин, реализующий этот интерфейс.

Kubernetes также позволяет использовать kubenet, сетевой плагин, не относящийся к CNI. Это базовый вариант с ограниченным набором функций.

Если мы используем управляемый кластер (их много: Amazon EKS, Google GKE, DigitalOcean DOKS, OVH Managed Kubernetes, Scaleway Kapsule), сетевой плагин CNI уже выбран из числа многие существующие решения и установлены для нас. Но если мы устанавливаем собственный кластер, нам нужно выбрать и установить плагин вручную. Однако некоторые внешние инструменты, такие как Rancher, действительно упрощают процесс.

В этой статье мы представим CNI и посмотрим, как устанавливается, настраивается и используется сетевой плагин. Мы будем следовать следующим шагам:

  • Краткое введение в контейнерный сетевой интерфейс (CNI)
  • Настройте кластер k0s с помощью плагина Calico
  • Настройте кластер k0s с помощью плагина Weave Net

CNI - Краткое введение

Что такое CNI?

Согласно официальному определению,

CNI (Container Network Interface), проект Cloud Native Computing Foundation, состоит из спецификации и библиотек для написания плагинов для настройки сетевых интерфейсов в контейнерах Linux, а также ряда поддерживаемых плагинов. CNI занимается только подключением контейнеров к сети и удалением выделенных ресурсов при удалении контейнера ».

CNI - это спецификация и некоторые справочные реализации этой спецификации.

Спецификация CNI в настоящее время находится в версии 0.4.0 (январь 2021 г.) и доступна в официальном репозитории GitHub.

Плагин CNI (= сетевой плагин, соответствующий спецификации CNI) в основном отвечает за:

  • вставка сетевого интерфейса в контейнер
  • назначить IP этому интерфейсу
  • удаление этого интерфейса при удалении контейнера

Под капотом плагин представляет собой исполняемый файл, который должен реализовывать несколько методов (в будущих версиях могут быть добавлены другие):

  • ДОБАВИТЬ: добавить контейнер в сеть
  • DEL: удалить контейнер из сети
  • ПРОВЕРИТЬ: убедитесь, что сеть контейнера работает должным образом
  • ВЕРСИЯ: укажите текущую версию

Некоторые эталонные реализации

Команда сетевых контейнеров также поддерживает набор эталонных реализаций, разделенных на несколько категорий:

  • основные плагины для создания нескольких типов сетевых интерфейсов
  • Плагины IPAM, предназначенные для выделения IP-адресов
  • более специфические плагины

Некоторые из них используются другими плагинами сети CNI.

Сторонние сетевые плагины, реализующие спецификацию CNI

Помимо эталонных реализаций, существует несколько сторонних подключаемых модулей CNI. Среди самых известных:

Более полный список доступен в документации Kubernetes.

Один из этих сторонних плагинов необходимо установить в каждом кластере.

У каждого плагина свой набор функций. Иногда это затрудняет выбор в зависимости от требований к рабочей нагрузке (использование сетевых политик Kubernetes, шифрование плоскости данных, низкая задержка и т. Д.). Однако эта статья о тестировании CNI от Alexis Ducastel определенно может помочь вам в процессе выбора.

Как устанавливается и используется плагин?

Обычно сетевой плагин CNI запускается как DaemonSet, то есть Pod, гарантирующий, что сетевая конфигурация будет работать на каждом узле кластера. При установке подключаемого модуля по умолчанию в /etc/cni/net.d создается определенный файл конфигурации сети. Этот файл конфигурации должен соответствовать спецификации CNI, и любые подключаемые модули CNI, упомянутые в этом файле, должны находиться в / opt / cni / bin. Kubelet отвечает за чтение файла конфигурации CNI и вызов двоичных файлов плагина указанным способом, чтобы он мог настроить сеть для каждого модуля.

В следующей части мы создадим одноузловой кластер Kubernetes и установим на него плагин Calico CNI.

Запуск k0s с Calico

В предыдущей статье мы представили k0s, новый дистрибутив Kubernetes, разработанный Mirantis. В этой статье мы будем использовать k0s, чтобы проиллюстрировать использование подключаемых модулей CNI. Если вы еще не знакомы с k0s, я рекомендую быстро просмотреть эту статью, чтобы вы могли начать играть с этим замечательным дистрибутивом Kubernetes.

В этой первой части мы запустим одноузловой кластер Kubernetes на основе k0s. Мы будем использовать конфигурацию по умолчанию, поэтому сетевой плагин Calico устанавливается автоматически.

Сначала мы создаем виртуальную машину Ubuntu. В этом примере мы используем Multipass с конфигурацией по умолчанию (1 CPU, 1G RAM, 5G HDD). Если вы следуете этому руководству, вы можете создать виртуальную машину, используя любое другое решение (локально с помощью Vagrant, в инфраструктуре облачного провайдера и т. Д.)

$ multipass launch -n node1

Далее мы получаем последнюю версию бинарного файла k0s.

$ curl -sSLf get.k0s.sh | sudo sh

Затем мы запускаем кластер с одним узлом, убедившись, что узел действует как мастер и как рабочий одновременно:

$ sudo k0s server --enable-worker

Поскольку мы не указали какой-либо файл конфигурации во время работы сервера, используется конфигурация по умолчанию. Если нам нужно знать используемые параметры по умолчанию, мы могли бы использовать следующую команду, чтобы перечислить их все:

$ k0s default-config

Затем мы настраиваем наш локальный kubectl. Как мы уже делали несколько раз в предыдущих статьях, нам нужно получить файл kubeconfig, сгенерированный k0s, изменить IP-адрес, чтобы он использовал внешний IP-адрес виртуальной машины, и предоставить переменную среды, учитываемую kubectl:

# Get kubeconfig file
multipass exec node1 sudo cat /var/lib/k0s/pki/admin.conf > k0s.cfg
# Replace IP address
NODE_IP=$(multipass info node1 | grep IP | awk '{print $2}')
sed -i '' "s/localhost/$NODE_IP/" k0s.cfg
# Configure local kubectl
export KUBECONFIG=$PWD/k0s.cfg

Затем мы можем проверить состояние отдельного узла кластера.

$ kubectl get no
NAME    STATUS   ROLES    AGE    VERSION
node1   Ready    <none>   101m   v1.20.1-k0s1

Узел находится в состоянии «Готов», что означает, что все работает нормально: сетевой плагин Calico был правильно настроен и установлен.

Теперь мы более подробно рассмотрим сетевой плагин. На каждом узле кластера (только один узел в нашем примере) папка /etc/cni/net.d содержит файл, описывающий конфигурацию сети. Как мы видим ниже, существует также kubeconfig файл, который позволяет процессам calico взаимодействовать с сервером API.

# ls /etc/cni/net.d/
10-calico.conflist  calico-kubeconfig

Если мы посмотрим на файл .conflist, мы увидим, что он ссылается на несколько плагинов:

# cat /etc/cni/net.d/10-calico.conflist
{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "log_file_path": "/var/log/calico/cni/cni.log",
      "datastore_type": "kubernetes",
      "nodename": "node1",
      "mtu": 1450,
      "ipam": {
          "type": "calico-ipam"
      },
      "policy": {
          "type": "k8s"
      },
      "kubernetes": {
          "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "snat": true,
      "capabilities": {"portMappings": true}
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

Среди этих плагинов есть две реализации, которые можно найти в официальном репозитории GitHub CNI: https://github.com/containernetworking/plugins

  • portmap перенаправляет трафик от одного или нескольких портов хоста к контейнеру (подробная информация в документации https://www.cni.dev/plugins/meta/portmap/)
  • bandwidth позволяет использовать и настраивать подсистему управления трафиком (TC) Linux (подробная информация в документации https://www.cni.dev/plugins/meta/bandwidth/)

Два дополнительных плагина взяты из проекта Calico: https://github.com/projectcalico/cni-plugin

  • calico - плагин верхнего уровня Calico CNI.
  • calico-ipam определяет, как IP-адреса выделяются модулям в кластере.

Этот файл в основном определяет конфигурацию сети и сообщает kubelet (процессу, который читает эту конфигурацию), какие плагины нужно вызывать и как они должны вызываться. Плагины в списке plugins вызываются последовательно.

По умолчанию kubelet обращается к этим плагинам из папки /opt/cni/bin. Перечисляя плагины, доступные на нашем единственном узле, некоторые из них не упоминаются в файле конфигурации выше (фланель, локальный хост, настройка,…). Эти плагины также поставляются Calico (как и карта портов и полоса пропускания), поскольку они могут использоваться, если этого требует другой файл конфигурации.

# ls /opt/cni/bin/
bandwidth
calico
calico-ipam
flannel
host-local
install
loopback
portmap
tuning

Что касается всех сетевых плагинов Kubernetes, Calico развертывается как DaemonSet в кластере. Перечисленные выше сетевые подключаемые модули CNI устанавливаются initContainer в этом DaemonSet. Спасибо, Юсси Нуммелин за указание на это.

Примечание. Перед установкой Calico на узлах нет директорий /etc/cni/net.d и /opt/cni/bin.

Благодаря сетевой конфигурации kubelet знает, какие плагины CNI нужно использовать, и где их найти. Каждый раз, когда запускается новый Pod, kubelet вызывает специальные плагины, чтобы присоединить сетевой интерфейс к Pod и дать ему IP-адрес. Когда под удаляется, kubelet вызывает плагин, чтобы освободить IP-адрес и удалить сетевой интерфейс для этого пода.

В следующей части мы снова будем использовать k0s для создания одноузлового кластера Kubernetes, но на этот раз мы установим сетевой плагин Weave Net CNI.

Запуск k0s с Weave Net

В этой части мы выполняем те же шаги, что и выше: мы начинаем с создания новой виртуальной машины, называемой node2, и получаем для нее последнюю версию k0s.

Затем и перед запуском кластера k0s с одним узлом мы создаем файл конфигурации, чтобы сообщить k0s не устанавливать Calico, поскольку мы установим плагин Weave Net на более позднем этапе. Мы устанавливаем значение custom в свойстве .spec.network.provider и сохраняем этот файл конфигурации как k0s.yaml.

apiVersion: k0s.k0sproject.io/v1beta1
kind: Cluster
metadata:
  name: k0s
spec:
  api:
    address: 192.168.64.12
    sans:
    - 192.168.64.12
  network:
    provider: custom

Примечание. 192.168.64.12 - это IP-адрес виртуальной машины, созданной с помощью Multipass.

k0s автоматически использует файл конфигурации с именем k0s.yaml, если он его находит, поэтому нам не нужно указывать его при запуске кластера в командной строке.

$ sudo k0s server --enable-worker

Затем мы проверяем состояние отдельного узла кластера:

$ kubectl get no
NAME    STATUS     ROLES    AGE   VERSION
node1   NotReady   <none>   18s   v1.20.1-k0s1

Узел отображается как NotReady, поскольку сетевые подключаемые модули CNI еще не установлены. Мы можем получить более подробное сообщение об ошибке в описании узла:

$ kubectl describe node node2
...
Conditions:
    Ready            False   Fri, 08 Jan 2021 14:05:17 +0100   Fri, 08 Jan 2021 14:04:47 +0100   KubeletNotReady              runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
...

Чтобы решить эту проблему, мы установим сетевой плагин. В этом примере мы выбрали Weave Net. Из документации установка выполняется с помощью следующей команды:

  • создает ServiceAccount
  • дает ему некоторые права за счет использования _17 _ / _ 18_ и _19 _ / _ 20_
  • запускает Weave Net Pods как DaemonSet
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created
daemonset.apps/weave-net created

При установке Weave Net автоматически создается файл конфигурации в файловой системе хоста in /etc/cni/net.d.

# find /etc/cni/
/etc/cni/
/etc/cni/net.d
/etc/cni/net.d/10-weave.conflist

Файл /etc/cni/net.d/10-weave.conflist определяет конфигурацию сетевых плагинов Weave Net. Если присмотреться, мы увидим, что он ссылается на несколько плагинов:

# cat /etc/cni/net.d/10-weave.conflist
{
    "cniVersion": "0.3.0",
    "name": "weave",
    "plugins": [
        {
            "name": "weave",
            "type": "weave-net",
            "hairpinMode": true
        },
        {
            "type": "portmap",
            "capabilities": {"portMappings": true},
            "snat": true
        }
    ]
}
  • weave-net - плагин верхнего уровня Weave Net CNI.
  • portmap перенаправляет трафик от одного или нескольких портов хоста к контейнеру (подробная информация в документации https://www.cni.dev/plugins/meta/portmap/)

Установка Weave Net DaemonSet также создает некоторые двоичные файлы CNI в /opt/cni/bin файловой системы хоста:

# find /opt/cni/
/opt/cni/
/opt/cni/bin
/opt/cni/bin/weave-plugin-2.7.0
/opt/cni/bin/weave-net
/opt/cni/bin/weave-ipam

Примечание. weave-net и weave-ipam - две символические ссылки, указывающие на weave-plugin-2.7.0.

Кажется, что отсутствуют некоторые двоичные файлы - например, portmap, на который есть ссылка в файле conflist, - но есть и другие, которые используются косвенно (например, loopback). В результате все модули, не относящиеся к DaemonSet, по-прежнему Pending.

$ kubectl get po -A
NAMESPACE     NAME                     READY STATUS  RESTARTS   AGE
kube-system   coredns-5c98d7d4d8-5pxs8 1/1   Pending 0          2m
kube-system   konnectivity-agent-gghlv 1/1   Pending 0          1m
kube-system   kube-proxy-557gw         1/1   Running 0          2m
kube-system   metrics-server-...       1/1   Pending 0          2m

Если мы более внимательно рассмотрим pod dns, мы поймем причину этой проблемы: плагин loopback отсутствует.

$ kubectl describe po -l k8s-app=kube-dns -n kube-system
...
Warning  FailedCreatePodSandBox  8s (x11 over 2m17s)   kubelet            (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "fcbf107aea021ba123c25ee746dbb59f37a3bc9687ac49287295ec805edc80f4": failed to find plugin "loopback" in path [/opt/cni/bin]

Я до сих пор не уверен, кто должен нести ответственность за доставку плагинов, необходимых Weave Net для правильной работы. Должно ли это быть частью шага инициализации при запуске Weave Net DaemonSet (как это делается при установке Calico) или предварительным условием, которое должен гарантировать k0s (как это делается kubeadm)? Я бы сказал, что первый вариант более логичен.

Между тем, мы можем легко это исправить, загрузив плагины из официального репозитория GitHub и распаковав их в /opt/cni/bin.



Примечание. В этом примере мы копируем все плагины, даже если некоторые из них не обязательно используются Weave Net.

# Get cni plugins
root@node2:~# curl -sSL -o cni.tgz https://github.com/containernetworking/plugins/releases/download/v0.9.0/cni-plugins-linux-amd64-v0.9.0.tgz
# Extract the plugins into the /opt/cni/bin folder
root@node2:~# tar xvf cni.tgz -C /opt/cni/bin/

Через пару секунд мы можем убедиться, что модули работают правильно.

$ kubectl get po -A
NAMESPACE     NAME                     READY STATUS  RESTARTS   AGE
kube-system   coredns-5c98d7d4d8-5pxs8 1/1   Running 0          6m
kube-system   konnectivity-agent-gghlv 1/1   Running 0          5m
kube-system   kube-proxy-557gw         1/1   Running 0          6m
kube-system   metrics-server-...       1/1   Running 0          6m
kube-system   weave-net-pw9mz          2/2   Running 0          1m

Заключение

Я надеюсь, что примеры, приведенные в этой статье, помогут прояснить, что такое плагины CNI. Не вдаваясь в подробности того, как реализован плагин (существует так много реализаций), я думаю, что важно увидеть, как эти плагины поставляются, как конфигурация предоставляется kubelet и как они вызываются.