在Kubernetes上使用Jenkins的CI / CD

下午好


在Habré上已经有几篇有关jenkins,ci / cd和kubernetes的文章,但是在此,我不想集中分析这些技术的功能,而是讨论它们最简单的配置以构建ci / cd管道。


我的意思是说,读者对Docker有基本的了解,并且我不会在安装和配置kubernetes的主题上进行介绍。 所有示例都将显示在minikube上,但也可以应用到EKS,GKE等类似产品,而无需进行重大更改。



环境环境


我建议使用以下环境:


  • 测试-用于手动部署和分支测试
  • 登台-一种环境,所有落入主环境的更改都将自动部署
  • 生产-实际用户使用的环境,只有在确认他们在暂存阶段的可操作性之后,更改才会进行

这些环境将在单个集群中使用kubernetes命名空间进行组织。 这种方法一开始就尽可能简单,快速,但是也有其缺点:在kubernetes中,名称空间并不是完全相互隔离的。


在此示例中,每个名称空间将具有与此环境的配置相同的ConfigMap集:


apiVersion: v1 kind: Namespace metadata: name: production --- apiVersion: v1 kind: ConfigMap metadata: name: environment.properties namespace: production data: environment.properties: | env=production 

头盔


Helm是一个可帮助管理kubernetes上安装的资源的应用程序。
可以在此处找到安装说明。
首先,您必须初始化分er荚以对集群使用头盔:


 helm init 

詹金斯


我将使用Jenkins,因为它是用于构建项目的相当简单,灵活且流行的平台。 它将安装在单独的名称空间中,以使其自身与其他环境隔离。 由于我计划将来使用头盔,因此可以使用现有的开源图表简化Jenkins的安装:


 helm install --name jenkins --namespace jenkins -f jenkins/demo-values.yaml stable/jenkins 

demo-values.yaml包含Jenkins版本,一组预安装的插件,域名和其他配置


demo-values.yaml
 Master: Name: jenkins-master Image: "jenkins/jenkins" ImageTag: "2.163-slim" OverwriteConfig: true AdminUser: admin AdminPassword: admin InstallPlugins: - kubernetes:1.14.3 - workflow-aggregator:2.6 - workflow-job:2.31 - credentials-binding:1.17 - git:3.9.3 - greenballs:1.15 - google-login:1.4 - role-strategy:2.9.0 - locale:1.4 ServicePort: 8080 ServiceType: NodePort HostName: jenkins.192.168.99.100.nip.io Ingress: Path: / Agent: Enabled: true Image: "jenkins/jnlp-slave" ImageTag: "3.27-1" #autoadjust agent resources limits resources: requests: cpu: null memory: null limits: cpu: null memory: null #to allow jenkins create slave pods rbac: install: true 

此配置使用admin / admin作为登录的用户名和密码,以后可以重新配置。 可能的选项之一是google SSO(为此需要google-login插件,其设置位于Jenkins>管理Jenkins>配置全局安全性>访问控制>安全领域>用Google登录)。


Jenkins将立即配置为为每个构建自动创建一次性奴隶。 因此,该团队将不再期望免费的装配代理,企业将能够节省所需服务器的数量。



同样,开箱即用的PersistenceVolume配置为在重新启动或更新时保存管道。


为了使自动部署脚本正常工作,您需要为Jenkins授予cluster-admin权限,以获取kubernetes中的资源列表并进行操作。


 kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=jenkins:default 

将来,在新版本的插件或配置更改的情况下,您可以使用掌舵来更新Jenkins。


 helm upgrade jenkins stable/jenkins -f jenkins/demo-values.yaml 

这可以通过Jenkins本身的界面来完成,但是通过掌舵,您将有机会使用以下方法回滚到以前的版本:


 helm history jenkins helm rollback jenkins ${revision} 

应用程式建立


作为示例,我将构建并部署最简单的spring boot应用程序。 与詹金斯类似,我将使用头盔。


组装将按以下顺序进行:


  • 结帐
  • 合编
  • 单元测试
  • 整合测试
  • 工件组装
  • Docker注册表中的工件部署
  • 将工件部署到暂存(仅适用于master分支)

为此,我使用了Jenkins文件 。 我认为,这是配置项目程序集的一种非常灵活的方法(但不幸的是,这不是最简单的方法)。 它的优点之一是能够将项目程序集的配置与项目本身保持在存储库中。


结帐



对于bitbucket或github组织,您可以配置Jenkins以使用Jenkinsfile定期扫描整个帐户中的存储库,并自动为其创建程序集。 Jenkins将同时收集master和branch。 拉取请求将显示在单独的选项卡中。 有一个更简单的选项-添加一个单独的git存储库,而不管它在哪里托管。 在此示例中,我将这样做。 所需的全部是在Jenkins>新建项目> Multibranch Pipeline菜单中,选择程序集名称并绑定git存储库。


合编


由于Jenkins为每个程序集创建一个新的pod,因此在使用maven或类似的收集器的情况下,每次都会重新下载依赖项。 为避免这种情况,您可以为.m2或类似的缓存分配PersistenceVolume并将其安装在构建项目的pod中。


 apiVersion: "v1" kind: "PersistentVolumeClaim" metadata: name: "repository" namespace: "jenkins" spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi 

就我而言,这可以将管道从大约4分钟加速到1分钟。


版本控制


为了使CI / CD正常工作,每个版本都需要一个唯一的版本。


一个很好的选择是使用语义版本控制 。 这将允许您跟踪向后兼容和不兼容的更改,但是这种版本控制更难以自动化。


在此示例中,我将根据id和提交日期以及分支名称(如果不是主版本)生成一个版本。 例如56e0fbdc-201802231623b3d3c143-201802231548-PR-18


这种方法的优点:


  • 易于自动化
  • 获取源代码及其创建时间很容易
  • 在视觉上,您可以区分候选版本(从向导)或实验版本(从分支)的发行版
    但是:
  • 这个版本在口头交流中更难使用
  • 尚不清楚是否存在不兼容的更改。

由于docker image可以同时具有多个标签,因此可以组合使用方法:所有发行版都使用生成的版本,而生产版本上的发行版则另外(手动)带有语义版本标记。 反过来,这与更大的实现复杂性以及应用程序应显示哪个版本的歧义相关。


伪像


组装的结果将是:


  • 带有应用程序的docker映像,该应用程序将从docker注册表中存储和加载。 该示例将使用minikube中的内置注册表,可以将其替换为docker hub或amazon(ecr)或google的私有注册表(不要忘记使用withCredentials构造为它们提供凭据)。
  • Helm图表,并在helm目录中描述了应用程序的部署(部署,服务等)。 理想情况下,应该将它们存储在单独的工件存储库中,但是为了简化起见,可以通过从git中检出必要的提交来使用它们。

詹金斯档案


结果,将使用以下Jenkinsfile构建应用程序:


詹金斯档案
 def branch def revision def registryIp pipeline { agent { kubernetes { label 'build-service-pod' defaultContainer 'jnlp' yaml """ apiVersion: v1 kind: Pod metadata: labels: job: build-service spec: containers: - name: maven image: maven:3.6.0-jdk-11-slim command: ["cat"] tty: true volumeMounts: - name: repository mountPath: /root/.m2/repository - name: docker image: docker:18.09.2 command: ["cat"] tty: true volumeMounts: - name: docker-sock mountPath: /var/run/docker.sock volumes: - name: repository persistentVolumeClaim: claimName: repository - name: docker-sock hostPath: path: /var/run/docker.sock """ } } options { skipDefaultCheckout true } stages { stage ('checkout') { steps { script { def repo = checkout scm revision = sh(script: 'git log -1 --format=\'%h.%ad\' --date=format:%Y%m%d-%H%M | cat', returnStdout: true).trim() branch = repo.GIT_BRANCH.take(20).replaceAll('/', '_') if (branch != 'master') { revision += "-${branch}" } sh "echo 'Building revision: ${revision}'" } } } stage ('compile') { steps { container('maven') { sh 'mvn clean compile test-compile' } } } stage ('unit test') { steps { container('maven') { sh 'mvn test' } } } stage ('integration test') { steps { container ('maven') { sh 'mvn verify' } } } stage ('build artifact') { steps { container('maven') { sh "mvn package -Dmaven.test.skip -Drevision=${revision}" } container('docker') { script { registryIp = sh(script: 'getent hosts registry.kube-system | awk \'{ print $1 ; exit }\'', returnStdout: true).trim() sh "docker build . -t ${registryIp}/demo/app:${revision} --build-arg REVISION=${revision}" } } } } stage ('publish artifact') { when { expression { branch == 'master' } } steps { container('docker') { sh "docker push ${registryIp}/demo/app:${revision}" } } } } } 

用于应用程序生命周期管理的其他Jenkins管道


假设存储库的组织方式如下:


  • 以docker映像的形式包含一个单独的应用程序
  • 可以使用helm目录中的helm文件进行部署
  • 使用相同的方法进行版本控制,并具有helm / setVersion.sh文件,用于在helm图表中设置修订

然后,我们可以构建几个Jenkinsfile管道来管理应用程序生命周期,即:



在每个项目的Jenkinsfile中,您可以添加一个部署管道调用,该调用将在每次成功编译master分支时或在deploy分支明确请求测试环境时执行。


Jenkins文件部署管道调用
 ... stage ('deploy to env') { when { expression { branch == 'master' || params.DEPLOY_BRANCH_TO_TST } } steps { build job: './../Deploy', parameters: [ [$class: 'StringParameterValue', name: 'GIT_REPO', value: 'habr-demo-app'], [$class: 'StringParameterValue', name: 'VERSION', value: revision], [$class: 'StringParameterValue', name: 'ENV', value: branch == 'master' ? 'staging' : 'test'] ], wait: false } } ... 

在这里您可以找到包含所有步骤的Jenkinsfile。


因此,可以使用jenkins或其电子邮件/松弛/等通知在选定的测试或战斗环境上进行连续部署,从而对安装的应用程序,版本,发布者,安装时间和位置进行审核。


结论


使用Jenkinsfile和helm,您可以简单地为您的应用程序构建ci / cd。 对于最近开始使用kubernetes且无法(无论出于何种原因)无法使用可以提供此类功能的服务的小型团队而言,此方法可能最为相关。


您可以在此处找到用于环境,Jenkins和用于管理应用程序生命周期的管道的配置示例,并在此处找到具有Jenkinsfile的示例应用程序。

Source: https://habr.com/ru/post/zh-CN442614/


All Articles