基于Kubernetes、Docker、Helm、Jenkins的CI/CD实践,对于前文Jenkins-pipeline-Docker 实现CI/CD 的补充扩展
Helm
Helm是Kubernetes生态系统中的一个包管理工具;Helm使用的包格式称为 charts,chart就是一个描述Kubernetes相关资源的文件集合。
Kubernetes应用部署面临的一些问题
kubernetes提供了基于容器的应用集群管理,容器编排,路由网关、水平扩展、监控、备份、灾难恢复等运维功能。用户通过使用Kubernetes API对象来描述应用程序规格,如Pod,Service,Volume,Namespace,ReplicaSet,Deployment,Job…;而对这些对象的操作都需要写入到yaml文件通过kubectl进行部署;这时就会遇到如下的几个问题:
- 如何对kubernetes应用配置文件进行管理
- 如何把一套的相关配置文件作为一个应用进行管理
- 如何分发和重用Kubernetes的应用配置
这时候就可以引进Helm之类的包管理工具解决如上问题
Helm提供了什么功能
对于开发者而言,可以通过Helm打包应用,管理应用依赖关系,管理应用版本并发布应用到软件仓库;Helm提供了Kubernetes上的软件部署,删除,升级,回滚应用的功能。
Helm相关组件
- Helm Client: Kubernetes应用打包工具。
- Tiller: Helm服务端,部署于Kubernetes集群中,用于接收处理Helm的相关命令(Helm3中将要被移除,改为从Kubernetes API server获取信息,在Helm Client处理并在Kubernetes中存储安装记录)。
- Chart: Helm的打包格式,描述Kubernetes相关资源的文件集合。
- Repoistory: Helm的软件仓库。
相关内容可以参考helm官方文档:https://helm.sh/zh/docs
Kubernetes Helm Jenkins CI/CD流程
准备
- 一个安装了Tiller的kubernetes集群
- 部署服务器,安装配置了Helm、Docker、Kubectl、Jenkins、Git
- 代码托管服务,如Gitlab、Github
- Docker Registry
基本流程
- 开发人员提交代码到Git仓库
- 运维人员触发Jenkins deploy任务,拉取代码进行Docker镜像打包,推送到远程仓库
- Jenkins使用 Helm 更新 Release到Kubernetes集群中
实践
项目
本次实践的项目是一个简单的Spring Boot 后端服务
仓库地址为:https://github.com/ChinaLHR/shovel-kubernetes-helm-devops
项目目录如下:
├── Jenkinsfile (Jenkins Pipeline Script)
├── Makefile (打包构建相关markfile)
├── README.md
├── chart (chart文件夹)
│ ├── Chart.yaml
│ ├── templates
│ │ ├── NOTES.txt
│ │ ├── _helpers.tpl
│ │ ├── configmap.yaml
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ ├── serviceaccount.yaml
│ │ └── tests
│ └── values.yaml
├── package.Dockerfile
├── pom.xml
├── release.Dockerfile
├── script (运行脚本)
│ └── bin
│ └── deploy.sh
└── src (项目源码)
├── main
│ ├── java
│ └── resources
└── test
└── java
Jenkins Pipeline
- Jenkins流水线项目配置
新增一个流水线项目,配置Pipeline from SCM,配置对应的参数BRANCH
- 代码拉取
这里使用Jenkins Checkout插件进行Git代码拉取
stage('Clone target repo') {
checkout([$class: 'GitSCM', branches: [[name: branch]],
doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: 'deploy', url: 'https://gitee.com/***.git']]])
}
- 代码编译打包-Docker镜像构建
这里使用Docker多阶段构建,第一阶段使用maven:3-jdk-8
基础镜像对项目进行打包,第二阶段将第一阶段生成的jar与项目的script部署脚本添加到镜像中进行镜像打包。
最后编写makefile,使用make命令进行构建打包镜像、项目打包操作、构建项目镜像、推送项目镜像到远程Docker仓库的操作。
具体可以参考项目对应package.Dockerfile
、release.Dockerfile
、Makefile
、deploy.sh
文件;相关pipeline script如下:
stage('Build package') {
sh "make build-package-image package-image=${packageImage}"
sh "make package package-image=${packageImage}"
}
stage('Build release') {
sh "make build-release-image release-image=${releaseImage}"
}
stage('Push release image to registry') {
withDockerRegistry(credentialsId: 'docker-user', url: 'https://registry.cn-hangzhou.aliyuncs.com') {
sh "docker push ${releaseImage}"
}
}
- Helm Chart更新 Release到Kubernetes集群中
初始化Helm Chart:
helm create shovel
mv shovel chart
# 删除非必要文件
cd chart-copy/templates && rm hpa.yaml && rm ingress.yaml
修改Chart配置文件:
# values.yaml文件
# 修改镜像image相关配置,去除ingress相关配置,增加自定义配置如下:
env:
app:
port: 8088
name: shovel-server-app
# templates/deployment.yaml
# 修改containers下的configMap、ports、livenessProbe标签配置
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
envFrom:
- configMapRef:
name: {{ include "shovel.fullname" . }}
ports:
- name: http
containerPort: 8088
protocol: TCP
livenessProbe:
httpGet:
path: /ping
port: http
readinessProbe:
httpGet:
path: /ping
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
# 添加templates/configmap.yaml设置应用的环境变量
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "shovel.fullname" . }}
labels:
{{- include "shovel.labels" . | nindent 4 }}
data:
SERVER_PORT: "{{ .Values.env.app.port}}"
APPLICATION_NAME: {{ .Values.env.app.name }}
- 使用Helm发布/更新服务
切换当前context到dev(这里的env可以通过jenkins parameters 进行传递),使用Helm发布/更新服务
stage('Deploy') {
sh """
kubectl config use-context dev
helm -n ${namespace} upgrade ${project} chart \
-f chart/values.yaml \
--set-string image.tag=${imageTag} \
--wait --install
"""
}