自从Docker容器工具诞生以来,当我们在正式线上环境进行容器部署以及容器管理任务时,通常容器之间是需要进行数据通信的,来支撑正常业务运转。因此容器间的通信也随之成为热点,也是生产环境中的重中之重。
容器间的网络通信可分为两大方面:单主机容器上的相互通信,跨主机的容器相互通信。
一、Docker单主机容器通信
1、假设通过容器ip访问 【不可行】
由于docker容器每次在重启后,其IP会发生变化。因此通过容器ip访问不可行。
2、假设通过宿主机的ip:port访问 【不可行】
通过宿主机的ip:port访问,只能依靠监听在暴露出的端口的进程来进行有限的通信,过于狭隘,不可行
单主机容器通信最佳选择:
通过docker network建立桥接网络。User-defined networks
实现步骤:
1、首先通过docker network来创建一个桥接网络,在docker run的时候将容器指定到新创建的桥接网络中,这样同一桥接网络中的容器就可以通过互相访问。
创建网络
docker network create test-network
2、启动容器时,加入创建的网络
docker run -it --network test-network --network-alias mysql -e MYSQL-ROOT_PASSWORD=123 mysql:5.7
3、启动被链接的容器
docker run -it --network test-network --network-alias centos centos /bin/bash
可通过如下指令进入被链接容器test-centos,查看是否能够进行容器间通信
docker exec -it test-centos /bin/bash
输入指令mysql -h test-mysql -uroot -p123
如此,便说明容器间通信成功建立!
二、容器网络模式总结
按照docker官方说法,docker容器的网络有五种模式:
1、bridge模式,–net=bridge(默认)
这是docker网络的默认设置,为容器创建独立的网络命令空间,容器具有独立的网卡等所有单独的网络栈,是最常用的使用方式。
在进行docker run启动容器的时候,如果不加–net参数,就默认采用这种网络模式。
安装完docker,系统会自动添加一个供docker使用的网桥docker0,我们创建一个新的容器时,容器通过DHCP获取一个与docker0同网段的IP地址,并默认连接到docker0网桥,以此实现容器与宿主机的网络互通。
2、host模式,–net=host
这个模式创建出来的容器,直接使用容器宿主机的网络命名空间,将不拥有自己独立的Network Namespace,即没有独立的网络环境。它使用的是宿主机的ip和端口。
3、none模式,–net=none
为容器创建独立网络命名空间,但不为它做任何网络配置,容器中只有lo,用户可以在此基础上,对容器网络做任意定制。这种模式下,docker不为容器进行任何网络配置,需要我们自己为容器添加网卡,配置IP。
因此,如果要使用pipework配置docker容器的ip地址,必须要在none模式下才可以。
4、其他容器模式(即container模式,join模式),–net=container:NAME_or_ID
与host模式类似,只是容器将与指定的容器共享网络命名空间。这个模式就是指定一个已有的容器,共享该容器的IP和端口。
注意:除了网络方面两个容器共享,其他的如文件系统,进程等还是隔离开的。
这些网络模式在相互通信下的对比如下:
注:南北向通信指的是容器与宿主机外界的访问机制
东西向通信指的是同一宿主机上,与其他容器相互访问的机制
可通过docker network ls查看容器的网络模式:
在本机中,如下图,
三、吃个栗子
举例nginx与php两个容器之间通信:
1、启动php容器
[root@docker ~]# docker run -d --name=php -v /www:/usr/local/nginx/html php
2、启动nginx容器
[root@docker ~]# docker run -d --name=nginx --link=php:php -v /www:/usr/local/nginx/html -p 81:80 nginx
3、通过docker ps -a 查看容器状态
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
58280fe851f9 nginx "/usr/local/nginx/..." 15 seconds ago Up 14 seconds 0.0.0.0:81->80/tcp nginx
9ea150c35587 php "/usr/local/php/sb..." 36 seconds ago Up 35 seconds 9000/tcp php
通过容器间的选项–link指定容器名称进行不同容器间的通信(–link container_name或者将container_name取一个别名)
现在使用另外一种方式替代–link来达到容器间的通信:docker network
查看local的网络信息:
docker network ls
[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5133ec415c3c bridge bridge local
f359ca4e2d39 host host local
8d68673c045c none null local
4、现在开始创建网络名,名为test_net且driver为bridge的网络:(默认创建的就是bridge)
[root@docker ~]# docker network create test_net
67e29f0e4a77c79144efc337a081a889188b5b8e289968f22be6e4ddd9b80610
[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5133ec415c3c bridge bridge local
f359ca4e2d39 host host local
67e29f0e4a77 my_net bridge local
8d68673c045c none null local
5、利用–network启动容器提供服务
[root@docker ~]# docker run -d --name=php --network test_net --network-alias php -v /www:/usr/local/nginx/html php
6b493cbe8207dee4cb4d5945cfce305dba96914083bd7f46841b0b42376bcb99
[root@docker ~]# docker run -d --name=nginx --network test_net --network-alias nginx -v /www:/usr/local/nginx/html -p 80:80 nginx
5ab220196b52bb768bef508433f0b920eecee70c3ee47880ebc5e2a74b5ee254
注意:可通过–network-alias 给网络命名取别名,方便区分
6、至此,便可以进行测试
[root@docker ~]# docker exec -it nginx ping php
PING php (172.18.0.2) 56(84) bytes of data.
64 bytes from php.my_net (172.18.0.2): icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from php.my_net (172.18.0.2): icmp_seq=2 ttl=64 time=0.090 ms
两个容器之间可以ping通,说明两个容器在同一个网络test_net内,而nginx里面的ping的php是容器名(管理的是容器级别)
因为nginx是可以ping通php的,所以在nginx配置中:
server {
listen 80;
root /usr/local/nginx/html;
index index.htm index.html index.php;
location ~ \.php$ {
root /usr/local/nginx/html;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
配置文件中的php不会导致nginx启动失败,可通过network的driver bridge实现容器间的访问
在此,查看容器之间是否共用同一个网络:
可通过查询compose配置文件。
上面在容器启动的时候使用的是选项–network,而在compose的配置文件中则是networks,现在通过配置文件来进行阐述该参数的作用:
[root@docker lnmp]# cat lnmp.yml
version: '3'
services:
nginx:
image: nginx
container_name: lnmp-nginx
depends_on:
- php
ports:
- "80:80"
networks:
- "net1"
volumes:
- "/www:/usr/local/nginx/html"
php:
image: php
container_name: lnmp-php
expose:
- "9000"
networks:
- "net1"
volumes:
- "/www:/usr/local/nginx/html"
networks:
net1:
driver: bridge
由上述文件中可以看出networks定义了一个名称为net1的网络,由于networks是top-level(顶层级别,所以需要在顶层设置),而在创建网络时候需要指定driver(单一网络使用bridge,swarm集群使用overlay),而且driver内容不能省略,
在nginx和php两个service中使用了同一网络net1,现在将服务启动:
[root@docker lnmp]# docker-compose -f lnmp.yml up
Creating network "lnmp_net1" with driver "bridge"
Creating lnmp-php ...
Creating lnmp-php ... done
Creating lnmp-nginx ...
Creating lnmp-nginx ... done
Attaching to lnmp-php, lnmp-nginx
可看出在启动服务的时候创建了服务级别的网络lnmp_net1
[root@docker lnmp]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5133ec415c3c bridge bridge local
f359ca4e2d39 host host local
29d798852b52 lnmp_net1 bridge local
67e29f0e4a77 my_net bridge local
8d68673c045c none null local
服务nginx和php在网络lnmp_net1中实现了互联通信
[root@docker lnmp]# docker-compose -f lnmp.yml exec nginx ping php
PING php (172.19.0.2) 56(84) bytes of data.
64 bytes from lnmp-php.lnmp_net1 (172.19.0.2): icmp_seq=1 ttl=64 time=0.060 ms
在nginx服务中能够访问php的服务,在compose编排中级别的访问是对service级别的访问,所以在nginx服务配置文件中对应的php能够实现,不会导致nginx启动失败。
好了,至此,nginx与php之间的网络通信就已经实现。
休息,tired。