富 Web 时代,应用变得越来越强大,与此同时也越来越复杂。集群部署、隔离环境、灰度发布以及动态扩容缺一不可,而容器化则成为中间的必要桥梁。
IT 软件中所说的 “Docker” ,是指容器化技术,用于支持创建和使用 Linux 容器。而 Docker 技术就是这样一种神奇的存在:懂,万物皆可容器化;不懂,则重复“搬砖”,繁忙而不自知。
我们的容器需要对外提供访问的话,就是必须使用端口暴露。
Docker 容器暴露端口的形式有四种:
-p #将指定的容器端口映射到宿主机所有地址的一个随机端口 -p: #将容器端口映射到指定的主机端口 -p:: #将容器端口映射到主机指定ip的随机端口 -p:: #将容器端口映射到指定主机ip的指定端口
在日常工作环境中,我们会部署多个相同的服务来对外提供服务,这样可以有效保证集群的高可用性,从而使用户得到很好的体验。
那么,如果多个容器提供一个服务,对外只暴露一个端口,怎么做呢?
通常有以下三种主流方法。
反向代理
当请求达到后,通过反向代理比如nginx、haproxy等,负载均衡的方式将流量转发到后端不同的容器里面。对外就可以暴露一个端口了。
步骤一:创建一个网络
首先,我们需要创建一个网络,使得多个容器能够相互通信。我们可以使用Docker命令docker network create来创建网络。下面是创建一个名为my-network的网络的代码示例:
dockernetworkcreatemy-network
这将创建一个名为my-network的网络,供后续的容器使用。
启动多个容器
接下来,我们需要启动多个容器,并将它们连接到之前创建的网络上。同时,我们需要将容器的端口映射到宿主机的端口上,以便外部可以访问。以下是启动三个容器并进行端口映射的代码示例:
dockerrun-d--networkmy-network--namecontainer1-p8080:80image1 dockerrun-d--networkmy-network--namecontainer2-p8080:80image2 dockerrun-d--networkmy-network--namecontainer3-p8080:80image3
上述代码中,我们使用docker run命令分别启动了三个容器,并指定了容器的网络为my-network。--name参数用于指定容器的名称,-p参数用于进行端口映射,将容器的80端口映射到宿主机的8080端口上。
步骤三:配置负载均衡
最后,我们需要配置一个负载均衡容器,将外部对于宿主机的访问请求分发到多个容器上。在本示例中,我们使用了Nginx作为负载均衡容器。以下是配置负载均衡容器的代码示例:
dockerrun-d--networkmy-network-p8080:80--nameload-balancernginx
上述代码中,我们使用docker run命令启动了一个Nginx容器,并将容器的网络设置为my-network。-p参数用于进行端口映射,将容器的80端口映射到宿主机的8080端口上。
DNAT
熟悉k8s Nodeport 实现的话就会发现,k8s里面service iptables 实现就是基于DNAT。关于k8s Nodeport的实现参见之前k8s的文章。我们可以先通过docker 命令启动两个容器:
$CONT_PORT=9090 $dockerrun-d--rm --namehttp_server_foo -eINSTANCE=foo -ePORT=$CONT_PORT http_server $dockerrun-d--rm --namehttp_server_bar -eINSTANCE=bar -ePORT=$CONT_PORT http_server
获取这个两个容器的IP
$CONT_FOO_IP=$(dockerinspect-f'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'http_server_foo) $echo$CONT_FOO_IP 172.17.0.2 $CONT_BAR_IP=$(dockerinspect-f'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'http_server_bar) $echo$CONT_BAR_IP 172.17.0.3
然后创建 DNAT 规则
$FRONT_PORT=80 #Notsecure!UseglobalACCEPTonlyfortests. $sudoiptables-IFORWARD1-jACCEPT #DNAT本地流量,宿主机访问 $sudoiptables-tnat-IOUTPUT1-ptcp--dport$FRONT_PORT -mstatistic--moderandom--probability1.0 -jDNAT--to-destination$CONT_FOO_IP:$CONT_PORT $sudoiptables-tnat-IOUTPUT1-ptcp--dport$FRONT_PORT -mstatistic--moderandom--probability0.5 -jDNAT--to-destination$CONT_BAR_IP:$CONT_PORT #DNAT外部流量,其他机器访问 $sudoiptables-tnat-IPREROUTING1-ptcp--dport$FRONT_PORT -mstatistic--moderandom--probability1.0 -jDNAT--to-destination$CONT_FOO_IP:$CONT_PORT $sudoiptables-tnat-IPREROUTING1-ptcp--dport$FRONT_PORT -mstatistic--moderandom--probability0.5 -jDNAT--to-destination$CONT_BAR_IP:$CONT_PORT
通过上面模仿k8s Nodeport的实现,就是可以轻松实现一个端口对应多个容器了。
多服务监听
这个方法稍微hack 一点,其实 socket 在listen 的时候,支持 SO_REUSEPORT ,它的效果是运行多个程序监听同一个端口。
funcmain(){ lc:=net.ListenConfig{ Control:func(network,addressstring,connsyscall.RawConn)error{ varoperrerror iferr:=conn.Control(func(fduintptr){ operr=syscall.SetsockoptInt( int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1, ) });err!=nil{ returnerr } returnoperr }, } ln,err:=lc.Listen( context.Background(), "tcp", os.Getenv("HOST")+":"+os.Getenv("PORT"), ) iferr!=nil{ panic(err) } http.HandleFunc("/",func(whttp.ResponseWriter,_req*http.Request){ w.Write([]byte(fmt.Sprintf("Hellofrom%s ",os.Getenv("INSTANCE")))) }) iferr:=http.Serve(ln,nil);err!=nil{ panic(err) } }
这个我们就启动多个容器,共享 network namespace,同时监听这个端口了。
审核编辑:汤梓红
-
网络
+关注
关注
14文章
7505浏览量
88593 -
主机
+关注
关注
0文章
984浏览量
35041 -
端口
+关注
关注
4文章
952浏览量
32000 -
容器
+关注
关注
0文章
492浏览量
22035 -
Docker
+关注
关注
0文章
454浏览量
11803
原文标题:面试官:如何将多个容器暴露到一个端口上?问倒一大片。。。
文章出处:【微信号:良许Linux,微信公众号:良许Linux】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论