4. Docker网络:网络模式详解、端口映射、容器互联、自定义网络配置
聊到Docker网络,我脑子里第一个蹦出来的场景是——几年前有个线上服务突然挂了,排查了半天,结果是两个容器之间网络不通。嗯,从那以后,我再也不敢轻视Docker网络这块了。
说白了,Docker网络就是让容器之间、容器和宿主机之间、甚至跨主机之间能正常通信。你想想看,如果容器之间互相访问不了,那微服务架构还怎么玩?
4.1 Docker网络模式详解
Docker默认提供了几种网络模式,每种模式都有自己的适用场景。我个人习惯在生产环境中只用两种:bridge和host。但作为完整的手册,我们还是把五种都过一遍。
| 网络模式 | 命令参数 | 特点 | 适用场景 |
|---|---|---|---|
| bridge | --network bridge | 默认模式,容器通过虚拟网桥通信 | 单机多容器通信 |
| host | --network host | 容器直接使用宿主机网络栈 | 高性能网络需求 |
| none | --network none | 无网络,完全隔离 | 安全敏感型容器 |
| container | --network container:NAME | 共享另一个容器的网络栈 | sidecar模式 |
| overlay | --network overlay | 跨主机容器通信 | Swarm集群 |
核心理解:bridge模式是Docker自己创建了一个虚拟网桥(默认叫docker0),每个容器都会分配一个独立的IP。host模式则直接"借用"了宿主机的网卡,性能最好但隔离性最差。
4.2 端口映射:让外部访问容器
容器默认是隔离的,外部根本访问不到。那怎么让外面的人访问容器里的服务呢?端口映射就是干这个的。
我记得有一次帮客户排查问题,他们启动了一个Nginx容器,但怎么都访问不到。我一看,端口映射没做。嗯,这是个很常见的坑。
基本语法:
# 将宿主机的8080端口映射到容器的80端口
docker run -d -p 8080:80 nginx
# 随机映射宿主机端口到容器80端口
docker run -d -p 80 nginx
# 指定IP和端口映射
docker run -d -p 192.168.1.100:8080:80 nginx
# 映射UDP端口
docker run -d -p 8080:80/udp nginx
我的习惯:生产环境中我一般会明确指定宿主机端口,避免随机端口导致管理混乱。而且我建议用-p参数时,尽量保持宿主机端口和容器端口一致,这样排查问题的时候一眼就能看出来。
端口映射的原理其实很简单:Docker在宿主机上开了个端口,所有发到这个端口的数据包,都会被iptables规则转发到对应容器的端口上。你想想看,这不就是个反向代理嘛。
4.3 容器互联:容器之间的通信
容器互联,说白了就是让容器之间能互相找到对方。早期Docker用的是--link参数,但现在官方已经不建议用了。为什么?因为--link是静态的,容器重启后IP变了,链接就断了。
我曾经在一个老项目里看到过大量使用--link的配置,每次容器重启都要手动更新一堆链接关系,那叫一个痛苦。
现在推荐的做法是使用自定义网络。但为了完整性,我们还是先看看--link怎么用:
# 启动一个数据库容器
docker run -d --name mydb mysql
# 启动应用容器并链接到mydb
docker run -d --name myapp --link mydb:db myapp-image
这样myapp容器里就可以通过主机名"db"来访问mydb容器了。但注意,这只是单向的,mydb容器并不能反过来访问myapp。
避坑指南:我曾经在生产环境中看到有人还在用--link,结果容器扩缩容后链接全乱了。记住,--link是Docker早期产物,新项目千万别用。如果你在维护老项目,尽快迁移到自定义网络。
4.4 自定义网络配置
这才是Docker网络的精髓所在。自定义网络让你可以完全掌控容器之间的通信方式。我个人强烈建议,所有需要容器互联的场景,都用自定义网络。
创建自定义网络:
# 创建bridge类型的自定义网络
docker network create my-network
# 创建时指定子网和网关
docker network create --subnet=172.20.0.0/16 --gateway=172.20.0.1 my-network
# 查看网络列表
docker network ls
# 查看网络详情
docker network inspect my-network
在自定义网络中启动容器:
# 启动容器并连接到自定义网络
docker run -d --name web --network my-network nginx
docker run -d --name db --network my-network mysql
# 容器之间可以直接通过容器名通信
# 在web容器中 ping db 就能通
关键优势:自定义网络中的容器可以通过容器名直接通信,而且Docker内置了DNS解析。这意味着不管容器怎么重启、IP怎么变,只要容器名不变,通信就不会断。这才是生产环境该有的样子。
你还可以让一个容器同时接入多个网络:
# 先以默认网络启动
docker run -d --name app myapp
# 再连接到自定义网络
docker network connect my-network app
# 断开网络
docker network disconnect my-network app
这个特性特别有用。比如你的应用需要同时访问前端网络和后端数据库网络,但又不希望两个网络直接互通。一个容器接两个网络,既隔离又连通,完美。
4.5 网络排障常用命令
做运维的,难免要排查网络问题。我整理了几个我常用的命令:
# 查看容器网络详情
docker inspect container_name | grep IPAddress
# 进入容器测试网络
docker exec -it container_name ping another_container
# 查看端口映射
docker port container_name
# 查看网络连接的容器
docker network inspect my-network
小技巧:如果容器里没有ping命令,可以用docker exec -it container_name sh进去后,用cat /etc/hosts查看DNS解析。或者直接用docker exec container_name cat /etc/hosts。嗯,这个我经常用。
最后说一句,Docker网络这块,理解透了其实不难。核心就三点:端口映射让外部能进来,自定义网络让容器之间能互访,网络模式决定了隔离级别。把这三点搞明白,大部分网络问题都能迎刃而解。