DockOne微信分享(二三一):玩转Kubernetes开发测试环境


【编者的话】现在越来越多的研发团队开始完全基于Kubernetes开构建自己的研发基础设施和持续交付过程,Kubernetes原生的一些特性在一定程度上可以帮助开发者解决一些开发过程中的联调问题,但是软件研发根本要解决的是人与人直接的协作问题,今天的分享将围绕云原生下的开发协作和效能提升问题来讨论。

流水线是工程效率的提升的银弹?

说到软件交付效率提升,我们的第一反应一定会是持续集成/持续交付的相关实践。而在CI/CD中最重要的一个关键实践就是建立团队的持续交付流水线。而实践持续交付时,我们基本的目标就是竟可能的自动化所有需要人工参与的流程,通过自动化流水线的方式将代码发布到线上环境。
屏幕快照_2019-10-27_下午5.20_.03_.png

因此流水线就像是一个万能的框,什么合适都能往里面装。不对不同类型的团队流水线也能被玩出各种不同的花样。通过更多的测试自动化/运维自动化来消除整个发布过程中的风险。通过优化每个流程执行来优化交付等待时间。

发现效率瓶颈

而最终我们会发现,从瀑布-敏捷到持续交付,整个软件交付过程中耗时最多的部分是开发阶段。而在开发阶段,作为程序员,应该大体都是在 “编码 - 单元测试(可选) - 等待 - 集成”的流程里面循环往复,直到代码能够满足预期交付上线。那如果我们能够有有效的减少软件交付团队在 “开发”这个阶段的耗时,那么整个软件的交付效率在已经实施持续交付实践的基础上又能往前迈出巨大的一步。这也是我们为什么会开始关注“开发阶段”效能的最重要原因。
屏幕快照_2019-10-27_下午5.23_.23_.png

而在实际的工作场景下,我们会发现在某些情况下,持续交付流水线虽然能一定程度上解决自动化的问题。但是对于开发人员来说其实并不友好。其中最关键的问题就是日常测试环境的稳定性。一般来说,我们希望开发人员能够频繁的提交代码,通过构建测试,并部署到日常测试环境已触发一些验收测试用例,以及方便开发人员自测。而这个模式除非整个团队只有一个人的情况以外,只要涉及到多人协作就会产生巨大的问题。这里列举了一些典型的问题,例如:
  • 越来越长的等待时间和反馈周期:一次提交,集成1小时;
  • 测试环境发布频率高,稳定性极差(测试环境抢占,Debug测试)
  • 多套环境维护成本巨大


流水线的副作用与应对

为了解决上述问题,在内部的交付平台中,我们做了大量的工作来确保开发人员能够高效的实现开发与集成。
屏幕快照_2019-10-27_下午5.25_.55_.png

在研发基础设施上:
  • 每一个开发人员的变更可以按需独占一套完整的项目测试环境,当变更发布后自动销毁;
  • 在网络层面日常办公网络与日常测试环境之间网络是直接打通的,开发人员可以直接在本地与日常环境上的服务联调;
  • 强大的中间件隔离能力,从而实现本地联调远端以及远端联调本地;


除此之外,除了日常测试环境以外还有单独的主干环境,用以确保当多服务联调时的测试环境稳定性。通过上述的应对策略,在内部,开发人员在变更上完成项目的集成与测试,并尽可能的减少开发人员之间的依赖,从而提升开发效率。

Kubernetes面向开发者的不足

上述这些能力都是建立在阿里内部常年积累的研发基础设施之上,在外部环境中其实很难找到类似的环境,或者要重新实现的话,成本是非常大的。那我们回到Kubernetes。对于Kubernetes而言,其原生提供的一些能力,在一定程度上能够帮助开发人员完成一些日常测试环境的治理能力。

例如,在基础设施资源充足的情况下,我们完全可以基于Namespace为开发人员部署独占的集成测试环境。同时原生的kubectl命令中使用port-forward也可以方便的将远程服务映射到本地。从而实现本地与远端服务的联调。

但是,我们也应该知道其原生能力的不足,在开发场景特别是微服务模式下,联调这个动作除了我依赖别人以外,还有别人依赖我。而在目前的能力上,唯一能够让其它人与“我”进行联调的唯一方式就是通过流水线将其部署到测试环境,那这就意味着我们可能会碰到很多刚才列举的实践流水线的种种问题。

例如:
  • Port-forward虽然能够将远端服务映射到本地,但是意味着本地调用服务的方式和在集群中调用服务的方式不一致,联调完可能还要继续修改代码,发布到集群进一步测试。
  • 单向调用:只能是从本地向集群的请求,集群内部的请求无法转发到本地
  • 多个服务之间的集成成本大:需要依赖一个独立稳定的联调测试环境


探索可行的解决方案

那么我们这时就可以开一下脑洞了,如果我们希望在原生Kubernetes上能够实现在阿里内部的研发流程。那就意味着:
  • 本地能否直接访问PodIP/ClusterIP甚至是集群内的DNS域名
  • 集群内的网络请求能否知己访问到我本地?这样我其实都不需要进行部署动作,直接在本地进行联调测试
  • 在调用链路上,我能直接访问其他开发者的本地服务吗?


那接下来,我们看看这些问题在原生Kubernetes下是否可行。

方案一:SSH端口转发

我们知道要想透过网络隔离访问内网资源,SSH端口转发是个简单可行的方式。通过SSH隧道,我们可以将本地的端口映射到远端的内网服务,或者是通过远程端口转发,把内网中对服务器特定端口的请求转发到本地。
屏幕快照_2019-10-27_下午5.33_.14_.png

在Kubernetes下,我们也能实现类似的能力,首先是在集群内部部署一个启动了SSH服务的Pod容器。为了能够在本地使用SSH隧道,直接通过kubectl的port-forward转发即可。如上,SSH协议配合kubectl的port forward我们可以打通本地到集群或者是从集群到本地的网络。但是实际的体验和操作效果其实非常差。

方式二:sshuttle

同样是基于SSH协议,sshuttle提供了一个基于SSH的VPN网络,使用方式也很简单,同样在集群中部署一个包含SSH服务的代理容器,在通过kubectl将SSH端口映射到本地,最后通过sshuttle建立VPN网络连接,在sshuttle的命令中需要指定需要代理的网段,在Kubernetes下指定PodIP和ClusterIP的网段即可。
屏幕快照_2019-10-27_下午5.34_.27_.png

同时sshuttle也支持使用—ns指定nameserver。所以如果你实现一个kubernetes集群的nameserver服务,那就可以直接在本地访问集群内Service的域名地址。

构建面向研发协作的解决方案

将上述能力进行封装和整合,我们就可以得到一个轻量级的开发协作工具KT Connect。KT Connect通过将上述基础能力进行封装,并对外提供了3个命令已支持不同场景的研发协作。如上,分别是connect,exchange和mesh。这三个命令的组合可以达到什么效果呢?我们来看看具体的场景。

场景一:本地与远端服务联调

在这种场景下,开发者只要直接使用ktctl connect打通本地到集群的网络即可,就可以直接在本地通过PodIP/ClusterIP/DNS地址访问集群中的服务。
屏幕快照_2019-10-27_下午5.37_.58_.png

场景二:集群内服务于联调本地

在这个场景下,我们希望集群中所有对服务C的访问能够请求到本地正在开发的C’。因此开发者可以通过ktctl exhcnage命令,在集群内部署一个Shadow容器已接管所有原本对C实例的请求,再通过Shadow容器将请求转发到本地。并在退出命令式恢复集群中C的实例状态。
屏幕快照_2019-10-27_下午5.38_.53_.png

但是这种模式有一个问题就是开发测试环境依然会出现独占的情况,那有没有更灵活的方式呢?

场景三:集群内服务于联调本地(共享模式)

mesh与exchange的最大区别在于,exchange会完全替换原有的应用实例。mesh命令创建代理容器,但是会保留原应用容器,代理容器会动态生成version标签,以便用于可以通过Istio流量规则将特定的流量转发到本地,同时保证环境正常链路始终可用。在这种场景下,在确保开发测试环境稳定的同时,基于Istio的流量规则,我们可以把部分流量转到本地,从而实现在共享的开发测试环境中的联调能力。
屏幕快照_2019-10-27_下午5.40_.10_.png

场景四:更复杂的联调场景

在微服务模式下,服务将往往会存在大量的相互依赖。通过connect/exchange/mesh的组合,可以方便的实现在团队协作下的开发与联调。通过这种方式,开发人员可以极大的提升本地的开发效率,并且可以快速的实现服务与服务之间的集成与测试。
屏幕快照_2019-10-27_下午5.40_.46_.png

总结

最后总结一下:如在最开始所说的,软件研发效率提升最终是要解决人与人直接的协作问题,通过DevOps以及持续交付流水线我们可以打通部门墙,让开发/测试/运维人员进行不断协作,持续的交付可靠的软件。而当持续交付成熟度达到一定程度后,我们会发现最整合软件交付过程中最大的效率问题依然是出现在开发阶段的协作上。如果能解决开发阶段的效率问题,整个软件交付的效率又能往前迈一个大步。

今天的分享为大家提供了一种在Kubernetes下实现开发阶段效率提升的一个参考实现。KT Connect目前开源在:https://github.com/alibaba/kt-connect,如果觉得今天分享的内容还不错,可以star或者关注一下项目。最后,作为开发人员,你在Kubernetes基础设施上进行代码开发和测试时,有遇到什么问题?是怎么解决的?效果如何?

Q&A

Q:目前遇到的一个实际问题,就是开发测试环境如何进行数据冷启动?一般需求开发都依赖各类数据,开发人员需要在开发阶段往开发测试环境中灌入数据,还是从线上同步部分数据?请教下阿里内部是如何进行的。
A:在阿里内部开发测试环境是共用的一个日常测试数据库,不需要冷启动。在刚才介绍的模式里面我们希望日常测试环境足够稳定,只在需要发布的时候做部署和更新,其它的开发和联调测试行为都发生在本地,直接开发和测试

Q:数据库表的Schema、nginx conf、RabbitMQ的Queue等结构,开发测试环境是怎么和线上实时同步的呢?
A:会有专门平台做数据结构变更流程,从日常开始执行,测试完成后同步到预发、生产。开发中的Schema本身不够稳定,一定是日常验证通过之后才会往预发同步变更。

Q:我们公司有上千个应用,如何利用Kubernetes进行多版本并行开发?同时部署开发都非常困难,目前采用各个产品线提供多套服务的IP,利用hosts配置进行联调,能否用Kubernetes改进?
A:首先应用的数量大,但是并不意味着所有的应用都会有相互间依赖。可能是一组应用共同对外提供了一个业务能力。Kubernetes本身是可以简化应用的部署问题。在上面的分享里面,阿里每个应用在发布之后都会部署一个主干环境,这个主干环境的目标就是为了方便其他应用如果有联调需求的时候有一个相对文档的测试环境可以使用。另外提到的利用hosts配置,在Kubernetes中自带的服务发现能力可以帮忙解决这个问题。不过具体的场景会很多,这里就不展开。可以是按照组织架构的模式来划分,也可以按照应用所属的业务领域来划分。剩下的就是怎么把这些映射到Kubernetes的模型上。

Q:问一下,Service走的IPVS模式,默认不是轮询的嘛,那么发布后端Pod的时候,哪怕是滚动更新,在旧的Pod消失前,不也会有流量走过来吗,怎么能让Service先把旧的Pod摘掉,在停服务呢?
A:KT Connect的应用场景主要还是在开发测试环境,这块对本身的稳定性要求和线上环境不太一样。另外如果想确保服务始终可用的话Mesh模式刚好使用与这个场景。原生Kubernetes也提供了探针的能力来确保当服务不可用时,流量不会转发到Pod实例。

Q:容器化改造,业务转化成容器个数应该怎么评估?
A:这个问题也是一个很具体的问题,容器化改造和你目前的应用部署逻辑会有很大的关系。容器的个数并不是关键的衡量指标。比如在Kubernetes下一个Pod可以包含1到多个容器,这几个容器共同提供一个服务。所以还是看具体场景哈。

Q:对于开发人员来说,使用Kubernetes除了可以快速的生成代码运行环境之外,和传统的代码提交、拉取到指定运行环境的方式比较而言,还有什么好处?
A:在简单场景下直接对比这两种模式,不会有太大差异。不过就像今天的分享内容,我们希望对于开发人员而言,写完代码就不要去提交-部署然后在联调。直接在本地编码,本地运行然后和远端的服务进行集成。这样效率会明显提升很多

Q:KT和kubectl exec的区别是?
A:KT Connect主要是解决本地与集群内服务集成联调的问题,kubectl exec是在已有的Pod上运行命令,不是一个维度的问题。

以上内容根据2019年10月22日晚微信群分享内容整理。 分享人郑云龙,阿里巴巴研发效能技术专家,CNCF基金会Certified Kubernetes Administrator。在敏捷和DevOps领域有丰富的实践经验。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

0 个评论

要回复文章请先登录注册