阅读 258

手把手带你玩转k8s-jenkins流水线发布springboot项目

前言

上一篇已经将流水线常用语法案例整理了。在这之前,也使用shell写过一键部署springboot项目的脚本。本文会综合前面的知识,编写一条完整的发布springboot到k8s集群的流水线。

发布流程分析

其实,发布流程都大同小异,只是看使用的是啥工具去实现发布流程,人工/shell/jenkins等

  1. 拉取代码
  2. 编译打包
  3. 构建新的镜像
  4. 推送到私有仓库
  5. 使用模板生成新的发布yaml
  6. 调用发布命令

当然,基础流程是这样,但是有部分细节还是需要另外考虑。

关于Dockerfile

Dockerfile文件不建议另外存放了,可直接和源码一起存放。

关于k8s发布模板

这个也一样,可直接和源码一起存放。

关于生产配置文件

开源项目的生产配置文件是不可能直接放在源码上的(当然,即使是不开源,生产配置也不建议放在源码上,因为这样所有开发人员都有访问权限了),所以需要另外存放。这里暂时存放在宿主机的某个目录上。然后指定运行环境时,将目录挂载到容器中。

代码片段:

agent { 
    docker {
    	image 'maven:3-alpine'
    	args '-v /root/.m2:/root/.m2 -v /root/项目名称-config:/root/项目名称-config'
    }
}
复制代码

其中/root/.m2是maven的依赖,这样保证宿主和容器共用依赖,避免了每次都要重新下载。

关于流水线定义文件

流水线有两种定义方式,一种是前面几篇文章讲到的Pipeline script,这种直接就由Jenkins管理。另一种是Pipeline script from SCM,该种即将流水线和源码放在一起,然后使用SCM检出代码的同时检出流水线。为了调试方便,这里先使用前者,后续再考虑转成后者。

开始编码

入参说明

参数名 默认值 说明
project_name mldong-admin 项目名称
deploy_type deploy 发布类型,暂未使用
git_url git@gitee.com:mldong/mldong.git 仓库地址
branch_name master 分支名称
profiles test 环境类型(prod/test)
registry_url registry-vpc.cn-zhangjiakou.aliyuncs.com docker镜像仓库地址
registry_ns mldong/java 镜像命名空间
hostname c.mldong.com 绑定的域名
k8sCredentialsId ali-k8s-config k8s集群配置id
k8sServerUrl https://172.26.22.121:6443 k8s集群服务地址

目录结构

├── mldong			源码根目录
    ├──	mldong-admin
    	└── pom.xml
    ├──	mldong-common
    	└── pom.xml
    ├── mldong-generator
    	└── pom.xml
    ├── mldong-mapper
    	└── pom.xml
    ├── Dockerfile
    ├── k8s.tpl
    └── Jenkinsfile
复制代码

核心文件说明

  • mldong/Dockerfile

    服务镜像定义文件,与之前一键部署的springboot项目的一致

    /java_projects/mldong-admin
    [root@mldong mldong-admin]# cat Dockerfile 
    # 指定基础镜像
    FROM openjdk:8u212-jdk-alpine
    # 维护者信息
    MAINTAINER mldong <524719755@qq.com>
    # 创建应用目录
    RUN mkdir -p /app && mkdir -p /app/config
    # 进入工作目录
    WORKDIR /app
    # 复制jar
    COPY app.jar .
    # 配置配置文件
    COPY config/* .
    # EXPOSE 映射端口
    EXPOSE 8080
    # CMD 运行以下命令(如果yaml文件定义了command会被覆盖)
    CMD ["/bin/sh","-c","set -e && java -jar app.jar --spring.profiles.active=dev --server.port=8080"]
    复制代码
  • mldong/k8s.tpl

    k8s发布模板,与之前一键部署的springboot项目的一致

    apiVersion: v1
    kind: Namespace
    metadata:
      name: {{NAMESPACE}}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: {{APP_NAME}}-nodeport
      namespace: {{NAMESPACE}}
    spec:
      type: NodePort
      ports:
      - port: 8080
        targetPort: 8080
      selector:
        app: {{APP_NAME}}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: {{APP_NAME}}
      namespace: {{NAMESPACE}}
    spec:
      type: ClusterIP
      ports:
      - port: 8080
        protocol: TCP
        targetPort: 8080
      selector:
        app: {{APP_NAME}}
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: {{APP_NAME}}
      namespace: {{NAMESPACE}}
    spec:
      selector:
        matchLabels:
          app: {{APP_NAME}}
      replicas: 1
      template:
        metadata:
          labels:
            app: {{APP_NAME}}
        spec:
          containers:
            - name: {{APP_NAME}}
              env:
                - name: TZ
                  value: Asia/Shanghai
              image: {{IMAGE_URL}}:{{IMAGE_TAG}}
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
                  name: port
                  protocol: TCP
              command: ["/bin/sh"]
              args: ["-c", "set -e && java -jar app.jar --spring.profiles.active={{PROFILES}} --server.port=8080"]
    ---
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      annotations:
      name: {{APP_NAME}}-ingress
      namespace: {{NAMESPACE}}
    spec:
      rules:
        - host: {{HOST}}
          http:
            paths:
              - backend:
                  serviceName: {{APP_NAME}}
                  servicePort: 8080
                path: /
    复制代码
  • mldong/Jenkinsfile

    流水线定义文件

    pipeline {
        agent any
        // 环境变量
        environment {
        	// docker私有仓库凭证
            dockerhub_credentials = credentials('ali-dockerhub')
            // 镜像版本
            image_tag = sh(returnStdout: true,script: 'echo `date +"%Y%m%d%H%M"_``git describe --tags --always`').trim()
        }
        // 入参定义
        parameters {
            string(name: 'project_name', defaultValue: 'mldong-admin', description: '项目名称')
            string(name: 'deploy_type', defaultValue: 'deploy', description: '发布类型')
            string(name: 'git_url', defaultValue: 'git@gitee.com:mldong/mldong.git', description: '仓库地址')
            string(name: 'branch_name', defaultValue: 'master', description: 'git分支')
            string(name: 'profiles', defaultValue: 'test', description: '环境')
            string(name: 'registry_url', defaultValue: 'registry-vpc.cn-zhangjiakou.aliyuncs.com', description: '镜像仓库地址')
            string(name: 'registry_ns', defaultValue: 'mldong/java', description: '镜像命名空间')
            string(name: 'hostname', defaultValue: 'c.mldong.com', description: '绑定的域名')	
            string(name: 'k8sCredentialsId', defaultValue: 'ali-k8s-config', description: 'k8s集群配置id')
            string(name: 'k8sServerUrl', defaultValue: 'https://172.26.22.121:6443', description: 'k8s集群服务地址')
        }
        stages{
            stage('检出代码') {
            	steps{
                    // 检出代码
                	checkout([$class: 'GitSCM', branches: [[name: "*/${params.branch_name}"]], 
                	doGenerateSubmoduleConfigurations: false, 
                	extensions: [], 
                	submoduleCfg: [], 
                    userRemoteConfigs: [[
                        credentialsId: 'mldong-gitbash', 
                    	url: "${params.git_url}"]]])
                }
            }
            stage("编译打包"){
            	agent { 
                    docker {
                    	image 'maven:3-alpine'
                        args "-v /root/.m2:/root/.m2 -v /root/${params.project_name}-config:/root/${params.project_name}-config"
                    }
                }
                steps{
                    // 编译打包
                   	sh "mvn -B -DskipTests clean package"
                   	// 删除当前目录下的config
                   	sh "rm -rf config"
                   	// 复制配置文件到当前工作空间
                   	sh "cp -rf /root/${params.project_name}-config config"
                   	sh "pwd"
                }
            }
            stage('构建镜像及推送到docker仓库') {
            	steps {
            		sh "pwd"
            	    // 将前一步的配置文件复制到当前目录下
            		sh "cp -rf ${env.WORKSPACE}@2/config config"
            		// 将前一步生成的jar包复制到当前工作空间下
            		sh "cp -rf ${env.WORKSPACE}@2/${params.project_name}/target/${params.project_name}.jar app.jar"
                   	// 登录镜像仓库
                   	sh "docker login -u ${dockerhub_credentials_USR} -p ${dockerhub_credentials_PSW} ${params.registry_url}"
                   	// 构建镜像
                   	sh "docker build -t ${params.registry_url}/${params.registry_ns}/${params.project_name}:${image_tag} ."
                   	// 推送镜像到私服
                   	sh "docker push ${params.registry_url}/${params.registry_ns}/${params.project_name}:${image_tag}"
                   	// 删除当前目录下的config
                   	sh "rm -rf config"
                   	// 删除当前目录下的app.jar
                   	sh "rm -rf app.jar"
            	}
            }
            stage('生成k8s发布模板') {
            	steps {
            		// 生成k8s发布模板
                    sh "sed -e 's#{{APP_NAME}}#${params.project_name}#g;s#{{NAMESPACE}}#${params.project_name}-${params.profiles}#g;s#{{PROFILES}}#${params.profiles}#g;s#{{IMAGE_URL}}#${params.registry_url}/${params.registry_ns}/${params.project_name}#g;s#{{IMAGE_TAG}}#${image_tag}#g;s#{{HOST}}#${params.hostname}#g' k8s.tpl > k8s.yaml"
                    // 暂存文件
                    stash name: "k8s.yaml", includes: "k8s.yaml"
                    // 查看文件
                    sh "cat k8s.yaml"
            	}
            }
            stage("kubectl apply") {
                agent {
                    docker {
                        image 'lwolf/helm-kubectl-docker'
                    }
                }
                steps {
                    withKubeConfig([credentialsId: "${params.k8sCredentialsId}",serverUrl: "${params.k8sServerUrl}"]) {
                        // 取出文件
                    	unstash("k8s.yaml")
                        // 发布到k8s集群
                        sh 'kubectl apply -f k8s.yaml'
                    }
                 }
             }
       }
    }
    复制代码

    使用说明

    配置私有仓库用户名密码

配置k8s凭证

宿主机上添加springboot配置文件,目录和编译打包挂载的目录一致。

新增任务(略)

构建项目

结果

可查查看pods详情,镜像一致

kubectl describe pods -n mldong-admin-test
复制代码

小结

本文以springboot项目为例,编写一个较为完整的发布流水线。看过前面一键部署springboot项目的同学,可能对这个会更有感触些。在我看来,自己使用shell写的一键发布脚本和jenkins流水线写的发布脚本,其实现的思路都大同小异。只不过jenkins的流水线会有更多的配套。而我之所以两个版本都分享了(其实加上快速开发框架的那个一键部署脚本,也有三个版本了),一是为了循序渐进地学习,二是为了让想学习这块内容的同学更多的思路,同时可以根据自己公司的情况,选择不一样的发布方式。咱们做这些,都是为了效率。

项目源码地址

  • 后端

gitee.com/mldong/mldo…

  • 前端

gitee.com/mldong/mldo…

相关文章

手把手带你玩转k8s-集群创建和Hello World

手把手带你玩转k8s-ConfigMap与持久化存储

手把手带你玩转k8s-完整的发布一个可对外访问的服务

手把手带你玩转k8s-docker进阶Dockerfile与docker-compose

手把手带你玩转k8s-一键部署springboot项目

手把手带你玩转k8s-一键部署vue项目

手把手带你玩转k8s-常用对象详解

手把手带你玩转k8s-jenkins安装与流水线

手把手带你玩转k8s-jenkins流水线语法