容器没有关于它所连接的网络类型的信息,无论是网桥、覆盖网络、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 提供了在隔离环境(容器)中打包和运行应用的能力。
你一定会想 - 得了吧,解释 Docker 的文章随处可见。

别担心,我们会跳过基础知识。本文的目标人群需要对 Docker 和容器有一定的了解。
但是你是否也曾好奇过应该怎样获得 Docker 容器的 IP 地址呢?
Docker 网络解释
首先来了解一下 Docker 的网络是如何工作的。首先是默认的bridge 网络。当使用 Docker 时,如果没有指定其它驱动默认会使用桥接网络。

Docker 网络图摘自了解Docker网络驱动程序及其用例
bridge 网络是主机内部的专用网络,容器可以通过它进行通信。也可暴漏端口在外部访问。
单独容器中的应用通过桥接网络互相通讯时。
在上图中db 和 web 可以通过用户创建的桥接网络 mybridge 互相通讯。
如果还没有在 Docker 中添加网络过,可以看到类似下面的信息:
1 | $ docker network ls |
默认的bridge 网络显示在列表中,下面还有 host 和 none。我们会暂时忽略这两个网络,在接下来的例子使用 bridge 网络。
Docker 容器的 IP 地址
默认情况下,会为连接到容器的每个 Docker 网络分配一个IP地址,并为每个网络分配一个默认的子网掩码,用作稍后分配 IP的地址池。
通常 Docker 默认使用****172.17. 0.0/16 作为容器网络的子网。
为了便于理解,运行一个真实的用例。

Docker 例子
作为说明,我们会使用 Hive 和 Hadoop 环境,一共 5 个 Docker 容器。
检查将要执行的docker-compose.yml 如下文件:
1 | version: "3" |
没人想要阅读 这么长的配置文件对吗?这是图解:

好多了,来启动这些容器吧:
1 | docker-compose up -d |
可以看到 5 个容器:
1 | $ docker ps --format \ |
接下来检查一下 Docker 的网络:
1 | $ docker network ls |
**等一下,怎么有一个没见过的 **docker-hive_default 网络。
默认情况下,docker compose 会为应用设置一个网络。 应用的网络会根据 “project name” 来命名,该名称源自其所在目录的名称。
由于项目目录是docker-hive,这就解释了新出现的网络。
接下来的例子解释如何获取容器的 IP 地址。
如何获得容器的 IP 地址 - 例子
接下来,就是见证奇迹的时刻。

1. 使用 Docker Inspect
Docker inspect 是检索 Docker 对象底层信息的很棒的方式。可以以非常简单的方式在返回的 JSON 里找出想要的字段。
所以dockerhive_datanode 里面有我们想要的 IP 地址吗?
1 | $ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 75000c343eb7 |
之前不是说 Docker 使用容器网络默认的****172.17. 0.0/16 的子网吗?为什么和返回的 IP地址 172.18.0.5 并不在一个网段呢?

需要查看一下网络设置才能解答它:
1 | $ docker network inspect -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 9f6bc3c15568 |
我们在虚拟计算引擎里执行了这个例子,在这个测试里,docker 网络分配了一个不同的子网****172.18.0.0/16。原来如此。
另外,我们可以找到在docker-hive_default 网络内所有的 IP 地址。
也就是不必去每个容器里找它的 IP:
1 | $ docker network inspect -f '{{json .Containers}}' 9f6bc3c15568 | jq '.[] | .Name + ":" + .IPv4Address' |

上面使用了jq 来解析 Containers 映射对象。
2. 使用 Docker exec
**在接下来的例子里会用到 **dockerhive_namenode。
1 | $ docker exec dockerhive_namenode cat /etc/hosts |
3. 在 Docker Container 内部
1 | $ docker exec -it dockerhive_namenode /bin/bash |
我们甚至可以在容器内部找到同一网络下的其他容器的 IP 地址:
Data node
1 | # running inside the dockerhive_namenode container |
Hive mestastore
1 | # running inside the dockerhive_namenode container |
Hive server
1 | # running inside the container |
拓展
所有的例子都是在 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 | version: "3" |
当执行docker-compose up 的时候。会发生以下事情:
- **会创建一个名字是 **
myapp_default的网络(networks) web这个容器会加入到myapp_default网络中,并且在网络中的名称为:web。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 | version: "3" |
- 一级配置
networks用来创建自定义的网络 。这里配置了两个frontend和backend. 且自定义了网络类型。 - 每一个serviceg下,
proxy,app,db,networks 都定义了一下配置。proxy只加入到frontend网络。db只加入到backend网络。app同时加入到frontend和backend。db和proxy不能通讯,因为不在一个网络中。app和两个都能通讯,因为app在两个网络中都有配置。db和proxy要通讯,只能通过app这个应用来连接。
配置默认网络
不指定网络时,默认的网络也是可以配置的。不配置的话,默认是使用:brige,也可以修改为其他 的。
1 | version: "3" |
指定一个已经存在的网络
多个容器,不在相同的配置中,也会有网络通讯的需求 。那么就可以使用公共的网络配置。
容器可以加入到已经存在的网络。
1 | networks: |
这里name就是指定已经存在的网络名称。