docker compose network

容器没有关于它所连接的网络类型的信息,无论是网桥、覆盖网络、macvlan 网络还是自定义网络插件(bridge, an overlay, a macvlan network, or a custom network plugin)。容器只能看到具有 IP 地址、网关、路由表、DNS 服务和其他网络详细信息的网络接口。也就是说,除非容器使用 none 网络驱动程序。本页从容器的角度描述网络。

发布端口

默认情况下,当您使用 docker create 或 docker run 创建或运行容器时,容器不会将其任何端口暴露给外界。要使端口可用于 Docker 外部的服务,或可用于在不同网络上运行的 Docker 容器,请使用 --publish 或 -p 标志。这会在容器中创建一个防火墙规则,将容器端口映射到 Docker 主机上的一个端口到外部世界。这里有些例子:

Ip地址和hostname

默认情况下,容器会为其连接的每个 Docker 网络获取一个 IP 地址。容器从它所连接的网络的 IP 池中接收一个 IP 地址。 Docker 守护进程有效地充当每个容器的 DHCP 服务器。每个网络还有一个默认的子网掩码和网关。

当容器启动时,它只能连接到一个网络,使用 --network 标志。您可以使用 docker network connect 命令将正在运行的容器连接到多个网络。当您使用 --network 标志启动容器时,您可以使用 --ip 或 --ip6 标志为该网络上的容器指定 IP 地址。

当您使用 docker network connect 将现有容器连接到不同的网络时,您可以在该命令上使用 --ip 或 --ip6 标志来指定容器在附加网络上的 IP 地址。

同样,容器的主机名默认为容器在 Docker 中的 ID。您可以使用--hostname 覆盖主机名。使用 docker network connect 连接到现有网络时,您可以使用 --alias 标志为该网络上的容器指定一个额外的网络别名。

如何获取 Docker 容器的 IP 地址

Docker 提供了在隔离环境(容器)中打包和运行应用的能力。

你一定会想 - 得了吧,解释 Docker 的文章随处可见。

docker-i-see

别担心,我们会跳过基础知识。本文的目标人群需要对 Docker 和容器有一定的了解。

但是你是否也曾好奇过应该怎样获得 Docker 容器的 IP 地址呢?

Docker 网络解释

首先来了解一下 Docker 的网络是如何工作的。首先是默认的bridge 网络。当使用 Docker 时,如果没有指定其它驱动默认会使用桥接网络。

docker-network

Docker 网络图摘自了解Docker网络驱动程序及其用例

bridge 网络是主机内部的专用网络,容器可以通过它进行通信。也可暴漏端口在外部访问。

单独容器中的应用通过桥接网络互相通讯时。

在上图中dbweb 可以通过用户创建的桥接网络 mybridge 互相通讯。

如果还没有在 Docker 中添加网络过,可以看到类似下面的信息:

1
2
3
4
5
6
$ docker network ls

NETWORK ID         NAME                 DRIVER             SCOPE
c3cd46f397ce       bridge               bridge             local
ad4e4c24568e       host                 host               local
1c69593fc6ac       none                 null               local

默认的bridge 网络显示在列表中,下面还有 hostnone。我们会暂时忽略这两个网络,在接下来的例子使用 bridge 网络。

Docker 容器的 IP 地址

默认情况下,会为连接到容器的每个 Docker 网络分配一个IP地址,并为每个网络分配一个默认的子网掩码,用作稍后分配 IP的地址池。

通常 Docker 默认使用****172.17. 0.0/16 作为容器网络的子网。

为了便于理解,运行一个真实的用例。

drawing

Docker 例子

作为说明,我们会使用 Hive 和 Hadoop 环境,一共 5 个 Docker 容器。

检查将要执行的docker-compose.yml 如下文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
version: "3"
services:
namenode:
  image: bde2020/hadoop-namenode:2.0.0-hadoop2.7.4-java8
  volumes:
    - namenode:/hadoop/dfs/name
  environment:
    - CLUSTER_NAME=test
  env_file:
    - ./hadoop-hive.env
  ports:
    - "50070:50070"
datanode:
  image: bde2020/hadoop-datanode:2.0.0-hadoop2.7.4-java8
  volumes:
    - datanode:/hadoop/dfs/data
  env_file:
    - ./hadoop-hive.env
  environment:
    SERVICE_PRECONDITION: "namenode:50070"
  ports:
    - "50075:50075"
hive-server:
  image: bde2020/hive:2.3.2-postgresql-metastore
  env_file:
    - ./hadoop-hive.env
  environment:
    HIVE_CORE_CONF_javax_jdo_option_ConnectionURL: "jdbc:postgresql://hive-metastore/metastore"
    SERVICE_PRECONDITION: "hive-metastore:9083"
  ports:
    - "10000:10000"
hive-metastore:
  image: bde2020/hive:2.3.2-postgresql-metastore
  env_file:
    - ./hadoop-hive.env
  command: /opt/hive/bin/hive --service metastore
  environment:
    SERVICE_PRECONDITION: "namenode:50070 datanode:50075 hive-metastore-postgresql:5432"
  ports:
    - "9083:9083"
hive-metastore-postgresql:
  image: bde2020/hive-metastore-postgresql:2.3.0

代码源自 docker-hive GitHub

没人想要阅读 这么长的配置文件对吗?这是图解:

Screen-Shot-2020-06-21-at-2.48.18-PM

好多了,来启动这些容器吧:

1
docker-compose up -d

可以看到 5 个容器:

1
2
3
4
5
6
7
8
9
$ docker ps --format \
"table {{.ID}}\t{{.Status}}\t{{.Names}}"

CONTAINER ID       STATUS                   NAMES
158741ba0339       Up 1 minutes             dockerhive_hive-metastore-postgresql
607b00c25f29       Up 1 minutes             dockerhive_namenode
2a2247e49046       Up 1 minutes             dockerhive_hive-metastore
7f653d83f5d0       Up 1 minutes (healthy)   dockerhive_hive-server
75000c343eb7       Up 1 minutes (healthy)   dockerhive_datanode

接下来检查一下 Docker 的网络:

1
2
3
4
5
6
7
$ docker network ls

NETWORK ID         NAME                 DRIVER             SCOPE
c3cd46f397ce       bridge               bridge             local
9f6bc3c15568       docker-hive_default   bridge             local
ad4e4c24568e       host                 host               local
1c69593fc6ac       none                 null               local

**等一下,怎么有一个没见过的 **docker-hive_default 网络。

默认情况下,docker compose 会为应用设置一个网络。 应用的网络会根据 “project name” 来命名,该名称源自其所在目录的名称。

由于项目目录是docker-hive,这就解释了新出现的网络。

接下来的例子解释如何获取容器的 IP 地址。

如何获得容器的 IP 地址 - 例子

接下来,就是见证奇迹的时刻。

drawing

1. 使用 Docker Inspect

Docker inspect 是检索 Docker 对象底层信息的很棒的方式。可以以非常简单的方式在返回的 JSON 里找出想要的字段。

所以dockerhive_datanode 里面有我们想要的 IP 地址吗?

1
2
3
$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 75000c343eb7

172.18.0.5

之前不是说 Docker 使用容器网络默认的****172.17. 0.0/16 的子网吗?为什么和返回的 IP地址 172.18.0.5 并不在一个网段呢?

Screen-Shot-2020-06-22-at-3.25.07-PM

图片截自IP address in CIDR range

需要查看一下网络设置才能解答它:

1
2
3
$ docker network inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}'  9f6bc3c15568

172.18.0.0/16

我们在虚拟计算引擎里执行了这个例子,在这个测试里,docker 网络分配了一个不同的子网****172.18.0.0/16。原来如此。

另外,我们可以找到在docker-hive_default 网络内所有的 IP 地址。

也就是不必去每个容器里找它的 IP:

1
2
3
4
5
6
7
$ docker network inspect -f '{{json .Containers}}' 9f6bc3c15568 | jq '.[] | .Name + ":" + .IPv4Address'

"dockerhive_hive-metastore-postgresql:172.18.0.6/16"
"dockerhive_hive-metastore:172.18.0.2/16"
"dockerhive_namenode:172.18.0.3/16"
"dockerhive_datanode:172.18.0.5/16"
"dockerhive_hive-server:172.18.0.4/16"

drawing

上面使用了jq 来解析 Containers 映射对象。

2. 使用 Docker exec

**在接下来的例子里会用到 **dockerhive_namenode

1
2
3
4
5
6
7
8
9
$ docker exec dockerhive_namenode cat /etc/hosts

127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.3     607b00c25f29

3. 在 Docker Container 内部

1
2
3
4
5
$ docker exec -it dockerhive_namenode /bin/bash
# running inside the dockerhive_namenode container
ip -4 -o

7: eth0   inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0

我们甚至可以在容器内部找到同一网络下的其他容器的 IP 地址:

Data node

1
2
3
4
5
# running inside the dockerhive_namenode container
ping dockerhive_datanode

PING dockerhive_datanode (172.18.0.5): 56 data bytes
64 bytes from 172.18.0.5: icmp_seq=0 ttl=64 time=0.092 ms

Hive mestastore

1
2
3
4
5
# running inside the dockerhive_namenode container
ping dockerhive_hive-metastore

PING dockerhive_hive-metastore_1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.087 ms

Hive server

1
2
3
4
5
# running inside the container
ping dockerhive_hive-server

PING dockerhive_hive-server (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: icmp_seq=0 ttl=64 time=0.172 ms

拓展

所有的例子都是在 Linux 的VM 计算引擎下执行,如果你用的是 macOS 或者 Windows 例子里的命令可能会有些区别。

**另外请记住,例子中所有的 IP 地址都是针对 **docker-hive_default 网络内部的。如果需要在外部连接这些容器,则需要主机的外部 IP(假设容器正确暴漏了端口)。

**或者你用的是 kubernetes 来管理 Docker 容器,可以让它来为你搞定 IP 地址 **kubernetes-expose-external-ip-address

docker-compose配置networks

默认网络

例如, 假设有一个项目,目录名myapp, docker-compose.yml 配置如下:

1
2
3
4
5
6
7
8
9
10
version: "3"
services:
web:
  build: .
  ports:
     - "8000:8000"
db:
  image: postgres
  ports:
     - "8001:5432"

当执行docker-compose up 的时候。会发生以下事情:

  1. **会创建一个名字是 **myapp_default的网络(networks)
  2. web这个容器会加入到 myapp_default网络中,并且在网络中的名称为:web
  3. db这个容器会加入到 myapp_default网络中,并且在网络中的名称为:db

这里,每个容器都能通过应用名找到对方,例如,web容器可以通过 postgres://db:5432 来使用 Pg数据库。

上面例子还有一个注意点就是端口号,注意区分HOST_PORTCONTAINER_PORT,以上面的db为例:

  • 8001 是宿主机的端口
  • 5432(postgres的默认端口) 是容器的端口

当容器之间通讯时 , 是通过CONTAINER_PORT 来连接的。

这里有宿主机端口,那么容器就可以通过宿主机端口和外部应用连接。

更新容器

对已经启动的容器,再执行docker-compose up 的时候,旧容器删除,然后创建一个新的容器。

新容器会加入到网络,相同的网络名称,但容器IP是不一样的。已经连接的其他容器会自己重连到新的容器IP上。

自定义网络

可能通过一级配置networks来自定义网络,可以创建更复杂的网络选项和配置,也可以用来连接已经存在的网络(不是通过compose创建的)

每个service 配置下也可以指定networks配置,来指定一级配置的网络。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
version: "3"
services:

proxy:
  build: ./proxy
  networks:
     - frontend
app:
  build: ./app
  networks:
     - frontend
     - backend
db:
  image: postgres
  networks:
     - backend

networks:
frontend:
   # Use a custom driver
  driver: custom-driver-1
backend:
   # Use a custom driver which takes special options
  driver: custom-driver-2
  driver_opts:
    foo: "1"
    bar: "2"
  1. 一级配置networks 用来创建自定义的网络 。这里配置了两个frontendbackend . 且自定义了网络类型。
  2. 每一个serviceg下,
    proxy,app,db,networks 都定义了一下配置。
    1. proxy 只加入到 frontend网络。
    2. db 只加入到backend网络。
    3. app同时加入到 frontendbackend
    4. dbproxy不能通讯,因为不在一个网络中。
    5. app和两个都能通讯,因为app在两个网络中都有配置。
    6. dbproxy要通讯,只能通过app这个应用来连接。

配置默认网络

不指定网络时,默认的网络也是可以配置的。不配置的话,默认是使用:brige,也可以修改为其他 的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: "3"
services:

 web:
   build: .
   ports:
     - "8000:8000"
 db:
   image: postgres

networks:
 default:
   # Use a custom driver
   driver: custom-driver-1

指定一个已经存在的网络

多个容器,不在相同的配置中,也会有网络通讯的需求 。那么就可以使用公共的网络配置。

容器可以加入到已经存在的网络。

1
2
3
4
networks:
 default:
   external:
     name: my-pre-existing-network

这里name就是指定已经存在的网络名称。