为容器分配ipv6公网ip

2025 年 9 月 30 日 星期二(已编辑)
30
这篇文章上次修改于 2025 年 10 月 25 日 星期六,可能部分内容已经不适用,如有疑问可询问作者。

为容器分配ipv6公网ip

一、前置检查

  1. 确认 IPv6 连通性(宿主机)
ip -6 addr show
ping -6 -c 3 google.com
  1. 安装 Docker(若未安装)
curl -fsSL https://get.docker.com | sh

全局启动ipv6 编辑 /etc/docker/daemon.json ,加上以下内容。(如果没有这个文件直接创建。)

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

重启docker engine

sudo systemctl restart docker

测试

sudo docker run --rm -it busybox ping -6 -c4 ipv6-test.com
  1. 安装并启用 nftables
apt-get update && apt-get install -y nftables || yum install -y nftables
systemctl enable --now nftables
nft --version
  1. (建议)开启 IPv6 转发

便于容器经宿主机转发出网(NAT66)。

sysctl -w net.ipv6.conf.all.forwarding=1
echo 'net.ipv6.conf.all.forwarding=1' > /etc/sysctl.d/99-ipv6-forward.conf
sysctl --system

二、准备一个不冲突的 Docker IPv6 网络

你之前的 docker0 里有 fd00:dead:beef::/48,所以新网络不要使用该段下的前缀,以免报 “Pool overlaps”。

  1. 选择一个 ULA 子网(示例用 fd00:1:1:1::/64
docker network create \
  --ipv6 \
  --subnet fd00:1:1:1::/64 \
  psnet
  • 成功后可查看:
docker network inspect psnet | grep -A3 IPv6

如果报 overlaps,就换个段,比如:fd00:2:2:2::/64fd10:abcd:1:1::/64 等,总之与现有网络不重叠即可。


三、为容器分配内部 IPv6(落在上面的子网)

用户自定义网络里才可以用 --ip6 指定静态 IPv6。 下面示例给容器分配 fd00:1:1:1::101(在 fd00:1:1:1::/64 内)。

docker run -d \
  --restart unless-stopped \
  --network psnet \
  --ip6 fd00:1:1:1::101 \
  --name pxxxxe \
  pxxxe/pxxxe \

检查分配结果:

docker inspect -f '{{.NetworkSettings.Networks.psnet.GlobalIPv6Address}}' packetshare

应显示:fd00:1:1:1::101


四、把容器的内部 IPv6 映射到指定公网 IPv6(NAT66)

你的宿主机上有多个公网 IPv6(例如 2a0e:97c0:3f0:1::1d63),我们要让这个容器的出口永远使用它。 使用 nftables NAT(IPv6)postrouting 做 SNAT。

  1. 创建表与链(首次)
nft add table ip6 docker_snat
nft add chain ip6 docker_snat postrouting '{ type nat hook postrouting priority srcnat; }'
  1. 添加 SNAT 规则 将容器内部 IPv6 fd00:1:1:1::101 映射为公网 2a0e:97c0:3f0:1::1d63
nft add rule ip6 docker_snat postrouting ip6 saddr fd00:1:1:1::101 snat to 2a0e:97c0:3f0:1::1d63
  1. 查看当前规则
nft list table ip6 docker_snat
# 或带 handle 编号:
nft -a list table ip6 docker_snat

五、验证

  1. 容器内测试出网地址
docker exec -it packetshare curl -6 ifconfig.co
# 也可:docker exec -it packetshare curl -6 ip.sb

返回应为:2a0e:97c0:3f0:1::1d63(你在 SNAT 指定的那个)。

  1. 连通性测试
docker exec -it packetshare ping -6 -c 3 google.com

六、多个容器、多公网地址

  • 再起一个容器(内部 IPv6 换一个):
docker run -d \
  --restart unless-stopped \
  --network psnet \
  --ip6 fd00:1:1:1::102 \
  --name packetshare2 \
  packetshare/packetshare \
    -accept-tos \
    [email protected] \
    -password=Abcdefg6
  • 给它 SNAT 到另一个公网 IPv6(如 2a0e:97c0:3f0:1::1d62):
nft add rule ip6 docker_snat postrouting ip6 saddr fd00:1:1:1::102 snat to 2a0e:97c0:3f0:1::1d62
  • 验证:
docker exec -it packetshare2 curl -6 ifconfig.co

应看到 2a0e:97c0:3f0:1::1d62


七、常用维护操作

1) 查看/删除 SNAT 规则

  • 查看含 handle 编号:
nft -a list table ip6 docker_snat
  • 按 handle 删除:
nft delete rule ip6 docker_snat postrouting handle <编号>
  • 清空整链(谨慎):
nft flush chain ip6 docker_snat postrouting
  • 删整张表(谨慎):
nft delete table ip6 docker_snat

2) 持久化 nft 规则(防重启丢失)

  • 保存当前规则:
nft list ruleset > /etc/nftables.conf
  • 确保开机加载:
systemctl enable --now nftables

若你的发行版使用 nftables-persistent,也可以:

apt-get install -y nftables-persistent
netfilter-persistent save

3) 容器与网络排查

  • 查看网络详情:
docker network inspect psnet
  • 查看容器 IPv6:
docker inspect -f '{{.NetworkSettings.Networks.psnet.GlobalIPv6Address}}' <容器名>

八、常见报错与快速修复

  • invalid pool request: Pool overlaps... 说明你新建的子网与现有 Docker 网络(比如 docker0)重叠。换一个 ULA 前缀,如 fd00:1:1:1::/64fd10:abcd:1:1::/64

  • user specified IP address is supported on user defined networks only 你在默认 bridge 网络里指定了 --ip6。请先用 docker network create 创建自定义网络,再在该网络里指定 --ip6

  • invalid subinterface vlan name eth0, example formatting is eth0.10(macvlan/ipvlan) 说明云环境不允许直接用二层(或需要显式子接口),多数 VPS 建议放弃 macvlan/ipvlan,改用本指南的 NAT66 方案

  • 容器能解析但出不了网 先检查宿主 ping -6 是否通,再确认:

  1. sysctl net.ipv6.conf.all.forwarding=1
  2. nft 的 SNAT 规则是否匹配了容器 ip6 saddr
  3. 云厂商安全组/防火墙是否放行所需端口/协议(ICMPv6、TCP/UDP)

九、(可选)一体化管理脚本

下面的脚本提供:add / del / list

  • add <name> <public_ipv6> <email> <password>:创建网络(如无)→ 启容器 → 添加 SNAT
  • del <name>:删除容器 + 清理对应 SNAT
  • list:查看容器与 SNAT

保存为 ps_manager.sh

#!/bin/bash
set -e

NETNAME="psnet"
SUBNET="fd00:1:1:1::/64"    # 如与现有冲突可改
TABLE="docker_snat"
CHAIN="postrouting"

init_env() {
  if ! docker network inspect $NETNAME >/dev/null 2>&1; then
    echo "[+] 创建 Docker 网络 $NETNAME ($SUBNET)"
    docker network create --ipv6 --subnet $SUBNET $NETNAME
  fi
  if ! nft list table ip6 $TABLE >/dev/null 2>&1; then
    echo "[+] 创建 nftables 表 $TABLE"
    nft add table ip6 $TABLE
    nft add chain ip6 $TABLE $CHAIN '{ type nat hook postrouting priority srcnat; }'
  fi
}

gen_internal_ip() { # 根据容器名生成稳定的末尾
  local NAME=$1
  local SUFFIX=$(echo -n $NAME | md5sum | cut -c1-4)
  echo "fd00:1:1:1::${SUFFIX}"
}

add_container() {
  local NAME=$1 PUBIP=$2 EMAIL=$3 PASSWORD=$4
  if [ -z "$NAME" ] || [ -z "$PUBIP" ] || [ -z "$EMAIL" ] || [ -z "$PASSWORD" ]; then
    echo "用法: $0 add <name> <public_ipv6> <email> <password>"; exit 1
  fi
  init_env
  local INIP=$(gen_internal_ip "$NAME")
  echo "[+] 启动容器 $NAME (内部=$INIP, 公网=$PUBIP)"
  docker rm -f "$NAME" >/dev/null 2>&1 || true
  docker run -d --restart unless-stopped --network "$NETNAME" --ip6 "$INIP" --name "$NAME" \
    packetshare/packetshare -accept-tos -email="$EMAIL" -password="$PASSWORD"
  echo "[+] 添加 SNAT: $INIP -> $PUBIP"
  nft add rule ip6 $TABLE $CHAIN ip6 saddr $INIP snat to $PUBIP
  sleep 2
  docker exec -it "$NAME" curl -6 ifconfig.co || true
}

del_container() {
  local NAME=$1
  if [ -z "$NAME" ]; then echo "用法: $0 del <name>"; exit 1; fi
  local INIP=$(gen_internal_ip "$NAME")
  echo "[+] 删除容器 $NAME"
  docker rm -f "$NAME" >/dev/null 2>&1 || true
  echo "[+] 清理 SNAT(匹配 $INIP)"
  local HANDLES=$(nft -a list table ip6 $TABLE 2>/dev/null | awk "/$INIP/ {print \$NF}")
  for h in $HANDLES; do
    nft delete rule ip6 $TABLE $CHAIN handle $h && echo "  删除规则 handle $h"
  done
}

list_all() {
  echo "=== 容器 ==="
  docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Networks}}"
  echo; echo "=== SNAT 规则 ==="
  nft list table ip6 $TABLE || echo "暂无 SNAT 规则"
}

case "$1" in
  add) shift; add_container "$@";;
  del) shift; del_container "$@";;
  list) list_all;;
  *) echo "用法: $0 {add|del|list}"; exit 1;;
esac

使用:

chmod +x ps_manager.sh
./ps_manager.sh add ps1 2a0e:97c0:3f0:1::1d63 [email protected] Abcdefg6
./ps_manager.sh list
./ps_manager.sh del ps1

完成! 照这份指南操作,你就能让每个容器用固定的内部 IPv6映射到指定的公网 IPv6对外通信。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...