一、概述与技术栈选型
1.1 什么是 CI/CD
- CI (持续集成):开发人员频繁将代码合并到主干,自动进行构建、测试和代码质量检查
- CD (持续交付 / 部署):将经过验证的代码自动部署到测试、预生产和生产环境
1.2 企业级 CI/CD 流水线架构
我们采用 "CI+CD 分离"的现代 GitOps 架构:
- CI 层:Jenkins 负责代码构建、测试、镜像打包和推送
- CD 层:ArgoCD 负责基于 Git 仓库的声明式部署和状态同步
- 制品层:Harbor 作为企业级镜像仓库,Nexus 作为 Maven/NPM 仓库
- 质量层:SonarQube 进行代码质量检测和安全扫描
1.3 技术栈对比与选型
| 工具 | 市场份额 (2025) | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| Jenkins | 48% | 插件生态丰富 (2000+)、高度定制化、性能优异 | 配置复杂、维护成本高 | 大型企业、复杂流水线需求 |
| GitLab CI | 28% | 与 GitLab 深度集成、开箱即用、YAML 配置简单 | 性能较差、定制化有限 | 中小型团队、GitLab 生态用户 |
| GitHub Actions | 42% | 开发者体验好、与 GitHub 集成紧密 | 私有部署成本高 | 开源项目、GitHub 生态用户 |
| ArgoCD | 75%+ | GitOps 标准、声明式部署、自动自愈、多集群支持 | 仅负责 CD 部分 | 所有 K8s 环境的应用部署 |
本教程选择:GitLab + Jenkins + Harbor + ArgoCD + SonarQube + Nexus,这是目前企业级最成熟、最灵活的组合。
二、环境准备
2.1 基础环境要求
- Kubernetes 集群:v1.24+,3 个以上节点,每个节点至少 4C8G
- Helm:v3.8+
- kubectl:与集群版本匹配
- 存储类:支持动态 PV 供应 (如 Longhorn、Ceph 或云存储)
- 域名:至少 3 个域名 (jenkins.example.com、harbor.example.com、argocd.example.com)
- 证书:SSL 证书 (可使用 Let's Encrypt)
2.2 命名空间规划
bash
# 创建统一的devops命名空间
kubectl create namespace devops
# 创建应用环境命名空间
kubectl create namespace dev
kubectl create namespace staging
kubectl create namespace production
三、核心组件部署
3.1 部署 Harbor 企业级镜像仓库
Harbor 是 VMware 开源的企业级 Docker 镜像仓库,支持镜像签名、漏洞扫描、权限管控等功能。
javascript
# 添加Harbor Helm仓库
helm repo add harbor https://helm.goharbor.io
helm repo update
# 创建Harbor values.yaml文件
cat > harbor-values.yaml << EOF
expose:
type: ingress
tls:
enabled: true
secretName: harbor-tls
ingress:
hosts:
core: harbor.example.com
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
persistence:
persistentVolumeClaim:
registry:
storageClass: longhorn
size: 100Gi
database:
storageClass: longhorn
size: 10Gi
redis:
storageClass: longhorn
size: 5Gi
# 启用Trivy漏洞扫描
trivy:
enabled: true
skipUpdate: false
# 启用OIDC认证(可选,与GitLab集成)
oidc:
enabled: false
# 管理员密码
harborAdminPassword: "Harbor12345"
EOF
# 安装Harbor
helm install harbor harbor/harbor -f harbor-values.yaml -n devops
验证部署:
bash
kubectl get pods -n devops | grep harbor
# 访问 https://harbor.example.com,使用admin/Harbor12345登录
3.2 部署 Jenkins (支持 K8s 动态代理)
Jenkins 是最流行的 CI 工具,我们将部署支持 K8s 动态代理的 Jenkins 集群,实现构建资源的按需分配。
javascript
# 添加Jenkins Helm仓库
helm repo add jenkins https://charts.jenkins.io
helm repo update
# 创建Jenkins values.yaml文件
cat > jenkins-values.yaml << EOF
controller:
image:
repository: jenkins/jenkins
tag: "2.440.3-lts"
ingress:
enabled: true
hostName: jenkins.example.com
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
tls:
- secretName: jenkins-tls
hosts:
- jenkins.example.com
# 资源配置
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
# 持久化存储
persistence:
enabled: true
storageClass: longhorn
size: 20Gi
# 安装必要插件
installPlugins:
- kubernetes:4229.v0a_55a_3d269b_
- git:5.2.1
- pipeline:596.v8c21c963d92d
- workflow-aggregator:596.v8c21c963d92d
- blueocean:1.27.11
- sonar:2.15
- docker-plugin:1.5
- docker-workflow:563.vd5d2e5c4007f
- credentials-binding:636.v55f1275c755d
- configuration-as-code:1775.v810dc2758832
- role-strategy:685.v2e40f0a_46b_31
# Jenkins配置即代码(JCasC)
JCasC:
defaultConfig: true
configScripts:
kubernetes-cloud: |
jenkins:
clouds:
- kubernetes:
name: "kubernetes"
serverUrl: "https://kubernetes.default"
namespace: "devops"
jenkinsUrl: "http://jenkins.devops.svc.cluster.local:8080"
jenkinsTunnel: "jenkins-agent.devops.svc.cluster.local:50000"
maxRequestsPerHost: 32
podRetention: "Never"
templates:
- name: "maven-agent"
label: "maven"
containers:
- name: "jnlp"
image: "jenkins/inbound-agent:3206.vb_15dcf73f6a_9-3"
args: "^${computer.jnlpmac} ^${computer.name}"
resourceRequestCpu: "500m"
resourceRequestMemory: "512Mi"
resourceLimitCpu: "1"
resourceLimitMemory: "1Gi"
- name: "maven"
image: "maven:3.8.6-eclipse-temurin-17"
command: "sleep"
args: "99d"
resourceRequestCpu: "2"
resourceRequestMemory: "4Gi"
resourceLimitCpu: "4"
resourceLimitMemory: "8Gi"
volumeMounts:
- mountPath: "/root/.m2"
name: "maven-cache"
- name: "docker-agent"
label: "docker"
containers:
- name: "jnlp"
image: "jenkins/inbound-agent:3206.vb_15dcf73f6a_9-3"
args: "^${computer.jnlpmac} ^${computer.name}"
resourceRequestCpu: "500m"
resourceRequestMemory: "512Mi"
resourceLimitCpu: "1"
resourceLimitMemory: "1Gi"
- name: "docker"
image: "docker:24.0.9-dind"
privileged: true
command: "dockerd"
args: "--host=tcp://0.0.0.0:2375 --tls=false"
resourceRequestCpu: "2"
resourceRequestMemory: "4Gi"
resourceLimitCpu: "4"
resourceLimitMemory: "8Gi"
volumes:
- hostPathVolume:
hostPath: "/var/run/docker.sock"
mountPath: "/var/run/docker.sock"
volumes:
- persistentVolumeClaim:
claimName: "jenkins-maven-cache"
mountPath: "/root/.m2"
name: "maven-cache"
# 创建Maven缓存PVC
persistentVolumeClaims:
- name: jenkins-maven-cache
storageClassName: longhorn
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
agent:
enabled: true
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
EOF
# 安装Jenkins
helm install jenkins jenkins/jenkins -f jenkins-values.yaml -n devops
获取 Jenkins 管理员密码:
bash
kubectl exec -n devops -it svc/jenkins -c jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
验证部署:
bash
kubectl get pods -n devops | grep jenkins
# 访问 https://jenkins.example.com,使用admin和上面获取的密码登录
3.3 部署 ArgoCD (GitOps 持续交付)
ArgoCD 是 GitOps 的事实标准,它监听 Git 仓库中的应用定义,并自动将集群状态同步到期望状态。
javascript
# 创建ArgoCD命名空间
kubectl create namespace argocd
# 安装ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.10.7/manifests/install.yaml
# 创建ArgoCD Ingress
cat > argocd-ingress.yaml << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- argocd.example.com
secretName: argocd-tls
rules:
- host: argocd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
name: https
EOF
kubectl apply -f argocd-ingress.yaml
获取 ArgoCD 管理员密码:
bash
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
验证部署:
bash
kubectl get pods -n argocd
# 访问 https://argocd.example.com,使用admin和上面获取的密码登录
3.4 部署 SonarQube 代码质量检测
javascript
# 添加SonarQube Helm仓库
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update
# 创建SonarQube values.yaml文件
cat > sonarqube-values.yaml << EOF
ingress:
enabled: true
hosts:
- name: sonar.example.com
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
tls:
- secretName: sonar-tls
hosts:
- sonar.example.com
postgresql:
enabled: true
persistence:
storageClass: longhorn
size: 10Gi
persistence:
storageClass: longhorn
size: 20Gi
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
EOF
# 安装SonarQube
helm install sonarqube sonarqube/sonarqube -f sonarqube-values.yaml -n devops
验证部署:
bash
kubectl get pods -n devops | grep sonarqube
# 访问 https://sonar.example.com,使用admin/admin登录
四、组件集成配置
4.1 Jenkins 与 Harbor 集成
-
在 Harbor 中创建项目:
- 登录 Harbor 控制台
- 创建项目:
myproject(设置为公开或私有) - 创建机器人账户:
jenkins-robot,赋予推送权限 - 保存机器人账户的用户名和密码
-
在 Jenkins 中添加 Harbor 凭证:
- 登录 Jenkins 控制台
- 进入 "Manage Jenkins" → "Credentials" → "System" → "Global credentials"
- 添加 "Username with password" 类型凭证:
- ID:
harbor-credentials - Username: 机器人账户用户名
- Password: 机器人账户密码
- ID:
4.2 Jenkins 与 SonarQube 集成
-
在 SonarQube 中生成令牌:
- 登录 SonarQube 控制台
- 进入 "My Account" → "Security" → "Generate Tokens"
- 生成令牌:
jenkins-token,保存令牌值
-
在 Jenkins 中添加 SonarQube 凭证:
- 添加 "Secret text" 类型凭证:
- ID:
sonar-token - Secret: 上面生成的令牌值
- ID:
- 添加 "Secret text" 类型凭证:
-
配置 SonarQube 服务器:
- 进入 "Manage Jenkins" → "System" → "SonarQube servers"
- 添加 SonarQube 服务器:
- Name:
SonarQube - Server URL:
https://sonar.example.com - Server authentication token: 选择上面添加的
sonar-token凭证
- Name:
4.3 Jenkins 与 GitLab 集成
-
在 GitLab 中生成个人访问令牌:
- 登录 GitLab 控制台
- 进入 "User Settings" → "Access Tokens"
- 生成令牌,勾选
api、read_repository、write_repository权限 - 保存令牌值
-
在 Jenkins 中添加 GitLab 凭证:
- 添加 "Username with password" 类型凭证:
- ID:
gitlab-credentials - Username: GitLab 用户名
- Password: 上面生成的令牌值
- ID:
- 添加 "Username with password" 类型凭证:
-
配置 GitLab Webhook:
- 在 GitLab 项目中进入 "Settings" → "Webhooks"
- 添加 Webhook URL:
https://jenkins.example.com/project/my-pipeline - 勾选 "Push events" 和 "Merge request events"
- 点击 "Add webhook"
五、完整 CI/CD 流水线实现
5.1 项目结构设计
采用 "应用代码与部署配置分离" 的最佳实践:
bash
# 应用代码仓库
myapp/
├── src/ # 应用源代码
├── pom.xml # Maven配置文件
├── Dockerfile # Docker镜像构建文件
└── Jenkinsfile # CI流水线定义
# 部署配置仓库(GitOps)
myapp-manifests/
├── base/ # 基础配置(所有环境共享)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
│ └── kustomization.yaml
└── overlays/ # 环境覆盖配置
├── dev/
│ ├── kustomization.yaml
│ └── configmap.yaml
├── staging/
│ ├── kustomization.yaml
│ └── configmap.yaml
└── production/
├── kustomization.yaml
└── configmap.yaml
5.2 CI 流水线 (Jenkinsfile)
这是一个完整的 Java 应用 CI 流水线,包含代码拉取、编译、单元测试、代码质量检测、镜像构建和推送、更新 GitOps 仓库等阶段。
javascript
pipeline {
agent {
kubernetes {
label 'maven && docker'
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.8.6-eclipse-temurin-17
command: ['sleep']
args: ['99d']
volumeMounts:
- mountPath: /root/.m2
name: maven-cache
- name: docker
image: docker:24.0.9-dind
command: ['dockerd']
args: ['--host=tcp://0.0.0.0:2375', '--tls=false']
privileged: true
volumeMounts:
- mountPath: /var/run/docker.sock
name: docker-sock
volumes:
- name: maven-cache
persistentVolumeClaim:
claimName: jenkins-maven-cache
- name: docker-sock
hostPath:
path: /var/run/docker.sock
"""
}
}
environment {
// 项目配置
PROJECT_NAME = 'myapp'
HARBOR_REGISTRY = 'harbor.example.com'
HARBOR_PROJECT = 'myproject'
GITOPS_REPO = 'git@gitlab.example.com:myteam/myapp-manifests.git'
// 版本号(使用Git提交哈希)
IMAGE_TAG = "${env.GIT_COMMIT.substring(0, 8)}"
FULL_IMAGE_NAME = "${HARBOR_REGISTRY}/${HARBOR_PROJECT}/${PROJECT_NAME}:${IMAGE_TAG}"
// 凭证
HARBOR_CREDENTIALS = credentials('harbor-credentials')
GITLAB_CREDENTIALS = credentials('gitlab-credentials')
}
stages {
stage('代码拉取') {
steps {
checkout scm
script {
// 获取当前分支
env.BRANCH_NAME = env.GIT_BRANCH.replace('origin/', '')
echo "当前分支: ${env.BRANCH_NAME}"
}
}
}
stage('编译与单元测试') {
steps {
container('maven') {
sh 'mvn clean package -DskipTests=false'
}
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
stage('代码质量检测') {
steps {
container('maven') {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
}
stage('质量门禁检查') {
steps {
timeout(time: 1, unit: 'HOURS') {
waitForQualityGate abortPipeline: true
}
}
}
stage('构建Docker镜像') {
steps {
container('docker') {
sh """
docker build -t ${FULL_IMAGE_NAME} .
"""
}
}
}
stage('推送镜像到Harbor') {
steps {
container('docker') {
sh """
docker login ${HARBOR_REGISTRY} -u ${HARBOR_CREDENTIALS_USR} -p ${HARBOR_CREDENTIALS_PSW}
docker push ${FULL_IMAGE_NAME}
docker logout ${HARBOR_REGISTRY}
"""
}
}
}
stage('更新GitOps仓库') {
steps {
container('maven') {
// 安装Git
sh 'apt-get update && apt-get install -y git'
// 配置Git
sh """
git config --global user.name "Jenkins CI"
git config --global user.email "jenkins@example.com"
"""
// 克隆GitOps仓库
sh "git clone ${GITOPS_REPO}"
// 根据分支更新对应环境
script {
def envDir = ''
if (env.BRANCH_NAME == 'develop') {
envDir = 'dev'
} else if (env.BRANCH_NAME == 'staging') {
envDir = 'staging'
} else if (env.BRANCH_NAME == 'main') {
envDir = 'production'
}
if (envDir) {
// 更新镜像标签
sh """
cd myapp-manifests/overlays/${envDir}
sed -i "s|image:.*|image: ${FULL_IMAGE_NAME}|g" kustomization.yaml
git add kustomization.yaml
git commit -m "Update ${envDir} image to ${IMAGE_TAG}"
git push origin main
"""
}
}
}
}
}
}
post {
success {
echo "流水线执行成功!镜像版本: ${IMAGE_TAG}"
}
failure {
echo "流水线执行失败!"
}
}
}
5.3 CD 流水线 (ArgoCD 应用配置)
创建 ArgoCD 应用配置文件,实现多环境自动部署:
javascript
# argocd-applications.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-dev
namespace: argocd
spec:
project: default
source:
repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
targetRevision: main
path: overlays/dev
destination:
server: https://kubernetes.default.svc
namespace: dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-staging
namespace: argocd
spec:
project: default
source:
repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
targetRevision: main
path: overlays/staging
destination:
server: https://kubernetes.default.svc
namespace: staging
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-production
namespace: argocd
spec:
project: default
source:
repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
# 生产环境禁用自动同步,需要手动审批
syncOptions:
- CreateNamespace=true
应用配置:
bash
kubectl apply -f argocd-applications.yaml
5.4 Kustomize 配置示例
base/kustomization.yaml:
javascript
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- hpa.yaml
base/deployment.yaml:
javascript
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: harbor.example.com/myproject/myapp:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
overlays/dev/kustomization.yaml:
javascript
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
images:
- name: harbor.example.com/myproject/myapp:latest
newTag: "abc1234" # 这个标签会被Jenkins自动更新
patches:
- path: configmap.yaml
六、高级部署策略详解
在企业级应用发布过程中,直接替换所有 Pod 的滚动更新 (RollingUpdate) 虽然简单,但存在以下问题:
- 无法在发布过程中进行流量控制
- 新版本问题会直接影响所有用户
- 回滚速度慢,需要重新拉取旧版本镜像
- 无法进行 A/B 测试和灰度验证
Argo Rollouts 是 Argo 项目的子项目,专门为 Kubernetes 提供高级部署能力,支持蓝绿部署、金丝雀发布、渐进式交付等策略,与 ArgoCD 完美集成,是 GitOps 模式下的最佳部署解决方案。
6.1 Argo Rollouts 基础
6.1.1 什么是 Argo Rollouts
Argo Rollouts 是一个 Kubernetes 控制器,扩展了原生 Deployment 的功能,提供了更强大的部署策略。它通过自定义资源定义 (CRD) 来管理应用的发布过程,能够:
- 逐步将流量切换到新版本
- 自动分析应用指标判断发布是否成功
- 在检测到问题时自动回滚
- 支持手动审批和暂停
- 与服务网格 (Istio、Linkerd) 和入口控制器 (Nginx、ALB) 集成
6.1.2 安装与配置
javascript
# 安装Argo Rollouts控制器
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/v1.6.6/install.yaml
# 安装kubectl argo rollouts插件(推荐)
curl -LO https://github.com/argoproj/argo-rollouts/releases/download/v1.6.6/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
# 验证安装
kubectl argo rollouts version
6.1.3 核心概念
- Rollout:替代原生 Deployment 的自定义资源,定义应用的部署策略
- Revision:每次发布生成的版本,对应一个 ReplicaSet
- Canary:金丝雀版本,接收部分流量进行验证
- Stable:稳定版本,接收大部分流量
- AnalysisRun:用于自动分析发布质量的资源
- Experiment:用于同时运行多个版本进行对比测试的资源
6.2 蓝绿部署 (Blue/Green Deployment) 详解
6.2.1 蓝绿部署原理
蓝绿部署是一种零停机部署策略,它同时维护两个完全相同的环境:
- 蓝色环境:当前运行的稳定版本,接收所有生产流量
- 绿色环境:即将发布的新版本,不接收任何流量
部署流程:
- 部署新版本到绿色环境
- 在绿色环境进行全面测试
- 测试通过后,将流量从蓝色环境切换到绿色环境
- 观察绿色环境运行情况
- 确认无误后,销毁蓝色环境
- 下一次发布时,角色互换
优势:
- 零停机时间
- 发布过程风险低,可随时回滚
- 支持完整的发布前测试
- 避免版本共存问题
劣势:
- 需要双倍的资源
- 数据库变更处理复杂
- 不适合大型单体应用
适用场景:
- 资源充足的中小型应用
- 对可用性要求极高的服务
- 数据库变更较少的应用
- 需要完整测试新版本的场景
6.2.2 完整蓝绿部署配置示例
javascript
# rollout-bluegreen.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp-bluegreen
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: harbor.example.com/myproject/myapp:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
# 蓝绿部署策略配置
strategy:
blueGreen:
# 活跃服务(接收生产流量)
activeService: myapp-active
# 预览服务(仅用于内部测试)
previewService: myapp-preview
# 是否自动提升新版本为活跃版本
# false表示需要手动审批
autoPromotionEnabled: false
# 提升新版本后,等待多久再销毁旧版本(秒)
# 给旧版本足够时间处理正在进行的请求
scaleDownDelaySeconds: 300
# 旧版本保留的数量(用于快速回滚)
revisionHistoryLimit: 5
# 预热期:新版本就绪后等待多久再允许提升
# 给应用足够时间完成初始化
prePromotionAnalysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-preview
interval: 30s
count: 3
failureLimit: 1
创建对应的 Service:
javascript
# services.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-active
namespace: production
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: myapp-preview
namespace: production
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP
应用配置:
javascript
kubectl apply -f rollout-bluegreen.yaml
kubectl apply -f services.yaml
# 查看Rollout状态
kubectl argo rollouts get rollout myapp-bluegreen -n production
6.2.3 蓝绿部署完整流程演示
步骤 1:初始部署
bash
# 部署v1.0.0版本
kubectl apply -f rollout-bluegreen.yaml
# 查看状态,此时所有流量都指向v1.0.0(蓝色环境)
kubectl argo rollouts get rollout myapp-bluegreen -n production
步骤 2:发布新版本 v1.1.0
bash
# 更新镜像版本
kubectl argo rollouts set image myapp-bluegreen \
myapp=harbor.example.com/myproject/myapp:v1.1.0 \
-n production
# 查看状态,此时会创建绿色环境(v1.1.0)
# 但所有流量仍然指向蓝色环境(v1.0.0)
kubectl argo rollouts get rollout myapp-bluegreen -n production
步骤 3:测试预览版本
bash
# 访问预览服务测试新版本
kubectl port-forward svc/myapp-preview 8080:80 -n production
curl http://localhost:8080/version
# 应该返回v1.1.0
# 访问活跃服务确认仍然是旧版本
kubectl port-forward svc/myapp-active 8081:80 -n production
curl http://localhost:8081/version
# 应该返回v1.0.0
步骤 4:手动提升新版本
bash
# 测试通过后,提升新版本为活跃版本
kubectl argo rollouts promote myapp-bluegreen -n production
# 查看状态,此时流量已切换到绿色环境(v1.1.0)
kubectl argo rollouts get rollout myapp-bluegreen -n production
步骤 5:观察运行情况
bash
# 持续观察新版本运行情况
kubectl argo rollouts get rollout myapp-bluegreen -n production --watch
# 300秒后,旧版本(蓝色环境)会自动销毁
6.2.4 蓝绿部署回滚操作
如果发现新版本有问题,可以随时回滚:
bash
# 查看发布历史
kubectl argo rollouts history myapp-bluegreen -n production
# 回滚到上一个版本
kubectl argo rollouts undo myapp-bluegreen -n production
# 回滚到指定版本
kubectl argo rollouts undo myapp-bluegreen --to-revision=2 -n production
6.3 金丝雀部署 (Canary Deployment) 详解
6.3.1 金丝雀部署原理
金丝雀部署是一种渐进式发布策略,它将流量逐步从旧版本切换到新版本:
- 先部署一小部分新版本实例
- 将一小部分流量 (如 5%) 路由到新版本
- 观察新版本的运行情况和指标
- 如果一切正常,逐步增加新版本的流量比例
- 最终将 100% 流量切换到新版本
- 销毁旧版本实例
优势:
- 风险低,问题只影响少量用户
- 资源消耗少,不需要双倍资源
- 可以基于真实用户流量进行验证
- 支持自动回滚
劣势:
- 发布周期长
- 流量控制复杂
- 需要处理版本共存问题
- 数据库变更需要兼容
适用场景:
- 大型应用和微服务架构
- 资源有限的环境
- 对发布风险敏感的场景
- 需要进行 A/B 测试的场景
6.3.2 金丝雀部署配置示例 (基础版)
javascript
# rollout-canary-basic.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp-canary
namespace: production
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: harbor.example.com/myproject/myapp:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
# 金丝雀部署策略配置
strategy:
canary:
# 金丝雀服务(可选,用于直接访问金丝雀版本)
canaryService: myapp-canary
# 稳定服务
stableService: myapp-stable
# 流量切换步骤
steps:
- setWeight: 10 # 将10%流量切换到金丝雀版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 30 # 将30%流量切换到金丝雀版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 50 # 将50%流量切换到金丝雀版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 70 # 将70%流量切换到金丝雀版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 100 # 将100%流量切换到金丝雀版本
# 最大不可用实例比例
maxUnavailable: 1
# 最大超额实例比例
maxSurge: 1
# 旧版本保留数量
revisionHistoryLimit: 5
6.3.3 金丝雀部署配置示例 (高级版 - 带自动分析)
这是生产环境推荐的配置,集成了自动指标分析和自动回滚:
javascript
# rollout-canary-advanced.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp-canary
namespace: production
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: harbor.example.com/myproject/myapp:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
strategy:
canary:
canaryService: myapp-canary
stableService: myapp-stable
# 流量切换步骤
steps:
- setWeight: 5
- pause: {} # 无限暂停,需要手动审批
- setWeight: 10
- pause: {duration: 5m}
- analysis:
templates:
- templateName: success-rate
- templateName: error-rate
- templateName: response-time
args:
- name: service-name
value: myapp-canary
interval: 30s
count: 5
failureLimit: 1
inconclusiveLimit: 2
- setWeight: 30
- pause: {duration: 10m}
- analysis:
templates:
- templateName: success-rate
- templateName: error-rate
args:
- name: service-name
value: myapp-canary
interval: 30s
count: 10
failureLimit: 1
- setWeight: 50
- pause: {duration: 10m}
- analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-canary
interval: 30s
count: 10
failureLimit: 1
- setWeight: 100
# 发布后分析
postPromotionAnalysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-stable
interval: 1m
count: 10
failureLimit: 1
# 自动回滚配置
abortScaleDownDelaySeconds: 30
6.3.5 金丝雀部署完整流程演示
步骤 1:初始部署
bash
# 部署v1.0.0版本
kubectl apply -f rollout-canary-advanced.yaml
kubectl apply -f analysis-templates.yaml
# 查看状态
kubectl argo rollouts get rollout myapp-canary -n production
步骤 2:发布新版本 v1.1.0
bash
# 更新镜像版本
kubectl argo rollouts set image myapp-canary \
myapp=harbor.example.com/myproject/myapp:v1.1.0 \
-n production
# 查看状态,此时会创建1个金丝雀实例(10%流量)
# 并进入无限暂停状态,等待手动审批
kubectl argo rollouts get rollout myapp-canary -n production
步骤 3:手动审批第一阶段
bash
# 测试金丝雀版本
kubectl port-forward svc/myapp-canary 8080:80 -n production
curl http://localhost:8080/version
# 应该返回v1.1.0
# 审批通过,继续发布流程
kubectl argo rollouts promote myapp-canary -n production
步骤 4:自动执行后续阶段
bash
# 观察发布过程
kubectl argo rollouts get rollout myapp-canary -n production --watch
# 系统会自动执行以下步骤:
# 1. 将流量增加到10%,观察5分钟
# 2. 运行第一次自动分析
# 3. 如果分析通过,将流量增加到30%,观察10分钟
# 4. 运行第二次自动分析
# 5. 如果分析通过,将流量增加到50%,观察10分钟
# 6. 运行第三次自动分析
# 7. 如果分析通过,将流量增加到100%
# 8. 运行发布后分析
6.3.6 金丝雀部署回滚操作
如果在发布过程中发现问题:
bash
# 立即中止发布并回滚
kubectl argo rollouts abort myapp-canary -n production
# 查看回滚状态
kubectl argo rollouts get rollout myapp-canary -n production
# 如果已经完成发布,回滚到上一个版本
kubectl argo rollouts undo myapp-canary -n production
6.4 蓝绿部署 vs 金丝雀部署对比
| 对比维度 | 蓝绿部署 | 金丝雀部署 |
|---|---|---|
| 资源消耗 | 高 (需要双倍资源) | 低 (只需要少量额外资源) |
| 发布风险 | 低 (发布前完整测试) | 中 (问题只影响少量用户) |
| 发布速度 | 快 (一次性切换) | 慢 (逐步切换) |
| 回滚速度 | 极快 (瞬间切换) | 快 (逐步回滚) |
| 流量控制 | 粗粒度 (0% 或 100%) | 细粒度 (0-100% 任意比例) |
| 版本共存 | 短时间共存 | 长时间共存 |
| 数据库变更 | 复杂 (需要兼容两个版本) | 更复杂 (需要长期兼容) |
| A/B 测试支持 | 差 | 好 |
| 适用规模 | 中小型应用 | 大型应用和微服务 |
| 学习曲线 | 简单 | 复杂 |
6.5 部署策略选择建议
选择蓝绿部署的情况:
- 应用规模较小,资源充足
- 对可用性要求极高
- 发布频率较低
- 数据库变更较少
- 需要在发布前进行完整测试
选择金丝雀部署的情况:
- 应用规模较大,采用微服务架构
- 资源有限
- 发布频率较高
- 对发布风险非常敏感
- 需要进行 A/B 测试或用户反馈收集
混合使用策略:
- 开发和测试环境使用滚动更新
- 预生产环境使用蓝绿部署
- 生产环境使用金丝雀部署
6.6 与 CI/CD 流水线集成
将高级部署策略与之前的 CI/CD 流水线集成:
6.6.1 更新 GitOps 仓库结构
javascript
myapp-manifests/
├── base/
│ ├── rollout.yaml # 替换原来的deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── configmap.yaml
├── staging/
│ ├── kustomization.yaml
│ └── configmap.yaml
└── production/
├── kustomization.yaml
├── configmap.yaml
└── analysis-templates.yaml # 生产环境专用分析模板
创建 Rollout 配置(蓝绿部署):
javascript
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: harbor.example.com/myproject/myapp:latest
ports:
- containerPort: 8080
strategy:
blueGreen:
activeService: myapp-active
previewService: myapp-preview
autoPromotionEnabled: false # 手动审批后才切换流量
scaleDownDelaySeconds: 30 # 切换后30秒再销毁旧版本
创建 Rollout 配置(金丝雀发布):
javascript
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: harbor.example.com/myproject/myapp:latest
ports:
- containerPort: 8080
strategy:
canary:
steps:
- setWeight: 10 # 先将10%流量切换到新版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 30 # 再将30%流量切换到新版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 50 # 再将50%流量切换到新版本
- pause: {duration: 10m} # 观察10分钟
- setWeight: 100 # 最后全量切换
6.6.2 更新 Jenkinsfile
在 CI 流水线中添加自动审批和通知功能:
javascript
stage('触发部署') {
steps {
script {
if (env.BRANCH_NAME == 'main') {
// 生产环境:触发金丝雀发布并等待第一阶段审批
sh """
kubectl argo rollouts set image myapp-canary \
myapp=${FULL_IMAGE_NAME} \
-n production
# 等待发布进入暂停状态
kubectl argo rollouts wait myapp-canary \
-n production --for=paused
# 发送审批通知
echo "金丝雀发布已启动,请访问ArgoCD控制台进行审批"
echo "ArgoCD地址: https://argocd.example.com/applications/myapp-production"
"""
} else if (env.BRANCH_NAME == 'staging') {
// 预生产环境:自动完成蓝绿部署
sh """
kubectl argo rollouts set image myapp-bluegreen \
myapp=${FULL_IMAGE_NAME} \
-n staging
# 等待发布完成
kubectl argo rollouts wait myapp-bluegreen \
-n staging --for=completed
# 自动提升新版本
kubectl argo rollouts promote myapp-bluegreen \
-n staging
"""
} else {
// 开发环境:直接更新
sh """
kubectl rollout restart deployment myapp -n dev
"""
}
}
}
}
6.6.3 更新 ArgoCD 应用配置
javascript
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-production
namespace: argocd
spec:
project: default
source:
repoURL: git@gitlab.example.com:myteam/myapp-manifests.git
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
# 生产环境禁用自动同步,由Jenkins触发
syncOptions:
- CreateNamespace=true
ignoreDifferences:
- group: argoproj.io
kind: Rollout
jsonPointers:
- /spec/template/spec/containers/0/image
