Docker容器不映射端口并内网访问
环境:宿主机:192.168.1.5,客户机:192.168.1.4,内部网络:172.40.0.0/16
创建docker网络
在 Docker 中创建一个基于 bridge 驱动、同时支持自定义 IPv4 和 IPv6 子网的自定义网络,命令格式如下:
docker network create \
--driver bridge \
--subnet=172.40.0.0/24 \
--gateway=172.40.0.1 \
--ipv6 \
--subnet=2001:db8:4::/64 \
--gateway=2001:db8:4::1 \
my-test-network
🔍 参数说明
| 参数 | 说明 |
|---|---|
--driver bridge | 指定使用桥接驱动(默认),也可省略 |
--subnet=172.40.0.0/24 | 指定 IPv4 子网 |
--gateway=172.40.0.1 | 指定 IPv4 网关(可选,不指定则自动分配第一个 IP) |
--ipv6 | 必须,启用 IPv6 支持,否则不会创建 IPv6 子网 |
--subnet=2001:db8:4::/64 | 指定 IPv6 子网(必须与 --ipv6 同时使用) |
--gateway=2001:db8:4::1 | 指定 IPv6 网关(可选) |
my-test-network | 网络名称,可自定义 |
⚙️ 先决条件:Docker 守护进程必须启用 IPv6
在运行上述命令前,必须在 Docker 守护进程配置中启用 IPv6,否则会报错。
-
编辑
/etc/docker/daemon.json(如不存在则创建):{ "ipv6": true, "fixed-cidr-v6": "2001:db8:1::/64" }fixed-cidr-v6是默认的 IPv6 池,但若创建网络时指定了子网,此配置可留空,不过需要确保"ipv6": true已设置。 -
重启 Docker 服务:
sudo systemctl restart docker
✅ 验证网络创建
# 查看网络列表
docker network ls
# 查看详细信息(包括子网、网关、已连接的容器)
docker network inspect my-test-network
📌 其他实用选项
- 指定网卡名称:
-o com.docker.network.bridge.name=br-test(自定义网桥设备名) - 限制 IP 分配范围:使用
--ip-range参数,例如--ip-range=172.40.0.128/25 - 隔离外部访问:添加
--internal参数(则容器无外部网络访问,但跨容器通信仍可)
⚠️ 注意事项
- 外部访问 IPv6:如果希望外部主机通过 IPv6 访问容器,需确保宿主机 IPv6 路由及防火墙已正确配置(例如在
FORWARD链放行,或使用 NAT66)。 - 网络隔离:自定义 bridge 网络默认允许容器间通信,如需隔离,可创建多个网络并限制互联。
- docker-compose:在 compose 文件中定义网络时,同样需要指定
enable_ipv6: true和子网参数。
使用自定义网络
🚀 连接容器到该网络
启动容器时指定网络和静态 IP:
docker run -d --name nginx \
--network my-test-network \
--ip 172.40.0.41 \
--ip6 2001:db8:4::41 \
nginx:latest
静态 IP 必须在子网范围内且未被使用。
🚩 Compose方式连接网络
services:
halo:
image: halohub/halo:2.25.4
container_name: halo
restart: on-failure:3
volumes:
- /docker-data/halo:/root/.halo2
command:
- --halo.external-url=https://www.youcats.cn
# 端口号 默认8090
- --server.port=8090
environment:
- JVM_OPTS=-Xms512m -Xmx1024m
networks:
my-test-network: # 与下对应
ipv4_address: 172.40.0.61
ipv6_address: 2001:db8:4::61
hostname: halo
networks:
my-test-network:
external: true # 让Docker不新建网络
name: my-test-network
🧱 设置防火墙
以下步骤并未完全验证,请理性看待
# 检查转发 -> 1
sysctl net.ipv4.ip_forward
# 添加DOCKER-USER入站规则,允许192.168.1.0/24所有的请求到172.40.0.0/16
iptables -I DOCKER-USER -s 192.168.1.0/24 -d 172.40.0.0/16 -j ACCEPT
# 在防火墙检查的最早阶段,显式地允许内网客户端(192.168.1.x)访问整个 Docker 容器网段(172.40.0.x),从而让流量不受后续针对特定容器 IP 的丢弃规则影响。
iptables -t raw -I PREROUTING 1 -s 192.168.1.0/24 -d 172.40.0.0/16 -j ACCEPT
# 其他操作命令,查看、删除
iptables -L DOCKER-USER --line-numbers
iptables -D DOCKER-USER 1 # <- 上一步的序号
客户端添加路由
# 临时
sudo ip route add 172.40.0.0/16 via 192.168.1.5
# 持久化方式1 使用netplan
# Netplan(18.04+):编辑 /etc/netplan/01-netcfg.yaml,在对应网卡下添加
routes:
- to: 172.40.0.0/16
via: 192.168.1.5
# 然后 sudo netplan apply
# 持久化方式2 /etc/rc.local:在 exit 0 前添加 ip route add 172.40.0.0/16 via 192.168.1.5
📌 防火墙持久化
因Dokcer重启会清空DOCKER-USER链配置,且系统重启会重置raw链配置,可以采用以下方案持久化
重启持久化,通过安装iptables-persistent实现
apt install iptables-persistent
netfilter-persistent save
Docker重启持久化,创建系统服务,在Docker重启后执行
sudo vim /etc/systemd/system/docker-user-rules.service
# 写入以下内容
[Unit]
Description=Apply custom iptables rules to DOCKER-USER chain
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/iptables -F DOCKER-USER
ExecStart=/sbin/iptables -I DOCKER-USER 1 -s 192.168.1.0/24 -d 172.40.0.0/16 -j ACCEPT
# 如果规则较多,也可以将命令写入一个脚本,然后在这里执行:
# ExecStart=/usr/local/bin/apply-docker-rules.sh
[Install]
WantedBy=multi-user.target
# 启用启动服务
sudo systemctl daemon-reload
sudo systemctl enable docker-user-rules.service
sudo systemctl start docker-user-rules.service
应对系统重启:netfilter-persistent save是有效的。它将当前的iptables规则保存到文件中(如/etc/iptables/rules.v4),并在系统重启时自动恢复。因此,由系统重启导致的规则丢失,可以靠它来解决。
应对Docker升级:情况则不确定。Docker升级时可能会重置自己的网络规则。netfilter-persistent的恢复机制在Docker之后启动,有可能覆盖掉Docker刚生成的新规则,导致冲突。因此,Docker升级后规则能否保留,存在不确定性,取决于具体的升级场景和时序。
🧪 测试
在客户机上直接ping可以到达容器内部,这样可以不用端口映射,也能通过ip端口的方式访问服务,同一个网络,一可以通过容器名进行访问
Docker容器不映射端口并内网访问
https://www.youcats.cn/archives/1782458539821
评论