ping6.net

IPv6 в Docker и Kubernetes: Руководство по сетям контейнеров

Настройка IPv6 для контейнерных приложений. Рассматриваются параметры демона Docker, dual-stack в Kubernetes, плагины CNI и публикация сервисов.

ping6.net14 декабря 2024 г.10 min read
IPv6DockerKubernetescontainersnetworkingDevOps

Сети контейнеров по умолчанию используют IPv4. Это проблема, если вы эксплуатируете production-нагрузки в 2024 году.

TL;DR - Краткое резюме

Ключевые моменты:

  • Docker и Kubernetes по умолчанию используют только IPv4; IPv6 требует явной настройки
  • Docker требует изменений daemon.json и пользовательских сетей с флагом --ipv6
  • Kubernetes 1.23+ поддерживает стабильный dual-stack с IPv6 для pod и сервисов
  • CNI-плагины (Calico, Cilium, Flannel) обрабатывают dual-stack по-разному

Перейти к: Настройка Docker | Dual-Stack Kubernetes | CNI-плагины | Тестирование


Зачем IPv6 в контейнерах#

Ваши контейнеры могут использовать только IPv4, в то время как остальной интернет переходит на IPv6. Мобильные сети, провайдеры и облачные платформы приоритизируют IPv6. Если ваши контейнерные сервисы не поддерживают IPv6, вы добавляете задержку через шлюзы NAT64 или, что еще хуже — теряете клиентов.

Платформы оркестрации контейнеров (Docker, Kubernetes, ECS, Nomad) были созданы в эпоху IPv4. Поддержка IPv6 появилась позже как дополнение. Настройки по умолчанию все еще предполагают использование только IPv4.

Включение IPv6 не сложно, но требует явной настройки на нескольких уровнях: демон, сеть, контейнер и сервис. Пропустите один уровень — и получите частичную связь, которая ломается непредсказуемо.

Настройка IPv6 в Docker#

Демон Docker не включает IPv6 по умолчанию. Нужно настроить его в /etc/docker/daemon.json.

Настройка демона#

Создайте или измените /etc/docker/daemon.json:

{
  "ipv6": true,
  "fixed-cidr-v6": "fd00::/80",
  "experimental": false,
  "ip6tables": true
}

Разберем это:

  • "ipv6": true включает поддержку IPv6
  • "fixed-cidr-v6" задает подсеть для контейнеров (используйте префикс ULA или GUA)
  • "ip6tables": true включает правила IPv6-файрвола (Docker 20.10+)

Для production с глобально маршрутизируемыми адресами используйте префикс вашего провайдера:

{
  "ipv6": true,
  "fixed-cidr-v6": "2001:db8:1234::/64"
}

Перезапустите Docker для применения изменений:

sudo systemctl restart docker

Проверьте, что IPv6 включен:

docker network inspect bridge | grep IPv6

Вы должны увидеть "EnableIPv6": true.

Сеть bridge по умолчанию#

Сеть bridge по умолчанию не получает IPv6 автоматически даже после настройки демона. Создайте пользовательскую сеть:

docker network create --ipv6 \
  --subnet=172.20.0.0/16 \
  --subnet=fd00:dead:beef::/48 \
  mynetwork

Запустите контейнеры в этой сети:

docker run -d --network mynetwork nginx

Контейнеры теперь получают как IPv4, так и IPv6 адреса.

Пользовательские сети#

Пользовательские bridge-сети поддерживают dual-stack:

docker network create --ipv6 \
  --subnet=10.1.0.0/24 \
  --gateway=10.1.0.1 \
  --subnet=fd00:cafe::/64 \
  --gateway=fd00:cafe::1 \
  appnetwork

Контейнеры в этой сети могут общаться через IPv6:

# Терминал 1
docker run -it --rm --network appnetwork --name container1 alpine sh
 
# Терминал 2
docker run -it --rm --network appnetwork alpine sh
ping6 container1

Встроенный DNS-резолвер Docker возвращает записи AAAA для имен контейнеров, когда включен IPv6.

NAT и маршрутизация IPv6#

По умолчанию Docker использует NAT для IPv4, но может не использовать NAT для IPv6. Это зависит от вашей конфигурации fixed-cidr-v6.

С префиксами ULA (fd00::/8) вам нужен NAT для доступа в интернет:

# Включите переадресацию IPv6
sudo sysctl -w net.ipv6.conf.all.forwarding=1
 
# Добавьте правило masquerade
sudo ip6tables -t nat -A POSTROUTING -s fd00::/80 ! -o docker0 -j MASQUERADE

С префиксами GUA (глобально маршрутизируемыми) маршрутизируйте напрямую без NAT:

# Добавьте маршрут для подсети контейнеров
sudo ip -6 route add 2001:db8:1234::/64 via <docker-host-ipv6>

Настройте вышестоящий роутер для маршрутизации вашей подсети контейнеров к хосту Docker.

Docker Compose с IPv6#

Docker Compose требует явной настройки IPv6 в определении сети.

Пример docker-compose.yml:

version: '3.8'
 
services:
  web:
    image: nginx
    networks:
      - frontend
    ports:
      - "80:80"
      - "[::]:8080:80"  # Явная привязка IPv6
 
  app:
    image: myapp:latest
    networks:
      - frontend
      - backend
 
  db:
    image: postgres:14
    networks:
      - backend
    environment:
      POSTGRES_HOST_AUTH_METHOD: trust
 
networks:
  frontend:
    enable_ipv6: true
    ipam:
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1
        - subnet: fd00:1::/64
          gateway: fd00:1::1
 
  backend:
    enable_ipv6: true
    ipam:
      config:
        - subnet: 172.21.0.0/16
        - subnet: fd00:2::/64

Флаг enable_ipv6: true требуется для каждой сети. Конфигурация IPAM (IP Address Management) назначает как IPv4, так и IPv6 подсети.

Синтаксис привязки портов для IPv6:

ports:
  - "80:80"              # IPv4 и IPv6
  - "0.0.0.0:8080:80"    # Только IPv4
  - "[::]:8081:80"       # Только IPv6
  - "127.0.0.1:8082:80"  # IPv4 localhost
  - "[::1]:8083:80"      # IPv6 localhost

Запустите сервисы:

docker-compose up -d

Проверьте, что контейнеры имеют IPv6:

docker-compose exec web ip -6 addr show

Dual-Stack в Kubernetes#

Kubernetes поддерживает dual-stack сети начиная с версии 1.21 (beta) и 1.23 (stable).

Предварительные требования#

  1. Kubernetes 1.23 или новее
  2. Плагин CNI с поддержкой dual-stack (Calico, Cilium, Flannel, Weave)
  3. kube-proxy в режиме dual-stack
  4. Поддержка облачного провайдера (для сервисов LoadBalancer)

Включение Dual-Stack#

Для новых кластеров включите dual-stack при инициализации. С kubeadm:

# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
networking:
  podSubnet: "10.244.0.0/16,fd00:10:244::/56"
  serviceSubnet: "10.96.0.0/16,fd00:10:96::/112"

Инициализируйте кластер:

kubeadm init --config kubeadm-config.yaml

Для управляемого Kubernetes (EKS, GKE, AKS) включите dual-stack при создании кластера:

# EKS
eksctl create cluster \
  --name mycluster \
  --ip-family ipv4,ipv6
 
# GKE
gcloud container clusters create mycluster \
  --enable-ip-alias \
  --stack-type=IPV4_IPV6
 
# AKS
az aks create \
  --resource-group myResourceGroup \
  --name mycluster \
  --network-plugin azure \
  --ip-families IPv4,IPv6

Проверьте, что dual-stack включен:

kubectl get nodes -o jsonpath='{.items[*].spec.podCIDRs}'

Вы должны увидеть как IPv4, так и IPv6 CIDR.

Сети подов#

Поды автоматически получают адреса из обоих семейств. В большинстве случаев специальная настройка не требуется.

Пример пода:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: nginx
    image: nginx

Разверните и проверьте адреса:

kubectl apply -f pod.yaml
kubectl get pod test-pod -o jsonpath='{.status.podIPs}'

Вывод показывает как IPv4, так и IPv6:

[{"ip":"10.244.1.5"},{"ip":"fd00:10:244:1::5"}]

Приложения внутри подов должны привязываться к :: (все адреса) или конкретно 0.0.0.0:

# Пример Python — привязка к IPv4 и IPv6
import socket
 
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(("::", 8080))
sock.listen(5)

Или привязывайтесь к 0.0.0.0 для IPv4 и :: для IPv6 отдельно.

Настройка сервисов#

Сервисы могут быть только IPv4, только IPv6 или dual-stack. Управляйте этим с помощью полей ipFamilyPolicy и ipFamilies.

Dual-stack сервис (по умолчанию в dual-stack кластерах):

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ipFamilyPolicy: RequireDualStack
  ipFamilies:
    - IPv4
    - IPv6
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 8080

Сервис только IPv4:

apiVersion: v1
kind: Service
metadata:
  name: myservice-v4
spec:
  ipFamilyPolicy: SingleStack
  ipFamilies:
    - IPv4
  selector:
    app: myapp
  ports:
    - port: 80

Сервис только IPv6:

apiVersion: v1
kind: Service
metadata:
  name: myservice-v6
spec:
  ipFamilyPolicy: SingleStack
  ipFamilies:
    - IPv6
  selector:
    app: myapp
  ports:
    - port: 80

Проверьте IP сервисов:

kubectl get svc myservice -o jsonpath='{.spec.clusterIPs}'

Вывод:

["10.96.100.5","fd00:10:96::a5"]

Сервисы LoadBalancer#

Сервисы LoadBalancer создают облачные балансировщики с dual-stack интерфейсами (если облачный провайдер поддерживает).

apiVersion: v1
kind: Service
metadata:
  name: web-lb
spec:
  type: LoadBalancer
  ipFamilyPolicy: RequireDualStack
  ipFamilies:
    - IPv4
    - IPv6
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

Проверьте внешние IP:

kubectl get svc web-lb

Вывод:

NAME     TYPE           CLUSTER-IP      EXTERNAL-IP                        PORT(S)
web-lb   LoadBalancer   10.96.100.10    203.0.113.10,2001:db8:1234::10    80:30123/TCP

Не все облачные провайдеры пока поддерживают dual-stack балансировщики. Проверьте поддержку для вашей платформы.

Ingress-контроллеры#

Поддержка IPv6 в Ingress зависит от реализации контроллера.

Популярные контроллеры с поддержкой IPv6:

  • nginx-ingress: Поддерживает dual-stack, слушает на IPv4 и IPv6
  • Traefik: Полная поддержка dual-stack
  • HAProxy Ingress: Поддерживает IPv6
  • Contour: Поддерживает dual-stack

Настройка nginx-ingress для dual-stack:

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: LoadBalancer
  ipFamilyPolicy: RequireDualStack
  ipFamilies:
    - IPv4
    - IPv6
  selector:
    app.kubernetes.io/name: ingress-nginx
  ports:
    - name: http
      port: 80
      targetPort: http
    - name: https
      port: 443
      targetPort: https

Ресурсы Ingress не требуют специальной настройки IPv6 — они работают автоматически, если контроллер поддерживает его.

Сетевые политики#

Ресурсы NetworkPolicy поддерживают IPv6 CIDR-блоки:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ipv6
spec:
  podSelector:
    matchLabels:
      app: myapp
  ingress:
    - from:
      - ipBlock:
          cidr: 2001:db8::/32
      - ipBlock:
          cidr: fd00::/8
  egress:
    - to:
      - ipBlock:
          cidr: ::/0
          except:
            - fc00::/7  # Блокировать ULA

Правила IPv4 и IPv6 могут сосуществовать в одной политике.

Особенности плагинов CNI#

Разные плагины CNI обрабатывают dual-stack по-разному.

Calico#

Calico поддерживает dual-stack с IP-пулами:

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: ipv4-pool
spec:
  cidr: 10.244.0.0/16
  ipipMode: Never
  natOutgoing: true
  nodeSelector: all()
 
---
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: ipv6-pool
spec:
  cidr: fd00:10:244::/56
  natOutgoing: false
  nodeSelector: all()

Включите IPv6 в Calico:

kubectl set env daemonset/calico-node -n kube-system IP6=autodetect
kubectl set env daemonset/calico-node -n kube-system FELIX_IPV6SUPPORT=true

Cilium#

Cilium имеет нативную поддержку dual-stack. Включите при установке:

helm install cilium cilium/cilium \
  --namespace kube-system \
  --set ipv4.enabled=true \
  --set ipv6.enabled=true \
  --set tunnel=disabled \
  --set autoDirectNodeRoutes=true

Flannel#

Flannel требует режим dual-stack в конфигурации DaemonSet:

apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
data:
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "IPv6Network": "fd00:10:244::/56",
      "Backend": {
        "Type": "vxlan"
      }
    }

Weave#

Weave поддерживает dual-stack с обоими режимами IPAM:

kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')&env.IPALLOC_RANGE=10.244.0.0/16&env.IPALLOC_RANGE=fd00:10:244::/56"

Распространенные ошибки#

Привязка приложений#

Приложения должны явно слушать на IPv6-адресах. Привязка к 0.0.0.0 слушает только IPv4.

Неправильно:

server.bind(("0.0.0.0", 8080))  # Только IPv4

Правильно:

server.bind(("::", 8080))  # IPv6 (и IPv4 если IPV6_V6ONLY=0)

Многие языки по умолчанию используют только IPv4. Проверьте документацию вашего фреймворка.

DNS-резолвинг#

DNS-запросы в dual-stack окружениях возвращают как A, так и AAAA записи. Приложения должны пробовать оба, предпочитая IPv6.

Некоторые старые библиотеки запрашивают только A-записи. Обновите зависимости или явно настройте DNS-резолвинг.

Правила файрвола#

Файрволы контейнеров (iptables/ip6tables) требуют правил для обоих семейств. Docker и Kubernetes обрабатывают это автоматически при правильной настройке, но пользовательские правила могут блокировать IPv6.

Проверьте правила IPv6-файрвола:

sudo ip6tables -L -n -v

Внешняя связь#

Контейнерам с IPv6 нужна правильная маршрутизация к внешним сетям. Если у вашего хоста нет IPv6-связи, контейнеры тоже ее не получат.

Сначала проверьте IPv6 хоста:

ping6 google.com

Если это не работает, исправьте сеть хоста перед диагностикой контейнеров.

Headless-сервисы StatefulSet#

StatefulSet с headless-сервисами возвращают как IPv4, так и IPv6 адреса для DNS-имен подов:

nslookup web-0.myservice.default.svc.cluster.local

Приложения, подключающиеся к подам StatefulSet, должны корректно обрабатывать множественные адреса.

Тестирование Dual-Stack развертываний#

Разверните тестовое приложение:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
 
---
apiVersion: v1
kind: Service
metadata:
  name: test-svc
spec:
  type: LoadBalancer
  ipFamilyPolicy: RequireDualStack
  ipFamilies:
    - IPv4
    - IPv6
  selector:
    app: test
  ports:
    - port: 80

Примените и протестируйте:

kubectl apply -f test-app.yaml
 
# Получите IP сервисов
kubectl get svc test-svc
 
# Тест IPv4
curl http://<ipv4-external-ip>
 
# Тест IPv6
curl -6 http://[<ipv6-external-ip>]

Изнутри пода проверьте dual-stack связь:

kubectl run -it --rm debug --image=alpine --restart=Never -- sh
 
# Внутри пода
apk add curl bind-tools
nslookup test-svc
curl -4 http://test-svc  # IPv4
curl -6 http://test-svc  # IPv6

Рекомендации для production#

  1. Мониторьте оба семейства адресов в инструментах наблюдения
  2. Тестируйте поведение failover когда одно семейство недоступно
  3. Настройте health checks для IPv4 и IPv6
  4. Документируйте топологию сети включая IPv6-префиксы
  5. Планируйте выделение IP-адресов чтобы избежать конфликтов
  6. Включите IPv6 в CI/CD пайплайнах для тестирования
  7. Обучите команду диагностике dual-stack

Связанные статьи#

Проверьте связь с контейнерами

Используйте наш Ping-инструмент и Валидатор IPv6 для проверки доступности ваших контейнерных сервисов через IPv6.

Дополнительные ресурсы#