伴随着k8s的大量使用,无论是基于应用隔离或者高可用,容灾的需要还是运维管理的需求,很多企业都会部署多个K8S集群。这就会导致有些应用依赖于其它k8s集群的微服务,需要从一个集群里的pod访问另外一个集群里的pod或者service。为了解决跨集群服务调用的问题,我们试验了一种基于隧道的方案,下面就让我们一起来体验一下吧。
作者:鲍盈海, 中国移动云能力中心软件开发工程师,专注于云原生领域。
环境要求:
01
单隧道单服务访问
先介绍一下最简单的场景,让集群A中的服务访问集群B中的服务,架构图如下:
集群A和集群B由一条隧道连接,隧道的左边是代理了隧道入口的service,隧道的右边是一个业务服务,我们在左边集群A中在宿主机上通过curl+ip的方式(或者在容器中通过域名的方式)访问集群B的业务服务。下面我们一起来实际操作一下。我们从右往左来部署服务。
1.先部署demo-service,这个服务是golang官网的demo : https://go.dev/doc/tutorial/web-service-gin, 将它打包成镜像后使用k8s来部署在集群B中充当业务服务,创建pod和svc的yaml文件如下 :
2. 创建隧道,我们使用ssh命令来创建一个ssh隧道,在clusterB上执行如下的命令:
其中8079是集群A上监听的端口,31080是集群B上监听的端口,也是demo-service的svc暴露出来的端口,后面root@[集群A的隧道入口机器IP]是集群A中的机器。需要注意的是集群A的机器的ssh必须开启网关转发功能,具体修改步骤是将/etc/ssh/sshd_config文件中的GatewayPorts改为yes,并重启sshd。
3. 我们还创建了一个tunnel-service的svc,这是一个没有selector的服务,目的是方便集群A中的服务来访问隧道。详细的yaml如下:
至此,在集群A中就可以通过隧道来访问集群B的服务了。但是目前这种方案还不适合在生产环境中使用,因为一般情况下跨集群之间因为安全、性能、成本等因素多个服务会复用一条隧道,而上面的方案中,一个服务独占了一条隧道,如果有多个demo-service服务则需要创建多个隧道。所以我们设计了单隧道多服务的方案。
02
单隧道多服务访问
单隧道多服务的实现原理是在隧道两头增加一个隧道的代理,隧道左端监听多个端口,用来区分集群A中服务要访问的集群B中的不同服务。并且将此信息告知隧道游段的代理,隧道右段代理根据此信息来转发给对应的ClusterB中的服务。架构图如下:
我们定义了一个配置文件,来描述隧道左侧监听的端口与隧道右侧服务映射的关系,如下:
03
关键代码列举
上面配置的功能是被隧道发送方和接收方共享的,所以在代码设计中做了三个module,分别是common(读取配置文件),receive(隧道右侧代理),send(隧道左侧代理)。目录结构如下:
其中三个go文件和go.mod文件如下:
common/config.go
common/go.mod
receive/main.go
receive/go.mod
send/main.go
send/go.mod
构建tunnel-service和tunnel-sned的dockerfile文件分别如下:
打包镜像的脚本可以参考如下:
最终我们打包了nexus.cmss.com:8086/cnp/tunnel/receive:v1.0.0和nexus.cmss.com:8086/cnp/tunnel/send:v1.0.0两个镜像。
04
部署实操
下面我们也来一起部署一下,同样是从右往左部署:
1.我们依然使用golang官网的demo : https://go.dev/doc/tutorial/web-service-gin, 作为demo-service,不同的是我们要创建两个。yaml文件如下:
然后我们需要造一点数据,来区分两个服务,在这个demo中支持创建数据,命令如下:
然后在浏览器中输入http://[集群B中宿主机IP]31050/albums
和http://[集群B中宿主机IP]:31051/albums来查看插入的数据是否生效。
2. 部署tunnel-receive服务,即隧道接收端的服务,其中namespace已经在第一步中创建了,yaml文件如下:
服务部署后,可以通过在clusterB上执行下面的命令来检查receive服务是否正常,注意此处已经在header中设置了X-Proxy-Condition。
3. 创建隧道,同单隧道单服务中的步骤,执行下面的命令
在集群A上执行下面的命令来检查隧道是否成功创建:
同样的我们还需要创建一个没有selector的svc来代理隧道的左边,yaml文件如下:
4. 部署tunnel-send服务,即隧道发送端的服务,yaml文件如下:
至此,已经全部部署结束,执行下面的命令测试一下:
或者在浏览器里访问上面的地址,结果如下:
05
结束语
以上我们通过ssh隧道实现了跨集群的访问,目前只是在demo的程度,要在正式环境中使用的话,还需要考虑整个通信的稳定可靠的问题,例如给隧道增加心跳,多条隧道做负载均衡等。实际在业界还有例如Submariner(https://submariner.io/)等开源项目能轻松提供跨集群的安全应用访问,大家可以进一步学习了解。
领取专属 10元无门槛券
私享最新 技术干货