pipeline { agent any environment { // GitLab配置 GITLAB_URL = 'https://gitlab.xpaas.lenovo.com' GITLAB_REPO = 'artificial-intelligence-platform/lenovo-ipc' GITLAB_CREDENTIALS_ID = 'more_dots' // 新镜像仓库配置 (10.128.62.130:8843) REGISTRY_URL = '10.128.62.130:8843' REGISTRY_CREDENTIALS_ID = 'docker-registry-130' // 镜像配置 IMAGE_NAME = 'more_dots' FULL_IMAGE_NAME = "${REGISTRY_URL}/${IMAGE_NAME}" // 是否跳过构建(运行时赋值) SKIP_BUILD = 'false' } parameters { choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: '选择部署环境') choice(name: 'TARGET_CLUSTER', choices: ['cluster1', 'cluster2', 'both'], description: '选择目标集群') } stages { stage('Checkout') { steps { git( url: "${GITLAB_URL}/${GITLAB_REPO}.git", credentialsId: GITLAB_CREDENTIALS_ID, branch: 'master' ) script { env.GIT_COMMIT = sh(script: 'git rev-parse HEAD', returnStdout: true).trim() env.SOURCE_TREE = sh(script: 'git rev-parse HEAD:more_dots', returnStdout: true).trim() env.IMAGE_TAG = env.SOURCE_TREE.take(12) echo "GIT_COMMIT: ${env.GIT_COMMIT}" echo "SOURCE_TREE: ${env.SOURCE_TREE}" echo "IMAGE_TAG: ${env.IMAGE_TAG}" echo "FULL_IMAGE_NAME: ${FULL_IMAGE_NAME}:${env.IMAGE_TAG}" } } } stage('Check Code Changes') { steps { script { def prev = env.GIT_PREVIOUS_SUCCESSFUL_COMMIT ?: '' if (!prev?.trim()) { echo "未找到上次成功构建提交,默认执行构建" env.SKIP_BUILD = 'false' } else { int rc = sh( script: "git diff --quiet ${prev} HEAD -- more_dots", returnStatus: true ) env.SKIP_BUILD = (rc == 0) ? 'true' : 'false' } echo "代码变更检查结果: SKIP_BUILD=${env.SKIP_BUILD}" } } } stage('Build Docker Image') { when { expression { env.SKIP_BUILD != 'true' } } steps { dir('more_dots') { sh "docker build --progress=plain -t ${IMAGE_NAME}:${env.IMAGE_TAG} ." } } } stage('Push to Registry') { when { expression { env.SKIP_BUILD != 'true' } } steps { script { withCredentials([usernamePassword( credentialsId: REGISTRY_CREDENTIALS_ID, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASS' )]) { sh """ set -eux echo "登录到镜像仓库 ${REGISTRY_URL}..." echo "${REGISTRY_PASS}" | docker login ${REGISTRY_URL} -u ${REGISTRY_USER} --password-stdin """ } sh """ set -eux echo "打标签: ${IMAGE_NAME}:${IMAGE_TAG} -> ${FULL_IMAGE_NAME}:${IMAGE_TAG}" docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${FULL_IMAGE_NAME}:${IMAGE_TAG} echo "推送镜像到仓库..." docker push ${FULL_IMAGE_NAME}:${IMAGE_TAG} echo "镜像推送完成: ${FULL_IMAGE_NAME}:${IMAGE_TAG}" """ } } } stage('Deploy to k3s') { steps { dir('more_dots') { script { def cluster = params.TARGET_CLUSTER def fullImageWithTag = "${FULL_IMAGE_NAME}:${env.IMAGE_TAG}" if (cluster == 'cluster1' || cluster == 'both') { withCredentials([ file(credentialsId: 'k3s-cluster1-config', variable: 'KUBECONFIG_CLUSTER1'), usernamePassword(credentialsId: REGISTRY_CREDENTIALS_ID, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASS') ]) { sh """ set -eux # 查找kubectl路径 KUBECTL_PATH=\$(command -v kubectl 2>/dev/null || true) if [ -z "\$KUBECTL_PATH" ]; then for p in /usr/local/bin/kubectl /usr/bin/kubectl /bin/kubectl; do if [ -x "\$p" ]; then KUBECTL_PATH="\$p" break fi done fi if [ -z "\$KUBECTL_PATH" ]; then echo "kubectl 未找到,请检查 Jenkins 节点环境" exit 1 fi echo "使用kubectl路径: \$KUBECTL_PATH" # 定义kubectl函数 k() { sudo \$KUBECTL_PATH --kubeconfig=${KUBECONFIG_CLUSTER1} "\$@" } # 检查命名空间 k get namespace ${params.DEPLOY_ENV} || k create namespace ${params.DEPLOY_ENV} # 创建imagePullSecret k create secret docker-registry regcred-130 \\ --docker-server=${REGISTRY_URL} \\ --docker-username=${REGISTRY_USER} \\ --docker-password=${REGISTRY_PASS} \\ --namespace=${params.DEPLOY_ENV} \\ --dry-run=client -o yaml | k apply -f - # 替换镜像并部署 sed "s|image:.*python-app.*|image: ${fullImageWithTag}|g" k8s/deployment.yaml > /tmp/deployment-${params.DEPLOY_ENV}.yaml k apply -f /tmp/deployment-${params.DEPLOY_ENV}.yaml -n ${params.DEPLOY_ENV} # 确保使用imagePullSecret k patch deployment python-app -n ${params.DEPLOY_ENV} \\ -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred-130"}]}}}}' || true # 重启并等待 k rollout restart deployment/python-app -n ${params.DEPLOY_ENV} 2>/dev/null || true k rollout status deployment/python-app -n ${params.DEPLOY_ENV} --timeout=300s """ } } if (cluster == 'cluster2' || cluster == 'both') { withCredentials([ file(credentialsId: 'k3s-cluster2-config', variable: 'KUBECONFIG_CLUSTER2'), usernamePassword(credentialsId: REGISTRY_CREDENTIALS_ID, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASS') ]) { sh """ set -eux # 查找kubectl路径 KUBECTL_PATH=\$(command -v kubectl 2>/dev/null || true) if [ -z "\$KUBECTL_PATH" ]; then for p in /usr/local/bin/kubectl /usr/bin/kubectl /bin/kubectl; do if [ -x "\$p" ]; then KUBECTL_PATH="\$p" break fi done fi if [ -z "\$KUBECTL_PATH" ]; then echo "kubectl 未找到,请检查 Jenkins 节点环境" exit 1 fi echo "使用kubectl路径: \$KUBECTL_PATH" # 定义kubectl函数 k() { sudo \$KUBECTL_PATH --kubeconfig=${KUBECONFIG_CLUSTER2} "\$@" } # 检查命名空间 k get namespace ${params.DEPLOY_ENV} || k create namespace ${params.DEPLOY_ENV} # 创建imagePullSecret k create secret docker-registry regcred-130 \\ --docker-server=${REGISTRY_URL} \\ --docker-username=${REGISTRY_USER} \\ --docker-password=${REGISTRY_PASS} \\ --namespace=${params.DEPLOY_ENV} \\ --dry-run=client -o yaml | k apply -f - # 替换镜像并部署 sed "s|image:.*python-app.*|image: ${fullImageWithTag}|g" k8s/deployment.yaml > /tmp/deployment-${params.DEPLOY_ENV}.yaml k apply -f /tmp/deployment-${params.DEPLOY_ENV}.yaml -n ${params.DEPLOY_ENV} # 确保使用imagePullSecret k patch deployment python-app -n ${params.DEPLOY_ENV} \\ -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred-130"}]}}}}' || true # 重启并等待 k rollout restart deployment/python-app -n ${params.DEPLOY_ENV} 2>/dev/null || true k rollout status deployment/python-app -n ${params.DEPLOY_ENV} --timeout=300s """ } } } } } } } post { always { script { sh """ docker logout ${REGISTRY_URL} || true docker rmi ${FULL_IMAGE_NAME}:${env.IMAGE_TAG} || true """ } cleanWs() echo "构建结束。镜像标签: ${env.IMAGE_TAG}" echo "完整镜像: ${FULL_IMAGE_NAME}:${env.IMAGE_TAG}" echo "是否跳过构建: ${env.SKIP_BUILD}" } failure { echo "构建失败,请检查日志" } } }