富 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文章
7580瀏覽量
88933 -
主機
+關注
關注
0文章
998瀏覽量
35178 -
端口
+關注
關注
4文章
978瀏覽量
32102 -
容器
+關注
關注
0文章
496瀏覽量
22074 -
Docker
+關注
關注
0文章
478瀏覽量
11873
原文標題:面試官:如何將多個容器暴露到一個端口上?問倒一大片。。。
文章出處:【微信號:良許Linux,微信公眾號:良許Linux】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論