基于Kubernetes容器集群环境的持续交付平台
2020-12-01韩东
韩东
摘要:传统软件交付模式偏向于手动,软件从源码到编译、打包、测试、交付生产经历不同的形态和系统环境。手动模式效率较低,且不同环境存在差异,容易引起错误导致软件部署失败。设计基于Kubernetes容器集群环境的持续交付平台,利用容器技术的隔离特性屏蔽程序运行环境的差异,利用git、docker、Kubernetes的C/S架构模式,集成每个平台的客户端及相关构建工具,使程序在拉取项目、编译、部署过程中摆脱命令行模式,只需在持续交付平台中通过表单方式配置每个过程中所需的各项参数即可,将软件交付过程中所有操作集中到统一平台上,实现自动化交付。系统测试结果表明,持续交付平台能够自动完成软件交付工作,打通了软件交付各个环节的数据流和业务流。
关键词:容器集群;持续交付;Docker;Kubernetes
DOI:10. 11907/rjdk. 201144
中图分类号:TP319文献标识码:A 文章编号:1672-7800(2020)010-0186-05
Abstract:The traditional software delivery model tends to be manual, and the software experiences different forms and system environments from source code to compilation, packaging, testing, and production. Manual mode is inefficient, and differences in different environments can easily cause unexpected errors and cause software deployment failure. A continuous delivery platform is designed based on the Kubernetes container cluster environment, the isolation characteristics of container technology are used to shield the differences in program operating environments, and the C / S architecture model of git, docker, and Kubernetes is used to integrate the clients of each platform and related construction tools so that the program can get rid of the command-line mode in the process of pulling the project, compiling, and deploying. It only needs to configure the various parameters required in each process through the form in the continuous delivery platform to centralize all operations in the software delivery process so that a unified platform for automated delivery can be made. The system test results show that the continuous delivery platform can automatically complete the software delivery work and open up the data flow and business flow in all links of software delivery.
Key Words: container cluster; continuous delivery; Docker; Kubernetes
0 引言
持续交付是DevOps中的一个重要方法,主要研究如何通过自动化方法使得软件部署与交付变得更加便利[1]。DevOps强调应持续集成与部署,做到每天进行价值交付,测试人员在软件开发过程中就参与进来,每天进行集成测试,运维人员进行持续部署。DevOps强调通过自动化“软件交付”和“架构变更”流程,使构建、测试、发布软件更加快捷、频繁及可靠[2-4]。矛盾在于软件部署通常是复杂的,经常出现测试环境、生产环境与开发环境不一致的情况,从而导致软件交付失败。开发人员希望每天交付新功能,而运维人员期望系统保持稳定,这似乎是不可调和的矛盾[5-6]。在传统模式下,软件交付花费成本过高,导致DevOps期望的每天都能集成、测试与部署不能真正实现。
面对持续交付中的矛盾,相关学者与从业人员给出多种解决方法,如周振兴[7]提出通过 GitHub以及 IBM Urban Code Deploy(UCD)工具进行自动化部署实现持续交付,并通過虚拟化技术 Docker 对系统进行水平扩展;丘晖[8]利用Jenkins的Pipeline功能实现Docker容器环境下的持续交付;郭雪[9]提出基于神经网络的自调节持续集成与交付框架,利用神经网络的自学习和自调节优势为开发者预测与计划最佳的下一次构建时机,从而帮助开发者合理平衡开发与集成;金泽锋等[10]认为软件交付时应注重价值交付,提出面向完整价值交付的文档DevOps,并认为产品文档应与软件同时交付,避免出现软件版本与文档不匹配的问题。
本文提出基于Kubernetes容器集群环境的持续交付平台,利用Docker容器的隔离特性屏蔽程序不同运行环境的差异性,引入Kubernetes容器集群及应用编排技术实现自动化交付。Kubernetes能够应对复杂的软件架构和应用场景,在DevOps理念被普遍接受的背景下,Docker、Kubernetes在软件交付中已得到广泛应用[11-12]。目前使用形式大多为:开发人员将编译完成的程序包发送给运维人员,由运维人员根据需要使用命令行方式在运行Docker引擎的服务器上生成Docker镜像,再使用Kubernetes的kubectl命令行工具,将容器部署到容器集群环境中。整个过程偏向于手动模式,各平台之间的数据流断裂。本文通过分析Docker、Kubernetes及gitlab各平台系统架构,得出三者都是基于C/S架构模式,可利用各平台开源的客户端,集成到持续交付平台中,在统一的平台上集成所有操作,以表单方式配置各流程中的参数,避免使用命令行方式,实现交付流程的自动化。
1 持续交付平台设计
持续交付平台旨在管理一个软件项目从开发到交付生产等一系列生命周期,从静态代码开始到编译与打包,再到将程序包构建成Docker镜像。Docker镜像如果只存在于开发环境的机器中,则不便于共享与分发[13],还需将镜像推送至镜像仓库中。最后将构建好的镜像按照软件架构设计部署在容器集群中。在整个过程中,软件项目从可编辑的源代码到二进制程序包,再到容器镜像,都需要借助相关平台和工具。例如,使用github作为代码版本控制工具[14],而Docker镜像是通过Docker引擎构建的[15]。持续交付平台本身不是各项具体工作的实施者,而是交付流程的创建者,以及各平台的统一组织者。持续交付平台通过图形化界面的方式进行操作,以表单方式配置各流程中的参数。
对于企业而言,首先要搭建一套基础设施服务。受企业内部规定的限制,软件项目可能只在企业内部网络使用,并不想公开在互联网上,例如企业不希望自己的项目源代码托管在github上,也不希望自己的镜像发布在Docker hub中,所以不能使用一些互联网的公开服务,而需要企业自己在内部网络构建一套基础设施服务。本文使用gitlab作为代码版本控制工具,以及harbor作为Docker镜像Registry服务,搭建Docker引擎服务与Kubernetes服务集群。
持续交付流程是整个交付平台的核心内容,基于各基础平台的持续交付平台交付流程如图1所示。
具体流程包括:
(1)从gitlab上拉取指定项目的源码,并进行编译与打包。
(2)根据项目的Dockerfile文件,向Docker平台发出构建镜像命令,Docker引擎会根据项目的Dockerfile进行镜像构建。
(3)镜像构建完成后,驱动Docker引擎将镜像推送至指定镜像仓库中。
(4)根据项目架构设计,命令Kubernetes创建Deployment资源,指定相关镜像和Pod副本数,部署至容器集群中。
(5)还需命令Kubernetes创建Service资源,用于服务发现。此时整个软件项目完成交付,并提供访问。
2 持续交付平台实现
持续交付平台实现的重点内容在于如何在平台上操作各个基础服务平台,完成编译打包、镜像构建,以及对推送至容器集群中的各个资源进行创建与管理。幸运的是,各平台都提供了不同语言版本的客户端,并且开源。持续交付平台需要集成这些客户端,使用这些客户端提供的API进行平台化操作。其中,如何使用这些API实现编码是一个关键问题。
2.1 客户端选择
持续交付平台总体上使用Java语言加以实现,并使用Maven工具进行项目构建与管理,因此需要选择各平台Java版本的客户端。各平台客户端Maven依赖详情如表1所示。
2.2 编程实现
在编码实现环节,省略了一般性通用功能的编码实现,如平台注册/登录等,而重点聚焦持续交付流程中核心功能的编码实现。持续交付平台需要与各基础平台进行交互,编码重点在于如何利用集成的各平台客户端提供的API进行交互实现。
(1)將项目源码克隆至本地。持续交付平台与gitlab进行交互,需要传递代码仓库的URL和仓库用户名、密码以及保存至本地的目录。编码实现如下:
public static void cloneGitRepository(GitRepository gitRepository, String localPath) throws Exception {
CloneCommand cc = Git.cloneRepository().setURI(gitRepository.getUrl());
cc.setCredentialsProvider(
new UsernamePasswordCredentialsProvider(gitRepository.getUsername(),
gitRepository.getPassword()));
cc.setDirectory(new File(localPath))。call();
}
(2)创建Docker镜像。根据Dockerfile文件利用编译完成的程序包构建Docker镜像[16],需要在操作系统的环境变量中配置Docker服务所在地址,如:DOCKER_HOST=tcp://192.168.18.150:2375。Docker客户端读取DOCKER_HOST的值与Docker服务建立连接。编码实现如下:
public String buildImage(String dockerFileDir, String tag) throws Exception {
final DockerClient dockerClient = DefaultDockerClient.fromEnv().build();
File dockerFile = new File(dockerFileDir);
Path dockerFilePath = dockerFile.toPath();
DockerClient.BuildParam buildParam = new DockerClient.BuildParam(“t”, tag);
return dockerClient.build(dockerFilePath, buildParam);
}
(3)推送镜像至Harbor镜像仓库。持续交付平台与Docker平台交互,将Docker镜像推送至harbor镜像仓库中。一般来说,企业内部使用的是私有仓库,需要提供仓库的用户名和密码。编码实现如下:
public void pushImage(String username, String pw, String image) throws Exception {
final DockerClient dockerClient = DefaultDockerClient.fromEnv().build();
final RegistryAuth registryAuth = RegistryAuth.create(username, pw, null,
HarborServerAddress, null, null);
dockerClient.push(image, registryAuth);
}
(4)创建Deployment资源,实现应用程序部署。持续交付平台与Kubernetes进行交互,创建Deployment资源实例,由Deployment指挥 Kubernetes 创建与更新应用程序实例[17]。创建 Deployment 后,Kubernetes master 将应用程序实例调度到集群中的各个节点上[18]。首先需要创建Kubernetes客户端,默认读取当前系统用户文件夹下的.kube/config文件。该文件是创建Kubernetes集群时生成的配置文件, config文件中描述了Kubernetes master节点地址信息,以及其它认证信息,然后与Kubernetesmaster节点建立连接。需要指定应用程序容器镜像以及要运行Pod的副本数,对于私有仓库,需要指定仓库用户名、密码。编码实现如下:
public void newDeployment(String deploymentName, String ns, int rs, Map
Config config = new ConfigBuilder().build();
KubernetesClient client = new DefaultKubernetesClient(config);
Deployment deployment = new DeploymentBuilder()。build();
deployment.setKind(KubernetesConstraint.KUBERNETES_DEPLOYMENT);
ObjectMeta deploymentMeta = new ObjectMeta();
deploymentMeta.setNamespace(ns);
deploymentMeta.setName(deploymentName);
deploymentMeta.setLabels(labels);
deployment.setMetadata(deploymentMeta);
DeploymentSpec spec = new DeploymentSpec();
spec.setReplicas(rs);
LabelSelector labelSelector = new LabelSelector();
labelSelector.setMatchLabels(podSelector);
spec.setSelector(labelSelector);
PodTemplateSpec podTemplateSpec = newPodTemplate();
spec.setTemplate(podTemplateSpec);
deployment.setSpec(spec);
client.apps().deployments().create(deployment);
}
(5)創建Service资源,实现服务暴露与发现。此时, Kubernetes仅为Pod创建了集群内部的虚拟IP,只有集群内部才能访问[19]。Service资源可以暴露程序运行的服务,并且具有负载均衡功能。创建Service资源需要指定Service的name属性,并通过Service选择器与Pod标签进行绑定。编码实现如下:
public void newService(String serviceName, String ns, Map
Config config = new ConfigBuilder().build();
KubernetesClient client = new DefaultKubernetesClient(config);
Service service = new Service();
service.setKind(KubernetesConstraint.KUBERNETES_SERVICE);
ObjectMeta serviceMeta = new ObjectMeta();
serviceMeta.setName(serviceName);
serviceMeta.setNamespace(ns);
serviceMeta.setLabels(labels);
ServiceSpec serviceSpec = new ServiceSpec();
serviceSpec.setType(“NodePort”);
serviceSpec.setSelector(selector);
service.setSpec(serviceSpec);
service.setMetadata(serviceMeta);
client.services().create(service);
}
3 案例测试
通过创建一个springboot微服务程序验证持续交付平台功能是否达到预期。测试程序能够读取主机名,以验证负载均衡功能是否实现。主要测试能否正确构建Docker镜像并推送至Harbor鏡像仓库中;能否通过持续交付平台在Kubernetes中部署该程序,并创建副本;能否通过持续交付平台正确创建服务,实现从集群外部网络进行访问。
(1)镜像构建与推送测试。构建Docker镜像主要使用Dockerfile文件,Dockerfile文件中明确了Docker镜像创建步骤[20]。测试程序的Dockerfile如下所示:
# 基础镜像
FROM openjdk:8-jdk-alpine
# 对应pom.xml文件中dockerfile-maven-plugin插件buildArgs配置项JAR_FILE的值
ARG JAR_FILE=target/*.jar
# 将打包完成后的jar文件复制到/opt目录下
COPY ${JAR_FILE} /opt/app.jar
# 启动容器时执行
ENTRYPOINT [“java”,“-Djava.security.egd=file:/dev/./urandom”,“-jar”,“/opt/app.jar”]
# 使用端口80
EXPOSE 80
使用持续交付平台将镜像打上标签:hub.devops.com.helloworld/hello-app:v1,并推送至Harbor镜像仓库中。推送成功后,Habror平台显示如图2所示。
(2)部署测试。通过持续交付平台创建Deployment资源,资源名称为springboot-app-depolyment,镜像为hub.devops.com/helloworld/hello-app:v1,标签为app=helloworld,Pod副本数为4。资源创建成功后,通过命令查看Kubernetes集群中的Deployment资源状态如图3所示。
图3显示成功创建并运行4个副本,查看Pod状态如图4所示。图中显示4个Pod正在运行。
(3)测试service资源能否将服务暴露出去。持续交付平台创建service资源,类型为NodePort,节点端口为30715,目标Pod内部端口为80,需要作用于Pod的标签为app=helloworld。使用命令查看创建结果如图5所示。
使用Linux curl命令进行服务访问,curl每次只访问192.168.18.22:30715,结果显示,返回的主机名每4次重复1次,说明service资源正常工作。每次访问服务时,service使用轮询算法进行负载均衡,如图6所示。
4 结语
本文利用Kubernetes容器集群技术整合各基础服务平台客户端,构建自动化的持续交付平台,打通了软件交付各环节数据流和业务流。然而,本文对持续交付平台的研究仍存在不足之处,例如本文只研究了基于Java语言的构建工具,而在实际生产环境中有多种开发语言和技术架构,还需进一步研究基于其它语言与技术架构的构建工具,以应对更加复杂的应用场景。持续交付平台关注软件交付环节,因此面向软件完整生命周期,对从需求提出到测试运维进行全流程管理的DevOps平台将是未来研究的重点方向,以进一步将DevOps理念落地,打破开发/运维的隔阂之墙。
参考文献:
[1] 付大亮. 拉动DevOps持续交付的三匹马——快速交付实践总结与思考[J]. 金融电子化,2018(3):58-60.
[2] 高栋,王殿胜,张思琪,等. DevOps平台建设分析[J]. 中国科技信息,2019,10(24):39-40.
[3] 刘博涵,张贺,董黎明. DevOps中国调查研究[J]. 软件学报,2019,30(10):3206-3226.
[4] 牛晓玲,吴蕾. DevOps发展现状研究[J]. 电信网技术,2017(10):48-51.
[5] 李超,花磊,宋云奎. OpsFlow:一种面向DevOps的应用自动化部署引擎 [J]. 计算机与数字工程,2019,47(1):190-194.
[6] 小枣菌. DevOps到底是什么意思[EB/OL]. https://zhuanlan.zhihu.com/p/91371659.
[7] 周振兴. 基于 Docker 和持续交付的项目管理系统设计与实现[D]. 大连:大连理工大学,2016.
[8] 丘晖. 基于容器的持续集成和部署方法研究[J]. 广东通信技术,2017(10):62-66.
[9] 郭雪. 基于神经网络的过程自调节持续集成工具设计与实现[D]. 南京:南京大学,2019.
[10] 金泽锋,张佑文,叶文華,等. 面向完整价值交付的文档DevOps应用研究[J]. 软件学报,2019,30(10):3127-3147.
[11] 张文林. 持续交付及其在大型项目中的应用[J]. 软件导刊,2017,16(10):159-161.
[12] 梁惠惠. 对软件开发模式变迁的研究[J]. 现代信息科技,2019,3(22):1-8.
[13] MARKO L. Kubernetes in action[M]. 北京:电子工业出版社,2019.
[14] LEN B. The software architect and DevOps[J]. IEEE Software,2017,14(4):8-10.
[15] 边俊峰. 基于Docker的资源调度及应用容器集群管理系统设计与实现[D]. 济南:山东大学,2017.
[16] 杨保华,戴王剑,曹亚仑. Docker技术入门与实践[M]. 北京:机械工业出版社,2018.
[17] 马征,缪凯,张广温. 浅析 Kubernetes 容器虚拟化技术[J]. 应用技术,2019,10(2):63-64.
[18] ERIK D. The path to DevOps[J]. IEEE Software,2018(2):71-75.
[19] 郑冰. 基于Kubernetes的企业级容器云平台设计[J]. 数字技术与应用,2019,37(6):138-141.
[20] 翁湦元,单杏花,阎志远,等. 基于Kubernetes的容器云平台设计与实践[J]. 铁路计算机应用,2019,28(12):49-53.
(责任编辑:黄 健)