Featured image of post Docker 容器固定 IP 实战:自定义网络与运行时指定

Docker 容器固定 IP 实战:自定义网络与运行时指定

docker 默认 bridge 模式下容器 IP 会变——用 docker network create 自定义子网、运行时 --ip 指定固定 IP、重启不变 IP

默认 bridge 模式下,容器每次启动 IP 都会变——docker-compose restart 后连接配置失效、监控告警的 IP 列表刷新不了。生产环境要"容器 IP 稳定",必须用自定义网络 + --ip 指定

这一篇把"容器固定 IP"这件事的最短路径写清楚。

阅读对象:需要容器 IP 在重启后保持不变、要把 IP 写进监控 / DNS / Nginx upstream 的同学 覆盖范围:默认 bridge 模式的局限 + 自定义网络 + –ip 指定 + 多容器共享子网

一、为什么默认 bridge 模式 IP 会变

Docker 启动时按顺序从子网 172.17.0.0/16 分配 IP 给容器。容器重启 / 删除后,IP 池会重新分配——172.17.0.2 这次给 nginx,下次可能给 mysql。

1
2
3
4
5
6
7
8
$ docker run -d --name nginx1 nginx
$ docker inspect nginx1 | grep IPAddress
"IPAddress": "172.17.0.2"

$ docker stop nginx1 && docker rm nginx1
$ docker run -d --name nginx2 nginx
$ docker inspect nginx2 | grep IPAddress
"IPAddress": "172.17.0.2"   # 同一个 IP 给新容器了

直接用默认 bridge 模式指定 IP 会报错

1
2
3
$ docker run -itd --name test --network bridge --ip 172.17.0.5 centos:7 /bin/bash
Error response from daemon: User specified IP address is supported only when
connecting to networks with user configured subnets.

结论:要让容器 IP 稳定,必须先 docker network create 自定义网络

二、自定义网络:三步搞定

2.1 创建自定义网络

1
docker network create --subnet=172.18.0.0/16 mynetwork

参数说明:

  • --subnet=172.18.0.0/16:定义子网范围
  • mynetwork:网络名

Why 自定义子网

  • 避开默认 172.17.0.0/16 防止和宿主机其他网络冲突
  • 显式规划 IP 段,方便和监控 / DNS 同步

可选参数:

1
2
3
4
5
# 指定网桥名(让 ifconfig 看到的是 docker1,不是 br-xxx)
docker network create \
  --subnet=172.18.0.0/16 \
  --opt "com.docker.network.bridge.name"="docker1" \
  mynetwork

2.2 验证网络

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
xxxx           bridge      bridge    local
xxxx           host        host      local
xxxx           none        null      local
xxxx           mynetwork   bridge    local

$ docker network inspect mynetwork
[
  {
    "Name": "mynetwork",
    "Id": "abc123...",
    "Created": "...",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
      "Driver": "default",
      "Options": null,
      "Config": [
        {
          "Subnet": "172.18.0.0/16",
          "Gateway": "172.18.0.1"
        }
      ]
    },
    "Internal": false,
    "Containers": {},
    "Options": {
      "com.docker.network.bridge.name": "docker1"
    }
  }
]

2.3 启动容器 + 指定 IP

1
2
3
4
5
docker run -itd \
  --name test \
  --net mynetwork \
  --ip 172.18.0.10 \
  centos:7 /bin/bash

关键参数

  • --net mynetwork:加入自定义网络
  • --ip 172.18.0.10:固定 IP

2.4 验证

1
2
$ docker inspect test | grep IPAddress
"IPAddress": "172.18.0.10"

重启后 IP 不变

1
2
3
$ docker restart test
$ docker inspect test | grep IPAddress
"IPAddress": "172.18.0.10"   # 还是这个 IP

三、实战场景

3.1 场景 1:MySQL 容器固定 IP,方便其他容器配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 创建网络
docker network create --subnet=172.18.0.0/16 backend

# 启动 MySQL,固定 IP
docker run -d \
  --name mysql-server \
  --net backend \
  --ip 172.18.0.5 \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

# 启动应用,配置连 172.18.0.5
docker run -d \
  --name myapp \
  --net backend \
  -e DB_HOST=172.18.0.5 \
  -e DB_PORT=3306 \
  myapp:latest

优势

  • 应用容器可以不挂 mysql:3306 的端口映射——直接用内网 IP 通信
  • MySQL 不暴露给宿主机外网,减少攻击面

3.2 场景 2:Nginx 反向代理到后端固定 IP

1
2
3
4
5
6
7
8
9
# 后端 web 固定 IP
docker run -d --name web1 --net mynetwork --ip 172.18.0.10 nginx
docker run -d --name web2 --net mynetwork --ip 172.18.0.11 nginx

# Nginx 反代配置
# upstream backend {
#     server 172.18.0.10:80;
#     server 172.18.0.11:80;
# }

Why 固定 IP:upstream 写死的 IP 重启后不失效——不用每次重启都重新查 IP 改配置。

3.3 场景 3:多容器共享子网 + DNS 解析

1
2
3
4
docker network create --subnet=172.18.0.0/16 mynetwork

docker run -d --name app1 --net mynetwork --ip 172.18.0.10 myapp
docker run -d --name db1  --net mynetwork --ip 172.18.0.20 mysql:8

在 app1 容器内

1
2
3
4
5
6
7
$ docker exec -it app1 bash
# ping db1
PING db1 (172.18.0.20): 56 data bytes
64 bytes from 172.18.0.20: icmp_seq=0 ttl=64 time=0.045 ms

# 也支持直接 ping IP
$ ping 172.18.0.20

关键能力:自定义网络默认开启容器名 DNS 解析——app1 容器内 ping db1 直接解析成 172.18.0.20默认 bridge 模式没有这个能力

四、host 网络模式的"另类固定 IP"

如果不在意隔离,host 模式下容器直接用宿主机 IP——变相"固定"了:

1
docker run -d --name nginx --net host nginx

容器内看到的 hostname -I = 宿主机 IP

优劣

  • 简单粗暴
  • 性能最佳(无 NAT)
  • 端口冲突会直接报错——多个容器想用 80 端口就崩

五、清理自定义网络

1
2
3
4
5
6
7
8
# 列出
docker network ls

# 删指定网络
docker network rm mynetwork

# 删所有未使用的网络
docker network prune

注意有容器正在使用的网络不能删——必须先停 / 删容器。

六、常见问题

6.1 address already in use

启动容器时 IP 冲突:

1
2
$ docker run -d --name test --net mynetwork --ip 172.18.0.10 centos
Error response from daemon: Address already in use

解决:

  • 换 IP(172.18.0.11
  • 看哪个容器占着:docker network inspect mynetwork

6.2 不同网络之间容器无法直接通信

1
2
3
4
5
6
7
8
$ docker run -d --name web1 --net mynetwork --ip 172.18.0.10 nginx
$ docker run -d --name app1 --net othernet --ip 172.19.0.5 myapp

# 在 app1 内 ping web1
$ docker exec app1 ping web1
ping: bad address 'web1'   # 解析不到
$ docker exec app1 ping 172.18.0.10
# 也不通——不同 network

解决:用 docker network connect 把容器加到另一个网络:

1
docker network connect mynetwork app1

app1 现在有两个网络接口,能直接 ping web1

6.3 容器已经启动,想改 IP

容器运行中不能改 IP。必须:

1
2
docker rm -f <container>
docker run --ip <new-ip> ...

K8s 里这个操作对应 kubectl edit podpodIP——同样需要删 Pod 重建。

七、要点回顾

  1. 默认 bridge 模式不能指定 IP——必须 docker network create 自定义网络
  2. --net mynetwork --ip 172.18.0.10 启动容器,重启后 IP 不变
  3. 自定义网络自带 DNS 解析——容器名能直接 ping 通
  4. 跨网络通信docker network connect
  5. host 网络模式“IP 等于宿主机 IP”——性能最佳但端口冲突
  6. 运行中的容器不能改 IP——必须删了重建

八、小结

固定 IP 是 docker 网络的"基础款"——理解了 docker network create + --net + --ip 三个参数,90% 的"IP 不稳定"问题就解决了

下一步:理解了单机 Docker 固定 IP,下一步是 K8s 的 Service / StatefulSet / Headless Service——K8s 用 Service 抽象"一组稳定 IP 的 Pod"(哪怕 Pod IP 变了),StatefulSet 给每个 Pod 分配稳定 DNS 名字(pod-0.svc.namespace.svc.cluster.local),Headless Service 直接暴露 Pod IP 给 DNS。是云原生的"稳定标识"模式。

参考资料

使用 Hugo 构建
主题 StackJimmy 设计