默认 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 pod 改 podIP——同样需要删 Pod 重建。
七、要点回顾
- 默认 bridge 模式不能指定 IP——必须
docker network create 自定义网络 --net mynetwork --ip 172.18.0.10 启动容器,重启后 IP 不变- 自定义网络自带 DNS 解析——容器名能直接 ping 通
- 跨网络通信用
docker network connect - host 网络模式“IP 等于宿主机 IP”——性能最佳但端口冲突
- 运行中的容器不能改 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。是云原生的"稳定标识"模式。
参考资料