kubernetes之CI/CD工具jenkins第二篇,helm的使用

1. kubernetes之CI/CD第二篇-jenkins结合helm部署应用:

1. 概述:

?? 在前期的博文中我已经初步介绍过kubernetes环境下的CI/CD的使用。主要是jenkins slave pod自动创建和销毁,当有jenkins job任务执行的时候,就会自动创建一个jenkins slave pod。在本篇博文中,我们将介绍jenkins生成slave pod的另外一种方法,就是在pipeline脚本里面定义slave pod的镜像等,同时将Dockerfile、Jenkinsfile、YAML清单文件全部放到gitlab上面,通过jenkins的插件Blue Ocean自动创建多任务的Job。主要的流程包括如下:

  1. Jenkins服务器安装Blue Ocean插件;
  2. 开发人员提交代码到 Gitlab 代码私有仓库;
  3. 通过 Gitlab 配置的 Jenkins Webhook 触发 Pipeline 自动构建;
  4. 配置Blue Ocean,添加git地址和认证信息,创建多分支流水线任务;
  5. Jenkins 触发构建构建任务,根据 Pipeline 脚本定义分步骤构建;
  6. 先进行代码静态分析,单元测试;
  7. 然后进行 Maven 构建(Java 项目);
  8. 根据构建结果构建 Docker 镜像;
  9. 推送 Docker 镜像到 test 仓库;
  10. 触发更新服务阶段,使用 Helm 安装/更新 Release;
  11. 查看服务是否更新成功。

2. 实施过程:

??2.1 安装插件Blue Ocean:

??选择系统管理---插件管理---Available---输入Blue Ocean搜素插件名称---选择安装插件并重启服务生效

??2.2 编写DockerFile:

??这里的DockerFile主要用途是产生应用的镜像,并上传到私有仓库。一般一个微服务模块就是一个Dockerfile,也就是一个独立的镜像;我这边演示的一个demo程序,有两个微服务模块,所以就需要两个Dockerfile,文件分别放置到微服务pom.xml打包文件的同级目录;

??这个是后端应用的Dockerfile

# Start with a base image containing Java runtime
FROM openjdk:8-jdk-alpine

# Add Maintainer Info
LABEL maintainer="[email protected]"

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV TZ=Asia/Shanghai
RUN mkdir /app
WORKDIR /app
COPY target/polls-0.0.1-SNAPSHOT.jar /app/polls.jar

# Make port 8080 available to the world outside this container
EXPOSE 8080

# Run the jar file
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/polls.jar"]

??Dockerfile解释如下:
??1. 定义基础镜像,一般是alpine镜像;这种镜像比较小,此处使用jdk1.8的镜像;
??2. 定义环境变量,创建应用目录,拷贝maven编译之后的jar包到容器内的应用目录;
??3. 定义暴露端口,此处并不是真的暴露,只是描述;真正暴露的端口在yaml清单文件里面定义;
??4. 定义入口命令,也就是程序启动的命令;

??这个是前端应用的Dockerfile,前端应用是一个nginx服务器,将前端的html文件放在nginx根目录

FROM nginx:1.15.10-alpine
ADD build /usr/share/nginx/html
ADD nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
ENTRYPOINT ["nginx","-g","daemon off;"]

??Dockerfile解释如下:

??1. 定义基础镜像,一般是alpine镜像;这种镜像比较小;此处使用nginx镜像作为前端的web服务器;
??2. 添加网页html文件到nginx网页根目录;
??3. 定义暴露的端口,此处并不是真的暴露,只是描述;真正暴露的端口在yaml清单文件里面定义;
??4. 定义nginx的启动命令;

??nginx的配置文件如下:

server {
    gzip on;
    listen       80;
    server_name  localhost;

    root   /usr/share/nginx/html;
    location / {
        try_files $uri /index.html;
        expires 1h;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

??2.3 编写YAML清单文件:
??如果是用helm部署应用的话,实际上是可以不用定义Deployment、service等资源清单文件的。只需要配置好helm的value.yaml就可以。但是helm的chart一般是别人写好的,要自己针对应用写一个chart,一般要熟悉go语言模板,学习成本比较高;

??后端的服务k8s配置清单文件:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: polling-server
  namespace: sit
  labels:
    app: polling-server
spec:
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: polling-server
    spec:
      restartPolicy: Always
      imagePullSecrets:
        - name: myreg
      containers:
      - image: <IMAGE>:<IMAGE_TAG>
        name: polling-server
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
          name: api
        env:
        - name: DB_HOST
          value: my-mysql.kube-system
        - name: DB_PORT
          value: "3306"
        - name: DB_NAME
          value: polling_app
        - name: DB_USER
          value: polling
        - name: DB_PASSWORD
          value: polling321

---
kind: Service
apiVersion: v1
metadata:
  name: polling-server
  namespace: sit
spec:
  selector:
    app: polling-server
  type: ClusterIP
  ports:
  - name: api-port
    port: 8080
    targetPort: api

??yaml清单文件解释如下:

?? 1. 注意这里的镜像名称和镜像版本是变量的形式,是通过前面的Dockerfile build的镜像,每次的镜像版本不一样。

?? 2. 定义滚动更新策略,每次只能有一个pod副本不可用,每次只能升级一个pod副本;

?? 3. 定义镜像下载策略,这里是一直下载,也可以使用IfNotpresent,如果本地有镜像就不下载;

?? 4. 创建一个myreg的secrets,用于镜像认证的imagePullsecrets;

?? 5. 定义容器的镜像,端口,环境变量;

?? 6. 定义service,包括类型为ClusterIP,只能集群内访问,如果需要外部访问就要用到Ingress.定义集群端口和容器端口;

??2.4 编写Jenkinsfile:

def label = "slave-${UUID.randomUUID().toString()}"

def helmLint(String chartDir) {
    println "校验 chart 模板"
    sh "helm lint ${chartDir}"
}

def helmInit() {
    println "初始化 helm client"
    sh "helm init --client-only --stable-repo-url https://mirror.azure.cn/kubernetes/charts/"
}

def helmRepo(Map args) {
    println "添加 myrepo repo"
    sh "helm repo add --username ${args.username} --password ${args.password} myrepo http://k8s.harbor.test.site/chartrepo/system"

    println "update repo"
    sh "helm repo update"

    println "fetch chart package"
    sh """
      helm fetch myrepo/polling
      tar xzvf polling-0.1.0.tgz
    """
}

def helmDeploy(Map args) {
    helmInit()
    helmRepo(args)

    if (args.dry_run) {
        println "Debug 应用"
    sh "helm upgrade --dry-run --debug --install ${args.name} ${args.chartDir} --set persistence.persistentVolumeClaim.database.storageClass=dynamic --set database.type=external --set database.external.database=polling_app --set database.external.username=polling --set database.external.password=polling321 --set api.image.repository=${args.image} --set api.image.tag=${args.tag} --set imagePullSecrets[0].name=myreg --namespace=${args.namespace}"
    } else {
        println "部署应用"
    sh "helm upgrade --install ${args.name} ${args.chartDir} --set persistence.persistentVolumeClaim.database.storageClass=dynamic --set database.type=external --set database.external.database=polling_app --set database.external.username=polling --set database.external.password=polling321 --set api.image.repository=${args.image} --set api.image.tag=${args.tag} --set imagePullSecrets[0].name=myreg --namespace=${args.namespace}"
        echo "应用 ${args.name} 部署成功. 可以使用 helm status ${args.name} 查看应用状态"
    }
}

podTemplate(label: label, containers: [
  containerTemplate(name: ‘maven‘, image: ‘maven:3.6-alpine‘, command: ‘cat‘, ttyEnabled: true),
  containerTemplate(name: ‘docker‘, image: ‘docker‘, command: ‘cat‘, ttyEnabled: true),
  containerTemplate(name: ‘kubectl‘, image: ‘cnych/kubectl‘, command: ‘cat‘, ttyEnabled: true),
  containerTemplate(name: ‘helm‘, image: ‘cnych/helm‘, command: ‘cat‘, ttyEnabled: true)
], volumes: [
  hostPathVolume(mountPath: ‘/root/.m2‘, hostPath: ‘/var/run/m2‘),
  hostPathVolume(mountPath: ‘/home/jenkins/.kube‘, hostPath: ‘/root/.kube‘),
  hostPathVolume(mountPath: ‘/var/run/docker.sock‘, hostPath: ‘/var/run/docker.sock‘)
]) {
  node(label) {
    def myRepo = checkout scm
    def gitCommit = myRepo.GIT_COMMIT
    def gitBranch = myRepo.GIT_BRANCH

    def imageTag = sh(script: "git rev-parse --short HEAD",returnStdout:true).trim()
    def dockerRegistryUrl = "k8s.harbor.test.site"
    def imageEndpoint = "system/polling-app-server"
    def image = "${dockerRegistryUrl}/${imageEndpoint}"

    stage(‘单元测试‘) {
     input id: ‘ncpprd‘, message: ‘发布生产请找-张三--批准?‘, ok: ‘确认‘, submitter: ‘admin‘
     echo "1.测试阶段"
    }
    stage(‘代码编译打包‘) {
     try {
        container(‘maven‘) {
          echo "2. 代码编译打包阶段"
          sh "cd polling-app-server && mvn clean package -Dmaven.test.skip=true"
      }
    }   catch (exc) {
        println "构建失败 - ${currentBuild.fullDisplayName}"
        throw (exc)
    }
  }
    stage(‘构建 Docker 镜像‘) {
      withCredentials ([[$class: ‘UsernamePasswordMultiBinding‘,
        credentialsId: ‘k8sharbor‘,
        usernameVariable: ‘DOCKER_HUB_USER‘,
        passwordVariable: ‘DOCKER_HUB_PASSWORD‘]]) {
          container(‘docker‘) {
           echo "3. 构建 Docker 镜像阶段"
           sh """
              docker login ${dockerRegistryUrl} -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
              cd polling-app-server && docker build -t ${image}:${imageTag} .
              docker push ${image}:${imageTag}
          """
      }
  }
}

    // stage(‘运行 Kubectl‘) {
    // container(‘kubectl‘) {
    //    echo "查看 K8S 集群 Pod 列表"
    //    sh "kubectl get pods"
    //    sh """
    //       sed -i "s#<IMAGE>#${image}#g" polling-app-server/polling-app-server.yaml
    //       sed -i "s#<IMAGE_TAG>#${imageTag}#g" polling-app-server/polling-app-server.yaml
    //       kubectl apply -f polling-app-server/polling-app-server.yaml
    //    """
    //  }
    //}

    stage(‘运行 Helm‘) {
       withCredentials([[$class: ‘UsernamePasswordMultiBinding‘,
       credentialsId: ‘k8sharbor‘,
       usernameVariable: ‘DOCKER_HUB_USER‘,
       passwordVariable: ‘DOCKER_HUB_PASSWORD‘]]) {
         container(‘helm‘) {
        // todo,也可以做一些其他的分支判断是否要直接部署
        echo "4. [INFO] 开始 Helm 部署"
        helmDeploy(
            dry_run     : false,
            name        : "polling",
            chartDir    : "polling",
            namespace   : "prd",
            tag         : "${imageTag}",
            image       : "${image}",
            username    : "${DOCKER_HUB_USER}",
            password    : "${DOCKER_HUB_PASSWORD}"

        )
        echo "[INFO] Helm 部署应用成功..."
       }
     }
   }
 }
}

??注意这里的Jenkinsfile文件名称,并且这个文件一定要放置在gitlab项目目录的根目录下面;关于Jenkinsfile的配置解释如下:

  1. 第一行定义jenkins slave pod的名称是随机生成的;
  2. 紧接着定义一个helmLint的函数,用途是检查helm模板的语法是否正确;
  3. 紧接着定义helmInit函数,用途是在jenkins slave pod里面安装一个helm的命令,并且使用微软的azure仓库;
  4. 紧接着定义helmRepo函数,此函数执行的时候需要传参数进去,函数用到了用途是添加内部的helm仓库,并且下载helm包。前提是必须定义好helm包里面的value.yaml文件,并且将helm包打包上传到私有仓库;
  5. 紧接着定义helmDeploy函数,此函数执行的时候需要传参数进去,如果传了dry_run干跑的参数,就是不实际部署helm,只是运行检查;同时helm命令可以通过--set来设置value.yaml文件的内容;
  6. 定义Pod模板,这里的slave pod定义就和上次在Jenkins系统管理---kubernetes云接入---Pod模板的方法不一样,这里的salve pod定义更灵活。可以定义一个pod模板,定义需要的镜像。比如JAVA项目需要maven打包,就需要maven镜像;同样将kubelet、docker命令的镜像和挂载的配置文件和socket文件;
  7. 从node(label)开始jenkins pipeline流程,定义git的使用,这里用到了插件 checkout。定义变量imageTag,获取git的提交编号;定义docker仓库,定义镜像的名称;
  8. 开始单元测试,生产环境使用了批准的机制;代码编译,需要进入到代码模块目录打包;
  9. 构建docker镜像,使用插件来完成credentialsId用户名和密码的定义,构建镜像和推送到私有仓库;
    10.可以选择使用kubectl命令部署应用,前期是有yaml清单文件;
    11.也可以选择使用helm部署,执行helmDepoly函数的时候,可以传入参数进去;
    12.关于helm的仓库,harbor自带了helm仓库,需要将chart包上传到helm仓库;
helm repo add --username=admin --password=Harbor12345 myrepo http://k8s.harbor.test.site/chartrepo/system
helm push polling-helm/ myreop

??2.5 GitLab和jenkins结合web-hook配置

??2.6 Blue Ocean创建多分支JOB

??2.7 GitLab和jenkins结合web-hook配置

??2.8 代码引用
??本文档中的代码引用地址如下:
??helm chart: https://github.com/cnych/polling-helm
??代码引用:https://github.com/callicoder/spring-security-react-ant-design-polls-app

博文的更详细内容请关注我的个人微信公众号 “云时代IT运维”,本公众号旨在共享互联网运维新技术,新趋势; 包括IT运维行业的咨询,运维技术文档分享。重点关注devops、jenkins、zabbix监控、kubernetes、ELK、各种中间件的使用,比如redis、MQ等;shell和python等运维编程语言;本人从事IT运维相关的工作有十多年。2008年开始专职从事Linux/Unix系统运维工作;对运维相关技术有一定程度的理解。本公众号所有博文均是我的实际工作经验总结,基本都是原创博文。我很乐意将我积累的经验、心得、技术与大家分享交流!希望和大家在IT运维职业道路上一起成长和进步;

原文地址:https://blog.51cto.com/zgui2000/2396800

时间: 2024-10-29 16:25:57

kubernetes之CI/CD工具jenkins第二篇,helm的使用的相关文章

7款不错的 CI/CD工具

时至今日,越来越多的工程团队开始实行敏捷开发,借以推动更短.更快的发布周期.而代码库的增长与更高的生产构建频率,也带动持续集成与持续部署/交付工具快速兴起.如果您有意提升发布频率,或者是不太清楚哪些工具能够帮助您达成这一目标--别担心.在今天的文章中,我们将探讨一系列最流行的CI / CD工具,并对其特性作出剖析. 什么是CI / CD?其意义何在? 在深入研究CI / CD自动化工具之前,我们首先需要了解其基本概念.正如我们所提到的,持续集成和持续部署通常与敏捷开发环境齐头并进.在这类环境中,

【Devops】【docker】【CI/CD】jenkins 清除工作空间报错Error: Wipe Out Workspace blocked by SCM

jenkins 清除工作空间报错 错误如下: Error: Wipe Out Workspace blocked by SCM 解决方法: 进入jenkins服务器,进入workspace,手动rm cd /var/jenkins_home/workspace ===================注释================== 如果你的启动命令如下:[进行了外部目录的挂载] docker run -itd -p 9980:8080 -p 50000:50000 --restart a

jenkins-gitlab-harbor-ceph基于Kubernetes的CI/CD运用(三)

从最基础镜像到业务容器 构建 [为gitlab项目部署做铺垫] 业务镜像设计规划 目录结构 # pwd /data/k8s/app/myapp # tree . . ├── dockerfile │?? ├── pub-images │?? │?? ├── jdk-1.8.162 │?? │?? │?? ├── build-command.sh │?? │?? │?? ├── Dockerfile │?? │?? │?? ├── jdk-8u162-linux-x64.tar.gz │?? │?

kubernetes之pod超详细解读--第二篇(三)

8.资源对象对pod的调度 ??在kubernetes集群中,pod基本上都是容器的载体,通常需要通过相应的资源对象来完成一组pod的调度和自动控制功能,比如:deployment.daemonSet.RC.Job等等.接下来小编将一一介绍这些资源对象如何调度pod. (1)Deployment/RC 自动化调度 ??Deployment/RC的主要功能之一就是自动部署一个容器应用的多个副本,以及持续监控副本数量,在集群内始终维持用户指定的副本数量.举例:(这里以deployment为例) ap

jenkins的容器化部署以及k8s应用的CI/CD实现

1. 使用Helm安装Mysql: ??上一篇博文谈到了如何使用Helm安装Redis和RabbitMQ,下来我们来聊聊如何用Helm安装mysql. ??本人对于Mysql数据库不是非常熟悉,因为我们公司的分工比较明确,数据库这块的工作主要由DBA负责,运维同学只负责应用的维护. ??按照我们前面博文的描述,首先是在官方文档查看helm安装mysql的书名: https://github.com/helm/charts/tree/master/stable/mysql ??我根据官方文档的描述

5步实现规模化的Kubernetes CI/CD 流水线

一.背景在近几年,Kubernetes迅速成为了容器编排的事实上的开源标准.与虚拟机不同,Kubernetes在抽象化基础架构的同时可靠地大规模编排容器,这可以帮助开发人员将工作负载与基础架构的复杂性质分开.Kubernetes是CI/CD自动化的理想选择,因为它提供了许多内置功能,这些功能使应用程序部署实现标准化和可重用,提高了开发人员的生产力,并加快了云原生应用程序的采用.Platform9是成立于2013年的云服务提供商,能够提供业界唯一由SaaS管理的混合云解决方案,使用户能够快速采用云

Kubernetes如何加速UCloud内部代码部署的CI/CD流程

UCloud内部长期使用 Gitlab 来管理代码.虽然Gitlab作为一套开源平台已很优秀,但我们对于其能为CI/CD提供的敏捷性并不十分满意,内部实践中的代码发布周期仍需按天计算.为此,我们打造了一个基于Kubernetes的内部容器服务平台(名为KUN),用于托管内部服务,并将Gitlab对接到KUN平台,从而借助Kubernetes的云原生优势,获得更好的CI/CD效果.这套系统运行一年内,Gitlab的Pipeline一共触发了994次,执行了约20000+次Job,在测试环境和正式环

基于jenkins的k8s ci/cd实例

K8S ci/cd三剑客:jenkinsfile.dockerfile.k8s.yaml k8s的ci/cd实例jenkins+jenkinsfile+dockerfile+k8s.yaml 1.dockerfile 实例 FROM harbor.k8s.site/library/jdk/jre:1.8-aplineENV TZ=Asia/ShanghaiVOLUME /tmpADD build/libs/*.jar /app/app.jarRUN mkdir /logs/ && echo

iHealth基于Docker的DevOps CI/CD实践

本文由1月31日晚iHealth运维技术负责人郭拓在Rancher官方技术交流群内所做分享的内容整理而成,分享了iHealth从最初的服务器端直接部署,到现在实现全自动CI/CD的实践经验. 作者简介 郭拓,北京爱和健康科技有限公司(iHealth).负责公司基础服务构建与研发流程定制,曾供职于乐视.21vianet,高龄攻城狮活跃在一线研发工作中,乐此不疲. 前言 相信我,一切事情的发生都是赶鸭子上架,没有例外.人类所有伟大的变革都是迫不得已,可又是那么顺其自然.比如容器(docker)技术的