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,否则会报错。

  1. 编辑 /etc/docker/daemon.json(如不存在则创建):

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

    fixed-cidr-v6 是默认的 IPv6 池,但若创建网络时指定了子网,此配置可留空,不过需要确保 "ipv6": true 已设置。

  2. 重启 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端口的方式访问服务,同一个网络,一可以通过容器名进行访问