我有一个 docker 容器,我无法从容器内部运行 DNS 查找,尽管它在 docker 主机上运行良好。
已知构建 Docker 主机的配置管理代码可以在来自市场的标准 RHEL 7 映像上运行,因此已知问题出在 SOE RHEL 7 映像内部。
RHEL 7.2 / Docker 版本 1.12.6,内部版本 88a4867/1.12.6。容器为 RHEL 7.3。SELinux 处于启用/宽容模式。Docker 主机是 Amazon EC2 实例。
一些配置:
# /etc/sysconfig/docker
OPTIONS='--dns=10.0.0.10 --dns=10.0.0.11 --dns-search=example.com'
DOCKER_CERT_PATH=/etc/docker
ADD_REGISTRY='--add-registry registry.example.com'
no_proxy=169.254.169.254,localhost,127.0.0.1,registory.example.com
http_proxy=http://proxy.example.com:8080
https_proxy=http://proxy.example.com:8080
ftp_proxy=http://proxy.example.com:8080
容器和主机中的解析器配置相同:
# /etc/resolv.conf
search example.com
nameserver 10.0.0.10
nameserver 10.0.0.11
如果我重新启动docker守护进程,--debug
我会看到以下内容journalctl -u docker.service
:
Aug 08 11:44:23 myhost.example.com dockerd-current[17341]: time="2017-08-08T11:44:23.430769581+10:00" level=debug msg="Name To resolve: http://proxy.example.com."
Aug 08 11:44:23 myhost.example.com dockerd-current[17341]: time="2017-08-08T11:44:23.431488213+10:00" level=debug msg="Query http://proxy.example.com.[1] from 172.18.0.6:38189, forwarding to udp:10.162.182.101"
Aug 08 11:44:27 myhost.example.com dockerd-current[17341]: time="2017-08-08T11:44:27.431772666+10:00" level=debug msg="Read from DNS server failed, read udp 172.18.0.6:38189->10.162.182.101:53: i/o timeout"
进一步观察后发现,如果我指定 IP 地址而不是代理的 DNS 名称,我可以使一些网络正常工作;尽管这实际上只是避免使用 DNS 的一种方法,而不是真正的解决方案。
的确, (更新 #3)事实证明,我可以通过简单地将 DNS 配置为使用 TCP 而不是 UDP 来完全避免此问题,即
# head -1 /etc/sysconfig/docker
OPTIONS="--dns=10.0.0.10 --dns=10.0.0.11 --dns-search=example.com --dns-opt=use-vc"
(添加一行use-vc
告诉解析器使用 TCP 而不是 UDP。)
我确实注意到 iptables 中有一些看起来可疑的规则,但结果证明这些规则是正常的:
# iptables -n -L DOCKER-ISOLATION -v --line-numbers
Chain DOCKER-ISOLATION (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 DROP all -- br-1d6a05c10468 docker0 0.0.0.0/0 0.0.0.0/0
2 0 0 DROP all -- docker0 br-1d6a05c10468 0.0.0.0/0 0.0.0.0/0
3 34903 11M RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
删除这两条 DROP 规则后,我仍然看到该问题。
完整的 iptables:
# iptables -nL -v
Chain INPUT (policy ACCEPT 2518 packets, 1158K bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
23348 9674K DOCKER-ISOLATION all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
23244 9667K DOCKER all -- * br-1d6a05c10468 0.0.0.0/0 0.0.0.0/0
23232 9667K ACCEPT all -- * br-1d6a05c10468 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
104 6230 ACCEPT all -- br-1d6a05c10468 !br-1d6a05c10468 0.0.0.0/0 0.0.0.0/0
12 700 ACCEPT all -- br-1d6a05c10468 br-1d6a05c10468 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 2531 packets, 414K bytes)
pkts bytes target prot opt in out source destination
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- !br-1d6a05c10468 br-1d6a05c10468 0.0.0.0/0 172.18.0.2 tcp dpt:443
0 0 ACCEPT tcp -- !br-1d6a05c10468 br-1d6a05c10468 0.0.0.0/0 172.18.0.2 tcp dpt:80
0 0 ACCEPT tcp -- !br-1d6a05c10468 br-1d6a05c10468 0.0.0.0/0 172.18.0.3 tcp dpt:389
Chain DOCKER-ISOLATION (1 references)
pkts bytes target prot opt in out source destination
0 0 DROP all -- br-1d6a05c10468 docker0 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- docker0 br-1d6a05c10468 0.0.0.0/0 0.0.0.0/0
23348 9674K RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
桥梁配置
# ip addr show docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:a8:73:db:bb brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
# ip addr show br-1d6a05c10468
3: br-1d6a05c10468: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:d5:b6:2d:f5 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 scope global br-1d6a05c10468
valid_lft forever preferred_lft forever
和
# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "e159ddd37386cac91e0d011ade99a51f9fe887b8d32d212884beace67483af44",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
在日志中:
Aug 04 17:33:32 myhost.example.com systemd[1]: Starting Docker Application Container Engine...
Aug 04 17:33:33 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:33.056770003+10:00" level=info msg="libcontainerd: new containerd process, pid: 2140"
Aug 04 17:33:34 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:34.740346421+10:00" level=info msg="Graph migration to content-addressability took 0.00 seconds"
Aug 04 17:33:34 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:34.741164354+10:00" level=info msg="Loading containers: start."
Aug 04 17:33:34 myhost.example.com dockerd-current[2131]: .........................time="2017-08-04T17:33:34.903371015+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:35 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:35.325581993+10:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address"
Aug 04 17:33:36 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:36+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:37 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:37+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:37 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:37+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:38 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:38+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:39 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:39+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:40 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:40+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:40 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:40+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:42 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:42+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:42 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:42+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.541905145+10:00" level=info msg="Loading containers: done."
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.541975618+10:00" level=info msg="Daemon has completed initialization"
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.541998095+10:00" level=info msg="Docker daemon" commit="88a4867/1.12.6" graphdriver=devicemapper version=1.12.6
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.548508756+10:00" level=info msg="API listen on /var/run/docker.sock"
Aug 04 17:33:43 myhost.example.com systemd[1]: Started Docker Application Container Engine.
从容器中,我可以 ping 默认网关,但所有名称解析都失败。
我注意到日志中有一个奇怪的事情(更新 #2我现在知道这是一个转移注意力的话题——见下面的讨论):
# journalctl -u docker.service |grep insmod > /tmp/log # \n's replaced below
Jul 26 23:59:02 myhost.example.com dockerd-current[3185]: time="2017-07-26T23:59:02.056295890+10:00" level=warning msg="Running modprobe bridge br_netfilter failed with message: insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/bridge/bridge.ko
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-arptables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory
modprobe: ERROR: Error running install command for bridge
modprobe: ERROR: could not insert 'bridge': Unknown error 253
insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/llc/llc.ko
insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/802/stp.ko
install /sbin/modprobe --ignore-install bridge && /sbin/sysctl -q -w net.bridge.bridge-nf-call-arptables=0 net.bridge.bridge-nf-call-iptables=0 net.bridge.bridge-nf-call-ip6tables=0
insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/bridge/br_netfilter.ko
, error: exit status 1"
更新 #1:这是来自:
# tail -2 /etc/modprobe.d/dist.conf
# Disable netfilter on bridges when the bridge module is loaded
install bridge /sbin/modprobe --ignore-install bridge && /sbin/sysctl -q -w net.bridge.bridge-nf-call-arptables=0 net.bridge.bridge-nf-call-iptables=0 net.bridge.bridge-nf-call-ip6tables=0
还:
# cat /proc/sys/net/bridge/bridge-nf-call-{arp,ip,ip6}tables
1
1
1
然而,即使我这样做了:
# for i in /proc/sys/net/bridge/bridge-nf-call-{arp,ip,ip6}tables ; do echo 0 > $i ; done
还是没有运气。
我花了一整天时间处理这个问题,现在我都快抓狂了。如果您能告诉我还能尝试什么,或者还有什么问题,我将不胜感激。
更新 #4
我使用 Netcat 进行了一些实验,并证明如果从任何容器 -> 主机发送,则不会转发所有 UDP 数据包。我尝试使用多个端口,包括 53、2115 和 50000。但是 TCP 数据包没问题。如果我用 刷新 iptables 规则,情况仍然如此iptables -F
。
此外,我可以将 UDP 数据包从一个容器发送到另一个容器 - 只有来自容器->主机的 UDP 流量不会被转发。
要设置测试:
在 IP 为 10.1.1.10 的主机上:
# nc -u -l 50000
在容器上:
# echo "foo" | nc -w1 -u 10.1.1.10 50000
在 TCP 转储捕获期间,我看到:
17:20:36.761214 IP (tos 0x0, ttl 64, id 48146, offset 0, flags [DF], proto UDP (17), length 32)
172.17.0.2.41727 > 10.1.1.10.50000: [bad udp cksum 0x2afa -> 0x992f!] UDP, length 4
0x0000: 4500 0020 bc12 4000 4011 53de ac11 0002 E.....@[email protected].....
0x0010: 0aa5 7424 a2ff c350 000c 2afa 666f 6f0a ..t$...P..*.foo.
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
17:20:36.761214 IP (tos 0x0, ttl 64, id 48146, offset 0, flags [DF], proto UDP (17), length 32)
172.17.0.2.41727 > 10.1.1.10.50000: [bad udp cksum 0x2afa -> 0x992f!] UDP, length 4
0x0000: 4500 0020 bc12 4000 4011 53de ac11 0002 E.....@[email protected].....
0x0010: 0aa5 7424 a2ff c350 000c 2afa 666f 6f0a ..t$...P..*.foo.
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
我尝试通过以下方法修复错误的 UDP 校验和,但没有成功这。
但是,我注意到,即使在 UDP 数据包(主机 -> 主机)和容器 -> 容器成功传输期间,也会出现错误的 UDP 校验和。
总而言之,我现在知道:
路由很好
iptables 已刷新
SELinux 很宽容
所有 TCP 都在各个方向上工作
所有来自容器的 UDP -> 容器正常
所有来自主机的 UDP -> 主机均正常
从主机到容器的所有 UDP 均正常
但没有转发从容器到主机的 UDP 数据包
答案1
我想到了。
我们在 SOE 中运行了一个趋势科技(防病毒)代理,但我对此并不知情。
修复它很简单:
# systemctl stop ds_agent.service
# pkill ds_agent
目前还不完全清楚为什么它会阻止来自容器的 UDP 或者如何停止它。
答案2
似乎您的 modprobeinstall
指令无法工作。这可能是由于 RHEL 7.2 更新不完整或某些手动修复造成的。
尝试grep -r bridge /etc/modprobe.d /lib/modprobe.d
启动,或者以其他方式挖掘/etc/modprobe.d
或/lib/modprobe.d
尝试找到它在哪里定义install
调用的规则sysctl -q -w net.bridge.bridge-nf-call-arptables=0 net.bridge.bridge-nf-call-iptables=0 net.bridge.bridge-nf-call-ip6tables=0
这sysctl
显然是错误的位置。它要么是多余的,要么应该出现在之后br_netfilter
,而不是之前。为什么?最近,/proc/sys/net/bridge
处理已从bridge
模块移至br_netfilter
模块。这发生在某些版本的 中kernel*.rpm
,而目录的内容modprobe.d
与其他单独的软件包一起分发。我已经在我的 RHEL 7.2 上进行了验证:
# modprobe bridge
# sysctl -q -w net.bridge.bridge-nf-call-iptables=0
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory
# modprobe br_netfilter
# sysctl -q -w net.bridge.bridge-nf-call-iptables=0 # ok now
我在原始 RHEL 7.1 上没有看到这些“损坏”的规则,它们的来源对我来说很神秘。我尝试过:
# modprobe -n -vvv bridge
modprobe: INFO: custom logging function 0x40a130 registered
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/llc/llc.ko
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/802/stp.ko
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/bridge/bridge.ko
modprobe: INFO: context 0xf1c270 released
# echo "install bridge echo example_of_a_modprobe_rule" > /etc/modprobe.d/zzz5.conf
# modprobe -n -vvv bridge
modprobe: INFO: custom logging function 0x40a130 registered
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/llc/llc.ko
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/802/stp.ko
install echo example_of_a_modprobe_rule
modprobe: INFO: context 0xeaa270 released
# rm /etc/modprobe.d/zzz5.conf
更新:好像xenserver 使用类似的 modprobe hack. 无论您是否实际运行 xenserver,全局更改每个人的内核模块行为都是一个令人讨厌的错误;并且该错误已经对我们进行了反击。
更新 2:现在,您已经发现/etc/modprobe.d/dist.conf
导致此问题的原因不是 docker,而是 docker。无论您是否有 docker, modprobe bridge
都会始终返回 1 并打印错误。通常,dist.conf 是 RHEL6 上软件包的一部分module-init-tools
。此文件不应在 RHEL7 上使用。它不在我的任何 RHEL7 系统上,它们运行良好。在 RHEL7 中,软件包是,kmod
并且不包含 dist.conf。我会:
rpm -qf /etc/modprobe.d/dist.conf # what package owns this file?
如果 dist.conf 不属于包,请备份它并删除任何不会给您带来任何明显好处的行(甚至可能完全删除该文件)。
如果 dist.conf 属于某个包,请考虑删除/更新该包,因为它在 RHEL 7.2 兼容性方面明显存在缺陷。
答案3
我在 Docker 容器中遇到了 DNS 解析器问题。我尝试了很多不同的方法,最后我发现我的 VPSHostgator 尚未安装默认情况下 NetworkManager-tui (nmtui),我刚刚安装并重新启动。
sudo yum install NetworkManager-tui
并将我的resolv.conf
默认 DNS 重新配置为8.8.8.8
。
nano /etc/resolv.conf