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 网络是主机内部的专用网络,容器可以通过它进行通信。也可暴漏端口在外部访问。

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

在上图中db 和 web 可以通过用户创建的桥接网络 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 网络显示在列表中,下面还有 host 和 none。我们会暂时忽略这两个网络,在接下来的例子使用 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_PORT和CONTAINER_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 用来创建自定义的网络 。这里配置了两个frontend和backend . 且自定义了网络类型。
  2. 每一个serviceg下,
    proxy,app,db,networks 都定义了一下配置。
    1. proxy 只加入到 frontend网络。
    2. db 只加入到backend网络。
    3. app同时加入到 frontend和backend 。
    4. db和proxy不能通讯,因为不在一个网络中。
    5. app和两个都能通讯,因为app在两个网络中都有配置。
    6. db和proxy要通讯,只能通过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就是指定已经存在的网络名称。