前言
docker-compose是用于定义和运行多容器 Docker 应用程序的工具,通过docker-compose可以方便地协调多个容器的运行。
一般在使用docker-compose启动服务时,被同一个docker-compose.yml定于的服务(容器)会运行在一个隔离环境中,也就是说在这个环境中的容器是不能直接访问(如果没有设置的话)当前docker-compose外部的其它容器的,那么如何设置才能使compose中的容器访问外部容器呢?
本文以能够使compose启动的容器可以访问docker中已存在的mysql容器为目标进行讲解。docker容器间互相访问和docker-compose访问外部容器的原理是相同的。
环境
CentOS7 虚拟机环境
mysql 5.7
docker 19.03
docker-compose 1.29.1
问题描述
安装Docker时,Docker会默认创建一个内部的桥接网络docker0,每创建一个容器分配一个虚拟网卡,容器之间可以根据ip互相访问。
[root@master ~]# ifconfig
docker0: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:ecff:fefb:4f56 prefixlen 64 scopeid 0x20<link>
ether 02:42:ec:fb:4f:56 txqueuelen 0 (Ethernet)
RX packets 439143754 bytes 128557512771 (119.7 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 204101251 bytes 111401928320 (103.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
显然上面网卡的地址是172.17.0.1,然后看一下其他容器的IP地址,如:
[root@master ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13ed457ceeff redis "docker-entrypoint.s…" 6 months ago Up 5 hours 0.0.0.0:6379->6379/tcp redis
b7c1837830d4 mysql:5.7 "docker-entrypoint.s…" 6 months ago Up 6 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
mysql的这个容器(b7c1837830d4)已经提前创建,并进行了端口映射,映射到了宿主机的3306端口,可以在外部直接访问本mysql;
查看docker内mysql的地址:
[root@master ~]# docker inspect b7c1837830d4
...
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "4f2301f96036e312cf04bc2d893764f39493090e35d15a3d0bc46980a3817e9a",
"EndpointID": "1cc6ba035ff2f68d85db21651a2c0b09be94f54f84fd8cfca5ee779703fa1130",
"Gateway": "172.17.0.1", //关键
"IPAddress": "172.17.0.2", //关键
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:04",
"DriverOpts": null
}
}
...
如果不设置,默认创建的容器在172.17.0.0 网段下,gateway都是172.17.0.1,此时这些同一网段的容器是可以互通的。
但是使用docker-compose创建的容器一般都处于另一个网段,在本例中启动的docker-compose都在172.18.0.0的网段中,因此compose中的容器无法访问到mysql容器。
前置知识
docker的network模式
docker中有三种网络模式:
- bridge:桥接 docker (默认)
- none:不配置网络
- host:和宿主机共享网络
可通过以下命令查看:
[root@centos703 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
72f43ccbceb3 bridge bridge local
224a641ac129 host host local
4ce41b9297c2 none null local
创建出的容器默认都是bridge,可以通过以下命令查看当前network下有哪些容器(通过name和容器id辨识):
docker network inspect network名称
[root@centos703 ~]# docker network inspect bridge
...
"Containers": {
"13ed457ceeff98a4f47898016285a7cd84aab6655153c191c1cdb44b1ac8ee3f": {
"Name": "redis",
"EndpointID": "16b2fbb4a5893bdce43aac784a7f1dd52dda3bfd4930014b0bfae23aacb27a03",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"aded9df072e329423c7c24cc2f5722d49c5725a0eef024f3d382d4a09a84d826": {
"Name": "stupefied_perlman",
"EndpointID": "04db10227da5a189c23b80fab649573e96e510a71f62e4e1e2bb92841d91e5ed",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
...
如果想要使docker容器之间可以互通,最方便的方法就是让容器处于同一个network之下!
实现步骤
创建自定义network
这一步的目的是自己创建一个network,然后把docker-compose还有我们已有的mysql容器都放到这个network下,这样他们就可以互通了。
[root@centos703 ~]# docker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.1 mynet
--driver bridge 网络模式为 桥接模式
--subnet 172.18.0.0/16 设置子网
--gateway 172.18.0.1 设置网关
--mynet 自定义的network名
查看:
[root@centos703 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
72f43ccbceb3 bridge bridge local
224a641ac129 host host local
9832e7cfc6fd mynet bridge local
4ce41b9297c2 none null local
我们新创建的network —— mynet已经出现了。
把已有容器(mysql)的network修改为自定以network
###解除容器绑定的网络 bridge:容器以前的network b7c:容器标识符(容器id前缀,也可写完整)
[root@centos703 ~]# docker network disconnect bridge b7c
##为容器重新指定自定义网络
[root@centos703 ~]# docker network connect b7c mynet
##重新启动容器
[root@lcentos703 ~]# docker restart b7c
查看效果:
[root@centos703 ~]# docker inspect mynet
...
"Containers": {
"b7c1837830d4584d2e82f697bcb867e3f63801e5b51796f152e5815e0c48b01e": {
"Name": "mysql",
"EndpointID": "222689b1a48bce1fbce63b299b5cdd6026bf0e52bf1e04d0b924d5a49257d805",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
...
可以看到mynet的容器中出现了mysql(b7c);
查看容器的信息:
[root@centos703 ~]# docker inspect b7c
...
"Networks": {
"mynet": {
"IPAMConfig": {},
"Links": null,
"Aliases": [
"b7c1837830d4"
],
"NetworkID": "9832e7cfc6fd3d7557623178ad4b551c391f96fe7ce8636606005b531a1edd50",
"EndpointID": "222689b1a48bce1fbce63b299b5cdd6026bf0e52bf1e04d0b924d5a49257d805",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": {}
}
}
...
可以看到,网段已经变动到172.18.0.0了。
设置docker-compose的配置文件
设置docker-compose的配置文件可以参看官方文档或者中文介绍。
在这里因为我们已经自定义了一个network,为了使docker-compose启动的容器和mysql容器在同一个network下,要在docker-compose.yml中指定使用已存在的network:
version: '2'
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
networks:
default:
external:
name: my-pre-existing-network
这是官网给的例子,需要在networks标签下使用external选项,指定已有的networks。
在本具体案例中,如下,设置networks - default - external - name: mynet, 并且把所有连接mysql的ip都改为上边查出来的172.18.0.2
version: '3.6'
services:
xxl-job:
image: registry.xx.aliyuncs.com/xxx/xxx:0.3
container_name: xxl-job
environment:
#修改数据源连接
spring_datasource_url: jdbc:mysql://172.18.0.2:3306/xxl_job?Unicode=true&characterEncoding=UTF-8
spring_datasource_username: xxx
spring_datasource_password: xxx
spring_mail_host: smtp.qq.com
spring_mail_port: 25
spring_mail_username: xxx@qq.com
spring_mail_password: xxx
xxl_job_accessToken:
xxl_job_i18n:
ports:
- '10052:10052'
expose:
- '10052'
#networks: 可以在此处单独配置
# networkname
command: bash -c "/opt/froxxx/start.sh xxl-job"
froxxx-monitor:
image: registry.xxx.com/xxx/xxx:0.3
container_name: froxxx-monitor
environment:
# 修改数据源连接
datasource_frosxxx_url: jdbc:mysql://172.18.0.2:3306/froxxx?useSSL=false&verifyServerCertificate=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
datasource_frosxxx_username: xxx
datasource_frosxxx_password: xxx
...
ports:
- '10054:10054'
- '9999:9999'
expose:
- '10054'
- '9999'
depends_on:
- frosxxx-spi
#networks: 可以在此处单独配置
# networkname
command: bash -c "/opt/frosxxx/start.sh frosxxx-monitor"
# 网络设置
networks:
#可以自定义network名称,这里使用default表示如果不在service中单独配置就使用该设置
default:
external:
#使用自定义network
name: mynet
修改完成后,按照docker-compose的正常启动方法启动即可:
docker-compose up
记得要把以前错误的先docker-compose down
删除掉。
这时再使用下边这两条命令
# docker network inspect mynet
# docker inspect 容器名
查看mynet下是否出新了docker-compose启动的容器,以及docker-compose启动的容器是否已经和mysql容器在同一网段,如果相同,此时docker-compose中的容器就可以和mysql互通了。
总结
关键点在于docker中network的设置。
参考
官方 Networking in ComposeDocker系列教程24-Docker Compose网络设置docker compose 菜鸟教程