我在 SUSE Linux Enterprise Server 12 SP1 中使用 Docker 时遇到了一个奇怪的情况。
我使用 SSH 连接到服务器。首先,我尝试运行一个简单的 nginx 服务器来测试:
docker run -d -p 8081:80 nginx:alpine --name nginxtest
容器启动成功。然后就curl http://localhost:8081
可以运行了!
<!-- [...] -->
<title>Welcome to nginx!</title>
<!-- [...] -->
但是,当我尝试从计算机上的浏览器访问它时,http://10.etc.etc.etc:8081
请求超时。
然而令人惊讶的是,如果我暂时忘记docker,直接从服务器上使用一个简单的HTTP服务器,例如python3 -m http.server
,我会得到:
Serving HTTP on 0.0.0.0 port 8000 ...
当我从计算机上的浏览器访问它时,它就http://10.etc.etc.etc:8000
可以正常工作了!!
所以这一定与 docker 如何暴露其端口有关,但这很奇怪,因为curl http://localhost:8081
它有效......
我该如何进一步排除故障并修复此问题?
笔记:一切都运行正常,直到昨天我运行的时候systemctl restart wicked
(我当时正在尝试调查另一个不相关的问题)。我还尝试过重新启动服务器,但没有帮助。
这里还有一些可能相关的输出...
ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:49:68:4D:40:9B
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:8e6e:ee3f:6918/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:353842611 errors:0 dropped:0 overruns:0 frame:0
TX packets:450340200 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:509803756314 (486186.7 Mb) TX bytes:497391388709 (474349.3 Mb)
brctl show docker0
bridge name bridge id STP enabled interfaces
docker0 8000.0249143a607f no veth0e4b1f4
veth1f2fcc2
veth389f6fe
[[others]]
docker ps | grep nginx
是0.0.0.0:8081->80/tcp
cat /etc/sysctl.conf
# net.ipv6.conf.all.disable_ipv6 = 1
# net.ipv6.conf.all.disable_ipv6 = 1
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
(netstat -ltunp | head -2) && (netstat -ltunp | grep docker)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 :::9000 :::* LISTEN 28216/docker-proxy
tcp 0 0 :::5000 :::* LISTEN 28165/docker-proxy
tcp 0 0 :::8081 :::* LISTEN 30341/docker-proxy
iptables -n --list
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-ISOLATION all -- 0.0.0.0/0 0.0.0.0/0
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 172.17.0.11 tcp dpt:5000
ACCEPT tcp -- 0.0.0.0/0 172.17.0.13 tcp dpt:9000
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
Chain DOCKER-ISOLATION (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
iptables -nt nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 172.17.0.11 172.17.0.11 tcp dpt:5000
MASQUERADE tcp -- 172.17.0.13 172.17.0.13 tcp dpt:9000
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:80
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5000 to:172.17.0.11:5000
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9000 to:172.17.0.13:9000
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8081 to:172.17.0.2:80
docker network ls
NETWORK ID NAME DRIVER SCOPE
20c691cc38e6 bridge bridge local
cd95c7d14c38 host host local
eb6d8228f366 none null local
docker network inspect bridge
[
{
"Name": "bridge",
"Id": "20c691cc38e65be9bf0a377fd8560d49430f523608094b68145e8769e24b1764",
"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": {
"26887c686d3c8612e460b923692f371bef881065a525496dff9af993ed4b7949": {
"Name": "sleepy_fermi",
"EndpointID": "3e79d8c15423b2e145a900e796d316159d2dc51dcc10ced2099b77d1111b03e7",
"MacAddress": "02:49:68:4D:40:9B",
"IPv4Address": "172.17.0.6/16",
"IPv6Address": ""
}
},
"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": {}
}
]
iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-ISOLATION
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.11/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
-A DOCKER -d 172.17.0.13/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 9000 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER-ISOLATION -j RETURN
docker version
Client:
Version: 1.12.6
API version: 1.24
Go version: go1.6.2
Git commit: 78d1802
Built: Thu Mar 2 12:26:00 2017
OS/Arch: linux/amd64
Server:
Version: 1.12.6
API version: 1.24
Go version: go1.6.2
Git commit: 78d1802
Built: Thu Mar 2 12:26:00 2017
OS/Arch: linux/amd64
答案1
在这里,我们的 Docker 容器可以通过 docker0(网络:172.17.xx)访问
您正在尝试从浏览器连接到某个 10.xxx 地址。
在 Docker 主机上使用浏览器
假设您只想从浏览器连接到本地托管的容器,那么更简单的方法是访问您的容器地址(172.17.xx)。
在某些情况下,您甚至可以ssh -X
连接到您的 Docker 主机,然后启动您的 Web 浏览器,而无需将它们暴露给您的 LAN。
使用 HTTP 代理
假设您想要向 LAN 上的客户端公开 http/https 服务,那么您可以使用一些反向代理(nginx、traefik、apache 等)。
dnf install httpd
setsebool -P httpd_can_network_connect on
cat <<EOF >/etc/httpd/conf.d/welcome.conf
<VirtualHost *:80>
ServerName my-application.example.com
LogLevel debug
ErrorLog logs/tunnel_error.log
CustomLog logs/tunnel_access.log combined
<Location />
Require all granted
</Location>
ProxyPass / http://<container-address>:8080/
ProxyPassReverse / http://<container-address>:8080/
</VirtualHost>
EOF
systemctl enable httpd
systemctl start httpd
配置防火墙
假设您想要向 LAN 上的客户端公开 tcp/udp 服务,您可以相应地设置防火墙:
sysctl -w net.ipv4.conf.all.forwarding=1
echo net.ipv4.conf.all.forwarding=1 >>/etc/sysctl.conf
iptables -A FORWARD -i docker0 -o eth0 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -t nat -A PREROUTING -p tcp -d <my-eth0-address> --dport 8080 -j DNAT --to <my-container-address>:8080
请注意,如果您重新启动系统或以某种方式重置防火墙,这些 iptables 规则将不会保留。对 Wicked 不太熟悉。