为容器分配ipv6公网ip
一、前置检查
- 确认 IPv6 连通性(宿主机)
ip -6 addr show
ping -6 -c 3 google.com- 安装 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- 安装并启用 nftables
apt-get update && apt-get install -y nftables || yum install -y nftables
systemctl enable --now nftables
nft --version- (建议)开启 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”。
- 选择一个 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::/64、fd10: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。
- 创建表与链(首次)
nft add table ip6 docker_snat
nft add chain ip6 docker_snat postrouting '{ type nat hook postrouting priority srcnat; }'- 添加 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- 查看当前规则
nft list table ip6 docker_snat
# 或带 handle 编号:
nft -a list table ip6 docker_snat五、验证
- 容器内测试出网地址
docker exec -it packetshare curl -6 ifconfig.co
# 也可:docker exec -it packetshare curl -6 ip.sb返回应为:2a0e:97c0:3f0:1::1d63(你在 SNAT 指定的那个)。
- 连通性测试
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_snat2) 持久化 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::/64、fd10: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是否通,再确认:
sysctl net.ipv6.conf.all.forwarding=1nft的 SNAT 规则是否匹配了容器ip6 saddr- 云厂商安全组/防火墙是否放行所需端口/协议(ICMPv6、TCP/UDP)
九、(可选)一体化管理脚本
下面的脚本提供:
add/del/list
add <name> <public_ipv6> <email> <password>:创建网络(如无)→ 启容器 → 添加 SNATdel <name>:删除容器 + 清理对应 SNATlist:查看容器与 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对外通信。