0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何将多个容器暴露到一个端口上

dyquk4xk2p3d 来源:民工哥技术之路 2023-11-29 14:30 次阅读

富 Web 时代,应用变得越来越强大,与此同时也越来越复杂。集群部署、隔离环境、灰度发布以及动态扩容缺一不可,而容器化则成为中间的必要桥梁。

IT 软件中所说的 “Docker” ,是指容器化技术,用于支持创建和使用 Linux 容器。而 Docker 技术就是这样一种神奇的存在:懂,万物皆可容器化;不懂,则重复“搬砖”,繁忙而不自知。

我们的容器需要对外提供访问的话,就是必须使用端口暴露。

Docker 容器暴露端口的形式有四种:

-p
#将指定的容器端口映射到宿主机所有地址的一个随机端口

-p:
#将容器端口映射到指定的主机端口

-p::
#将容器端口映射到主机指定ip的随机端口

-p::
#将容器端口映射到指定主机ip的指定端口

在日常工作环境中,我们会部署多个相同的服务来对外提供服务,这样可以有效保证集群的高可用性,从而使用户得到很好的体验。

那么,如果多个容器提供一个服务,对外只暴露一个端口,怎么做呢?

通常有以下三种主流方法。

反向代理

当请求达到后,通过反向代理比如nginx、haproxy等,负载均衡的方式将流量转发到后端不同的容器里面。对外就可以暴露一个端口了。72a7e9ee-8e7f-11ee-939d-92fbcf53809c.png

步骤一:创建一个网络

首先,我们需要创建一个网络,使得多个容器能够相互通信。我们可以使用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

    文章

    7251

    浏览量

    87451
  • 主机
    +关注

    关注

    0

    文章

    898

    浏览量

    34613
  • 端口
    +关注

    关注

    4

    文章

    822

    浏览量

    31602
  • 容器
    +关注

    关注

    0

    文章

    481

    浏览量

    21884
  • Docker
    +关注

    关注

    0

    文章

    437

    浏览量

    11607

原文标题:面试官:如何将多个容器暴露到一个端口上?问倒一大片。。。

文章出处:【微信号:良许Linux,微信公众号:良许Linux】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何通过已占用的端口号找到占用端口容器

    在一个已启动了N多个容器Linux操作系统的宿主机上,新启动一个映射到宿主机端口号为portM的容器时,提示端口被占用。
    的头像 发表于 07-26 08:45 6837次阅读
    如何通过已占用的<b class='flag-5'>端口</b>号找到占用<b class='flag-5'>端口</b>的<b class='flag-5'>容器</b>?

    ADXL345如何将端口INT1重置为原始低电平?

    我用ADXL345在INT1端口上配置了双击中断,但是双击后触发了。INT1端口的值直保持在高电平,没有回到低电平。我想知道如何将端口 I
    发表于 12-27 07:44

    如何将信号从示意页面一个

    如何将信号从示意页面一个?我知道我可以使纸张更大,但是我知道连接器的符号,所以我想知道如何使用它。肖恩 以上来自于百度翻译 以下
    发表于 03-22 12:51

    如何将Strut暴露给程序的其他部分?

    在头文件(使用EXTEN)中声明变量,在这种情况下,如何将Strut暴露给程序的其他部分?如果我想在整个程序中访问头文件中的这个声明,我不需要在同
    发表于 08-02 10:27

    如何将分区划分为可读分区?

    我们设计了基于USB SD3的自定义板。我们已经移植的例子说明了使用FX3S固件API来实现大容量存储类设备,它允许访问连接到SD3的SD/MMC设备。应用程序存储端口上找到的存储设备划分为两
    发表于 09-27 14:22

    如何将多个LabVIEW生成的exe打包进同一个安装包??

    我用labview写了软件,生成了多个版本的exe,如何将多个exe打包一个安装包中。在安
    发表于 11-14 10:50

    如何将端口锁定银行?

    嗨,我在Vivado工作,想要在地点和路线之前端口锁定特定的银行(而不是站点),并且不确定Vivado是否允许您这样做。我以前使用过ISE,能够在.ucf文件中禁用它。我正在使用Artix 7。谢谢,埃米尔。
    发表于 03-17 09:26

    如何将信号映射到同一个引脚?

    。对于第二核心,我输入/输出连接到外部端口。在ucf文件中,我第二核心的网络添加到与第一个
    发表于 04-06 07:22

    如何将输出重定向不同的UART

    a)当使用和声时,我可以Prtuf直接指向任意的UART,或者总是指向UART2?如果是,我如何将输出重定向不同的UART?B)和声
    发表于 05-06 13:59

    如何将数据从SDI通道路由一个SDI通道

    嗨,KC705评估板中有4SDI通道。我想知道如何将通道上的SDI数据作为RX通过MGT收发器引脚路由
    发表于 05-20 14:52

    如何将freemodbus移植stm32平台

    modbus是非常好的串口协议(当然也能用在网口上),它简洁、规范、强大。可以满足大部分的工业、嵌入式需求。这里详细说下如何将freemodbus移植
    发表于 08-16 06:59

    如何将多个模块的代码合并到工程里?

    如何将多个模块的代码合并到工程里?
    发表于 11-18 07:39

    如何将CubeMX LSM303AH连接到I2C接口上呢?

    如何将CubeMX LSM303AH连接到I2C接口上呢?求大神告知
    发表于 12-16 06:44

    如何将按钮回调从小容器传播到容器中的当前视图呢?

    组相同的小型自定义容器放入容器中。然后我
    发表于 12-26 06:19

    如何将alpha值设置为模拟时钟或容器

    我想在所有屏幕的背景上制作透明时钟。所以我认为 Alpha 值是在两屏幕上实现透明的唯方法。模拟时钟和容器在他们的库中没有 Alph
    发表于 02-07 07:01