# Docker容器网络之服务名称或别名连接容器服务原理剖析

> 作者/来源: UCloud 运营管理员
> 发布时间: 2023-01-11T05:19:00.000Z
> 分类: AI专区
> 标签: AI, Python, Docker
> 原文链接: http://117.50.162.249:3000/yun/articles/1426

---

# Docker容器网络之服务名称或别名连接容器服务原理剖析

> 来源: https://www.ucloud.cn/yun/129499.html
> 作者: IT那活儿
> 发布日期: 发布于2023-01-11 13:19

Docker容器网络之服务名称或别名连接容器服务原理剖析

**点击上方“IT那活儿”公众号，关注后了解更多内容，不管IT什么活儿，干就完了！！！**

1

****概  述****

## Docker 容器启动的时候，使用默认的 bridge 网络是不支持指派固定 IP 的；

## 每次Docker 容器重启时，会按照顺序获取对应的IP 地址，这个就导致重启Docker， 容器的IP 地址会发生变化；

## 虽然可以通过创建自定义网络在启动容器时指定 IP ， 但此问题不在本文章中阐述。

## **本文讲述的是 docker-compose 通过服务名或者别名连接容器服务的原理以及分析，**其中主要涉及到 netfilter/iptables 防火墙，evth- pair 技术，DNS 等技术，由于 Docker 容器网络非常之复杂，本文不会对 docker 之外的技术以及 docker 无关本文的其它技术进行过多介绍。

关键字：evth-pair 技术，iptables，netfilter，embedded DNS server，Bridge，命名空间。

2

****Docker五种网络模式介绍****

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_000.png)

3

****veth-pair技术****

veth-pair 技术在网桥中的应用：

## ![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_001.png)

## **图1 veth-pair**

## **网桥是一种对帧转发的技术，**工作在 OSI 七层模型中的第二层（数据链路层），在数据链路层使用 MAC 地址转发数据。

## veth pair 是成对出现的一种虚拟网络设备接口，一端连着网络协议栈，一端彼此相连，如图1；正因为有这个特性，它充当着一个桥梁，连接着各种虚拟网络设备，常被用于构建虚拟网络拓扑。

通过与 Docker Bridge 网络相连，docker0 相当于一台交换机，可以中转 docker。

容器的流量，如图2 所示。

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_002.png)

**图2 docker0**

4

****Docker DNS实现****

在早期的容器服务发现使用的是 docker link，通过修改容器内的 /etc/hosts 文件来完成，其地址由 Docker 引擎维护，因此容器间才可以通过别名互访；这种方法存在很多的问题，目前已经过时不再使用。docker 自定义网络里的 dns（embedded DNS server），如图3 所示；

docker 会修改容器里的/etc/resolv.conf 文件，把 dns 服务器设置成 127.0.0.11，因为 127.0.0.0/8 地址都是本机回环地址， 所以 dns 查询的时候实际上是把请求发给了自己。虽然是发给自己，但是还是要走 netfilter 的 nat 表的 output 链里把发往 127.0.0.11:53 的 UDP/TCP 包转到了127.0.0.11:<随机端口>

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_003.png)

**图 3embedded DNS server**

```
*nat:PREROUTING ACCEPT [2:168]:INPUT ACCEPT [2:168]:OUTPUT ACCEPT [9:620]:POSTROUTING ACCEPT [17:1091]:DOCKER_OUTPUT - [0:0]:DOCKER_POSTROUTING - [0:0]-A OUTPUT -d 127.0.0.11/32 -j DOCKER_OUTPUT-A POSTROUTING -d 127.0.0.11/32 -j DOCKER_POSTROUTING-A DOCKER_OUTPUT -d 127.0.0.11/32 -p tcp -m tcp --dport 53 -j DNAT --to-destination 127.0.0.11:45481-A DOCKER_OUTPUT -d 127.0.0.11/32 -p udp -m udp --dport 53 -j DNAT --to-destination 127.0.0.11:46340-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p tcp -m tcp --sport 45481 -j SNAT --to-source :53-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p udp -m udp --sport 46340 -j SNAT --to-source :53 COMMIT# Completed on Fri Mar 25 13:15:36 2022
```

**表 1 容器 iptables**

**开始测试：**

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_004.png)

```
composetest_web_1[root@VM-12-7-centos composetest]#11 hours ago Up 10 hours 0.0.0.0:5000->5000/tcp, :::5000->5000/tcpcomposetest_redis_1af90b8c4350f composetest_web "flask run"
```

**表 2 docker ps**

进入 composetest\_redis\_1 容器使用 netstat -anp 查看发现是 dockerd 在监听TCP 3971/UDP 46340 端口，等 DNS 请求又回环到filter 表的input 链的时候dockerd 就接到了这个请求;

dockerd 自身处理这些请求，处理不了的要发给宿主机配置的dns 服务器。当端点加入网络时，dockerd 将设置一个 DNS 服务器，只需创建一个套接字并在容器 netns 中监听，这个套接字在主机 netns 中接受，由 dockerd 处理。

```
[root@VM-12-7-centos composetest]# netstat -pantuActive Internet connections (servers and established)Proto Recv-Q Send-Q Local AddressForeign AddressStatePID/Program name
```

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_005.png)

**表 3 查看容器网络连接**

```
[root@VM-12-7-centos composetest]# nc -nvz 127.0.0.11 53 Ncat: Version 7.50 ( https://nmap.org/ncat )Ncat: Connected to 127.0.0.11:53.Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.
```

**表 4  测试容器 DNS 连通性**

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_006.png)

**表 5 通过 docker 守护进程查找 TCP/UDP 套接字**

```
[root@VM-12-7-centos composetest]# lsof -o 3917|egrep UDP|TCP[root@VM-12-7-centos composetest]# docker inspect composetest_redis_1|grep Pid "Pid": 17693,"PidMode": "","PidsLimit": null,
```

**表 6 查找容器线程 id**

```
[root@VM-12-7-centos composetest]# ll /proc/17693/ns/netlrwxrwxrwx 1 polkitd mysql 0 Mar 25 10:07 /proc/17693/ns/net -> net:[4026532226]
```

**表 7 查找容器网络命名空间**

```
[root@VM-12-7-centos composetest]# ln -s /proc/17662/ns/net /var/run/netns/composetest_web_1 [root@VM-12-7-centos composetest]#[root@VM-12-7-centos composetest]# ll /var/run/netns/composetest_web_1lrwxrwxrwx 1 root root 18 Mar 25 04:52 /var/run/netns/composetest_web_1 -> /proc/17662/ns/net
```

**表 8 容器命名空间软连接到宿主机命名空间**

```
[root@VM-12-7-centos ~]# ip netns exec composetest_web_1 ping baidu.com PING baidu.com (220.181.38.251) 56(84) bytes of data.64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=1 ttl=48 time=27.5 ms64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=2 ttl=48 time=27.4 ms^C--- baidu.com ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 27.441/27.495/27.549/0.054 ms
```

**表 9 通过别名或者服务名验证容器连通性 1**

```
/ code # ping webPING web (172.18.0.2): 56 data bytes64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.031 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.059 ms^C--- web ping statistics ---2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.031/0.045/0.059 ms/code #/ code # ping redisPING redis (172.18.0.3): 56 data bytes64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.071 ms 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.073 ms 64 bytes from 172.18.0.3: seq=2 ttl=64 time=0.076 ms^C--- redis ping statistics ---3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.071/0.073/0.076 ms/code #
```

**表 10 通过别名或者服务名验证容器连通性 2**

**实验环境：**

```
[root@VM-12-7-centos ~]# cat /etc/*release CentOS Linux release 7.6.1810 (Core)[root@VM-12-7-centos ~]# docker --versionDocker version 20.10.14, build a224086
```

**实验配置：**

```
[root@VM-12-7-centos composetest]# pwd/root/composetest[root@VM-12-7-centos composetest]#[root@VM-12-7-centos composetest]# cat requirements.txt flaskredis[root@VM-12-7-centos composetest]#[root@VM-12-7-centos composetest]# cat Dockerfile FROM python:3.7-alpineWORKDIR /codeENV FLASK_APP app.pyENV FLASK_RUN_HOST 0.0.0.0RUN apk add --no-cache gcc musl-dev linux-headers COPY requirements.txt requirements.txtRUN pip install -r requirements.txt COPY . .CMD ["flask", "run"][root@VM-12-7-centos composetest]# [root@VM-12-7-centos composetest]#[root@VM-12-7-centos composetest]# cat docker-compose.yml # yaml 配置version: 3 services:web:build: . ports:- "5000:5000"redis:image: "redis:alpine"[root@VM-12-7-centos composetest]# [root@VM-12-7-centos composetest]# [root@VM-12-7-centos composetest]# cat app.py import timeimport redisfrom flask import Flaskapp = Flask( name )cache = redis.Redis(host=redis, port=6379)def get_hit_count(): retries = 5 while True:try:return cache.incr(hits)except redis.exceptions.ConnectionError as exc: if retries == 0:raise exc retries -= 1 time.sleep(0.5)@app.route(/) def hello():count = get_hit_count()return Hello World! I have been seen {} times.
.format(count) [root@VM-12-7-centos composetest]#[root@VM-12-7-centos composetest]#docker-compose up -d
```

END

## 本文作者：潘 安

## 本文来源：IT那活儿（上海新炬王翦团队）

![](https://ucloud-blog.cn-bj.ufileos.com/articles/129499/images/129499_007.png)