Jenkins + Kubernetes 自动化部署实践全流程笔记
✅ 1. Jenkins 安装与环境准备
📦 1.1 安装 OpenJDK 21(Adoptium 版本)
# 添加 Adoptium 仓库
sudo tee /etc/yum.repos.d/adoptium.repo <<EOF
[Adoptium]
name=Adoptium Temurin
baseurl=https://packages.adoptium.net/artifactory/rpm/centos/7/x86_64
enabled=1
gpgcheck=1
gpgkey=https://packages.adoptium.net/artifactory/api/gpg/key/public
EOF
# 安装 OpenJDK 21
sudo yum install -y temurin-21-jdk
# 设置默认 Java
sudo alternatives --install /usr/bin/java java /usr/lib/jvm/temurin-21-jdk/bin/java 1091
sudo alternatives --install /usr/bin/javac javac /usr/lib/jvm/temurin-21-jdk/bin/javac 1091
# 验证版本
java -version
输出应类似:
openjdk version "21.0.7"
OpenJDK Runtime Environment Temurin-21.0.7+6
📦 1.2 安装 Jenkins(本地包安装方式)
# 安装 RPM 包(需事先下载 Jenkins RPM 文件)
sudo yum localinstall -y jenkins-2.479.1-1.1.noarch.rpm
# 如提示缺少依赖
sudo yum install -y daemon fontconfig
🚀 1.3 启动 Jenkins
sudo systemctl daemon-reexec
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins
# 获取初始密码
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
访问:http://<服务器IP>:8080 进入 Jenkins 初始化界面。
✅ 2. Jenkins 配置 GitLab SSH 拉取权限
🔐 2.1 生成专用 SSH 密钥对
ssh-keygen -t rsa -b 4096 -C "jenkins-gitlab" -f /root/.ssh/id_rsa_jenkins
生成:
私钥:/root/.ssh/id_rsa_jenkins
公钥:/root/.ssh/id_rsa_jenkins.pub
🔑 2.2 GitLab 中添加 SSH Key
登录 GitLab → 用户头像 → Preferences → SSH Keys → 粘贴公钥内容
cat /root/.ssh/id_rsa_jenkins.pub
🔗 2.3 Jenkins 中添加凭据
路径:Jenkins → 系统管理 → 凭据 → 全局域 → 添加凭据:
类型:SSH Username with private key
Username:git
Private Key:选择 Enter directly,粘贴私钥内容:
cat /root/.ssh/id_rsa_jenkins
ID:gitlab-ssh
✅ 3. Jenkins 配置 Pipeline 自动部署项目
📁 项目目录结构(GitLab 仓库)
k8s-deploy-pipeline/
├── jenkins/
│ └── Jenkinsfile
├── manifests/
│ └── nginx/
│ ├── configmap.yaml
│ └── deployment.yaml
└── scripts/
└── healthcheck.sh
📜 Jenkinsfile 内容
pipeline {
agent any
parameters {
string(name: 'IMAGE_TAG', defaultValue: 'stable', description: '部署镜像标签')
string(name: 'NAMESPACE', defaultValue: 'default', description: '部署命名空间')
string(name: 'DEPLOYMENT', defaultValue: 'nginx-service', description: 'K8s Deployment 名')
string(name: 'CHECK_PATH', defaultValue: '/health', description: '探针路径')
}
environment {
KUBECONFIG = '/root/.kube/config'
TIMEOUT = 30
}
stages {
stage('Apply Kubernetes Deployment') {
steps {
sh '''
kubectl apply -f manifests/nginx/configmap.yaml -n ${params.NAMESPACE}
sed 's/{
{IMAGE_TAG}}/'"${params.IMAGE_TAG}"'/g' manifests/nginx/deployment.yaml | kubectl apply -n ${params.NAMESPACE} -f -
'''
}
}
stage('Health Check') {
steps {
script {
def result = sh(
script: "bash scripts/healthcheck.sh ${params.NAMESPACE} ${params.DEPLOYMENT} ${params.CHECK_PATH} ${env.TIMEOUT}",
returnStatus: true
)
if (result != 0) {
error("健康检查失败,准备回滚")
}
}
}
}
stage('Success') {
steps {
echo "部署成功!服务状态健康。"
}
}
}
post {
failure {
echo "自动回滚中..."
sh "kubectl rollout undo deployment/${params.DEPLOYMENT} -n ${params.NAMESPACE}"
}
}
}
🧪 healthcheck.sh
#!/bin/bash
NAMESPACE=$1
DEPLOYMENT=$2
CHECK_PATH=$3
TIMEOUT=${4:-30}
POD=$(kubectl get pod -n $NAMESPACE -l app=$DEPLOYMENT -o jsonpath='{.items[0].metadata.name}')
if [[ -z "$POD" ]]; then
echo "[ERROR] 未找到 Pod"
exit 1
fi
STATUS=$(kubectl exec -n $NAMESPACE $POD -- curl -s -o /dev/null -w "%{http_code}" http://localhost:80$CHECK_PATH)
if [[ "$STATUS" == "200" ]]; then
echo "[INFO] 健康检查通过"
exit 0
else
echo "[ERROR] 健康检查失败,返回码:$STATUS"
exit 1
fi
📦 manifests/nginx/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
default.conf: |
server {
listen 80;
location / {
return 200 'nginx ok';
}
location /health {
return 200 'healthy';
}
}
📦 manifests/nginx/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-service
spec:
replicas: 2
selector:
matchLabels:
app: nginx-service
template:
metadata:
labels:
app: nginx-service
spec:
containers:
- name: nginx
image: nginx:{
{IMAGE_TAG}}
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
volumes:
- name: nginx-config
configMap:
name: nginx-config
✅ Jenkins 创建 Pipeline 项目流程
打开 Jenkins → 新建任务 → 输入名称:k8s-deploy-pipeline
类型选择:Pipeline
勾选参数化构建流程,添加参数:
| 名称 | 默认值 |
|---|---|
| IMAGE_TAG | stable |
| NAMESPACE | default |
| DEPLOYMENT | nginx-service |
| CHECK_PATH | /health |
Pipeline → 定义方式选择:Pipeline script from SCM
SCM 类型:Git
仓库地址:<git@git.****.com>:system-ops/k8s-deploy-pipeline.git
凭据:选择 gitlab-ssh
分支:*/master
脚本路径:jenkins/Jenkinsfile
一、Kubernetes 基础信息确认
| 项目 | 示例值 | 说明 |
|---|---|---|
| K8s 版本 | v1.23.6 | 与 kubectl 版本需兼容 |
| 安装方式 | kubeadm | 决定权限与网络插件配置 |
| Jenkins 安装位置 | 独立虚拟机 (172.16.3.80) | 决定 kubeconfig 获取方式 |
| 网络插件 | Calico | 与服务连通性相关 |
| 是否启用 RBAC | 是 | 决定是否需要绑定角色权限 |
| 期望访问方式 | KubeConfig 文件 | 后续也可切换为 ServiceAccount |
获取集群信息命令
kubectl version --short
kubectl api-versions | grep rbac
kubectl get pods -n kube-system
kubectl auth can-i '*' '*' --all-namespaces
kubectl config current-context
二、配置 Jenkins 虚拟机访问 Kubernetes
1. 拷贝 kubeconfig 文件
# 假设你在 k8s-master 上:
scp /etc/kubernetes/admin.conf root@172.16.3.80:/root/.kube/config
2. 安装 kubectl
# 添加 kubernetes yum 源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
EOF
# 安装指定版本
yum install -y kubectl-1.23.6
3. 给 Jenkins 用户权限
mkdir -p /var/lib/jenkins/.kube
cp /root/.kube/config /var/lib/jenkins/.kube/config
chown -R jenkins:jenkins /var/lib/jenkins/.kube
chmod 600 /var/lib/jenkins/.kube/config
4. Jenkinsfile 设置
environment {
KUBECONFIG = '/var/lib/jenkins/.kube/config'
}
三、Jenkins 安装 Docker 权限问题
报错:
permission denied while trying to connect to the Docker daemon socket
解决方案:
usermod -aG docker jenkins
systemctl restart jenkins
四、Harbor 镜像仓库配置
| 内容 | 示例 | |
|---|---|---|
| Harbor 地址 | 172.16.3.252 | 内网 IP |
| 是否启用 HTTPS | 否 | 使用 HTTP 配置 insecure registry |
| 项目名 | k8s-test | Jenkins 推送目标 |
| 镜像命名 | nginx-demo:test | 示例 |
Docker insecure registry 设置
cat > /etc/docker/daemon.json <<EOF
{
"insecure-registries": ["172.16.3.252"]
}
EOF
systemctl daemon-reexec
systemctl restart docker
五、Jenkins 项目目录结构建议
pipeline {
agent any
environment {
REGISTRY = '172.16.3.252'
IMAGE_NAME = 'k8s-test/nginx-demo'
IMAGE_TAG = 'dev'
DEPLOY_NAMESPACE = 'jenkins-dev'
DEPLOYMENT_NAME = 'nginx-service'
}
stages {
stage('阶段一:拉取代码') {
steps {
checkout scm
}
}
stage('阶段二:构建并推送镜像') {
steps {
script {
echo '🔧 正在构建镜像...'
sh "docker build -t ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} ."
echo '🚀 正在推送镜像到 Harbor...'
withCredentials([usernamePassword(credentialsId: 'harbor-cred', usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
sh """
echo $HARBOR_PASS | docker login ${REGISTRY} -u $HARBOR_USER --password-stdin
docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
docker logout ${REGISTRY}
"""
}
}
}
}
stage('阶段三:渲染并应用 K8S 配置') {
steps {
script {
echo '📦 正在渲染并应用 Kubernetes 配置...'
sh """
sed -e 's|{
{REGISTRY}}|${REGISTRY}|g'
-e 's|{
{IMAGE_NAME}}|${IMAGE_NAME}|g'
-e 's|{
{IMAGE_TAG}}|${IMAGE_TAG}|g'
manifests/nginx/deployment.yaml > /tmp/rendered.yaml
kubectl apply -f /tmp/rendered.yaml -n ${DEPLOY_NAMESPACE}
"""
}
}
}
stage('阶段四:健康检查') {
steps {
script {
echo '🩺 正在检查 Deployment 状态...'
def rollout = sh (
script: "kubectl rollout status deployment/${DEPLOYMENT_NAME} -n ${DEPLOY_NAMESPACE} --timeout=60s",
returnStatus: true
)
if (rollout != 0) {
error "❌ Rollout 失败"
}
def statuses = sh(
script: "kubectl get pods -l app=${DEPLOYMENT_NAME} -n ${DEPLOY_NAMESPACE} -o jsonpath='{.items[*].status.phase}'",
returnStdout: true
).trim().split()
def failedPods = []
for (status in statuses) {
if (status != "Running") {
failedPods << status
}
}
if (failedPods) {
echo "❌ 检测到非 Running 状态的 Pod: ${failedPods.join(', ')}"
sh "kubectl get pods -l app=${DEPLOYMENT_NAME} -n ${DEPLOY_NAMESPACE}"
error "❌ 健康检查未通过,终止流程。"
}
}
}
}
stage('阶段五:构建成功') {
steps {
echo '✅ 部署成功!'
}
}
}
post {
failure {
echo '❌ 部署失败,尝试回滚...'
script {
def revisions = sh(script: "kubectl rollout history deployment/${DEPLOYMENT_NAME} -n ${DEPLOY_NAMESPACE} | grep Revision || true", returnStdout: true).trim()
if (revisions.contains("Revision")) {
sh "kubectl rollout undo deployment/${DEPLOYMENT_NAME} -n ${DEPLOY_NAMESPACE}"
echo '🔙 已执行回滚操作'
} else {
echo '⚠️ 没有可用的历史版本,跳过回滚'
}
}
}
}
}
常见报错与排查
1. Jenkins 构建时 docker 权限不足
permission denied while trying to connect to the Docker daemon socket
解决: Jenkins 用户加入 docker 组
2. kubectl 权限不足
open /root/.kube/config: permission denied
解决: 使用 Jenkins 用户专属 config,并修改权限为 jenkins:jenkins
3. Harbor 登录失败(443 被拒)
解决: 添加 insecure-registries 设置后重启 docker
4. 拉取镜像失败(443 被拒)
解决: 创建私有专属秘钥
[root@redis-test ~]# kubectl create secret docker-registry harbor-secret
> --docker-server=172.16.3.252
> --docker-username=admin
> --docker-password=Harbor12345
> --docker-email=devops@yourcompany.com
> -n jenkins-dev
=====================================
六、多分支自动部署
一、项目目标
构建一个支持多分支(dev/test/release-*/master)自动部署的 CI/CD 流水线,包含:
Jenkins 拉取 GitLab 指定分支代码
构建 Docker 镜像并推送到 Harbor 私有仓库
渲染 Kubernetes 部署模板并自动部署
部署健康检查与失败自动回滚
📁 二、项目目录结构
k8s-deploy-pipeline/
├── Dockerfile # 镜像构建文件
├── jenkins/
│ └── Jenkinsfile # Jenkins 流水线脚本
├── manifests/
│ └── nginx/
│ └── deployment.yaml # K8S Deployment 模板
├── scripts/
│ └── healthcheck.sh # 健康检查脚本(可选)
└── app/ # 应用源码目录
⚙️ 三、关键配置文件
1. jenkins/Jenkinsfile
pipeline {
agent any
parameters {
choice(
name: 'BRANCH',
choices: ['dev', 'test', 'master', 'release-xxx'],
description: '请选择要部署的分支'
)
}
environment {
REGISTRY = '172.16.3.252'
IMAGE_NAME = 'k8s-test/nginx-demo'
BUILD_TAG = ''
K8S_NAMESPACE = ''
RENDERED_YAML = "${env.WORKSPACE}/rendered.yaml"
}
stages {
stage('阶段一:初始化环境变量') {
steps {
script {
if (params.BRANCH == 'dev') {
BUILD_TAG = "dev-${env.BUILD_NUMBER}"
K8S_NAMESPACE = 'jenkins-dev'
} else if (params.BRANCH == 'test') {
BUILD_TAG = "test-${env.BUILD_NUMBER}"
K8S_NAMESPACE = 'jenkins-test'
} else if (params.BRANCH == 'master' || params.BRANCH.startsWith('release-')) {
BUILD_TAG = params.BRANCH
K8S_NAMESPACE = 'jenkins-prod'
} else {
error "❌ 非法分支:${params.BRANCH},请使用 dev/test/master/release-*"
}
echo "🌿 当前分支: ${params.BRANCH},部署到命名空间: ${K8S_NAMESPACE},镜像 Tag: ${BUILD_TAG}"
// 拉取对应分支的代码
checkout([$class: 'GitSCM',
branches: [[name: "origin/${params.BRANCH}"]],
userRemoteConfigs: [[
url: 'git@git.****.com:system-ops/k8s-deploy-pipeline.git',
credentialsId: 'gitlab-ssh-key'
]]
])
}
}
}
stage('阶段二:构建并推送镜像') {
steps {
echo '🔧 正在构建镜像...'
sh "docker build -t ${REGISTRY}/${IMAGE_NAME}:${BUILD_TAG} ."
withCredentials([usernamePassword(credentialsId: 'harbor-cred', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
sh """
echo $PASS | docker login ${REGISTRY} -u $USER --password-stdin
docker push ${REGISTRY}/${IMAGE_NAME}:${BUILD_TAG}
docker logout ${REGISTRY}
"""
}
}
}
stage('阶段三:渲染并部署 K8S 配置') {
steps {
script {
def rawYaml = readFile('manifests/nginx/deployment.yaml')
def rendered = rawYaml
.replaceAll('\{\{REGISTRY\}\}', REGISTRY)
.replaceAll('\{\{IMAGE_NAME\}\}', IMAGE_NAME)
.replaceAll('\{\{IMAGE_TAG\}\}', BUILD_TAG)
writeFile file: RENDERED_YAML, text: rendered
sh "kubectl apply -f ${RENDERED_YAML} -n ${K8S_NAMESPACE}"
}
}
}
stage('阶段四:健康检查') {
steps {
echo '🩺 正在检查 Deployment 状态...'
sh "kubectl rollout status deployment/nginx-service -n ${K8S_NAMESPACE} --timeout=60s"
}
}
}
post {
success {
echo "✅ 部署成功: ${IMAGE_NAME}:${BUILD_TAG} 到 ${K8S_NAMESPACE}"
}
failure {
echo '❌ 部署失败,尝试回滚...'
script {
def history = sh(returnStdout: true, script: "kubectl rollout history deployment/nginx-service -n ${K8S_NAMESPACE} | grep Revision || true").trim()
if (history) {
sh "kubectl rollout undo deployment/nginx-service -n ${K8S_NAMESPACE}"
echo '🔁 已回滚上一个版本'
} else {
echo '⚠️ 没有可回滚版本'
}
}
}
}
}
2. manifests/nginx/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-service
spec:
replicas: 1
selector:
matchLabels:
app: nginx-service
template:
metadata:
labels:
app: nginx-service
spec:
containers:
- name: nginx
image: {
{REGISTRY}}/{
{IMAGE_NAME}}:{
{IMAGE_TAG}}
ports:
- containerPort: 80
3. Jenkins 参数设置
参数名称:BRANCH
类型:字符串(或下拉列表)
默认值:dev
描述:指定部署分支,如 dev/test/release-x/master
** 四、常见问题及解决方案**
| 问题 | 原因 | 解决方法 |
|---|---|---|
| InvalidImageName | ${BUILD_TAG} 未替换 | 确保 replaceAll 正确生效 |
| credentials 不能在 env 中使用 | Groovy 限制 | 移入 withCredentials 中 |
| K8S_NAMESPACE 未定义报错 | post 中变量未初始化 | 使用全局变量方式定义 |
| 没有分支选择参数 | 没添加 BRANCH 参数 | Jenkins 添加参数即可 |
** 五、环境准备与命令**
kubectl create ns jenkins-dev
kubectl create ns jenkins-test
kubectl create ns jenkins-prod
Harbor 项目 k8s-test 手动创建并设置允许推送。
Jenkins 中添加:
GitLab 凭据 ID:gitlab-ssh-key
Harbor 凭据 ID:harbor-cred
七、 基于 Argo CD 实现 nginx-demo 自动部署
项目背景
目标:基于 ArgoCD 实现 GitOps 发布流程,部署测试应用 nginx-demo。
环境:已有 Jenkins 环境,命名空间为 jenkins-dev,Git 仓库为 <git@git.****.com>:system-ops/k8s-deploy-manifests.git。
镜像仓库:使用本地 Harbor(镜像前缀 172.16.3.252/k8s-test/),通过代理拉取并打 tag 推送。
** Git 仓库结构**
k8s-deploy-manifests/
├── applications/
│ └── nginx-demo.yaml # ArgoCD Application 配置
└── nginx-demo/
├── deployment.yaml # Deployment 配置
├── service.yaml # Service 配置
└── kustomization.yaml # kustomize 文件
** 部署流程详解**
1️⃣ 安装 Argo CD 到 K8S 集群
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
💡 注意修改 yaml 中所有镜像为本地 Harbor 镜像,并提前推送。
2️⃣ 修改并推送镜像到本地 Harbor
docker tag ghcr.kubesre.xyz/dexidp/dex:v2.41.1 172.16.3.252/k8s-test/dex:v2.41.1
docker tag quay.kubesre.xyz/argoproj/argocd:v3.0.6 172.16.3.252/k8s-test/argocd:v3.0.6
docker tag dhub.kubesre.xyz/redis:7.2.7-alpine 172.16.3.252/k8s-test/redis:7.2.7-alpine
docker push ...
3️⃣ 访问 Argo CD UI 并登录
kubectl get svc argocd-server -n argocd
# 查看 NodePort 端口
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# 初始用户名:admin,密码为上面命令输出
** 编写 nginx-demo 应用配置**
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
spec:
replicas: 1
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: 172.16.3.252/k8s-test/nginx-demo:20250710
ports:
- containerPort: 80
service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
kustomization.yaml
resources:
- deployment.yaml
- service.yaml
applications/nginx-demo.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo
namespace: argocd
spec:
project: default
source:
repoURL: git@git.****com:system-ops/k8s-deploy-manifests.git
targetRevision: master
path: nginx-demo
destination:
server: https://kubernetes.default.svc
namespace: jenkins-dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
遇到的问题与解决方案
| 问题描述 | 解决方法 |
|---|---|
| ArgoCD 拉取 Git 报错:SSH agent not specified | 改用 HTTPS 拉取,并在 UI 中添加 Git 凭据 |
| Git 仓库子模块导致路径错误 | 移除 submodule,重构为扁平结构 |
| service.yaml 文件未识别 | 文件名有尾部空格,mv 去除空格后重新提交 |
| 仓库推送被拒绝 | 关闭 master 分支保护,或使用新分支 |
| Manifest 缓存路径识别失败 | 逐步排查并上传正确 YAML 文件,确保本地同步提交 |
验证结果
kubectl -n jenkins-dev get deployment
kubectl -n jenkins-dev get svc
输出结果:
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-demo 1/1 1 1 ✅
八、Jenkins + GitLab + Harbor + ArgoCD 自动化部署流程
环境:Kubernetes(命名空间:jenkins-dev),Jenkins,GitLab,Harbor,ArgoCD
一、项目结构说明
1.1 Git 仓库结构(k8s-deploy-manifests)
k8s-deploy-manifests/
├── applications/
│ └── nginx-demo.yaml # ArgoCD 应用配置
├── nginx-demo/
│ ├── deployment.yaml # Deployment 模板文件,镜像 tag 会被 Jenkins 更新
│ ├── service.yaml # 暴露 Service
│ └── kustomization.yaml # 用于 Kustomize 管理资源
1.2 业务代码仓库结构(nginx-demo)
nginx-demo/
├── Dockerfile # 构建 Nginx 镜像
├── Jenkinsfile # Jenkins CI/CD 流水线定义
└── index.html # 示例静态页面
二、配置文件详解
2.1 Jenkinsfile
pipeline {
agent any
environment {
// 镜像仓库地址和 Tag 构造(按时间戳)
IMAGE_TAG = "build-${new Date().format('yyyyMMdd-HHmm')}"
IMAGE_REPO = "172.16.3.252/k8s-test/nginx-demo"
}
stages {
stage('拉取项目代码') {
steps {
git(
url: 'git@git.****.com:system-ops/nginx-demo.git',
credentialsId: 'gitlab-ssh-key',
branch: 'master'
)
}
}
stage('构建镜像并推送至 Harbor') {
steps {
withCredentials([usernamePassword(credentialsId: 'harbor-cred', usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
sh """
echo '开始构建镜像: ${IMAGE_REPO}:${IMAGE_TAG}'
docker login 172.16.3.252 -u $HARBOR_USER -p $HARBOR_PASS
docker build -t ${IMAGE_REPO}:${IMAGE_TAG} .
docker push ${IMAGE_REPO}:${IMAGE_TAG}
"""
}
}
}
stage('更新 deployment.yaml 中镜像 tag') {
steps {
sshagent(credentials: ['gitlab-ssh-key']) {
sh '''
set -e
# 清理已有目录,防止多次构建冲突
if [ -d "k8s-deploy-manifests" ]; then
echo "检测到已有 k8s-deploy-manifests 目录,正在删除..."
rm -rf k8s-deploy-manifests
fi
# 克隆 YAML 仓库
git clone git@git.****com:system-ops/k8s-deploy-manifests.git
# 切换到对应服务目录
cd k8s-deploy-manifests/nginx-demo
echo "将镜像地址替换为: ${IMAGE_REPO}:${IMAGE_TAG}"
sed -i "s|image: .*|image: ${IMAGE_REPO}:${IMAGE_TAG}|" deployment.yaml
# 配置提交信息并推送
git config user.name "jenkins"
git config user.email "jenkins@****.com"
git add deployment.yaml
git commit -m "auto: update nginx-demo image to ${IMAGE_TAG}"
git push origin master
'''
}
}
}
stage('部署阶段(ArgoCD 自动同步)') {
steps {
echo '无需手动部署,ArgoCD 将自动同步新镜像版本。'
}
}
}
post {
failure {
echo '❌ 流水线执行失败,请检查日志输出以定位问题。'
}
}
}
2.2 ArgoCD Application(nginx-demo.yaml)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo
namespace: argocd
spec:
project: default
source:
repoURL: git@git.****.com:system-ops/k8s-deploy-manifests.git
targetRevision: HEAD
path: nginx-demo
destination:
server: https://kubernetes.default.svc
namespace: jenkins-dev
syncPolicy:
automated:
prune: true
selfHeal: true
三、实际操作流程
创建两个 Git 仓库(代码仓库 nginx-demo,部署仓库 k8s-deploy-manifests)
初始化 nginx-demo 目录结构,写好 Jenkinsfile 和 Dockerfile
Jenkins 配置两个凭据:
gitlab-ssh-key(用于拉代码 + 推送 deployment.yaml 更新)
harbor-cred(用于推送镜像)
ArgoCD 添加并同步 nginx-demo.yaml 应用配置
Jenkins 成功执行流水线后会:
构建 + 推送镜像
更新 deployment.yaml 中镜像 tag
触发 ArgoCD 自动同步
四、遇到的问题及解决方法
4.1 Harbor 推送失败(unauthorized)
原因:凭据未配置或 ID 错误
解决:使用 harbor-cred 正确绑定用户名密码并注入登录脚本
4.2 Git 无法 push(权限拒绝)
原因:gitlab-ssh-key 无写权限
解决:确保对应 SSH Key 的用户对目标仓库拥有 Maintainer 权限
4.3 ArgoCD 未同步更新
原因:ArgoCD Application 中路径或 repoURL 配置错误
解决:确认 path 填写的是 k8s-deploy-manifests 中具体服务目录(如 nginx-demo)
**九、**GitOps 实践总结多环境部署
一、目标
实现多环境(dev/test/prod)下:
Jenkins 自动构建 + 推送镜像
自动更新对应 YAML 配置并提交 Git 仓库
通过 ArgoCD 实现自动部署与状态可视化
二、目录结构规范
GitOps 配置仓库目录结构:
k8s-deploy-manifests/
├── jenkins-dev/nginx-demo/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
├── jenkins-test/nginx-demo/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
├── jenkins-prod/nginx-demo/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
├── nginx-demo/ # 最早的单环境目录,已废弃
└── applications/ # 可作为 ArgoCD App of Apps 管理入口
✅ 每个环境单独管理,Jenkins 脚本按变量 BRANCH_ENV 自动切换路径。
三、Jenkinsfile 配置
** 支持内容:**
选择 dev/test/prod 环境
镜像构建 tag 自动带上时间戳(build-20250714-1415)
自动推送至 Harbor(私有仓库)
自动替换镜像字段并提交至配置仓库(含 commit message)
ArgoCD 自动同步,无需手动操作
参数化示例:
pipeline {
agent any
parameters {
// ✅ 选择部署环境:dev/test/prod
choice(name: 'BRANCH_ENV', choices: ['dev', 'test', 'prod'], description: '部署目标环境')
}
environment {
IMAGE_TAG = "build-${new Date().format('yyyyMMdd-HHmm')}"
IMAGE_REPO = "172.16.3.252/k8s-test/nginx-demo"
}
stages {
stage('拉取代码') {
steps {
git url: 'git@git.***.com:system-ops/nginx-demo.git',
credentialsId: 'gitlab-ssh-key',
branch: 'master'
}
}
stage('构建镜像并推送 Harbor') {
steps {
withCredentials([usernamePassword(credentialsId: 'harbor-cred', usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
sh """
docker login 172.16.3.252 -u $HARBOR_USER -p $HARBOR_PASS
docker build -t ${IMAGE_REPO}:${IMAGE_TAG} .
docker push ${IMAGE_REPO}:${IMAGE_TAG}
"""
}
}
}
stage('更新部署配置') {
steps {
sshagent(credentials: ['gitlab-ssh-key']) {
sh """
rm -rf k8s-deploy-manifests
git clone git@git.*****.com:system-ops/k8s-deploy-manifests.git
cd k8s-deploy-manifests/jenkins-${BRANCH_ENV}/nginx-demo
sed -i "s|image: .*|image: ${IMAGE_REPO}:${IMAGE_TAG}|" deployment.yaml
git config user.name "jenkins"
git config user.email "jenkins@****.com"
git add deployment.yaml
git commit -m "auto: update nginx-demo image to ${IMAGE_TAG} for ${BRANCH_ENV}"
git push origin master
"""
}
}
}
stage('ArgoCD 自动部署') {
steps {
echo "无需手动部署,ArgoCD 将自动同步 jenkins-${params.BRANCH_ENV} 环境的最新配置。"
}
}
}
post {
failure {
echo "❌ 构建失败,请检查日志并考虑手动回滚镜像 tag。"
}
}
}
四、ArgoCD 应用配置(已部署)
| 应用名 | Namespace | Git Path | 自动同步 | 状态 |
|---|---|---|---|---|
| nginx-demo-dev | jenkins-dev | jenkins-dev/nginx-demo | ✅ | ✅Synced |
| nginx-demo-test | jenkins-test | jenkins-test/nginx-demo | ✅ | ✅Synced |
| nginx-demo-prod | jenkins-prod | jenkins-prod/nginx-demo | ✅ | ✅Synced |
五、错误记录与排查过程
| 时间 | 问题描述 | 原因分析 | 解决方式 |
|---|---|---|---|
| 构建失败 | cd: k8s-deploy-manifests/jenkins-/nginx-demo: No such file or directory | jenkins-${BRANCH_ENV} 拼接出错,参数未传或路径命名不规范(多了空格) | 清理 Git 仓库路径中的空格字符;本地手动确认目录名称无误 |
| 拉取路径错误 | nginx-demo 直接作为 root path,非多环境模式结构 | 原 ArgoCD 只部署了 nginx-demo,未区分环境 | 后续改为 jenkins-dev/nginx-demo 等多环境路径,创建新的 ArgoCD 应用 |
| Git 提交失败 | Git clone 的配置仓库目录结构误判 | Jenkins 运行时默认进入工作目录,未处理多级子目录 cd 路径拼接异常 | 加入 set -e 严格执行;先 clone 后 cd k8s-deploy-manifests/… 补全路径 |
十、 Kubernetes 多环境 GitOps 最佳实践之kuustomize-demo
项目:nginx-kustomize-demo 多环境 GitOps 部署
所用技术: Jenkins + Harbor + ArgoCD + Kubernetes + Git + Kustomize
一、文件结构总览
k8s-deploy-kustomize/
├── base
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml 使用 images + newTag 管理
├── overlays
│ ├── jenkins-dev
│ │ ├── kustomization.yaml 使用 kustomize edit set image 更新
│ │ ├── service-patch.yaml patch 修改 Service 信息
│ │ └── ingress.yaml (optional)
│ ├── jenkins-test
│ └── jenkins-prod
二、base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
images:
- name: 172.16.3.252/k8s-test/nginx-kustomize-demo
newName: 172.16.3.252/k8s-test/nginx-kustomize-demo
newTag: latest # 初始化 tag ,待 overlays 重写
三、overlays/jenkins-dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: jenkins-dev # 指定环境命名空间
resources:
- ../../base
patches:
- path: service-patch.yaml
target:
kind: Service
name: nginx-kustomize-demo # 对 base 中 service.yaml 修改
service-patch.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-kustomize-demo
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
四、Jenkinsfile (images + newTag 配合 kustomize edit set image)
pipeline {
agent any
parameters {
choice(name: 'BRANCH_ENV', choices: ['dev', 'test', 'prod'], description: '部署目标环境')
}
environment {
IMAGE_REPO = "172.16.3.252/k8s-test/nginx-kustomize-demo"
IMAGE_TAG = "build-${new Date().format('yyyyMMdd-HHmm')}"
GIT_REPO = "git@git.****.com:system-ops/k8s-deploy-kustomize.git"
}
stages {
stage('拉取 NGINX 项目代码') {
steps {
git(
url: 'git@git.***.com:system-ops/nginx-kustomize-demo.git',
credentialsId: 'gitlab-ssh-key',
branch: 'master'
)
}
}
stage('构建镜像并推送 Harbor') {
steps {
withCredentials([usernamePassword(credentialsId: 'harbor-cred', usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
sh """
echo '🚧 构建镜像: ${IMAGE_REPO}:${IMAGE_TAG}'
docker login 172.16.3.252 -u $HARBOR_USER -p $HARBOR_PASS
docker build -t ${IMAGE_REPO}:${IMAGE_TAG} .
docker push ${IMAGE_REPO}:${IMAGE_TAG}
"""
}
}
}
stage('更新 Kustomize 镜像配置') {
steps {
sshagent(credentials: ['gitlab-ssh-key']) {
sh """
echo '🔄 克隆部署配置仓库'
rm -rf k8s-deploy-kustomize
git clone ${GIT_REPO}
cd k8s-deploy-kustomize/overlays/jenkins-${BRANCH_ENV}
echo '📝 设置镜像 tag 为 ${IMAGE_TAG}'
kustomize edit set image ${IMAGE_REPO}=${IMAGE_REPO}:${IMAGE_TAG}
echo '🚀 提交变更'
git config user.name "jenkins"
git config user.email "jenkins@****.com"
git add kustomization.yaml
if ! git diff --cached --quiet; then
git commit -m "auto: update jenkins-${BRANCH_ENV} image to ${IMAGE_TAG}"
git push origin master
else
echo '✅ 无需提交,镜像 tag 未变化'
fi
"""
}
}
}
stage('提示部署进度') {
steps {
echo "✅ 镜像 ${IMAGE_REPO}:${IMAGE_TAG} 已构建并更新配置,等待 ArgoCD 自动同步。"
}
}
}
post {
failure {
echo "❌ 构建失败,请检查日志并回滚上一个版本镜像 tag。"
}
}
}
五、错误纪录 & 故障解决
| 错误环节 | 错误信息 | 原因 | 解决策略 |
|---|---|---|---|
| 镜像拉取失败 | nginx:stable 拉取失败 | base deployment.yaml 中 image 默认镜像 | 改为使用私有 Harbor 镜像,通过 Kustomize images 控制 |
| ArgoCD 拉取失败 | deployment-patch.yaml: no such file or directory | patch 配置了不存在的文件 | 改为直接在 base 使用 images + newTag 管理镜像 |
| Jenkins 执行失败 | kustomize: command not found | Jenkins 中未安装 kustomize 工具 | 通过 yum 安装或配置 kustomize 二进制文件路径 |
| Git push 失败 | rejected (fetch first) | 本地未先 pull | 先执行 git pull –rebase 再 push |
六、ArgoCD 应用状态总览
| 应用 | Namespace | Path | Sync Status |
|---|---|---|---|
| nginx-kustomize-demo-dev | jenkins-dev | overlays/jenkins-dev | ✅ Synced |
| nginx-kustomize-demo-test | jenkins-test | overlays/jenkins-test | ✅ Synced |
| nginx-kustomize-demo-prod | jenkins-prod | overlays/jenkins-prod | ✅ Synced |


















暂无评论内容