常用发布方式有蓝绿发布、灰度发布、滚动发布,由于k8s中deployment的特性,默认情况下是滚动发布,其实只要更新deployment中的镜像标签,即是滚动发布,通过spec.strategy
中参数来具体实现,如下介绍所示:
strategy:
rollingUpdate:
maxSurge: 25% #最大峰值:是一个可选字段,用来指定可以创建的超出 期望 Pod 个数的 Pod 数量。此值可以是绝对数(例如,5)或所需 Pods 的百分比(例如,10%)。 如果 MaxUnavailable 为 0,则此值不能为 0。百分比值会通过向上取整转换为绝对数。 此字段的默认值为 25%。
maxUnavailable: 1 #最大不可用:是一个可选字段,用来指定 更新过程中不可用的 Pod 的个数上限。该值可以是绝对数字(例如,5),也可以是 所需 Pods 的百分比(例如,10%)。百分比值会转换成绝对数并去除小数部分。 如果 .spec.strategy.rollingUpdate.maxSurge 为 0,则此值不能为 0。 默认值为 25%。
type: RollingUpdate #更新创建策略,这里是滚动发布
// 所需插件: Git Parameter/Git/Pipeline/Config File Provider/kubernetes/Extended Choice Parameter
pipeline {
agent {
kubernetes {
label "jenkins-slave"
yaml """
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "47.8.9.9/library/jenkins-slave:jdk-1.8"
imagePullPolicy: Always
volumeMounts:
- name: docker-cmd
mountPath: /usr/bin/docker
- name: docker-sock
mountPath: /var/run/docker.sock
- name: maven-cache
mountPath: /root/.m2
volumes:
- name: docker-cmd
hostPath:
path: /usr/bin/docker
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: maven-cache
hostPath:
path: /tmp/m2
"""
}
}
environment {
//Harbor镜像仓库地址
registry = "47.8.8.9"
//Harbor项目名称,根据项目实际,可以直接引用,不用定义变量也行
project = "microservice"
harbor_registry_auth = "27e8ae31-f1c5-4asc-9811-XXXX"
image_pull_secret = "registry-pull-secret"
git_url = "http://47.8.8.9:580/root/ms.git"
git_auth = "7c0668b4-43ee-427f-b654-219e11XXXX"
k8s_auth = "9cabea02-4461-43b1-8cbe-798d0656XXXX"
}
parameters {
gitParameter branch: '', branchFilter: '.*', defaultValue: '', description: '选择发布的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
extendedChoice defaultValue: 'none', description: '选择发布的微服务', multiSelectDelimiter: ',', name: 'Service', type: 'PT_CHECKBOX', value: 'gateway, portal, product, order, stock'
choice (choices: ['ms', 'demo'], description: '部署模板', name: 'Template')
choice (choices: ['1', '2', '3', '5', '0'], description: '副本数', name: 'ReplicaCount')
//此处可以不用定义参数,直接引用ms
choice (choices: ['ms'], description: '命名空间', name: 'Namespace')
}
stages {
stage('拉取代码'){
steps {
checkout([$class: 'GitSCM', branches: [[name: "${params.Branch}"]], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
}
}
stage('代码编译') {
// 编译指定服务
steps {
sh "mvn clean package -Dmaven.test.skip=true"
}
}
stage('构建镜像') {
steps {
withCredentials([usernamePassword(credentialsId: "${harbor_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
docker login -u ${username} -p '${password}' ${registry}
#遍历extendedChoice中选定的service的Service
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service}-service
image_name=${registry}/${project}/\${service}:${BUILD_NUMBER}
cd \${service_name}
#if Dockerfile在biz目录下
if ls |grep biz &>/dev/null; then
cd \${service_name}-biz
fi
docker build -t \${image_name} .
docker push \${image_name}
cd ${WORKSPACE}
done
"""
configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){
sh """
# 添加镜像拉取认证
kubectl create secret docker-registry ${image_pull_secret} --docker-username=${username} --docker-password=${password} --docker-server=${registry} -n ${Namespace} --kubeconfig admin.kubeconfig |true
"""
}
}
}
}
stage('kubectl部署到K8S') {
steps {
sh """
common_args="--kubeconfig admin.kubeconfig" #yaml在子文件夹里面 cd k8s 后,不能识别admin.kubeconfig, 上一个stage进入ks后,在此步骤默认到workspace下面
#遍历extendedChoice中选定的service的Service
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_yaml=\${service}.yaml
sed -i "s//${BUILD_NUMBER}/" "\${service_yaml}"
sed -i "s//${ReplicaCount}/" "\${service_yaml}"
cat \${service_yaml}
kubectl apply -f \${service_yaml} \${common_args}
done
# 查看Pod状态
sleep 10
kubectl get pods \${common_args} -n ${Namespace}
"""
}
}
}
}
sed -i "s//${BUILD_NUMBER}/" "\${service_yaml}"
和sed -i "s//${ReplicaCount}/" "\${service_yaml}"
,同理service_yaml需要加上“\”,另外sed命令引用变量获取文件名,需要加上双引号;此处参考官方文档介绍
需要多标签的场景是用来区分同一组件的不同版本或者不同配置的多个部署。 常见的做法是部署一个使用金丝雀发布来部署新应用版本 (在 Pod 模板中通过镜像标签指定),保持新旧版本应用同时运行。 这样,新版本在完全发布之前也可以接收实时的生产流量,官方给的例子直接是pod,同理如果在deployment中,通过 spec.template.metadata.lables来区分”stable"和“canary”的pod。
例如,你可以使用 track 标签来区分不同的版本。
主要稳定的发行版将有一个 track 标签,其值为 stable:
name: frontend
replicas: 3
...
labels:
app: guestbook
tier: frontend
track: stable
...
image: gb-frontend:v3
然后,你可以创建 guestbook 前端的新版本,让这些版本的 track 标签带有不同的值 (即 canary),以便两组 Pod 不会重叠:
name: frontend-canary
replicas: 1
...
labels:
app: guestbook
tier: frontend
track: canary
...
image: gb-frontend:v4
前端service通过选择标签的公共子集(即忽略 track 标签)来覆盖两组副本, 以便流量可以转发到两个应用:
selector:
app: guestbook
tier: frontend
你可以调整 stable 和 canary 版本的副本数量,以确定每个版本将接收 实时生产流量的比例(在本例中为 3:1)。 一旦有信心,你就可以将新版本应用的 track 标签的值从 canary 替换为 stable,并且将老版本应用删除。
此种方法只需要额外部署金丝雀版本的deployment即可,不需要额外添加ingress和service
此种情况,ingress、service、deployment都需要部署一套新版本,比如之前是v1 版本,现在都需要部署一套v2 版本,然后基于ingress的canary-weight
注解来实现,另外可以通过七层请求头Request Header实现流量切分,此处可参考阿里官方文档介绍
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
nginx.ingress.kubernetes.io/canary-by-header: "ack"
nginx.ingress.kubernetes.io/canary-by-header-value: "alibaba"
由上介绍的两种灰度发布方式可知,如果没有需要基于七层请求头控制流量的需求,用第一种方法,基于service的方式实现灰度就可以了,这样更简单一些。
蓝绿发布需要存在新deployment和旧deployment相同的副本数(replicas),与上面金丝雀deployment相似,只是replicas都为3。
金丝雀发布是通过调整新旧deployment中的副本数来控制流量比例进行发布,而蓝绿发布直接通过service,来实现一次性从旧到新的迁移。