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

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

docker-i-see

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

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

Docker 网络解释

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

docker-network

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

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

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

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

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


$ 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 如下文件:

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

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

docker-compose up -d

可以看到 5 个容器:


$ 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 的网络:


$ 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 地址吗?

$ 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

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

$ 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:

$ 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

$ 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 内部

$ 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

# 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

# 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

# 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

* 插图由 icons8.comMurat Kalkavan 提供

原文:How to Get A Docker Container IP Address - Explained with Examples,作者:Marcelo Costa