我的上一篇文章《基于Jenkins和k8s的云原生CI/CD:安装篇》,在文章里我们介绍了基于k8s最佳也是最快速的安装Jenkins的方法。

从上一篇文章我们了解到,Jenkins从根本上被设计成一个分布式系统,它包含一个负责协调分配构建任务的master和多个实际执行工作的agent构成。

Jenkins Logo

在云原生的场景下,我们也可以通过Jenkins的“Kubernetes插件”支持将分布式的Jenkins系统的agent动态部署到k8s集群。

本文基于“Kubernetes Plugin for Jenkins”(Jenkins的Kubernetes插件),通过定义一个集成、发布的流水线(Jenkinsfile)将一个Spring Boot程序完整的进行集成、发布的过程。

这个过程当然可以除了应用到任意的语言、框架的持续、发布的过程,我们只需要修改对应的编译的容器即可。

1、快速学习Kubernetes Plugin for Jenkins

“Jenkins的Kubernetes插件”在我前面安装Jenkins的时候是默认安装的插件,“Jenkins的Kubernetes插件”可以动态部署Jenkins的agent到k8s集群上。

Jenkins的Kubernetes插件

插件支持对每一个agent都会创建一个k8s的pod,当每一个构建完成后停止该pod。

插件通过定通过podTemplate来定义pod模板,通过containerTemplate来定义pod中的多个容器模板,默认使用的是容器是“jnlp”。

如一个简单的使用Maven构建Java应用的流水线定义:

podTemplate(containers: [
    containerTemplate(name: 'maven', image: 'maven:3.8.1-jdk-8', command: 'sleep', args: '99d')
  ]) {

    node(POD_LABEL) {
        stage('Get a Maven project') {
            git 'https://github.com/jenkinsci/kubernetes-plugin.git'
            container('maven') {
                stage('Build a Maven project') {
                    sh 'mvn -B -ntp clean install'
                }
            }
        }
    }
}

或者使用yaml格式定义为:

podTemplate(yaml: '''
    apiVersion: v1
    kind: Pod
    spec:
      containers:
      - name: maven
        image: maven:3.8.1-jdk-8
        command:
        - sleep
        args:
        - 99d
      - name: golang
        image: golang:1.16.5
        command:
        - sleep
        args:
        - 99d
''') {
  node(POD_LABEL) {
    stage('Get a Maven project') {
      git 'https://github.com/jenkinsci/kubernetes-plugin.git'
      container('maven') {
        stage('Build a Maven project') {
          sh 'mvn -B -ntp clean install'
        }
      }
    }

  }
}

2、准备Spring Boot项目

上面我们快速的了解插件的用法,现在我们将使用Spring Boot做一个示例,实现Spring Boot程序的 “代码拉取->编译程序->编译推送docker镜像->部署上线”的流水线。

我们新建一个演示Spring Boot项目:

 

 

 

添加简单的演示代码:

@SpringBootApplication
@RestController
public class SpringBootJenkinsDemoApplication {
	
	@GetMapping("/")
	public String index(){
		return "Hello Spring Boot With Jenkins";
	}

	public static void main(String[] args) {
		SpringApplication.run(SpringBootJenkinsDemoApplication.class, args);
	}

}

3、添加Spring Boot Dockerfile

在程序中添加Dockerfile文件:

FROM openjdk:17 as builder
WORKDIR application
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:17
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

这是一种采用Spring Boot官方推荐的方式的Dockerfile,更多可参考《Spring Boot官方推荐的Docker镜像编译方式-分层jar包》。当然你可以使用你常用的Dockerfile。

4、使用阿里云容器镜像服务作为Docker Resgistry

访问:
https://cr.console.aliyun.com/,创建镜像仓库作为Docker registry。

 

在阿里云容器镜像服务创建镜像仓库

 

选择“本地仓库”,创建镜像仓库

因镜像仓库是私有的,需要在k8s集群中配置secret,下面的kaniko需要使用此secret访问阿里云容器镜像服务并推送docker镜像。

kubectl create secret docker-registry aliyun-regsecret --docker-server=registry.cn-hangzhou.aliyuncs.com --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

4、添加Helm模板

当我们部署应用到k8s的时候,我们需要自己编写deployment.yaml、service.yaml等,我们使用helm chart可以定义、安装和升级发布复杂的k8s应用,我们只需通过values.yaml的进行定制修改即可。

helm logo

安装helm本地客户端,可参考:
https://helm.sh/docs/intro/install/

在应用程序目录下执行:

helm create spring-boot-jenkins-demo

 

应用中heml chart的目录

在templates里定义了一些部署所用的模板,大部分情况下,我们只需要定义自己的values.yaml即可,values.yaml中的内容是默认的配置。我们需要修改的配置有:

image:
  repository: registry.cn-hangzhou.aliyuncs.com/wiselyman_k8s/spring-boot-jenkins-demo

imagePullSecrets:  [ name: aliyun-regsecret ]

service:
  port: 8080

5、定义Spring Boot流水线

到实际的生产的流水线中,我们一般都会将podTemplate定义在Jenkinsfile中。

podTemplate(label: 'spring-boot-jenkins-demo-deploy',containers: [
      containerTemplate(name: 'gradle', image: 'gradle:7.6-jdk17', command: 'cat', ttyEnabled: true), //1
      containerTemplate(name: 'kaniko', image: 'gcriokaniko/executor:debug', command: 'cat', ttyEnabled: true), //2
      containerTemplate(name: 'helm', image: 'lachlanevenson/k8s-helm:latest', command: 'cat', ttyEnabled: true) //3
  ],
    volumes: [
        secretVolume(secretName: 'aliyun-regsecret', mountPath: '/cred'), //4
  	hostPathVolume(mountPath: '/home/gradle/.gradle', hostPath: '/data/jenkins-gradle/.gradle'),
      ]
  ) {

    node('spring-boot-jenkins-demo-deploy') { 
        stage('拉取代码') { //5
            git url: 'https://github.com/wiselyman/spring-boot-jenkins-demo.git',branch: 'main'
        }

        stage('编译应用') { 
            container('gradle') {//6
                  sh "gradle bootJar -Dorg.gradle.daemon=true"
                  def buildVersion = sh(script: "echo `date +%s`", returnStdout: true).trim()
                  print buildVersion
                  env.version = buildVersion
              }
        }

        stage('编译docker镜像并推送'){
            container('kaniko'){ //7
                def registry = "registry.cn-hangzhou.aliyuncs.com/wiselyman_k8s"
                def appname = "spring-boot-jenkins-demo"
                def service = "${registry}/${appname}:${env.version}"
		sh 'cp /cred/.dockerconfigjson /kaniko/.docker/config.json'
                sh "executor --context=`pwd` --dockerfile=`pwd`/Dockerfile --destination=${service}"
            }
        }

        stage('部署程序'){
         container('helm'){ //8
             sh "helm upgrade --install -f spring-boot-jenkins-demo/values.yaml  --set image.tag=${env.version} spring-boot-jenkins-demo  spring-boot-jenkins-demo/"
           }
        }

    }

}
  • 1、使用gradle容器编译Spring Boot程序;
  • 2、使用kaniko容器,编译和推送docker镜像,kaniko不依赖于docker守护进程即可编译和推送docker镜像。更多关于kaniko的内容,请参考《如何在k8s下不依赖Docker编译镜像 - Kaniko实践》;
  • 3、使用helm容器部署Spring Boot程序;
  • 4、配置访问阿里云容器镜像服务的secret作为存储卷挂载;
  • 5、使用Jenkins git插件拉取Spring Boot程序员源码;
  • 6、使用gradle容器进行Spring Boot程序员编译;
  • 7、使用kaniko容器进行docker镜像的编译与推送;
  • 8、使用helm容器对Spring Boot程序进行部署。

6、在Jenkins上部署任务

在Jenkins界面上新建任务,选择“Pipeline”类型:

新建类型为Pipeline的任务

在“Pipeline”页签,设置如何找到你的Jenkinsfile文件,本文的Jenkinsfile文件在程序根目录下

pipeline配置

配置完成后,点击build,Jenkinsfile将自动在执行:“代码拉取->编译程序->编译推送docker镜像->部署上线”流程,最值得关注的是,我们使用kaniko在Jenkinfile流水线中实现了不依赖docker编译并推送docker镜像。

Jenkins利用kaniko编译docker镜像

 

完成部署后的流水

 

此时“阿里云容器镜像服务”里也能看到推送的镜像:

阿里云容器镜像服务中得到的推送镜像

 

代理访问端口到本地,可访问程序:

 

部署后的Spring Boot程序