pipeline { agent any options { disableConcurrentBuilds() } environment { // ══════════════════════════════════════════════════════════════════ // 项目级参数 —— 基于 kra 模板的新项目只需修改本块。 // 这些变量自动 export 到 shell,由 envsubst 白名单渲染进 k8s 清单。 // ══════════════════════════════════════════════════════════════════ APP_NAME = 'kra' // 派生:服务名/Secret名/库名/hostPath/镜像后缀 IMAGE_REPO = 'library/kra' // Harbor 中镜像仓库路径 REGISTRY = '192.168.200.132' BIN_NAME = 'kratos-admin' // Makefile build 的产物名 KRA_REPLICAS = '3' KRA_FRONTEND_NODEPORT = '30080' KRA_BACKEND_NODEPORT = '30081' KRA_MYSQL_IMAGE = 'docker.m.daocloud.io/library/mysql:8.4' KRA_REDIS_IMAGE = 'docker.m.daocloud.io/library/redis:7-alpine' GO_IMAGE = 'docker.m.daocloud.io/golang:1.25' NODE_IMAGE = 'docker.m.daocloud.io/node:20-slim' PNPM_VERSION = '10.15.1' CACHE_DIR = '/var/lib/kra-ci-cache' // Jenkins 宿主机依赖缓存根目录 // 集群映射(dev -> 测试集群, main -> 生产集群) DEV_CLUSTER_HOST = '192.168.200.128' PROD_CLUSTER_HOST = '192.168.200.129' // Jenkins 凭据 ID(新项目若沿用同一套凭据可不改) CRED_HARBOR_PUSH = 'harbor-library-push' CRED_KUBECONFIG_DEV = 'kra-rke2-128-kubeconfig' CRED_KUBECONFIG_PROD = 'kra-rke2-129-kubeconfig' CRED_MYSQL_PASSWORD = 'kra-mysql-root-password' CRED_JWT_SIGNING_KEY = 'kra-jwt-signing-key' // k8s 清单渲染白名单:只替换列出的变量,避免误伤清单内 shell 变量(如 probe 里的 $MYSQL_ROOT_PASSWORD) K8S_RENDER_VARS = '$KRA_APP $KRA_NAMESPACE $KRA_ENV $KRA_BACKEND_IMAGE $KRA_FRONTEND_IMAGE $KRA_REPLICAS $KRA_FRONTEND_NODEPORT $KRA_BACKEND_NODEPORT $KRA_MYSQL_IMAGE $KRA_REDIS_IMAGE $BIN_NAME' } stages { stage('Select environment') { steps { script { // 分支来源优先级: // 1) env.gitlabBranch —— GitLab Jenkins 集成/webhook 触发时由 gitlab-plugin 注入(push payload 的 ref) // 2) env.BRANCH_NAME —— 多分支流水线场景 // 3) JOB_NAME —— 手动触发兜底:名字以 -dev 结尾按 dev 部署,否则默认 main def branch = env.gitlabBranch ?: env.BRANCH_NAME if (!branch) { branch = env.JOB_NAME.endsWith('-dev') ? 'dev' : 'main' } echo "gitlabBranch='${env.gitlabBranch ?: ''}' BRANCH_NAME='${env.BRANCH_NAME ?: ''}' -> resolved branch='${branch}'" env.KRA_BRANCH = branch if (branch == 'dev') { env.KRA_NAMESPACE = "${env.APP_NAME}-dev" env.KRA_ENV = 'development' env.KRA_CLUSTER_HOST = env.DEV_CLUSTER_HOST env.KUBECONFIG_CREDENTIALS = env.CRED_KUBECONFIG_DEV } else if (branch == 'main') { env.KRA_NAMESPACE = "${env.APP_NAME}-prod" env.KRA_ENV = 'production' env.KRA_CLUSTER_HOST = env.PROD_CLUSTER_HOST env.KUBECONFIG_CREDENTIALS = env.CRED_KUBECONFIG_PROD } else { error("Unsupported branch: ${branch}. Only dev and main deploy.") } } } } stage('Checkout branch') { steps { // job 的 SCM branch spec 固定(如 */main),集成触发时按实际推送分支重新检出, // 保证单 job 同时服务 dev/main 两个环境。 checkout([$class: 'GitSCM', branches: [[name: "*/${env.KRA_BRANCH}"]], userRemoteConfigs: [[credentialsId: 'gitlab-root-http', url: 'http://192.168.200.130/root/kra.git']]]) } } stage('Test') { steps { sh ''' set -eu HOST_WORKSPACE=$(printf '%s' "$WORKSPACE" | sed 's#/var/jenkins_home#/opt/jenkins#') docker run --rm -e GOPROXY=https://goproxy.cn,direct \\ -v "$HOST_WORKSPACE:/src" \\ -v "$CACHE_DIR/go-mod:/go/pkg/mod" \\ -v "$CACHE_DIR/go-build:/root/.cache/go-build" \\ -w /src "$GO_IMAGE" sh -c "git config --global --add safe.directory /src && go test ./... && make build" ''' sh ''' set -eu HOST_WORKSPACE=$(printf '%s' "$WORKSPACE" | sed 's#/var/jenkins_home#/opt/jenkins#') docker run --rm \\ -e COREPACK_NPM_REGISTRY=https://registry.npmmirror.com \\ -v "$HOST_WORKSPACE/web:/app" \\ -v "$CACHE_DIR/pnpm-store:/root/.local/share/pnpm/store" \\ -v "$CACHE_DIR/corepack:/root/.cache/node/corepack" \\ -w /app "$NODE_IMAGE" bash -lc "corepack enable && corepack prepare pnpm@$PNPM_VERSION --activate && pnpm install --frozen-lockfile && pnpm build" # 前端 Nginx 配置模板渲染(proxy_pass 指向 ${APP_NAME}-backend 服务)。 # KRA_APP 必须在此显式导出:envsubst 会把未设置的变量替换为空串。 export KRA_APP="$APP_NAME" envsubst '$KRA_APP' < web/.docker-compose/nginx/conf.d/my.conf > web/nginx.k8s.conf ''' } } stage('Build and push images') { steps { script { env.IMAGE_TAG = "${env.KRA_BRANCH}-${env.GIT_COMMIT.take(12)}" } withCredentials([usernamePassword(credentialsId: env.CRED_HARBOR_PUSH, usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASSWORD')]) { sh ''' set -eu echo "$HARBOR_PASSWORD" | docker login "$REGISTRY" --username "$HARBOR_USER" --password-stdin docker build --build-arg BIN_NAME="$BIN_NAME" -t "$REGISTRY/$IMAGE_REPO-backend:$IMAGE_TAG" . docker build -t "$REGISTRY/$IMAGE_REPO-frontend:$IMAGE_TAG" web docker push "$REGISTRY/$IMAGE_REPO-backend:$IMAGE_TAG" docker push "$REGISTRY/$IMAGE_REPO-frontend:$IMAGE_TAG" docker logout "$REGISTRY" ''' } } } stage('Deploy to Rancher cluster') { steps { withCredentials([file(credentialsId: "${env.KUBECONFIG_CREDENTIALS}", variable: 'KUBECONFIG_FILE'), string(credentialsId: env.CRED_MYSQL_PASSWORD, variable: 'KRA_MYSQL_ROOT_PASSWORD'), string(credentialsId: env.CRED_JWT_SIGNING_KEY, variable: 'KRA_JWT_SIGNING_KEY'), usernamePassword(credentialsId: env.CRED_HARBOR_PUSH, usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASSWORD')]) { sh ''' set -eu export KUBECONFIG="$KUBECONFIG_FILE" kubectl create namespace "$KRA_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - kubectl -n "$KRA_NAMESPACE" create secret docker-registry harbor-registry \\ --docker-server="$REGISTRY" --docker-username="$HARBOR_USER" --docker-password="$HARBOR_PASSWORD" \\ --dry-run=client -o yaml | kubectl apply -f - kubectl -n "$KRA_NAMESPACE" create secret generic "${APP_NAME}-runtime" \\ --from-literal=mysql-root-password="$KRA_MYSQL_ROOT_PASSWORD" \\ --from-literal=database-source="root:${KRA_MYSQL_ROOT_PASSWORD}@tcp(${APP_NAME}-mysql:3306)/${APP_NAME}?charset=utf8mb4&parseTime=True&loc=Local" \\ --from-literal=jwt-signing-key="$KRA_JWT_SIGNING_KEY" \\ --from-literal=redis-addr="${APP_NAME}-redis:6379" \\ --dry-run=client -o yaml | kubectl apply -f - export KRA_APP="$APP_NAME" export KRA_BACKEND_IMAGE="$REGISTRY/$IMAGE_REPO-backend:$IMAGE_TAG" export KRA_FRONTEND_IMAGE="$REGISTRY/$IMAGE_REPO-frontend:$IMAGE_TAG" envsubst "$K8S_RENDER_VARS" < deploy/k8s/kra.yaml | kubectl apply -f - kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-backend --timeout=10m kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-frontend --timeout=10m ''' } } } } }