前言

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 菜鸟教程