Przeglądaj źródła

生产 各 Jenkinsfile 等。

dujian 2 tygodni temu
rodzic
commit
2350b5ace7

+ 51 - 0
docs/jenkins/produ/_shared/service-registry.groovy

@@ -0,0 +1,51 @@
+/**
+ * 七个 Java 微服务注册表。单服务 Jenkinsfile 与 whole/Jenkinsfile 共用。
+ * 新增服务时:在此列表增加一项,并复制 produ/<prodDir>/Jenkinsfile。
+ */
+
+def getServiceRegistry() {
+    return [
+        [prodDir: 'gateway',        module: 'alien-gateway',        serverPort: '8000',  withLib: false, deployName: 'gateway'],
+        [prodDir: 'store',          module: 'alien-store',          serverPort: '50014', withLib: true,  deployName: 'store'],
+        [prodDir: 'second',         module: 'alien-second',         serverPort: '50015', withLib: false, deployName: 'second'],
+        [prodDir: 'store-platform', module: 'alien-store-platform', serverPort: '50016', withLib: false, deployName: 'store-platform'],
+        [prodDir: 'lawyer',         module: 'alien-lawyer',         serverPort: '50017', withLib: true,  deployName: 'lawyer'],
+        [prodDir: 'job',            module: 'alien-job',            serverPort: '50108', withLib: false, deployName: 'job'],
+        [prodDir: 'dining',         module: 'alien-dining',         serverPort: '50019', withLib: false, deployName: 'dining'],
+    ]
+}
+
+def multiParamName(String prodDir) {
+    return 'MULTI_' + prodDir.replace('-', '_')
+}
+
+def filterServices(List registry, Map params) {
+    def mode = params.DEPLOY_MODE
+    if (mode == 'whole') {
+        return registry
+    }
+    if (mode == 'single') {
+        def one = registry.find { it.prodDir == params.SINGLE_SERVICE }
+        if (one == null) {
+            throw new IllegalArgumentException("未找到 SINGLE_SERVICE=${params.SINGLE_SERVICE}")
+        }
+        return [one]
+    }
+    if (mode == 'multi') {
+        def selected = []
+        registry.each { s ->
+            def pn = multiParamName(s.prodDir)
+            def v = params[pn]
+            if (v instanceof Boolean ? v : 'true'.equalsIgnoreCase(v?.toString())) {
+                selected << s
+            }
+        }
+        if (selected.isEmpty()) {
+            throw new IllegalArgumentException('multi 模式:请至少勾选一项 MULTI_*')
+        }
+        return selected
+    }
+    throw new IllegalArgumentException("未知 DEPLOY_MODE: ${mode}")
+}
+
+return this

+ 95 - 0
docs/jenkins/produ/dining/Jenkinsfile

@@ -0,0 +1,95 @@
+// 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',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}
+

+ 27 - 0
docs/jenkins/produ/docker/Dockerfile.java-service

@@ -0,0 +1,27 @@
+# 单模块 Java 微服务镜像(Jenkins 构建上下文 = alien-<module>/ 目录)
+# build-arg: BASE_IMAGE, JAR_FILE, SERVER_PORT, WITH_LIB=true|false
+
+ARG BASE_IMAGE=39.106.135.88/alien/base/openjdk8-ffmpeg:v1
+FROM ${BASE_IMAGE}
+
+ARG JAR_FILE=alien-gateway-1.0.0.jar
+ARG SERVER_PORT=8000
+ARG WITH_LIB=false
+
+WORKDIR /app
+
+COPY ${JAR_FILE} /app/app.jar
+# 瘦 jar 时 target/lib 存在;WITH_LIB=false 时 COPY 空目录可能失败,构建脚本保证目录存在
+COPY lib /app/lib
+
+ENV SERVER_PORT=${SERVER_PORT}
+EXPOSE ${SERVER_PORT}
+
+# 与 docker-compose-prod 一致:prod profile;有 lib 时加 loader.path
+RUN if [ "${WITH_LIB}" = "true" ]; then \
+      echo 'exec java -Dspring.profiles.active=prod -Dfile.encoding=UTF-8 -Xms800m -Xmx800m -Dloader.path=/app/lib -jar /app/app.jar --server.port='"${SERVER_PORT}" > /app/entrypoint.sh; \
+    else \
+      echo 'exec java -Dspring.profiles.active=prod -Dfile.encoding=UTF-8 -Xms800m -Xmx800m -jar /app/app.jar --server.port='"${SERVER_PORT}" > /app/entrypoint.sh; \
+    fi && chmod +x /app/entrypoint.sh
+
+ENTRYPOINT ["/bin/sh", "/app/entrypoint.sh"]

+ 94 - 0
docs/jenkins/produ/gateway/Jenkinsfile

@@ -0,0 +1,94 @@
+// 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: 'gateway',
+    module: 'alien-gateway',
+    serverPort: '8000',
+    withLib: false,
+    deployName: 'gateway',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}

+ 95 - 0
docs/jenkins/produ/job/Jenkinsfile

@@ -0,0 +1,95 @@
+// 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',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}
+

+ 48 - 0
docs/jenkins/produ/k8s/examples/deployment-gateway-canary.yaml

@@ -0,0 +1,48 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: gateway-canary
+  namespace: alien-produ
+  labels:
+    app: gateway
+    track: canary
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: gateway
+      track: canary
+  template:
+    metadata:
+      labels:
+        app: gateway
+        track: canary
+    spec:
+      imagePullSecrets:
+        - name: harbor-registry
+      containers:
+        - name: app
+          image: 39.106.135.88/alien/gateway:build-1
+          imagePullPolicy: Always
+          ports:
+            - containerPort: 8000
+          resources:
+            requests:
+              cpu: 250m
+              memory: 512Mi
+            limits:
+              cpu: "1"
+              memory: 1200Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: gateway-canary
+  namespace: alien-produ
+spec:
+  selector:
+    app: gateway
+    track: canary
+  ports:
+    - port: 8000
+      targetPort: 8000

+ 65 - 0
docs/jenkins/produ/k8s/examples/deployment-gateway.yaml

@@ -0,0 +1,65 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: gateway
+  namespace: alien-produ
+  labels:
+    app: gateway
+    track: stable
+spec:
+  replicas: 2
+  selector:
+    matchLabels:
+      app: gateway
+      track: stable
+  template:
+    metadata:
+      labels:
+        app: gateway
+        track: stable
+    spec:
+      imagePullSecrets:
+        - name: harbor-registry
+      affinity:
+        podAntiAffinity:
+          preferredDuringSchedulingIgnoredDuringExecution:
+            - weight: 100
+              podAffinityTerm:
+                labelSelector:
+                  matchLabels:
+                    app: gateway
+                topologyKey: kubernetes.io/hostname
+      containers:
+        - name: app
+          image: 39.106.135.88/alien/gateway:build-1
+          imagePullPolicy: Always
+          ports:
+            - containerPort: 8000
+          resources:
+            requests:
+              cpu: 250m
+              memory: 512Mi
+            limits:
+              cpu: "1"
+              memory: 1200Mi
+          env:
+            - name: TZ
+              value: Asia/Shanghai
+            # - name: JASYPT_ENCRYPTOR_PASSWORD
+            #   valueFrom:
+            #     secretKeyRef:
+            #       name: alien-jasypt
+            #       key: password
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: gateway
+  namespace: alien-produ
+spec:
+  selector:
+    app: gateway
+    track: stable
+  ports:
+    - port: 8000
+      targetPort: 8000

+ 45 - 0
docs/jenkins/produ/k8s/examples/ingress-gateway-canary.example.yaml

@@ -0,0 +1,45 @@
+# 主 Ingress:backend 指向 stable Service gateway
+# 灰度:另建 Ingress 或使用同 host 下 canary 注解指向 gateway-canary(二选一,此处为「独立 canary Ingress」示例)
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: gateway
+  namespace: alien-produ
+  annotations:
+    kubernetes.io/ingress.class: nginx
+spec:
+  rules:
+    - host: api-produ.example.com
+      http:
+        paths:
+          - path: /
+            pathType: Prefix
+            backend:
+              service:
+                name: gateway
+                port:
+                  number: 8000
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: gateway-canary
+  namespace: alien-produ
+  annotations:
+    kubernetes.io/ingress.class: nginx
+    nginx.ingress.kubernetes.io/canary: "true"
+    nginx.ingress.kubernetes.io/canary-weight: "0"
+    # 与主 Ingress 相同 host,Nginx 按权重分流到本 Ingress 的 backend
+spec:
+  rules:
+    - host: api-produ.example.com
+      http:
+        paths:
+          - path: /
+            pathType: Prefix
+            backend:
+              service:
+                name: gateway-canary
+                port:
+                  number: 8000

+ 6 - 0
docs/jenkins/produ/k8s/examples/namespace.yaml

@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: alien-produ
+  labels:
+    app.kubernetes.io/part-of: alien-cloud

+ 18 - 0
docs/jenkins/produ/k8s/examples/secret-harbor.example.yaml

@@ -0,0 +1,18 @@
+# 复制为 secret-harbor.yaml,填入 Harbor 机器人账号后 apply(勿提交明文到 Git)
+apiVersion: v1
+kind: Secret
+metadata:
+  name: harbor-registry
+  namespace: alien-produ
+type: kubernetes.io/dockerconfigjson
+stringData:
+  .dockerconfigjson: |
+    {
+      "auths": {
+        "39.106.135.88": {
+          "username": "robot$alien",
+          "password": "REPLACE_HARBOR_ROBOT_TOKEN",
+          "auth": "REPLACE_BASE64_USER_COLON_PASS"
+        }
+      }
+    }

+ 95 - 0
docs/jenkins/produ/lawyer/Jenkinsfile

@@ -0,0 +1,95 @@
+// 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',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}
+

+ 95 - 0
docs/jenkins/produ/second/Jenkinsfile

@@ -0,0 +1,95 @@
+// 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',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}
+

+ 95 - 0
docs/jenkins/produ/store-platform/Jenkinsfile

@@ -0,0 +1,95 @@
+// 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',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}
+

+ 95 - 0
docs/jenkins/produ/store/Jenkinsfile

@@ -0,0 +1,95 @@
+// 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',
+]
+
+pipeline {
+    agent any
+    options {
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        timestamps()
+        timeout(time: 90, 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: 'HARBOR_PROJECT', defaultValue: 'alien', trim: true)
+        string(name: 'BASE_IMAGE', defaultValue: '39.106.135.88/alien/base/openjdk8-ffmpeg:v1', 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') {
+            steps {
+                script {
+                    def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
+                    k8s.checkoutBranch(this, branch, env.GIT_URL, env.GIT_CREDENTIALS)
+                }
+            }
+        }
+        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'}" }
+    }
+}
+