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网络这块,理解透了其实不难。核心就三点:端口映射让外部能进来,自定义网络让容器之间能互访,网络模式决定了隔离级别。把这三点搞明白,大部分网络问题都能迎刃而解。