计算机系统应用教程网站

网站首页 > 技术文章 正文

基于Helm的CICD应用交付

btikc 2024-09-03 11:42:16 技术文章 14 ℃ 0 评论

背景

首先,关于Helm,它是一个Kubernetes的包管理器,可以通过Helm来安装、更新和升级包。而且它还提供了许多小巧实用的命令,确保了应用顺利地部署和升级。

如果您是一名研发人员,一定会遇到这样一个问题:如何将自己的开发成果快速、安全地交付给客户?这时候,CICD就能派上用场了!利用CICD进行自动化的部署和测试,能够快速地将应用交付给客户,且可以降低错误率和提高产品质量。

而基于Helm的CICD应用交付,能够让我们更加方便地进行应用部署和集成测试。通过Helm命令,可以轻松地将应用打包成chart,由CI/CD平台负责自动部署和测试,确保应用的稳定性和正确性。

同时,利用Helm,我们可以快速地进行应用的升级、回滚等操作。这对于快速迭代的产品来说,是非常重要的;而且我们还可以通过Helm release管理来实现应用版本控制,保证应用的稳定和安全。

技术栈:Jenkins + Gitlab + Harbor + Helm + Kubernetes

主要流程

- 1. 开发人员提交代码到 Gitlab 代码仓库;

- 2. 通过 Gitlab 配置的 Jenkins Webhook 触发 Pipeline 自动构建;

- 3. Jenkins 触发构建构建任务,根据 Pipeline 脚本定义分步骤构建;

- 4. 先进行代码静态分析,单元测试;

- 5. 然后进行 Maven 构建(Java 项目);

- 6. 根据构建结果构建 Docker 镜像;

- 7. 推送 Docker 镜像到 Harbor 仓库;

- 8. 触发更新服务阶段,使用 Helm 安装/更新 Release;

- 9. 查看服务是否更新成功。

一、Helm的私仓配置

1.1 配置 harbor 支持 helm chart 存储

如下图登录harbor 仓库 ,选取项目这一栏没有helm chart 这个关键字就说明harbor仓库支持暂不支持存放chart。

开启Charts功能:

docker-compose down -v #停掉已安装的harbor
./install.sh --with-chartmuseum #启动时带上参数 --with-chartmuseum

等命令执行完再登录就能看到 上图中的helm chart 字样了。

1.2 配置国内Chart仓库

# helm repo add stable http://mirror.azure.cn/kubernetes/charts
# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# helm repo list

1.3 安装push插件

GitHub - chartmuseum/helm-push: Helm plugin to push chart package to ChartMuseum使用如下的方式下载 helm-push 安装包。

方式1:

# helm plugin install https://github.com/chartmuseum/helm-push

如果方式1安装不了,也可以通过如下方式:

安装位置:helm env 获取的 HELM_PLUGINS 这个变量的值当前用户主目录的.local 目录下

#创建 helm-push 目录
mkdir -p /root/.local/share/helm/plugins/helm-push
#把你下载好的tar 包拷到创建的目录下
cp helm-push_0.10.0_linux_amd64.tar.gz /root/.local/share/helm/plugins/helm-push
# 到 目录下解压文件
tar -zxvf helm-push_0.10.0_linux_amd64.tar.gz

#执行 helm plugin list 就能看到已经安装好的 push 插件了 这里一定要记住名字时cm-push 而不是push
[root@kanq helm-push]# helm plugin list
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /root/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /root/.kube/config
NAME   VERSION DESCRIPTION
cm-push 0.10.0 Push chart package to ChartMuseum

1.4 添加repo

# helm repo add --username admin --password xxxxxx devopsrepo http://harbor-local.kubernets.cn/chartrepo/demo

1.5 推送与安装Chart

# helm cm-push demohelm-0.2.2.tgz --username=admin --password=xxxxxx http://harbor-local.kubernets.cn/chartrepo/demo

二、Jenkins基于Helm的应用发布

相对于基于控制器文件部署的方式有哪些优点:

  • 利于形成DEVOPS标准化;
  • 控制器方式需要维护大量的yaml文件;
  • 相对于helm方式,控制器方式低效且不够灵活;

当前环境环境基于控制器文件部署:

//定义git相关数据
def git_address = "http://gitlab.kubernets.cn/demoteam/java_kubernetes.git"
def git_auth = "59d88ee6-8c38-4116-948d-66d812799c63"

//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "harbor-local.kubernets.cn"
//Harbor的项目名称
def harbor_project_name = "demo"
//Harbor的凭证
def harbor_auth = "6d2ada67-9f69-4f79-85e8-c76a0b9e0564"
//启动时间
def start = new Date().format('yyyy-MM-dd HH:mm:ss')


//创建一个Pod的模板,label为jenkins-slave
podTemplate(label: 'jenkins-slave-java', cloud: 'kubernetes', containers: [
   containerTemplate(
       name: 'jnlp',
       image: "harbor-local.kubernets.cn/library/jenkins-slave-maven",
       ttyEnabled: true
      ),
   containerTemplate(
       name: 'docker',
       image: "harbor-local.kubernets.cn/library/docker:stable",
       ttyEnabled: true,
       command: 'cat'
      ),
  ],
   volumes: [
       hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
  ],
)
{
node("jenkins-slave-java"){
   // 第一步
   stage('Pull'){
       checkout([$class: 'GitSCM', branches: [[name: "${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
  }

   stage('BuildDescription') {
       // 自定义设置构建历史显示的名称和描述信息
       // 不同的部署方式设置构建历史显示的名称和描述信息方式不一样,根据自己的部署方式自行百度找到设置方法
       script {
       //设置buildName
         wrap([$class: 'BuildUser']) {
          //修改Description
           buildDescription "${BUILD_USER} > ${project_name} > ${branch}"
          }
        }
    }

   // 第二步
   stage('Build&Tag&Push&Deploy'){
       //把选择的项目信息转为数组
       def selectedProjects = "${project_name}".split(',')

       for(int i=0;i<selectedProjects.size();i++){
           //取出每个项目的名称
           def currentProjectName = selectedProjects[i];

           //定义镜像名称
           def imageName = "${currentProjectName}:${tag}"

//定义newTag
def newTag = sh(returnStdout: true,script: 'echo `date +"%Y%m%d%H%M"_``git describe --tags --always`').trim()

           //编译,构建本地镜像
           //sh "sed -i 's#ACTIVEPROFILE#${springProfilesActive}#g' Dockerfile"
           sh "mvn clean package -Dmaven.test.skip=true"
           container('docker') {

               //镜像编译
               sh "docker build -t ${imageName} ."

               //给镜像打标签
               sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"

               //登录Harbor,并上传镜像
               withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')])
              {
                   //登录
                   sh "docker login -u ${username} -p ${password} ${harbor_url}"
                   //上传镜像
                   sh "docker push ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
              }

           //删除本地镜像
           sh "docker rmi -f ${imageName}"
           sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
          }
           def deploy_image_name = "${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
           
           //基于控制器的方式部署到K8S
           sh """
               sed -i 's#\$IMAGE_NAME#${deploy_image_name}#' deployment.yaml
               sed -i 's#\$APP_NAME#${currentProjectName}#' deployment.yaml
               sed -i 's#\$APP_REPLICAS#${replicas}#' deployment.yaml
               sed -i 's#\$NAMESPACE#${namespaces}#' deployment.yaml
               sed -i 's#\$SPRINGENV#${springProfilesActive}#' deployment.yaml
               sed -i 's#\$PODMEMORY#${podsMem}#' deployment.yaml
               sed -i 's#\$PODCPU#${podsCpu}#' deployment.yaml
               cat deployment.yaml
           """
           //部署到K8S
           kubernetesDeploy(kubeconfigId: "7ab1d5e8-0f10-4ca7-918a-f505b727c23d", configs: "deployment.yaml")
      }
  }
}
}

基于helm的部署方式

//定义git相关数据
def git_address = "http://gitlab.kubernets.cn/demoteam/java_kubernetes.git"
def git_auth = "59d88ee6-8c38-4116-948d-66d812799c63"

//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "harbor-local.kubernets.cn"
//Harbor的项目名称
def harbor_project_name = "demo"
//Harbor的凭证
def harbor_auth = "6d2ada67-9f69-4f79-85e8-c76a0b9e0564"
//启动时间
def start = new Date().format('yyyy-MM-dd HH:mm:ss')


//创建一个Pod的模板,label为jenkins-slave
podTemplate(label: 'jenkins-slave-java', cloud: 'kubernetes', containers: [
   containerTemplate(
       name: 'jnlp',
       image: "harbor-local.kubernets.cn/library/jenkins-slave-maven",
       ttyEnabled: true
      ),
   containerTemplate(
       name: 'docker',
       image: "harbor-local.kubernets.cn/library/docker:stable",
       ttyEnabled: true,
       command: 'cat'
      ),
   containerTemplate(
       name: 'helm3',
       image: "harbor-local.kubernets.cn/library/k8s-helm:v3.7.2",
       ttyEnabled: true,
       command: 'cat'
      ),
  ],
   volumes: [
       hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
  ],
)
{
node("jenkins-slave-java"){
   // 第一步
   stage('Pull'){
       checkout([$class: 'GitSCM', branches: [[name: "${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
  }

   stage('BuildDescription') {
       // 自定义设置构建历史显示的名称和描述信息
       // 不同的部署方式设置构建历史显示的名称和描述信息方式不一样,根据自己的部署方式自行百度找到设置方法
       script {
       //设置buildName
         wrap([$class: 'BuildUser']) {
          //修改Description
           buildDescription "${BUILD_USER} > ${project_name} > ${branch}"
          }
        }
    }

   // 第二步
   stage('Build&Tag&Push&Deploy'){
       //把选择的项目信息转为数组
       def selectedProjects = "${project_name}".split(',')

       for(int i=0;i<selectedProjects.size();i++){
           //取出每个项目的名称
           def currentProjectName = selectedProjects[i];

           //定义镜像名称
           def imageName = "${currentProjectName}:${tag}"

//定义newTag
def newTag = sh(returnStdout: true,script: 'echo `date +"%Y%m%d%H%M"_``git describe --tags --always`').trim()

           //编译,构建本地镜像
           //sh "sed -i 's#ACTIVEPROFILE#${springProfilesActive}#g' Dockerfile"
           sh "mvn clean package -Dmaven.test.skip=true"
           container('docker') {

               //镜像编译
               sh "docker build -t ${imageName} ."

               //给镜像打标签
               sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"

               //登录Harbor,并上传镜像
               withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')])
              {
                   //登录
                   sh "docker login -u ${username} -p ${password} ${harbor_url}"
                   //上传镜像
                   sh "docker push ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
              }

           //删除本地镜像
           sh "docker rmi -f ${imageName}"
           sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
          }
           def deploy_image_name = "${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
           
           //基于Helm的方式部署到K8S
           container('helm3') {
               withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
               sh """
                   helm repo add --username=${username} --password=${password} devopsrepo http://harbor-local.kubernets.cn/chartrepo/demo
               """
              }
               withCredentials([file(credentialsId: '7ab1d5e8-0f10-4ca7-918a-f505b727c23d', variable: 'KUBECONFIG')]) {
               sh """
                   mkdir -p /root/.kube/ && echo $KUBECONFIG >/root/.kube/config
                   echo "Helm应用配置信息确认..."
                   helm upgrade --install --dry-run --debug ${currentProjectName} --namespace ${namespaces} devopsrepo/demohelm \
                       --set replicaCount=${replicas} \
                       --set image.repository=${deploy_image_name} \
                       --set service.type=ClusterIP \
                       --set springActive=${springProfilesActive} \
                       --set resources.limits.memory=${limiteMem} \
                       --set ingress.enabled=${isIngress}
                   echo "应用部署..."
                   helm upgrade --install ${currentProjectName} --namespace ${namespaces} devopsrepo/demohelm \
                       --set replicaCount=${replicas} \
                       --set image.repository=${deploy_image_name} \
                       --set service.type=ClusterIP \
                       --set springActive=${springProfilesActive} \
                       --set resources.limits.memory=${limiteMem} \
                       --set ingress.enabled=${isIngress}
                   """
              }
          }
      }
  }
}
}

k8s-helm容器Dockerfile文件:

[root@master1 /tmp/charts]# cat Dockerfile
FROM alpine

# Metadata
LABEL org.opencontainers.image.title="lachlanevenson/k8s-helm" \
    org.opencontainers.image.url="https://helm.sh/docs/"

ENV HELM_LATEST_VERSION="v3.7.2"

ARG TARGETARCH
ENV TARGETARCH=${TARGETARCH:-amd64}

RUN apk add --update ca-certificates \
&& apk add --update -t deps wget git openssl bash \
&& wget -q https://get.helm.sh/helm-${HELM_LATEST_VERSION}-linux-${TARGETARCH}.tar.gz \
&& tar -xf helm-${HELM_LATEST_VERSION}-linux-${TARGETARCH}.tar.gz \
&& mv linux-${TARGETARCH}/helm /usr/local/bin \
&& apk del --purge deps \
&& rm /var/cache/apk/* \
&& rm -f /helm-${HELM_LATEST_VERSION}-linux-${TARGETARCH}.tar.gz

ENTRYPOINT ["helm"]
CMD ["help"]

三、总结:

  • 提高效率:通过使用自动化构建和部署流程,减少了手动操作的时间和出错率,从而提高了开发团队的工作效率。
  • 标准化:使用 Helm 和 Jenkins 构建的 CICD 流程可以定义标准化的流程和规范,使得整个开发流程更加清晰、规范和可控。
  • 统一平台:使用 Helm 和 Jenkins 提供了一个统一的开发平台,在不同的开发环境中实现了相同的构建和部署流程,从而提高了软件交付的质量和效率。
  • 可重复性:由于 CICD 流程是自动化的,因此可以实现部署的可重复性,使得在不同的环境中重复部署和测试更加容易和可靠。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表