Docker网络:IPv6使用与故障排查
1. NDP代理与IPv6子网
NDP代理类似于代理ARP,主机在响应邻居发现请求时提供自己的MAC地址。例如:
2003:ab11::c000:242:ac11:2 dev eth0 lladdr 00:0c:29:7f:3d:64 REACHABLE
通过查看邻居表和接口信息,可以发现邻居表中的MAC地址实际上是每个主机的eth0 MAC地址:
user@docker1:~$ ip link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:50:b8:cc brd ff:ff:ff:ff:ff:ff
这种方法在无法向外部网络通告Docker IPv6子网的情况下效果较好,但它依赖于为每个要代理的IPv6地址设置单独的代理条目,每创建一个容器都需要生成一个额外的IPv6代理地址。
2. 用户定义网络与IPv6
用户定义网络可以像使用IPv4一样利用IPv6寻址,网络相关参数同时适用于IPv4和IPv6。以下是定义一个同时具有IPv4和IPv6寻址的用户定义网络的步骤:
-
准备工作
:使用单个Docker主机,假设Docker已安装并处于默认配置,使用用户定义网络的IPv6寻址时,不需要使用
--ipv6
服务级参数启用Docker服务。
-
定义网络
:
user@docker1:~$ docker network create -d bridge \
--subnet 2003:ab11:0:0:c000::/66 --subnet 192.168.127.0/24 \
--ipv6 ipv6_bridge
这里需要注意:
-
--subnet
参数定义了两次,分别定义了IPv4子网和IPv6子网。
-
--ipv6
选项用于启用该网络的IPv6功能,如果不定义此选项,主机的网关接口将不会被定义。
-
启动容器
:
user@docker1:~$ docker run -d --name=web1 --net=ipv6_bridge \
--ip 192.168.127.10 --ip6 2003:ab11::c000:0:0:10 \
jonlangemak/web_server_1
通过
--ip
和
--ip6
参数分别为容器指定了IPv4和IPv6地址。
-
检查网络信息
:
user@docker1:~$ docker network inspect ipv6_bridge
[
{
"Name": "ipv6_bridge",
"Id": "0c6e760998ea6c5b99ba39f3c7ce63b113dab2276645e5fb7a2207f06273401a",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.127.0/24"
},
{
"Subnet": "2003:ab11:0:0:c000::/66"
}
]
},
"Containers": {
"38e7ac1a0d0ce849a782c5045caf770c3310aca42e069e02a55d0c4a601e6b5a": {
"Name": "web1",
"EndpointID": "a80ac4b00d34d462ed98084a238980b3a75093591630b5832f105d400fabb4bb",
"MacAddress": "02:42:c0:a8:7f:0a",
"IPv4Address": "192.168.127.10/24",
"IPv6Address": "2003:ab11::c000:0:0:10/66"
}
},
"Options": {
"com.docker.network.enable_ipv6": "true"
}
}
]
- 检查主机网络配置 :
user@docker1:~$ ip addr show
…<Additional output removed for brevity>…
9: br-0b2efacf6f85: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
noqueue state UP group default
link/ether 02:42:09:bc:9f:77 brd ff:ff:ff:ff:ff:ff
inet 192.168.127.1/24 scope global br-0b2efacf6f85
valid_lft forever preferred_lft forever
inet6 2003:ab11::c000:0:0:1/66 scope global
valid_lft forever preferred_lft forever
inet6 fe80::42:9ff:febc:9f77/64 scope link
valid_lft forever preferred_lft forever
inet6 fe80::1/64 scope link
valid_lft forever preferred_lft forever
…<Additional output removed for brevity>…
- 检查容器网络配置 :
user@docker1:~$ docker exec web1 ip route
default via 192.168.127.1 dev eth0
192.168.127.0/24 dev eth0 proto kernel scope link src 192.168.127.10
user@docker1:~$ docker exec web1 ip -6 route
2003:ab11:0:0:c000::/66 dev eth0 proto kernel metric 256
fe80::/64 dev eth0 proto kernel metric 256
default via 2003:ab11::c000:0:0:1 dev eth0 metric 1024
用户定义网络不支持主机防火墙集成以支持出站伪装或入站端口发布,IPv6在主机内外的连接与docker0桥接器一样,需要原生路由IPv6流量。此外,启动第二个容器时,嵌入式DNS对IPv4和IPv6寻址都有效:
user@docker1:~$ docker run -d --name=web2 --net=ipv6_bridge \
jonlangemak/web_server_1
user@docker1:~$
user@docker1:~$ docker exec -it web2 ping web1 -c 2
PING web1 (192.168.127.10): 48 data bytes
56 bytes from 192.168.127.10: icmp_seq=0 ttl=64 time=0.113 ms
56 bytes from 192.168.127.10: icmp_seq=1 ttl=64 time=0.111 ms
--- web1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.111/0.112/0.113/0.000 ms
user@docker1:~$
user@docker1:~$ docker exec -it web2 ping6 web1 -c 2
PING web1 (2003:ab11::c000:0:0:10): 48 data bytes
56 bytes from web1.ipv6_bridge: icmp_seq=0 ttl=64 time=0.113 ms
56 bytes from web1.ipv6_bridge: icmp_seq=1 ttl=64 time=0.127 ms
--- web1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.113/0.120/0.127/0.000 ms
3. 使用tcpdump验证网络路径
tcpdump是一个强大的故障排除工具,可用于捕获主机上一个或多个接口的网络流量。以下是使用tcpdump验证容器网络流量的步骤:
-
准备工作
:使用单个Docker主机,假设Docker已安装并处于默认配置,需要root权限来检查和更改主机的网络和防火墙配置,需要安装tcpdump:
sudo apt-get install tcpdump
- 启动容器 :
user@docker1:~$ docker run -dP --name web1 jonlangemak/web_server_1
ea32565ece0c0c22eace935113b6697bebe837f0b5ddf31724f371220792fb15
- 检查发布端口 :
user@docker1:~$ docker port web1
80/tcp -> 0.0.0.0:32768
- 捕获入站流量 :
user@docker1:~$ sudo tcpdump -qnn -i eth0 dst port 32768
tcpdump: verbose output suppressed, use -v or -vv for full protocol
decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:46:07.629747 IP 10.20.30.41.55939 > 10.10.10.101.32768: tcp 0
15:46:07.629997 IP 10.20.30.41.55940 > 10.10.10.101.32768: tcp 0
15:46:07.630257 IP 10.20.30.41.55939 > 10.10.10.101.32768: tcp 0
使用的参数说明:
-
q
:使tcpdump安静,不生成过多输出。
-
nn
:不尝试将IP解析为DNS名称。
-
i
:指定要捕获的接口。
-
dst port
:过滤目标端口为32768的流量。
-
捕获docker0桥接器上的流量
:
user@docker1:~$ sudo tcpdump -qnn -i docker0
tcpdump: verbose output suppressed, use -v or -vv for full protocol
decode
listening on docker0, link-type EN10MB (Ethernet), capture size 65535
bytes
16:34:54.193822 IP 10.20.30.41.53846 > 172.17.0.2.80: tcp 0
16:34:54.193848 IP 10.20.30.41.53847 > 172.17.0.2.80: tcp 0
16:34:54.193913 IP 172.17.0.2.80 > 10.20.30.41.53846: tcp 0
16:34:54.193940 IP 172.17.0.2.80 > 10.20.30.41.53847: tcp 0
如果有多个容器在docker0桥上运行,可以指定IP地址进行过滤:
user@docker1:~$ sudo tcpdump -qnn -i docker0 dst 172.17.0.2
tcpdump: verbose output suppressed, use -v or -vv for full protocol
decode
listening on docker0, link-type EN10MB (Ethernet), capture size 65535
bytes
16:42:22.332555 IP 10.20.30.41.53878 > 172.17.0.2.80: tcp 0
16:42:22.332940 IP 10.20.30.41.53878 > 172.17.0.2.80: tcp 0
- 关联容器MAC地址和接口 :
user@docker1:~$ sudo tcpdump -qnne -i docker0 host 172.17.0.2
tcpdump: verbose output suppressed, use -v or -vv for full protocol
decode
listening on docker0, link-type EN10MB (Ethernet), capture size 65535
bytes
16:59:33.334941 02:42:ab:27:0e:3e > 02:42:ac:11:00:02, IPv4, length 66:
10.20.30.41.57260 > 172.17.0.2.80: tcp 0
16:59:33.335012 02:42:ac:11:00:02 > 02:42:ab:27:0e:3e, IPv4, length 66:
172.17.0.2.80 > 10.20.30.41.57260: tcp 0
通过
e
参数显示每个帧的源和目标MAC地址,然后通过
ip link show dev docker0
查看docker0桥的MAC地址,再使用
bridge fdb show | grep 02:42:ac:11:00:02
找到容器MAC地址对应的接口:
user@docker1:~$ bridge fdb show | grep 02:42:ac:11:00:02
02:42:ac:11:00:02 dev vetha431055
最后在该接口上进行捕获验证:
user@docker1:~$ sudo tcpdump -qnn -i vetha431055
tcpdump: WARNING: vetha431055: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol
decode
listening on vetha431055, link-type EN10MB (Ethernet), capture size 65535
bytes
21:01:24.503939 IP 10.20.30.41.58035 > 172.17.0.2.80: tcp 0
21:01:24.503990 IP 172.17.0.2.80 > 10.20.30.41.58035: tcp 0
4. 验证VETH对
VETH对是Docker中连接容器网络命名空间和默认网络命名空间的重要组件。以下是验证VETH对的步骤:
-
准备工作
:使用单个Docker主机,假设Docker已安装并处于默认配置,需要root权限来检查和更改主机的网络和防火墙配置。
-
从主机端开始匹配
:
user@docker1:~$ ip -d link show
…<Additional output removed for brevity>…
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP mode DEFAULT group default
link/ether 02:42:ab:27:0e:3e brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge
6: vetha431055@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
noqueue master docker0 state UP mode DEFAULT group default
link/ether 82:69:cb:b6:9a:db brd ff:ff:ff:ff:ff:ff promiscuity 1
veth
- `-d`参数显示接口的详细信息,确认该接口是VETH对。
- VETH对命名遵循`<end1>@<end2>`命名约定,这里`vetha431055`是本地接口,`if5`是另一端。
- 使用`ethtool`命令确认另一端的接口索引:
user@docker1:~$ sudo ethtool -S vetha431055
NIC statistics:
peer_ifindex: 5
- 使用`xargs`命令查找具有该接口索引的容器:
docker ps -q | xargs --verb -I {} docker exec {} ip link | grep ^5:
输出示例:
user@docker1:~$ docker ps -q | xargs --verb -I {} docker exec {} ip link
| grep ^5:
docker exec 4b521df22184 ip link
docker exec 772e12b15c92 ip link
docker exec d8f3e7936690 ip link
docker exec a2e3201278e2 ip link
docker exec f9216233ba56 ip link
docker exec ea32565ece0c ip link
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP
- 从容器端开始匹配 :
user@docker1:~$ docker exec web1 ip -d link show dev eth0
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP
容器端接口命名告诉我们主机端匹配接口的索引。
总结
本文介绍了Docker网络中NDP代理、用户定义网络与IPv6的使用,以及使用tcpdump验证网络路径和验证VETH对的方法。通过这些方法,可以更好地配置和排查Docker网络问题。
流程图
graph TD;
A[准备工作] --> B[定义用户网络];
B --> C[启动容器];
C --> D[检查网络信息];
D --> E[检查主机网络配置];
E --> F[检查容器网络配置];
G[准备工作] --> H[启动容器];
H --> I[检查发布端口];
I --> J[捕获入站流量];
J --> K[捕获docker0桥流量];
K --> L[关联容器MAC和接口];
M[准备工作] --> N[从主机端匹配VETH对];
N --> O[从容器端匹配VETH对];
表格
| 操作 | 命令示例 | 说明 |
|---|---|---|
| 定义用户网络 |
docker network create -d bridge --subnet 2003:ab11:0:0:c000::/66 --subnet 192.168.127.0/24 --ipv6 ipv6_bridge
| 同时定义IPv4和IPv6子网,启用IPv6功能 |
| 启动容器 |
docker run -d --name=web1 --net=ipv6_bridge --ip 192.168.127.10 --ip6 2003:ab11::c000:0:0:10 jonlangemak/web_server_1
| 指定容器所属网络和IPv4、IPv6地址 |
| 捕获入站流量 |
sudo tcpdump -qnn -i eth0 dst port 32768
| 过滤目标端口为32768的入站流量 |
| 捕获docker0桥流量 |
sudo tcpdump -qnn -i docker0
| 捕获docker0桥上的所有流量 |
| 关联容器MAC和接口 |
bridge fdb show | grep 02:42:ac:11:00:02
| 根据容器MAC地址找到对应的接口 |
| 从主机端匹配VETH对 |
docker ps -q | xargs --verb -I {} docker exec {} ip link | grep ^5:
| 查找具有指定接口索引的容器 |
| 从容器端匹配VETH对 |
docker exec web1 ip -d link show dev eth0
| 查看容器端接口信息 |
5. 验证发布端口和出站伪装
在 Docker 网络中,验证发布端口和出站伪装是确保容器网络正常工作的重要环节。以下是验证的步骤:
- 准备工作 :使用单个 Docker 主机,确保 Docker 已安装并处于默认配置,需要 root 权限来检查和更改主机的网络和防火墙配置。
- 启动容器并发布端口 :
user@docker1:~$ docker run -dP --name web1 jonlangemak/web_server_1
ea32565ece0c0c22eace935113b6697bebe837f0b5ddf31724f371220792fb15
这里使用
-dP
参数,
-d
表示容器在后台运行,
-P
表示将容器内暴露的端口随机映射到主机的端口。
-
查看发布端口
:
user@docker1:~$ docker port web1
80/tcp -> 0.0.0.0:32768
可以看到容器内的 80 端口被映射到了主机的 32768 端口。
-
验证入站流量
:可以使用前面提到的
tcpdump
工具来验证入站流量是否能到达主机的映射端口。
user@docker1:~$ sudo tcpdump -qnn -i eth0 dst port 32768
-
验证出站伪装
:当容器访问外部网络时,容器的流量会通过主机的 IP 地址进行出站伪装。可以在容器内访问外部网络资源,然后在主机上使用
tcpdump查看流量是否以主机的 IP 地址发出。例如,在容器内执行ping命令:
user@docker1:~$ docker exec web1 ping 8.8.8.8 -c 2
同时在主机上捕获流量:
user@docker1:~$ sudo tcpdump -qnn -i eth0 src host 主机IP地址
这里的
主机IP地址
是 Docker 主机的实际 IP 地址。
6. 验证名称解析
在 Docker 网络中,名称解析是让容器能够通过名称相互访问的关键。以下是验证名称解析的方法:
- 准备工作 :使用单个 Docker 主机,确保 Docker 已安装并处于默认配置,启动多个容器并连接到同一个用户定义网络。
user@docker1:~$ docker network create -d bridge my_network
user@docker1:~$ docker run -d --name=web1 --net=my_network jonlangemak/web_server_1
user@docker1:~$ docker run -d --name=web2 --net=my_network jonlangemak/web_server_1
-
在容器内进行名称解析测试
:在
web2容器内尝试通过名称访问web1容器。
user@docker1:~$ docker exec -it web2 ping web1 -c 2
如果名称解析正常,应该能够看到
ping
命令成功响应。
PING web1 (172.x.x.x): 48 data bytes
56 bytes from 172.x.x.x: icmp_seq=0 ttl=64 time=0.113 ms
56 bytes from 172.x.x.x: icmp_seq=1 ttl=64 time=0.111 ms
--- web1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.111/0.112/0.113/0.000 ms
-
检查容器的 DNS 配置
:可以查看容器的
/etc/resolv.conf文件,确认 DNS 配置是否正确。
user@docker1:~$ docker exec web2 cat /etc/resolv.conf
7. 构建测试容器
构建测试容器可以帮助我们更好地验证 Docker 网络的各项功能。以下是构建测试容器的步骤:
-
创建 Dockerfile
:创建一个名为
Dockerfile的文件,内容如下:
FROM alpine:latest
RUN apk add --no-cache iputils ping curl
CMD ["sh"]
这里使用
alpine
作为基础镜像,安装
ping
和
curl
工具,方便后续进行网络测试。
-
构建镜像
:在包含
Dockerfile
的目录下执行以下命令构建镜像。
user@docker1:~$ docker build -t test_container .
- 运行测试容器 :
user@docker1:~$ docker run -it --net=my_network test_container
在测试容器内可以使用
ping
和
curl
等工具进行网络测试。例如:
/ # ping web1
/ # curl http://web1
8. 重置本地 Docker 网络数据库
当 Docker 网络出现问题时,重置本地 Docker 网络数据库可能会解决问题。以下是重置的步骤:
- 停止 Docker 服务 :
user@docker1:~$ sudo systemctl stop docker
- 删除 Docker 网络配置文件 :
user@docker1:~$ sudo rm -rf /var/lib/docker/network
- 重启 Docker 服务 :
user@docker1:~$ sudo systemctl start docker
总结
本文详细介绍了 Docker 网络中多个方面的操作和验证方法,包括 NDP 代理、用户定义网络与 IPv6 的使用、使用 tcpdump 验证网络路径、验证 VETH 对、验证发布端口和出站伪装、验证名称解析、构建测试容器以及重置本地 Docker 网络数据库。掌握这些方法可以帮助我们更好地配置和排查 Docker 网络问题,确保容器网络的稳定运行。
流程图
graph TD;
A[启动容器并发布端口] --> B[查看发布端口];
B --> C[验证入站流量];
B --> D[验证出站伪装];
E[创建用户定义网络并启动多个容器] --> F[在容器内进行名称解析测试];
F --> G[检查容器的DNS配置];
H[创建Dockerfile] --> I[构建镜像];
I --> J[运行测试容器];
K[停止Docker服务] --> L[删除网络配置文件];
L --> M[重启Docker服务];
表格
| 操作 | 命令示例 | 说明 |
|---|---|---|
| 启动容器并发布端口 |
docker run -dP --name web1 jonlangemak/web_server_1
| 容器在后台运行并随机映射端口 |
| 验证入站流量 |
sudo tcpdump -qnn -i eth0 dst port 32768
| 检查入站流量是否到达映射端口 |
| 验证出站伪装 |
sudo tcpdump -qnn -i eth0 src host 主机IP地址
| 检查容器流量是否以主机IP发出 |
| 名称解析测试 |
docker exec -it web2 ping web1 -c 2
| 测试容器间名称解析 |
| 检查 DNS 配置 |
docker exec web2 cat /etc/resolv.conf
| 查看容器 DNS 配置 |
| 构建镜像 |
docker build -t test_container .
| 使用 Dockerfile 构建镜像 |
| 运行测试容器 |
docker run -it --net=my_network test_container
| 运行测试容器进行网络测试 |
| 停止 Docker 服务 |
sudo systemctl stop docker
| 停止 Docker 服务 |
| 删除网络配置文件 |
sudo rm -rf /var/lib/docker/network
| 删除 Docker 网络配置文件 |
| 重启 Docker 服务 |
sudo systemctl start docker
| 重启 Docker 服务 |
超级会员免费看

被折叠的 条评论
为什么被折叠?



