فهرست منبع

流水线优化

dujian 2 هفته پیش
والد
کامیت
fca05f121d

+ 58 - 129
docs/jenkins/produ/README.md

@@ -1,158 +1,87 @@
-# 预生产分�?�?Harbor �?ACK 生产 K8S 流水�?
+# 预生产 Harbor 镜像 → ACK(仅晋升,不构建)
 
-Harbor 宿主机:**39.106.135.88**(与 Jenkins 同机或同 VPC)。ACK 集群�?*alien-ack-cluster**(华�? 北京)�?
+Harbor:**39.105.153.68**(`alien_cloud`)。Jenkins:**39.106.135.88:30003**。ACK:**alien-ack-cluster**。
 
-本目录支�?**单服务发�?* �?**整体/多选发�?*(Harbor + ACK);与宿主机 `docker compose` 晋升流水线(`Jenkinsfile-prod-promote-from-uat.groovy`)并行存在,互不替代,直�?ACK 全量切流�?
-
----
-
-## 常见问题(Harbor / 目录结构 / 发版粒度�?
-
-### Harbor 要不要在 Web 界面手工操作�?
-
-**日常发版不需要�?* 流水线在 `k8s-produ-lib.groovy` 里自动完成:
-
-1. `docker login 39.106.135.88`(凭�?`harbor-robot-alien`�?
-2. `docker build` �?�?tag `39.106.135.88/alien_cloud/<服务�?:<tag>`
-3. `docker push` �?Harbor
-4. `kubectl set image` �?ACK 拉同一 tag
-
-运维**仅需一次�?*�?Harbor Web(或 API)完成:`alien` 项目、机器人账号、(可选)镜像保留策略。之后查镜像/删旧 tag/排障时仍可打开 Harbor UI�?*不是发版必经步骤**�?
-
-### `_shared/k8s-produ-lib.groovy` 是什么?能单独部署吗�?
-
-| 文件 | 角色 | Jenkins Script Path 是否指向�?|
-|------|------|-------------------------------|
-| `produ/<服务>/Jenkinsfile` | **入口**(Jenkins 只认这个名字�?| **�?*,每�?Job 指向各自路径 |
-| `_shared/k8s-produ-lib.groovy` | **共享函数�?*(checkout、mvn、Harbor、kubectl�?| **�?*,由入口 `load '.../k8s-produ-lib.groovy'` 引用 |
-| `_shared/service-registry.groovy` | 七个服务列表 + whole/multi 筛�?| 否,�?`whole/Jenkinsfile` 使用 |
-
-每个微服�?Job 仍是 **单独一条流水线**(例如只构建 gateway);共享库是为避�?7 份重复脚本,**不代�?* `Jenkinsfile`�?
-
-### 既要单独发布,又要整体发布?
-
-| Jenkins Job(建议名�?| Script Path | 粒度 |
-|----------------------|-------------|------|
-| `gateway-k8s` �?`dining-k8s` | `docs/jenkins/produ/<prodDir>/Jenkinsfile` | **仅该服务**(Maven `-pl alien-xxx`�?|
-| `alien-cloud-k8s-whole` | `docs/jenkins/produ/whole/Jenkinsfile` | **whole / single / multi**(参�?`DEPLOY_MODE`,语义同 prod-promote�?|
-
-整体 Job 一�?`mvn package` 打全仓库,再按选中服务依次 build/push/deploy;单服务 Job 只打对应模块,适合热修复单个服务�?
-
----
-
-## 一、Jenkins 任务如何组织(对应你截图中的「新建任务」)
-
-### 1. 是否选「文件夹」?
-
-**是,推荐选「文件夹」�?*
-
-| 类型 | 用�?|
-|------|------|
-| **文件�?* | 独立命名空间,其下可建多�?**流水�?* 任务,例�?`gateway-k8s`、`store-k8s`,名称可与其他视图重�?|
-| **流水�?* | 真正执行构建�?Job,每个微服务一�?|
-| **多分支流水线** | �?Git 分支自动建子 Job;若预生产固定一条分支(�?`uat-20260202`),用普通流水线 + 参数 `GIT_BRANCH` 更简�?|
-
-建议结构�?
+与 `__alone/Jenkinsfile-prod-promote-from-uat.groovy`(拷 jar + compose)并列;本目录只做:
 
 ```text
-alien-cloud-produ-K8S-from-uat/     �?文件夹(你截图中的名称)
-├── alien-cloud-k8s-whole/          �?流水线,Script Path: docs/jenkins/produ/whole/Jenkinsfile(整�?多选)
-├── gateway-k8s/                    �?流水线,Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-├── store-k8s/
-├── second-k8s/
-├── store-platform-k8s/
-├── lawyer-k8s/
-├── job-k8s/
-└── dining-k8s/
+docker pull  39.105.153.68/alien_cloud/<服务>:<SOURCE_TAG>
+docker tag   → :<TARGET_TAG>
+docker push
+kubectl set image(可选)
 ```
 
-**不要**在文件夹里再选「文件夹」类型去套微服务——微服务一级用 **流水�?* 即可�?
-
-### 2. 是否�?Pipeline script from SCM�?
-
-**可以,且推荐�?*
-
-每个 `*-k8s` 流水�?Job 配置�?
-
-1. **流水�?* �?**定义**:Pipeline script from SCM  
-2. **SCM**:Git,仓�?`http://8.152.195.41:3000/alien/alien_cloud`(与现网一致)  
-3. **分支**:`*/uat-20260202` �?`*/你的预生产分支`  
-4. **Script Path**(按服务不同):
-   - gateway:`docs/jenkins/produ/gateway/Jenkinsfile`
-   - store:`docs/jenkins/produ/store/Jenkinsfile`
-   - �?见下�? 
-
-5. **凭据**:`zhanghaomimapingzheng`(与 UAT 流水线一致)  
-6. **构建触发**:可选手动;稳定后加「上�?UAT 构建成功」触�? 
-
-�?`Jenkinsfile-prod-promote-from-uat.groovy` 的关系:
-
-| 维度 | prod-promote-from-uat | produ K8S 流水�?|
-|------|----------------------|------------------|
-| 制品 | UAT 目录 jar �?生产�?compose | Maven jar �?镜像 �?Harbor �?ACK |
-| 发版粒度 | �?Job �?whole/single/multi | **whole Job** + **每服务一�?Job** |
-| 灰度 | �?| 支持 Ingress 权重灰度(见 README-ACK-GRAY-RELEASE.md�?|
+**不** Checkout 业务代码、**不** Maven、**不** docker build。
 
 ---
 
-## 二、微服务�?Script Path 对照
-
-| prodDir | 模块 | Jenkins Script Path | ACK Deployment(默认) |
-|---------|------|---------------------|-------------------------|
-| gateway | alien-gateway | `docs/jenkins/produ/gateway/Jenkinsfile` | `gateway` |
-| store | alien-store | `docs/jenkins/produ/store/Jenkinsfile` | `store` |
-| second | alien-second | `docs/jenkins/produ/second/Jenkinsfile` | `second` |
-| store-platform | alien-store-platform | `docs/jenkins/produ/store-platform/Jenkinsfile` | `store-platform` |
-| lawyer | alien-lawyer | `docs/jenkins/produ/lawyer/Jenkinsfile` | `lawyer` |
-| job | alien-job | `docs/jenkins/produ/job/Jenkinsfile` | `job` |
-| dining | alien-dining | `docs/jenkins/produ/dining/Jenkinsfile` | `dining` |
-| **(整体)** | 全部七个 | `docs/jenkins/produ/whole/Jenkinsfile` | �?`DEPLOY_MODE` |
+## Jenkins Job 与 Script Path
 
-**未纳入本目录**(属其他仓库或镜像名不同,需另建 Job):
+| 建议 Job 名 | Script Path | 粒度 |
+|-------------|-------------|------|
+| `gateway-k8s` | `docs/jenkins/produ/gateway/Jenkinsfile` | 单服务 |
+| `store-k8s` | `docs/jenkins/produ/store/Jenkinsfile` | 单服务 |
+| … | `produ/<prodDir>/Jenkinsfile` | … |
+| `alien-cloud-k8s-whole` | `docs/jenkins/produ/whole/Jenkinsfile` | whole/single/multi(界面见 [README-JOB-WHOLE.md](./README-JOB-WHOLE.md)) |
+| `alien-cloud-k8s-promote-image` | `docs/jenkins/produ/promote-image/Jenkinsfile` | 同 whole |
 
-- Python:`py_gateway_produ`、`py_contract_produ`、`py_esign_produ`(镜�?`alien_gateway:local` 等)  
-- `xxl-job-admin-produ`、Harbor 自身组件  
+### Pipeline from SCM 配置
 
----
+| 项 | 值 |
+|----|-----|
+| 定义 | Pipeline script from SCM |
+| 仓库 | `http://8.152.195.41:3000/alien/alien_cloud` |
+| 分支 | 预生产分支,如 `*/uat-20260202` |
+| **SCM Credentials** | **Git**(`zhanghaomimapingzheng` 或 `dujian/****** (git)`) |
 
-## 三、Jenkins 侧必配项(首次)
+### SCM Credentials ≠ Harbor 机器人
 
-�?Jenkins **全局**或文件夹 **凭据** 中准备:
+| 位置 | 选什么 | 不要选 |
+|------|--------|--------|
+| **Repository URL 旁 Credentials** | Git 账号 | `robot$alien_cloud+jenkins-k8s` |
+| Jenkins 凭据库 **`harbor-robot-alien`** | 用户名 `robot$alien_cloud+jenkins-k8s`,密码=Token | — |
+| Jenkins 凭据库 **`ack-kubeconfig-alien`** | ACK kubeconfig 文件 | — |
 
-| 凭据 ID(示例,按实际改 Jenkinsfile 内常量) | 类型 | 用�?|
-|---------------------------------------------|------|------|
-| `zhanghaomimapingzheng` | Username/password | Git 拉取 |
-| `harbor-robot-alien` | Username/password | `docker login 39.106.135.88` |
-| `ack-kubeconfig-alien` | Secret file | `KUBECONFIG`,内容来�?ACK「连接信息」下�?|
+Harbor 机器人只用于流水线里的 `docker login`,不是拉 Git 的 Jenkinsfile。
 
-Jenkins 节点需具备�?
+---
 
-- `mvn`、`docker`(能访问 Harbor 443/80)、`kubectl`  
-- �?Jenkins 在容器内:挂�?`docker.sock`,并�?`kubectl` �?kubeconfig 挂入或装在镜像内  
+## 参数说明(单服务 Job)
 
-Harbor 上建议建项目 **`alien_cloud`**,镜像名:`39.106.135.88/alien_cloud/<prodDir>:<tag>`�? 
-**新建项目与机器人账号步骤**�?**[README-HARBOR-SETUP.md](./README-HARBOR-SETUP.md)**�?
+| 参数 | 说明 |
+|------|------|
+| `SOURCE_TAG` | 预生产已在 Harbor 的 tag,如 `uat-build-42` |
+| `TARGET_TAG` | 生产 tag,默认 `produ-${BUILD_NUMBER}` |
+| `DEPLOY_STRATEGY` | `rolling` / `canary` / **`skip`**(只推镜像) |
+| `HARBOR_REGISTRY` | `39.105.153.68` |
 
-基础镜像 `my-openjdk8-ffmpeg:v1` 需 **�?push �?Harbor**(如 `39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1`),并在 Job 参数 `BASE_IMAGE` 中填写,否则 ACK 节点拉不到�?
+整体 Job 另增 `DEPLOY_MODE`:`whole` / `single` / `multi`(同 prod-promote)。
 
 ---
 
-## 四、ACK 与灰�?
+## 预生产镜像从哪来?
 
-详述见同目录 **[README-ACK-GRAY-RELEASE.md](./README-ACK-GRAY-RELEASE.md)**(集群接入、命名空间、Ingress 灰度、节�?Unknown 排查)�?
+88 上 UAT 构建后需有一次把镜像推到 Harbor,例如:
 
-K8S 清单示例:`k8s/examples/`�?
+`39.105.153.68/alien_cloud/gateway:uat-build-42`
 
----
+之后本目录流水线只做 **tag 晋升**。
 
-## 五、流水线参数速览
+详见 [README-HARBOR-SETUP.md](./README-HARBOR-SETUP.md)、[README-PROMOTE-IMAGE.md](./README-PROMOTE-IMAGE.md)、[README-ACK-GRAY-RELEASE.md](./README-ACK-GRAY-RELEASE.md)。
 
-各服�?Jenkinsfile 共用�?`docs/jenkins/produ/_shared/k8s-produ-lib.groovy`,主要参数:
+---
 
-- `GIT_BRANCH`:预生产分支  
-- `IMAGE_TAG`:默�?`build-${BUILD_NUMBER}`,可�?`uat-20260202-123`  
-- `DEPLOY_STRATEGY`:`rolling`(直接替换)�?`canary`(Ingress 权重�? 
-- `CANARY_WEIGHT`:灰度流量百分比(仅 canary�? 
-- `K8S_NAMESPACE`:默�?`alien-produ`  
+## 目录结构
 
-首次部署前在 ACK 应用 `k8s/examples/namespace.yaml` 与各服务 `deployment-*.yaml`(或让流水线 `kubectl apply` 首次全量 apply)�?
+```text
+docs/jenkins/produ/
+├── __alone/                    # 参考:jar 晋升 compose(勿删)
+├── _shared/
+│   ├── k8s-produ-lib.groovy
+│   └── service-registry.groovy
+├── gateway/Jenkinsfile           # 单服务
+├── store/ … dining/
+├── whole/Jenkinsfile             # 整体/多选
+├── promote-image/Jenkinsfile     # 同 whole
+└── k8s/examples/                 # ACK 清单示例
+```

+ 81 - 64
docs/jenkins/produ/_shared/k8s-produ-lib.groovy

@@ -1,75 +1,55 @@
 /**
- * ACK + Harbor �版共享函数库(�是 Jenkins 入�文件)�
- *
- * - ��务:produ/<prodDir>/Jenkinsfile 在顶�pipeline �load 本文件�调用�stage 函数�
- * - 整体/多选:produ/whole/Jenkinsfile load 本文�+ service-registry.groovy�
- *
- * Harbor:�水线�docker login / build / push,日常�版无需打开 Harbor Web�
+ * Harbor ???? + ACK ??????? Jenkins ????
+ * ????produ/<prodDir>/Jenkinsfile
+ * ??/???produ/whole/Jenkinsfile ? produ/promote-image/Jenkinsfile
  */
 
-def checkoutBranch(def script, String branch, String gitUrl, String credentialsId) {
-    script.git branch: branch, credentialsId: credentialsId, url: gitUrl
-    script.sh """
-        set -e
-        git fetch origin
-        git reset --hard origin/${branch}
-        git log -1 --oneline
-    """
+/** ???? tag */
+def resolveTargetTag(def script, String targetTagParam) {
+    def t = (targetTagParam ?: '').trim()
+    return t ?: "produ-${script.env.BUILD_NUMBER}"
 }
 
-def mavenPackageModule(def script, String module) {
-    script.sh """
-        set -e
-        unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY all_proxy || true
-        export MAVEN_OPTS="-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true"
-        mvn -version
-        mvn clean package -pl ${module} -am -DskipTests -e \\
-            -Dmaven.repo.local=\${WORKSPACE}/.m2/repository
-    """
-}
-
-/** 整体�版:根 POM 一次打包全部模�(�7 次�模� mvn 更快�*/
-def mavenPackageAll(def script) {
-    script.sh """
-        set -e
-        unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY all_proxy || true
-        export MAVEN_OPTS="-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true"
-        mvn -version
-        mvn clean package -DskipTests -e \\
-            -Dmaven.repo.local=\${WORKSPACE}/.m2/repository
-    """
-}
+/**
+ * ?????????? Harbor ???pull ? tag ? tag ?? tag ? push?
+ * cfg: harborRegistry, harborProject, sourceTag, targetTag, harborCredentialsId, dryRun
+ * services: List<Map> ? prodDir
+ */
+def promoteHarborImages(def script, List services, Map cfg) {
+    def regHost = cfg.harborRegistry.trim()
+    def proj = cfg.harborProject.trim()
+    def srcTag = (cfg.sourceTag ?: 'uat-latest').trim()
+    def tgtTag = cfg.targetTag
+    def dryRun = cfg.dryRun == true
 
-def dockerBuildAndPush(def script, Map cfg) {
-    def registry = cfg.harborRegistry
-    def project = cfg.harborProject
-    def prodDir = cfg.prodDir
-    def module = cfg.module
-    def serverPort = cfg.serverPort
-    def withLib = cfg.withLib ? 'true' : 'false'
-    def baseImage = cfg.baseImage
-    def imageRef = cfg.imageRef
-    def harborCreds = cfg.harborCredentialsId
-    def dockerfile = 'docs/jenkins/produ/docker/Dockerfile.java-service'
-    def moduleDir = "${script.env.WORKSPACE}/${module}"
-    def jarName = "${module}-1.0.0.jar"
+    services.each { s ->
+        def src = "${regHost}/${proj}/${s.prodDir}:${srcTag}"
+        def tgt = "${regHost}/${proj}/${s.prodDir}:${tgtTag}"
+        echo ">>> ???? ${s.prodDir}: ${src} ? ${tgt}"
+        if (dryRun) {
+            return
+        }
+    }
+    if (dryRun) {
+        return
+    }
 
     script.withCredentials([script.usernamePassword(
-        credentialsId: harborCreds, usernameVariable: 'HARBOR_USER', passwordVariable: 'HARBOR_PASS')]) {
-        script.sh """
-            set -e
-            mkdir -p ${moduleDir}/lib
-            touch ${moduleDir}/lib/.keep
-            echo "\${HARBOR_PASS}" | docker login ${registry} -u "\${HARBOR_USER}" --password-stdin
-            docker build -f ${dockerfile} \\
-                --build-arg BASE_IMAGE=${baseImage} \\
-                --build-arg JAR_FILE=${jarName} \\
-                --build-arg SERVER_PORT=${serverPort} \\
-                --build-arg WITH_LIB=${withLib} \\
-                -t ${imageRef} \\
-                ${moduleDir}
-            docker push ${imageRef}
-        """
+        credentialsId: cfg.harborCredentialsId,
+        usernameVariable: 'HARBOR_USER',
+        passwordVariable: 'HARBOR_PASS',
+    )]) {
+        script.sh "echo \${HARBOR_PASS} | docker login ${regHost} -u \${HARBOR_USER} --password-stdin"
+        services.each { s ->
+            def src = "${regHost}/${proj}/${s.prodDir}:${srcTag}"
+            def tgt = "${regHost}/${proj}/${s.prodDir}:${tgtTag}"
+            script.sh """
+                set -e
+                docker pull ${src}
+                docker tag ${src} ${tgt}
+                docker push ${tgt}
+            """
+        }
     }
 }
 
@@ -101,4 +81,41 @@ def deployToAck(def script, Map cfg) {
     }
 }
 
+/** ?????? + ?? ACK */
+def promoteOneServiceToAck(def script, Map svc, Map params, Map env) {
+    def regHost = params.HARBOR_REGISTRY.trim()
+    def proj = params.HARBOR_PROJECT.trim()
+    def srcTag = (params.SOURCE_TAG ?: 'uat-latest').trim()
+    def tgtTag = resolveTargetTag(script, params.TARGET_TAG)
+    def dryRun = params.DRY_RUN == true
+    def strategy = params.DEPLOY_STRATEGY ?: 'rolling'
+
+    env.TARGET_TAG_RESOLVED = tgtTag
+    env.IMAGE_REF = "${regHost}/${proj}/${svc.prodDir}:${tgtTag}"
+
+    promoteHarborImages(script, [svc], [
+        harborRegistry: regHost,
+        harborProject: proj,
+        sourceTag: srcTag,
+        targetTag: tgtTag,
+        harborCredentialsId: env.HARBOR_CREDENTIALS,
+        dryRun: dryRun,
+    ])
+
+    if (dryRun || strategy == 'skip') {
+        return
+    }
+
+    deployToAck(script, [
+        k8sNamespace: params.K8S_NAMESPACE,
+        imageRef: env.IMAGE_REF,
+        deployStrategy: strategy == 'canary' ? 'canary' : 'rolling',
+        deploymentStable: svc.deployName,
+        deploymentCanary: "${svc.deployName}-canary",
+        ingressCanary: "${svc.deployName}-canary",
+        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
+        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
+    ])
+}
+
 return this

+ 21 - 84
docs/jenkins/produ/dining/Jenkinsfile

@@ -1,95 +1,32 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-
-def SVC = [
-    prodDir: 'dining',
-    module: 'alien-dining',
-    serverPort: '50019',
-    withLib: false,
-    deployName: 'dining',
-]
-
+def SVC = [prodDir: 'dining', deployName: 'dining']
+def sparseCheckoutProduShared() {
+    checkout scm: [$class: 'GitSCM', branches: scm.branches,
+        extensions: [[$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'docs/jenkins/produ/_shared/']]]],
+        userRemoteConfigs: scm.userRemoteConfigs]
+}
 pipeline {
     agent any
-    options {
-        buildDiscarder(logRotator(numToKeepStr: '15'))
-        timestamps()
-        timeout(time: 90, unit: 'MINUTES')
-    }
+    options { buildDiscarder(logRotator(numToKeepStr: '15')); timestamps(); timeout(time: 45, unit: 'MINUTES') }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true)
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true)
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'])
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
-    environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
-        HARBOR_CREDENTIALS = 'harbor-robot-alien'
-        KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
-    }
+    environment { HARBOR_CREDENTIALS = 'harbor-robot-alien'; KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien' }
     stages {
-        stage('Checkout') {
-            steps {
-                script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                }
-            }
+        stage('Promote image & Deploy ACK') {
+            steps { script {
+                sparseCheckoutProduShared()
+                def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                k8s.promoteOneServiceToAck(this, SVC, params, env)
+                echo ">>> dining: ${env.IMAGE_REF}"
+            } }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }
-

+ 35 - 69
docs/jenkins/produ/gateway/Jenkinsfile

@@ -1,94 +1,60 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+/**
+ * gateway:仅晋升 Harbor 镜像 + 可选 ACK。
+ * Job:Gateway-K8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
+ *
+ * 与 Job「轻量级检出」配合:不拉 alien-* 业务源码,仅稀疏拉取 _shared 后 load。
+ */
+def SVC = [prodDir: 'gateway', deployName: 'gateway']
 
-def SVC = [
-    prodDir: 'gateway',
-    module: 'alien-gateway',
-    serverPort: '8000',
-    withLib: false,
-    deployName: 'gateway',
-]
+/** 稀疏检出流水线共享库(勿拉全仓业务代码) */
+def sparseCheckoutProduShared() {
+    checkout scm: [
+        $class: 'GitSCM',
+        branches: scm.branches,
+        extensions: [
+            [$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [
+                [path: 'docs/jenkins/produ/_shared/'],
+            ]],
+        ],
+        userRemoteConfigs: scm.userRemoteConfigs,
+    ]
+}
 
 pipeline {
     agent any
     options {
         buildDiscarder(logRotator(numToKeepStr: '15'))
         timestamps()
-        timeout(time: 90, unit: 'MINUTES')
+        timeout(time: 45, unit: 'MINUTES')
     }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true,
+            description: '预生产 Harbor tag,如 uat-build-42')
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true,
+            description: '留空则 produ-${BUILD_NUMBER}')
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'],
+            description: 'skip=只推 Harbor,不更新 ACK')
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
     environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
         HARBOR_CREDENTIALS = 'harbor-robot-alien'
         KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
     }
     stages {
-        stage('Checkout') {
+        stage('Promote image & Deploy ACK') {
             steps {
                 script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                    sparseCheckoutProduShared()
+                    def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                    k8s.promoteOneServiceToAck(this, SVC, params, env)
+                    echo ">>> gateway 完成: ${env.IMAGE_REF}"
                 }
             }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }

+ 21 - 84
docs/jenkins/produ/job/Jenkinsfile

@@ -1,95 +1,32 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-
-def SVC = [
-    prodDir: 'job',
-    module: 'alien-job',
-    serverPort: '50108',
-    withLib: false,
-    deployName: 'job',
-]
-
+def SVC = [prodDir: 'job', deployName: 'job']
+def sparseCheckoutProduShared() {
+    checkout scm: [$class: 'GitSCM', branches: scm.branches,
+        extensions: [[$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'docs/jenkins/produ/_shared/']]]],
+        userRemoteConfigs: scm.userRemoteConfigs]
+}
 pipeline {
     agent any
-    options {
-        buildDiscarder(logRotator(numToKeepStr: '15'))
-        timestamps()
-        timeout(time: 90, unit: 'MINUTES')
-    }
+    options { buildDiscarder(logRotator(numToKeepStr: '15')); timestamps(); timeout(time: 45, unit: 'MINUTES') }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true)
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true)
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'])
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
-    environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
-        HARBOR_CREDENTIALS = 'harbor-robot-alien'
-        KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
-    }
+    environment { HARBOR_CREDENTIALS = 'harbor-robot-alien'; KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien' }
     stages {
-        stage('Checkout') {
-            steps {
-                script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                }
-            }
+        stage('Promote image & Deploy ACK') {
+            steps { script {
+                sparseCheckoutProduShared()
+                def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                k8s.promoteOneServiceToAck(this, SVC, params, env)
+                echo ">>> job: ${env.IMAGE_REF}"
+            } }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }
-

+ 21 - 84
docs/jenkins/produ/lawyer/Jenkinsfile

@@ -1,95 +1,32 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-
-def SVC = [
-    prodDir: 'lawyer',
-    module: 'alien-lawyer',
-    serverPort: '50017',
-    withLib: true,
-    deployName: 'lawyer',
-]
-
+def SVC = [prodDir: 'lawyer', deployName: 'lawyer']
+def sparseCheckoutProduShared() {
+    checkout scm: [$class: 'GitSCM', branches: scm.branches,
+        extensions: [[$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'docs/jenkins/produ/_shared/']]]],
+        userRemoteConfigs: scm.userRemoteConfigs]
+}
 pipeline {
     agent any
-    options {
-        buildDiscarder(logRotator(numToKeepStr: '15'))
-        timestamps()
-        timeout(time: 90, unit: 'MINUTES')
-    }
+    options { buildDiscarder(logRotator(numToKeepStr: '15')); timestamps(); timeout(time: 45, unit: 'MINUTES') }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true)
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true)
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'])
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
-    environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
-        HARBOR_CREDENTIALS = 'harbor-robot-alien'
-        KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
-    }
+    environment { HARBOR_CREDENTIALS = 'harbor-robot-alien'; KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien' }
     stages {
-        stage('Checkout') {
-            steps {
-                script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                }
-            }
+        stage('Promote image & Deploy ACK') {
+            steps { script {
+                sparseCheckoutProduShared()
+                def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                k8s.promoteOneServiceToAck(this, SVC, params, env)
+                echo ">>> lawyer: ${env.IMAGE_REF}"
+            } }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }
-

+ 21 - 84
docs/jenkins/produ/second/Jenkinsfile

@@ -1,95 +1,32 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-
-def SVC = [
-    prodDir: 'second',
-    module: 'alien-second',
-    serverPort: '50015',
-    withLib: false,
-    deployName: 'second',
-]
-
+def SVC = [prodDir: 'second', deployName: 'second']
+def sparseCheckoutProduShared() {
+    checkout scm: [$class: 'GitSCM', branches: scm.branches,
+        extensions: [[$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'docs/jenkins/produ/_shared/']]]],
+        userRemoteConfigs: scm.userRemoteConfigs]
+}
 pipeline {
     agent any
-    options {
-        buildDiscarder(logRotator(numToKeepStr: '15'))
-        timestamps()
-        timeout(time: 90, unit: 'MINUTES')
-    }
+    options { buildDiscarder(logRotator(numToKeepStr: '15')); timestamps(); timeout(time: 45, unit: 'MINUTES') }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true)
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true)
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'])
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
-    environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
-        HARBOR_CREDENTIALS = 'harbor-robot-alien'
-        KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
-    }
+    environment { HARBOR_CREDENTIALS = 'harbor-robot-alien'; KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien' }
     stages {
-        stage('Checkout') {
-            steps {
-                script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                }
-            }
+        stage('Promote image & Deploy ACK') {
+            steps { script {
+                sparseCheckoutProduShared()
+                def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                k8s.promoteOneServiceToAck(this, SVC, params, env)
+                echo ">>> second: ${env.IMAGE_REF}"
+            } }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }
-

+ 21 - 84
docs/jenkins/produ/store-platform/Jenkinsfile

@@ -1,95 +1,32 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-
-def SVC = [
-    prodDir: 'store-platform',
-    module: 'alien-store-platform',
-    serverPort: '50016',
-    withLib: false,
-    deployName: 'store-platform',
-]
-
+def SVC = [prodDir: 'store-platform', deployName: 'store-platform']
+def sparseCheckoutProduShared() {
+    checkout scm: [$class: 'GitSCM', branches: scm.branches,
+        extensions: [[$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'docs/jenkins/produ/_shared/']]]],
+        userRemoteConfigs: scm.userRemoteConfigs]
+}
 pipeline {
     agent any
-    options {
-        buildDiscarder(logRotator(numToKeepStr: '15'))
-        timestamps()
-        timeout(time: 90, unit: 'MINUTES')
-    }
+    options { buildDiscarder(logRotator(numToKeepStr: '15')); timestamps(); timeout(time: 45, unit: 'MINUTES') }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true)
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true)
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'])
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
-    environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
-        HARBOR_CREDENTIALS = 'harbor-robot-alien'
-        KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
-    }
+    environment { HARBOR_CREDENTIALS = 'harbor-robot-alien'; KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien' }
     stages {
-        stage('Checkout') {
-            steps {
-                script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                }
-            }
+        stage('Promote image & Deploy ACK') {
+            steps { script {
+                sparseCheckoutProduShared()
+                def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                k8s.promoteOneServiceToAck(this, SVC, params, env)
+                echo ">>> store-platform: ${env.IMAGE_REF}"
+            } }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }
-

+ 21 - 84
docs/jenkins/produ/store/Jenkinsfile

@@ -1,95 +1,32 @@
-// Jenkins Job:gateway-k8s | Script Path: docs/jenkins/produ/gateway/Jenkinsfile
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-
-def SVC = [
-    prodDir: 'store',
-    module: 'alien-store',
-    serverPort: '50014',
-    withLib: true,
-    deployName: 'store',
-]
-
+def SVC = [prodDir: 'store', deployName: 'store']
+def sparseCheckoutProduShared() {
+    checkout scm: [$class: 'GitSCM', branches: scm.branches,
+        extensions: [[$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'docs/jenkins/produ/_shared/']]]],
+        userRemoteConfigs: scm.userRemoteConfigs]
+}
 pipeline {
     agent any
-    options {
-        buildDiscarder(logRotator(numToKeepStr: '15'))
-        timestamps()
-        timeout(time: 90, unit: 'MINUTES')
-    }
+    options { buildDiscarder(logRotator(numToKeepStr: '15')); timestamps(); timeout(time: 45, unit: 'MINUTES') }
     parameters {
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true)
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true)
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true)
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'])
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
-    environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
-        HARBOR_CREDENTIALS = 'harbor-robot-alien'
-        KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
-    }
+    environment { HARBOR_CREDENTIALS = 'harbor-robot-alien'; KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien' }
     stages {
-        stage('Checkout') {
-            steps {
-                script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                }
-            }
+        stage('Promote image & Deploy ACK') {
+            steps { script {
+                sparseCheckoutProduShared()
+                def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                k8s.promoteOneServiceToAck(this, SVC, params, env)
+                echo ">>> store: ${env.IMAGE_REF}"
+            } }
         }
-        stage('Maven Package') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageModule(this, SVC.module) }
-            }
-        }
-        stage('Docker Build & Push') {
-            steps {
-                script {
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    env.IMAGE_REF = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${SVC.prodDir}:${tag}"
-                    if (params.DRY_RUN) {
-                        echo "DRY_RUN: ${env.IMAGE_REF}"
-                        return
-                    }
-                    k8s.dockerBuildAndPush(this, SVC + [
-                        harborRegistry: params.HARBOR_REGISTRY,
-                        harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
-                        imageRef: env.IMAGE_REF,
-                        harborCredentialsId: env.HARBOR_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-        stage('Deploy to ACK') {
-            steps {
-                script {
-                    if (params.DRY_RUN) { return }
-                    k8s.deployToAck(this, [
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        imageRef: env.IMAGE_REF,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        deploymentStable: SVC.deployName,
-                        deploymentCanary: "${SVC.deployName}-canary",
-                        ingressCanary: "${SVC.deployName}-canary",
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ])
-                }
-            }
-        }
-    }
-    post {
-        success { echo ">>> ${SVC.prodDir} -> ${env.IMAGE_REF ?: 'dry-run'}" }
     }
 }
-

+ 68 - 55
docs/jenkins/produ/whole/Jenkinsfile

@@ -1,25 +1,35 @@
 /**
- * 整体 / 单服务 / 多选 发版(Harbor + ACK)。
- *
- * Jenkins Job 建议名:alien-cloud-k8s-whole
+ * 整体 / 单服务 / 多选:仅晋升 Harbor 镜像 + 可选 ACK。
  * Script Path:docs/jenkins/produ/whole/Jenkinsfile
- *
- * DEPLOY_MODE:whole=七个服务全发;single=选一个;multi=勾选 MULTI_*。
- * 与 prod-promote-from-uat 的 whole/single/multi 语义一致,制品改为镜像而非 jar 目录。
  */
-def k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
-def reg = load 'docs/jenkins/produ/_shared/service-registry.groovy'
+def sparseCheckoutProduShared() {
+    checkout scm: [
+        $class: 'GitSCM',
+        branches: scm.branches,
+        extensions: [
+            [$class: 'CloneOption', depth: 1, shallow: true, noTags: true],
+            [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [
+                [path: 'docs/jenkins/produ/_shared/'],
+            ]],
+        ],
+        userRemoteConfigs: scm.userRemoteConfigs,
+    ]
+}
+
+def k8s
+def reg
 
 pipeline {
     agent any
     options {
         buildDiscarder(logRotator(numToKeepStr: '15'))
+        disableConcurrentBuilds()
         timestamps()
-        timeout(time: 180, unit: 'MINUTES')
+        timeout(time: 120, unit: 'MINUTES')
     }
     parameters {
         choice(name: 'DEPLOY_MODE', choices: ['whole', 'single', 'multi'],
-            description: 'whole=全部七个 Java 服务;single=一个;multi=勾选下方复选框')
+            description: 'whole=7个服务;single=选一个;multi=勾选 MULTI_*')
         choice(name: 'SINGLE_SERVICE', choices: [
             'gateway', 'store', 'second', 'store-platform', 'lawyer', 'job', 'dining',
         ], description: '仅 DEPLOY_MODE=single 时生效')
@@ -30,76 +40,79 @@ pipeline {
         booleanParam(name: 'MULTI_lawyer', defaultValue: false)
         booleanParam(name: 'MULTI_job', defaultValue: false)
         booleanParam(name: 'MULTI_dining', defaultValue: false)
-        string(name: 'GIT_BRANCH', defaultValue: 'uat-20260202', trim: true)
-        string(name: 'IMAGE_TAG', defaultValue: '', trim: true,
-            description: '留空则各服务 tag 均为 build-${BUILD_NUMBER}')
-        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary'])
-        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
-        string(name: 'HARBOR_REGISTRY', defaultValue: '39.106.135.88', trim: true)
+        string(name: 'SOURCE_TAG', defaultValue: 'uat-latest', trim: true,
+            description: '七个服务共用的预生产 Harbor tag')
+        string(name: 'TARGET_TAG', defaultValue: '', trim: true,
+            description: '留空则 produ-${BUILD_NUMBER}')
+        string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
         string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
-        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien_cloud/base/openjdk8-ffmpeg:v1', trim: true)
+        choice(name: 'DEPLOY_STRATEGY', choices: ['rolling', 'canary', 'skip'],
+            description: 'skip=只晋升 Harbor,不 kubectl')
+        string(name: 'CANARY_WEIGHT', defaultValue: '10', trim: true)
         string(name: 'K8S_NAMESPACE', defaultValue: 'alien-produ', trim: true)
-        booleanParam(name: 'SKIP_MAVEN', defaultValue: false)
         booleanParam(name: 'DRY_RUN', defaultValue: false)
     }
     environment {
-        MAVEN_HOME = tool '3.6.3'
-        PATH = "${MAVEN_HOME}/bin:${env.PATH}"
-        GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
-        GIT_CREDENTIALS = 'zhanghaomimapingzheng'
         HARBOR_CREDENTIALS = 'harbor-robot-alien'
         KUBECONFIG_CREDENTIALS = 'ack-kubeconfig-alien'
     }
     stages {
-        stage('Checkout') {
+        stage('Plan') {
             steps {
                 script {
-                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
-                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
-                    env.SELECTED_SERVICES = reg.filterServices(reg.getServiceRegistry(), params)*.prodDir.join(',')
-                    echo ">>> 本次发版服务: ${env.SELECTED_SERVICES}"
+                    sparseCheckoutProduShared()
+                    k8s = load 'docs/jenkins/produ/_shared/k8s-produ-lib.groovy'
+                    reg = load 'docs/jenkins/produ/_shared/service-registry.groovy'
+                    def services = reg.filterServices(reg.getServiceRegistry(), params)
+                    env.PROMOTE_LIST = services*.prodDir.join(',')
+                    env.TARGET_TAG_RESOLVED = k8s.resolveTargetTag(this, params.TARGET_TAG)
+                    echo ">>> DEPLOY_MODE=${params.DEPLOY_MODE} 服务=${env.PROMOTE_LIST}"
+                    echo ">>> ${params.SOURCE_TAG} → ${env.TARGET_TAG_RESOLVED}"
                 }
             }
         }
-        stage('Maven Package (all modules)') {
-            when { expression { return !params.SKIP_MAVEN } }
-            steps {
-                script { k8s.mavenPackageAll(this) }
-            }
-        }
-        stage('Build Push & Deploy') {
+        stage('Promote images') {
             steps {
                 script {
                     def services = reg.filterServices(reg.getServiceRegistry(), params)
-                    def tag = (params.IMAGE_TAG ?: '').trim() ?: "build-${env.BUILD_NUMBER}"
-                    def common = [
+                    k8s.promoteHarborImages(this, services, [
                         harborRegistry: params.HARBOR_REGISTRY,
                         harborProject: params.HARBOR_PROJECT,
-                        baseImage: params.BASE_IMAGE,
+                        sourceTag: params.SOURCE_TAG,
+                        targetTag: env.TARGET_TAG_RESOLVED,
                         harborCredentialsId: env.HARBOR_CREDENTIALS,
-                        k8sNamespace: params.K8S_NAMESPACE,
-                        deployStrategy: params.DEPLOY_STRATEGY,
-                        canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
-                        kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
-                    ]
-                    services.each { svc ->
-                        def imageRef = "${params.HARBOR_REGISTRY}/${params.HARBOR_PROJECT}/${svc.prodDir}:${tag}"
-                        echo ">>> === ${svc.prodDir} === ${imageRef}"
-                        if (!params.DRY_RUN) {
-                            k8s.dockerBuildAndPush(this, svc + common + [imageRef: imageRef])
-                            k8s.deployToAck(this, svc + common + [
-                                imageRef: imageRef,
-                                deploymentStable: svc.deployName,
-                                deploymentCanary: "${svc.deployName}-canary",
-                                ingressCanary: "${svc.deployName}-canary",
-                            ])
-                        }
+                        dryRun: params.DRY_RUN == true,
+                    ])
+                }
+            }
+        }
+        stage('Deploy to ACK') {
+            when { expression { return params.DEPLOY_STRATEGY != 'skip' && !params.DRY_RUN } }
+            steps {
+                script {
+                    def services = reg.filterServices(reg.getServiceRegistry(), params)
+                    def regHost = params.HARBOR_REGISTRY.trim()
+                    def proj = params.HARBOR_PROJECT.trim()
+                    def tgtTag = env.TARGET_TAG_RESOLVED
+                    def strategy = params.DEPLOY_STRATEGY
+                    services.each { s ->
+                        def imageRef = "${regHost}/${proj}/${s.prodDir}:${tgtTag}"
+                        k8s.deployToAck(this, [
+                            k8sNamespace: params.K8S_NAMESPACE,
+                            imageRef: imageRef,
+                            deployStrategy: strategy == 'canary' ? 'canary' : 'rolling',
+                            deploymentStable: s.deployName,
+                            deploymentCanary: "${s.deployName}-canary",
+                            ingressCanary: "${s.deployName}-canary",
+                            canaryWeight: (params.CANARY_WEIGHT ?: '10').trim(),
+                            kubeCredentialsId: env.KUBECONFIG_CREDENTIALS,
+                        ])
                     }
                 }
             }
         }
     }
     post {
-        success { echo ">>> 整体流水线完成: ${env.SELECTED_SERVICES}" }
+        success { echo ">>> 完成: ${env.PROMOTE_LIST} tag=${env.TARGET_TAG_RESOLVED}" }
     }
 }