ping6.net

Docker 和 Kubernetes 中的 IPv6:容器网络指南

为容器化应用配置 IPv6。涵盖 Docker 守护程序设置、Kubernetes 双栈、CNI 插件和服务暴露。

ping6.net2024年12月14日7 min read
IPv6DockerKubernetescontainersnetworkingDevOps

容器网络默认为 IPv4。如果你在 2024 年运行生产工作负载,这就是一个问题。

TL;DR - 快速摘要

要点:

  • Docker 和 Kubernetes 默认仅支持 IPv4;IPv6 需要显式配置
  • Docker 需要 daemon.json 更改和带有 --ipv6 标志的自定义网络
  • Kubernetes 1.23+ 支持稳定的双栈,pod 和 service 都支持 IPv6
  • CNI 插件(Calico、Cilium、Flannel)处理双栈的方式不同

跳转至: Docker 配置 | Kubernetes 双栈 | CNI 插件 | 测试

为什么容器需要 IPv6#

你的容器可能仅支持 IPv4,而互联网的其他部分正在转向 IPv6。移动网络、ISP 和云提供商都是 IPv6 优先。如果你的容器化服务不支持 IPv6,你就会通过 NAT64 网关增加延迟,或者更糟——完全错过客户。

容器编排平台(Docker、Kubernetes、ECS、Nomad)是在 IPv4 时代构建的。IPv6 支持是后来作为附加功能添加的。默认设置仍然假定仅支持 IPv4 网络。

启用 IPv6 并不难,但需要在多个层面进行显式配置:守护程序、网络、容器和服务。缺少一个层次,你就会出现神秘故障的部分连接。

Docker IPv6 配置#

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+)

对于具有全局可路由地址的生产环境,使用提供商分配的前缀:

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

重启 Docker 以应用更改:

sudo systemctl restart docker

验证 IPv6 已启用:

docker network inspect bridge | grep IPv6

你应该看到 "EnableIPv6": true

默认桥接网络#

即使在守护程序配置后,默认桥接网络也不会自动获得 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 地址。

用户定义的网络#

用户定义的桥接网络支持双栈:

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

当启用 IPv6 时,Docker 的嵌入式 DNS 解析器返回容器名称的 AAAA 记录。

IPv6 NAT 和路由#

默认情况下,Docker 对 IPv4 使用 NAT,但可能不对 IPv6 进行 NAT。这取决于你的 fixed-cidr-v6 配置。

使用 ULA 前缀(fd00::/8),你需要 NAT 进行互联网访问:

# 启用 IPv6 转发
sudo sysctl -w net.ipv6.conf.all.forwarding=1
 
# 添加伪装规则
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 地址管理)配置分配 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 本地主机
  - "[::1]:8083:80"      # IPv6 本地主机

启动服务:

docker-compose up -d

验证容器具有 IPv6:

docker-compose exec web ip -6 addr show

Kubernetes 双栈#

Kubernetes 从版本 1.21(测试版)和 1.23(稳定版)开始支持双栈网络。

先决条件#

  1. Kubernetes 1.23 或更高版本
  2. 支持双栈的 CNI 插件(Calico、Cilium、Flannel、Weave)
  3. 具有双栈模式的 kube-proxy
  4. 云提供商支持(用于 LoadBalancer 服务)

启用双栈#

对于新集群,在初始化期间启用双栈。使用 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),在集群创建期间启用双栈:

# 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

验证双栈已启用:

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

你应该看到 IPv4 和 IPv6 CIDR。

Pod 网络#

Pod 自动从两个系列接收地址。在大多数情况下不需要特殊配置。

示例 pod:

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"}]

Pod 内的应用程序应该绑定到 ::(所有地址)或特别绑定到 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 或双栈。使用 ipFamilyPolicyipFamilies 字段控制这一点。

双栈服务(双栈集群中的默认值):

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 服务使用双栈前端配置云负载均衡器(如果云提供商支持)。

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

并非所有云提供商都支持双栈负载均衡器。验证平台的支持。

Ingress 控制器#

Ingress 对 IPv6 的支持取决于控制器实现。

支持 IPv6 的流行控制器:

  • nginx-ingress:支持双栈,监听 IPv4 和 IPv6
  • Traefik:完全双栈支持
  • HAProxy Ingress:支持 IPv6
  • Contour:支持双栈

为双栈配置 nginx-ingress:

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 插件以不同方式处理双栈。

Calico#

Calico 通过 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()

在 Calico 中启用 IPv6:

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 具有原生双栈支持。在安装期间启用:

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

Flannel#

Flannel 需要在 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 支持两种 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(如果 IPV6_V6ONLY=0 则包括 IPv4)

许多语言默认为仅 IPv4。检查框架文档。

DNS 解析#

双栈环境中的 DNS 查询返回 A 和 AAAA 记录。应用程序应该尝试两者,优先选择 IPv6。

一些较旧的库只查询 A 记录。更新依赖项或显式配置 DNS 解析。

防火墙规则#

容器防火墙(iptables/ip6tables)需要两个系列的规则。如果配置正确,Docker 和 Kubernetes 会自动处理这一点,但自定义规则可能会阻止 IPv6。

验证 IPv6 防火墙规则:

sudo ip6tables -L -n -v

外部连接#

具有 IPv6 的容器需要正确路由到外部网络。如果主机没有 IPv6 连接,容器也不会有。

首先测试主机 IPv6:

ping6 google.com

如果失败,在排查容器之前修复主机网络。

StatefulSet 无头服务#

具有无头服务的 StatefulSet 为 pod DNS 名称返回 IPv4 和 IPv6 地址:

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

连接到 StatefulSet pod 的应用程序必须优雅地处理多个地址。

测试双栈部署#

部署测试应用程序:

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>]

从 pod 内部测试双栈连接:

kubectl run -it --rm debug --image=alpine --restart=Never -- sh
 
# Pod 内部
apk add curl bind-tools
nslookup test-svc
curl -4 http://test-svc  # IPv4
curl -6 http://test-svc  # IPv6

生产注意事项#

  1. 在可观测性工具中监控两个地址系列
  2. 测试一个系列不可用时的故障转移行为
  3. 为 IPv4 和 IPv6 配置健康检查
  4. 记录网络拓扑,包括 IPv6 前缀
  5. 规划 IP 地址分配以避免冲突
  6. 在 CI/CD 流水线中启用 IPv6进行测试
  7. 培训团队进行双栈故障排除

相关文章#

验证容器连接

使用我们的Ping 工具IPv6 验证器测试容器化服务是否可通过 IPv6 访问。

其他资源#