背景:
首先,关于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 流程是自动化的,因此可以实现部署的可重复性,使得在不同的环境中重复部署和测试更加容易和可靠。
本文暂时没有评论,来添加一个吧(●'◡'●)