暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

如何给正在运行的docker容器暴露网络端口?

玩转云原生 2021-05-28
947

我们知道,docker
通过命名空间实现了环境隔离。

docker
官方文档,网络模式主要有几种不同程度的隔离方式,host
bridge
overlay
macvlan
none
。我们先讲讲这几种网络模式,稍后我们会提出题目所示的疑问,你可以先带着这个疑问往下走。

host

删除容器和Docker主机之间的网络隔离,直接使用主机的网络

bridge

默认网络模式,桥接网络,在网段之间转发流量的链路层设备。桥接网络使用软件桥,该软件桥允许连接到同一桥网络的容器进行通信,同时提供与未连接到该桥网络的容器的隔离。

overlay

覆盖网络将多个Docker守护程序连接在一起,使得Swarm服务彼此通信。

macvlan

MacVlan
网络允许为容器分配MAC
地址,使其显示为网络上的物理设备。

none

禁用容器所有网络,通常与自定义网络驱动程序一起使用。

网络插件

使用Docker安装第三方网络插件。

讨论容器网络方案时,我们可以只考虑bridge
host
模式,其他网络模式基本不常用。

host
模式,屏蔽了容器与宿主机的网络隔离,可以直连主机,这没有问题。

所以,我们一般所说的网络问题,指的是网桥模式下的网络问题。

因此我们要问:docker
容器之间怎么访问呢?

其实,docker
给容器创建网络时,使用linux
网络虚拟技术,在主机和容器内分别创建一个虚拟接口veth pair
, 主机这端的veth
接口给了docker0
,另一端,则给容器,修改名称为eth0
。随后,从网桥可用地址段中,获取一个空闲地址分配给容器的eth0
, 并把docker0
设置为默认网关。

也就是,同一主机上的docker
容器docker0
作为docker通信间的连接,充当默认网关。不同主机上的容器通信,需要将主机上的docker0
打通即可,这个不是本篇的重点,粗略来说,需要配置docker0
启动时候的bip
,并在本机上添加对端静态路由地址和路由转发规则,并且双向配置。例如,主机6上添加route add -net 10.0.6.0/24 gw 192.168.88.6
以及iptables -t nat -I PREROUTING -s 10.0.5.0/24 -d 10.0.6.0/24 -j DNAT --to 10.0.5.1

那我们接下来说说标题上的问题:如何给正在运行的容器暴露服务端口?

前提

假设你的环境跟我一样已经运行了一个名叫nginx-test
的容器,只知道容器内部端口是80, 并没有暴露主机端口映射, 镜像模板是一个nginx
镜像。

我们一起看看有哪些解决方案。

ip直连模式

$ docker inspect nginx-test |jq ".[0].NetworkSettings.IPAddress"
"172.17.0.4"

已知容器上的端口80,获取到docker
分配的容器ip
,直接访问http://172.17.0.4:80
可以直接访问容器服务。

$ curl 172.17.0.4:80
----
<p><em>Thank you for using nginx.</em></p>
----

docker
里面很方便获取到容器名称的,在知道容器端口的时候,这种方法简单有效。

Commit

通过commit
我们可以重新构建一个镜像,用新镜像替换旧的镜像,运行一个暴露了端口的容器:

$ echo "some_text" > test.txt
$ docker cp test.txt nginx-test:/home
$ docker commit nginx-test nginx-test-commit-1
$ docker stop nginx-test
$ docker run --name nginx-test-commit-1 -p 8001:80 nginx-test-commit-1:latest
$ curl localhost:8001
----
<p><em>Thank you for using nginx.</em></p>
----

虽然这种方式也能达到我们的需求,但是破坏性太大,需要重建容器。

nginx 反向代理

我们知道通过反向代理能完成请求转发给上游服务器,同时通过docker link
可以将已有运行容器的网络链接到新的容器,于是我们可以用这种方式达到暴露容器端口的服务。这种方式还是比较适合容器编排的处理方式的。

$ ls
default.conf run-docker.sh
$ cat default.conf
server {
listen 80;


location / {
proxy_pass http://upstream:80;
}
}
$ cat run-docker.sh
#!/bin/bash


link=$1


sed -i "s/upstream/$link/g" $(pwd)/default.conf
docker run --name nignx-test -p 8002:80 -v $(pwd):/etc/nginx/conf.d --link=${link} nginx


$ bash run-docker.sh nginx-test
$ curl localhost:8002
----
<p><em>Thank you for using nginx.</em></p>
----

其他方案

除了上述三种方案,还有诸如通过ssh
转发的,比较复杂,这里就不推荐了,感兴趣的童鞋可以自行探索。另外,值得一提的还有docker-proxy
,这个二进制命令完成了docker
容器ip
、端口到主机ip
、端口的转发,笔者尝试docker-proxy
手动构建转发规则时失败了,貌似这种方法不被docker
允许。借着这个思路,其实我们可以简单做一个iptables
的转发,也能实现我们的需求。

本章就这么多啦。

码字不易,如果本篇对你有所帮助,别忘了点个小星星!更多内容请关注公众号:玩转云原生!

文章转载自玩转云原生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论