pipeline { agent any environment { GITLAB_URL = 'https://gitlab.xpaas.lenovo.com' GITLAB_REPO = 'artificial-intelligence-platform/lenovo-ipc' GITLAB_CREDENTIALS_ID = 'git-ipc' 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' K8S_NAMESPACE = 'more-dots' CLUSTER_CONFIG_ID = 'k3s-cluster1-config' CLUSTER_NAME = 'cluster1' } parameters { choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: '选择部署环境') choice(name: 'BUILD_POLICY', choices: ['auto', 'force_build', 'skip_build'], description: '构建策略:auto=按代码变更判断') } 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 { if (params.BUILD_POLICY == 'force_build') { env.SKIP_BUILD = 'false' echo "BUILD_POLICY=force_build,强制构建" } else if (params.BUILD_POLICY == 'skip_build') { env.SKIP_BUILD = 'true' echo "BUILD_POLICY=skip_build,跳过构建" } else { 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 \ --build-arg PIP_OPTIONS=\"--no-hash-check\" \ -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}..." printf '%s' "$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 { script { if (!fileExists('more_dots/k8s/deployment.yaml')) { error "错误: more_dots/k8s/deployment.yaml 文件不存在于代码仓库中" } def fullImageWithTag = "${FULL_IMAGE_NAME}:${env.IMAGE_TAG}" deployToCluster(CLUSTER_CONFIG_ID, fullImageWithTag, params.DEPLOY_ENV) } } } } post { always { script { sh ''' docker logout "${REGISTRY_URL}" || true docker rmi "${FULL_IMAGE_NAME}:${IMAGE_TAG}" || true ''' } cleanWs() echo "构建结束。镜像标签: ${env.IMAGE_TAG}" echo "完整镜像: ${FULL_IMAGE_NAME}:${env.IMAGE_TAG}" echo "是否跳过构建: ${env.SKIP_BUILD}" echo "部署环境: ${params.DEPLOY_ENV}" echo "目标集群: ${CLUSTER_NAME}" } failure { echo "构建失败,请检查日志" } } } def deployToCluster(String clusterConfigId, String fullImageWithTag, String deployEnv) { withCredentials([ file(credentialsId: clusterConfigId, variable: 'KUBECONFIG'), usernamePassword(credentialsId: REGISTRY_CREDENTIALS_ID, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASS') ]) { sh ''' set -eux 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" echo "部署到集群: ${CLUSTER_NAME}" echo "部署环境: ${DEPLOY_ENV}" echo "镜像: ${FULL_IMAGE_NAME}:${IMAGE_TAG}" echo "命名空间: ${K8S_NAMESPACE}" k() { sudo "$KUBECTL_PATH" --kubeconfig="$KUBECONFIG" "$@" } k get namespace "${K8S_NAMESPACE}" || k create namespace "${K8S_NAMESPACE}" k create secret docker-registry regcred-130 \ --docker-server="${REGISTRY_URL}" \ --docker-username="${REGISTRY_USER}" \ --docker-password="${REGISTRY_PASS}" \ --namespace="${K8S_NAMESPACE}" \ --dry-run=client -o yaml | k apply -f - sed -e "s|image:.*more_dots:.*|image: ${FULL_IMAGE_NAME}:${IMAGE_TAG}|g" \ -e "s|value: \".*\"|value: \"${DEPLOY_ENV}\"|g" \ more_dots/k8s/deployment.yaml > /tmp/deployment-${K8S_NAMESPACE}.yaml k apply -f /tmp/deployment-${K8S_NAMESPACE}.yaml -n "${K8S_NAMESPACE}" echo "等待部署完成..." k rollout status deployment/more-dots -n "${K8S_NAMESPACE}" --timeout=300s echo "部署完成,查看pod状态:" k get pods -n "${K8S_NAMESPACE}" -l app=more-dots ''' } }