一、背景
学习docker除了镜像的创建、容器的运行还有网络的通信,docker支持多种网络模式,以适应不同场景的容器通信需要。
本文不讲原理,只讲实践,通过列举各种场景,说明在docker的场景下多个容器之间如何通信。
二、准备
准备一台安装有docker的机器,笔者使用的是一台名为myvm1的虚拟机,ip为192.168.99.100
三、单机环境
首先我们来看在单台服务器上,可能会出现的docker容器通信场景
单个容器独立对外提供服务
例:一个开着6379的redis实例,供开发同学连接使用
在myvm1上启动一个redis容器
docker@myvm1:~$ docker run -d --name redis -e ALLOW_EMPTY_PASSWORD=yes -p 6379:6379 bitnami/redis:5.0
-d 后台运行
--name redis 指定容器名称
-e ALLOW_EMPTY_PASSWORD=yes 设置环境变量,允许空密码
-p 6379:6379 映射容器的6379到myvm1的6379
bitnami/redis:5.0 镜像名称
测试一下连接
$ redis-cli -h 192.168.99.100 -p 6379
192.168.99.100:6379>
这是一种最简单也是常见的网络访问方式,通过映射主机与容器的端口,使用主机IP+端口,从而访问容器内的服务。
docker是如何映射端口的呢?
1.看一下docker的网络列表
docker@myvm1:~$ docker network ls
NETWORK ID NAME DRIVER SCOPE
4e695b8923b8 bridge bridge local
e0a70416f49a host host local
beca949fce33 none null local
2.可以看到有3个网络,再看下bridge的详情信息
docker@myvm1:~$ docker network inspect bridge
...
"Containers": {
"6bec72f8abe49b8d399bdd738fbe446882ed6941d0b15f20da7d113918fac4a7": {
"Name": "redis",
"EndpointID": "f056f583e6afa32fcc1887f2afbb22448fc69f3e5e8594e7602dd1dfd61fcfe7",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
}
...
输出比较长,只截取Containers这段,可以看到包含了刚创建的redis容器
3.再从主机的角度看下6379的端口
root@myvm1:~# netstat -tulp | grep 6379
tcp 0 0 :::6379 :::* LISTEN 2941/docker-proxy
可以看到myvm1的6379是被docker-proxy监听的
4.另外查看iptables chain
root@myvm1:~# iptables -L DOCKER
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- anywhere 172.17.0.2 tcp dpt:6379
可以看到6379被转发到了172.17.0.2,而这个ip正式上述b中出现的redis容器的ip地址
root@myvm4:~# ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:F7:68:34:D6
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
5.总结
- 安装docker后会在宿主机(myvm1)上创建一个虚拟网卡docker0
- 后续创建的容器所分配的ip将从该虚拟网卡分配
- 根据容器创建时的-p参数在主机上建立端口路由规则,将对主机端口的服务转发到具体容器对应的端口
两个容器之间访问
例:一个user-service服务访问redis实例
使用IP访问
为了方便我们使用bitnami/redis:5.0镜像的redis-cli代替user-service对redis的访问,模拟两个容器的通信
root@myvm1:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6bec72f8abe4 bitnami/redis:5.0 "/entrypoint.sh /run…" 9 hours ago Up 9 hours 0.0.0.0:6379->6379/tcp redis
可以看到主机的6379是绑定到0.0.0.0的,即主机上的所有ip,那现在主机有多少个ip呢
root@myvm1:~# ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:F7:68:34:D6
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
eth1 Link encap:Ethernet HWaddr 08:00:27:19:F6:25
inet addr:192.168.99.100 Bcast:192.168.99.255 Mask:255.255.255.0
说明在myvm1上通过192.169.99.100和172.17.0.2都是能访问到redis的,验证一下
root@myvm1:~# docker run -it --rm bitnami/redis:5.0 redis-cli -h 172.17.0.2
Welcome to the Bitnami redis container
172.17.0.2:6379>
root@myvm1:~# docker run -it --rm bitnami/redis:5.0 redis-cli -h 192.168.99.100
Welcome to the Bitnami redis container
192.168.99.100:6379>
所以方式一可以通过主机ip或者docker虚拟ip互相访问,但这有个问题,就是换了机器部署ip可能会变,需要改参数
使用link参数
root@myvm1:~# docker run -it --rm --link redis:redis-host bitnami/redis:5.0 redis-cli -h redis-host
Welcome to the Bitnami redis container
redis-host:6379>
--link redis:redis-host #冒号左边的redis表示要连接的容器名称,冒号右边为host别名,在容器内访问这个host别名就能访问到link的目标容器
通过link的方式,能够做到相对固定的写法,但官方已经不推荐使用link了,不久的将来将移除link模式,详见Legacy container links
创建自定义网络推荐
root@myvm1:~# docker network create my-bridge
a14418fb65607760962dff76e7e9ff0141bfdec3df1ee78677957fae244161ab
重新创建redis容器,指定使用自定义的my-bridge网络
root@myvm1:~# docker run -d --name redis -e ALLOW_EMPTY_PASSWORD=yes -p 6379:6379 --network my-bridge bitnami/redis:5.0
这次增加了--network my-bridge
参数
使用redis-cli验证一下连接
root@myvm1:~# docker run -it --rm --network my-bridge bitnami/redis:5.0 redis-cli -h redis
redis:6379>
注意这里也指定了--network my-bridge
,因为要加入同一个网络。访问redis容器直接使用容器名称,即redis
官方推荐使用自定义的bridge网络,创建容器之间的连接,是单台主机上多个容器之间网络通信的最佳实践,更多docker network create选项