Предисловие

Добрый времени всем. Меня зовут Минисламов Руслан. В этой статье хотел бы рассказать про замечательный CNI — Cilium.

  1. Вышла версия 1.20.0, вот ссылка на github.

  2. Поделится опытом смены Pool IP для Service и Pods в работающем кластере.

Вступление. Почему CNI — это фундамент, а Cilium — это алмаз в этом сегменте среди остальных CNI.

Без CNI Kubernetes‑кластер нежизнеспособен. Эта прослойка отвечает за всё: от выдачи IP‑адресов (IPAM) и маршрутизации до межсетевых экранов и политик доступа. На рынке давно обосновались Calico и Flannel, есть и нишевые решения вроде Multus, но сегодня речь пойдет про Cilium.

Перечислю ключевые аргументы.

Cilium — это Open Source. Изначально проект развивала компания Isovalent, а позже ее поглотил Cisco — гигант с колоссальной экспертизой в сетях. Сотрудничество инженеров стартапа и ветеранов индустрии дает уверенность в зрелости кода.

Экономия на компонентах. Вам не нужен отдельный MetalLB например балансировщик: L2-балансировка встроена прямо в Cilium. А L7-политики безопасности через CiliumNetworkPolicy настраиваются в пару кликов с помощью удобного веб‑генератора. Меньше сторонних сервисов — проще поддержка.

Наблюдаемость без слепых зон. Инструмент Hubble дает позволяет наблюдать за трафиком внутри кластера. В реальном времени вы видите, какие пакеты forwarded, а какие dropped — и всё это через любимый CLI или веб‑интерфейс, с фильтрацией стандартными утилитами Linux. В крайнем случае можно одним скриптом отключить политики.

Главный козырь — eBPF. Cilium работает на уровне ядра, а не в пользовательском пространстве. Это не просто скорость, это новые сценарии. Взять хотя бы продукт Tetragon от Isovalent: он перехватывает попытки контейнера открыть файлы и мгновенно убивает подозрительный процесс, логируя причину.

Бонусом идут Service Mesh, Cluster Mesh с mTLS‑шифрованием и канареечная балансировка трафика через Gateway API.

Вот фрагмент с Release Announcement с описанием новых возможностей упоминающихся в этой статье, основной мигрирование на Multi‑Pool и второй поддержка Gateway API 1.6.1, до этого была 1.4.1.

Migrate to Multi-Pool
Migrate to Multi‑Pool
Support Gateway API v1.6.1
Support Gateway API v1.6.1

Описание проблемы и подготовка к миграции.

У меня возникла проблема, при инициализации кластера я допустил досадный просчёт: зарезервировал для Cilium подсеть, которая частично пересекается с адресным пространством локальной сети (ЛВС). Cilium воспринимает объявленный при установке CIDR как свою вотчину и безоговорочно отвечает на broadcast‑запросы, но эти ответы не достигают адресатов. Итог — классический сетевой конфликт, требующий немедленного вмешательства.

Архитектура стенда:

  • Kubernetes v1.35.0 

  • CNI: Cilium v1.19.4 

  • CD: ArgoCD

  • Текущий IP Pool: 172.17.0.0/17 у Pods и 172.17.128.0/17

Посмотрев сначала информацию в интернете приготовился к долгим работам и начал уже тестировать переезд, нашел пример. Так как смена IP для Pods, а особенно для Service это дорогое удовольствие, простои и самое главное время + внимание. Решил в один из дней зайти на github и был приятно удивлен.

Сначала заметил что в обновлении указано Gateway API 1.6.1. и обрадовался, так как давно лежат идеи которые хочу реализовать с этой версией в тесте по маршрутизации трафика на L4 уровне с протоколами TCP и UDP, но на этом я остановил чтение и закрыл вкладку, на следующий день вернулся снова и обратил внимание на 3 абзац с IPAM и это был подарок для меня на НГ. Так как по описанию это то что мне нужно, немедленно присступил читать.

Как обычно открываем документацию, переписываем сразу новые атрибуты себе и тестируем.

--set ipam.mode=multi-pool \
--set ipam.operator.autoCreateCiliumPodIPPools.default.ipv4.cidrs='{10.0.0.0/8}' \
--set ipam.operator.autoCreateCiliumPodIPPools.default.ipv4.maskSize=24 \
--set-string extraConfig.enable-cluster-pool-to-multi-pool-migration=true \
--set operator.rollOutPods=true \
--set rollOutCiliumPods=false

cat <<EOF | kubectl apply -f -
apiVersion: cilium.io/v2alpha1
kind: CiliumPodIPPool
metadata:
  name: default
spec:
  ipv4:
    cidrs:
    - 172.24.0.0/20
    maskSize: 24
EOF

В команде мы указали новый Pool на который переезжаем {172.24.0.0/20} с максой подсети 24 на каждую worker node, можно конечно править под себя, если считаете что 254 ip на одной машине мало для Вас. Главные команды, это запрет на перезапуск ( rollOutPods ) Cilium агента, оператора и envoy, если их не активировать, ваши контейнеры перезапустятся, так же не менее важен и CRD CiliumPodIPPool, без этого файла cilium агент не запустится. Рекомендую применить заранее, до обновления на версию 1.20.0, встречал когда Агент не сразу его подхватывает.

Что я сделал чтобы еще контролировать процесс обновления в тех окно, это прописал для CoreDNS стандартную сущность в k8s PodDisruptionBudget, чтобы контейнеры перезапускались последовательно.

Так же я прописал новый диапозон, но сохранив пока старый.

ipam:
  mode: multi-pool
  operator:
    clusterPoolIPv4PodCIDRList:
      - "172.17.0.0/17"
      - "10.255.128.0/22"
    clusterPoolIPv4ServiceCIDRList:
      - "172.17.128.0/17"
      - "172.24.0.0/20"

На что стоит еще обращать внимание при проектировании своей сети, для масок от /16 до /24 шаг в третьем октете вычисляется как 2^(24 - N)

У нас маска /20. Подставляем в формулу:
2^(24 - 20) = 2^4 = 16.

Cilium берёт большой пул /20 и нарезает его на кусочки по /24 для каждой ноды. В калькуляторе подсетей считаем сколько и если у Вас как и у меня кластера объединены в Cluster Mesh и будет понимание какой диапозон ip будет у следующего кластера.

Приступаем к работам. Смена Pool для Pods.
Подготовительные действия:

  • установка необходимого ПО (crictl так как у меня cri‑o и etcdct).

  • бэкап etcd и каталога /etc/kubernetes.

Выполняем обновление с 1.19.4 на версию 1.20.0 с новыми параметрами используя Helm. Можно использовать и cilium‑cli, но под капотом он использует тот же Helm.

helm upgrade --install cilium cilium/cilium -n kube-system --version=1.20.0 -f cilium-values.yaml

Далее как показано в документации точечно перезапускаем Cilium Agent чтобы он выполнил работу, применил параметры, на что смотрим в этот момент, у нас сразу перезапустится на той Nodes где произвел работу Cilium Agent и затем рестартовать CoreDNS, так как у меня еще и ArgoCD я выполнял рестарт командой и его контейнеров, поскольку argocd‑repo‑server активно работает с гитлабом.

kubectl rollout restart deployment,statefulset -n argocd

Команда просто перезапустит компоненты и считает настройки CoreDNS новые, если на машине не было никаких работ, ничего страшного, ArgoCD стартует быстро, недоступность в пределах 15 секунд не проблема.

Что может пойти не так с остальными контейнерами которые крутятся на worker где Cilium Agent отработал, контейнеры которые активно с кем то во внешней сети общаются им потребуется перезапуск чтобы так же подхватить новые IP CoreDNS. Какие механики я выполнял опишу кратко и с чем столкнулся еще при обновлении:

Привязывал Pods к конкретной Worker Nodes где нет работ заранее. На Nodes оставались только контейнеры которые по некоторым причинам завязаны и не могут быть перемещены, например работа с Рутокеном.

  • По каким то причинам у меня отвалился NodeLocalDNS, даже после рестарта контейнеров были маленькие глюки в работе, пришлось пока отказаться и вернуться на CoreDNS и лишний раз перезапускать контейнеры чтобы они подхватили Endpoints пока нет желания. После переезда просто отваливается интерфейст с ip 169.254.20.10 и до него не могут достучаться контейнеры, возможно поможет полная переустановка, стоит это учитывать.

  • По моему мнению, обновление стало легче и более контролируемо, закладываем на работу Cilium агента 1 минуту, рестарт CoreDNS 1 минуту и затем на рестарт контейнеров разом так же 1–2 минуты. Постепенно на каждой Worker Nodes обновляем.

Главный вызов: Смена Pool IP для Service.

После манипуляций выше будем считать что все контейнеры у Вас получили IP из нового диапозона, и все работает, но у меня еще остались Service у которых так же надо изменить pool, поскольку до этого как вы помните мы указали по 2 пула для Pods и Service при обновлении Cilium на версию 1.20.0, Сейчас у меня Service получают IP в этом сегменте 172.17.128.0/17, а будут в этом 172.24.0.0/20. У нас есть возможность затянуть работы и сделать новый servicecidr, но на данный момент нельзя контролировать чтобы тот или иной service получал IP из конкретного servicecidr.

Как еще можно подстраховатся:

  1. Cilium отлично работает с Gateway Api в котором можно реализовать канареечную балансировку трафика, все ставится очень быстро по документации, создали HTTPRoute и используя веса (weight) направляете трафик на servicev2 например который привязан к тому же deployments что и servicev1, вы создадите отдельный service уже после, у него будет новый IP. Но если у Вас сотни контейнеров тогда создавать, а затем еще чистить это лишняя работа.

  2. Бэкап etcd для отката.

Для работ по смене IP к service лучше написать скрипт или Ansible Playbook, так как надо параллельно выполнить следующее на всех машинах в кластере. На мастерах меняем в файлах
/var/lib/kubelet/config.yaml /etc/kubernetes/manifests/kube‑controller‑manager.yaml /etc/kubernetes/manifests/kube‑apiserver.yaml ( не забудьте сделать бэкап файлов ).

Указав новый диапозон ip адресов, после правки этих файлов kube‑controller‑manager.yaml kube‑apiserver.yaml у нас перезапустятся контейнеры кубера соответствующих сервисов и какое то время к кластеру подключиться не получится, можно минимизировать простой, если service заранее были с типом NodePort и можно перенаправлять трафик на конкретный порт минуя мастер сразу на Worker Nodes.

Так же на всех master и worker nodes необходимо поменять в файле /var/lib/kubelet/config.yaml так же ip, надо очистить в etcd /registry/servicecidrs/kubernetes и /registry/servicecidrs/, а затем сразу перезапустить service kubelet на всех машинах, и моментально на одном мастер сервере создать ServiceCIDR и применить. Почему не заменить просто ServiceCIDR, даже удалив его вы примените новый манифест с параметрами, но CIlium все равно будет подтягивать старый, тут происходит постоянная гонка.

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: ServiceCIDR
metadata:
  name: kubernetes
spec:
  cidrs:
  - 172.24.0.0/20
EOF

Теперь мы можем неспеша переносить service удаляя старые и ArgoCD моментально сделает с новым IP service или если вы решили использовать канареечную версию переносить на новые сервисы трафик, а старое удалять. На что обращаем отдельное внимание, после того как например удалите и создадите новый service для CoreDNS, его снова надо будет перезапускать и контейнеры которые на этой worker nodes, от этого кратковременного обрыва никуда не уйти, он самый длинный будет, так как лучше перезапустить все, чем отдельно придут жаловаться. Я перезапускал CoreDNS в самую последнюю очередь, чтобы в последний ребут еще и перезапустить контейнеры.

По итогу, кластер полностью перенесен на новый Pool, и пока тестирую его работу, но по предварительным тестам все прошло корректно, не забываем в конце снова обновить cilium удалив упоминание о старой подсети, мало ли какие могут быть сюрпризы.

Что осталось и с чем не смог справится. У нас в ns default есть service kubernetes, если пересоздать Service и даже поиграться с Endpoint, например при рестарте CoreDNS не сможет подключиться к кластеру и будет ругаться на плагин kubernetes, сейчас кластер главное работает. я предполагаю, надо снова править запись в etcd, а именно /registry/services/specs/default/kubernetes, но это возможно очередные рестарты и новые тесты.

Итог

Смена Pool IP адресов стала намного удобнее, при наличии автоматизации можно будет успеть обновиться неспеша в тех окно за несколько шагов разделив этапы и практически незаметно для конечного пользователя. И стоит помнить multi‑pool режим пока конечная точка, откатиться обратно не получится, поэтому придется тестировать подойдет вам или нет этот вариант.

Надеюсь вам понравится работа с Cilium, так как инструмент мощный и требует чтения, есть огромное кол‑во атрибутов, документация всегда в актуальном состоянии, даже на версии 1.20.0 уже были указаны все параметры, по мне это говорит о многом.

Комментарии (0)