暂存提交
This commit is contained in:
Vendored
+154
@@ -0,0 +1,154 @@
|
||||
def serviceModules = [
|
||||
'gateway', 'auth', 'iam', 'firstleg', 'order', 'warehouse', 'file', 'print',
|
||||
'integration-service', 'event-bridge-service', 'audit-service'
|
||||
]
|
||||
|
||||
def serviceJars = [
|
||||
'gateway': 'gateway/target/gateway-1.0.0-SNAPSHOT.jar',
|
||||
'auth': 'auth/target/auth-service-1.0.0-SNAPSHOT.jar',
|
||||
'iam': 'iam/target/iam-service-1.0.0-SNAPSHOT.jar',
|
||||
'firstleg': 'firstleg/target/firstleg-service-1.0.0-SNAPSHOT.jar',
|
||||
'order': 'order/target/order-service-1.0.0-SNAPSHOT.jar',
|
||||
'warehouse': 'warehouse/target/warehouse-service-1.0.0-SNAPSHOT.jar',
|
||||
'file': 'file/target/file-service-1.0.0-SNAPSHOT.jar',
|
||||
'print': 'print/target/print-service-1.0.0-SNAPSHOT.jar',
|
||||
'integration-service': 'integration-service/target/integration-service-1.0.0-SNAPSHOT.jar',
|
||||
'event-bridge-service': 'event-bridge-service/target/event-bridge-service-1.0.0-SNAPSHOT.jar',
|
||||
'audit-service': 'audit-service/target/audit-service-1.0.0-SNAPSHOT.jar'
|
||||
]
|
||||
|
||||
def commonPaths = [
|
||||
'pom.xml', 'platform-bom/', 'common-core/', 'common-web/', 'common-security/',
|
||||
'common-openapi/', 'common-audit/', 'common-event-contract/', 'Jenkinsfile', 'deploy/'
|
||||
]
|
||||
|
||||
pipeline {
|
||||
agent { label 'docker-java17' }
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
timestamps()
|
||||
buildDiscarder(logRotator(numToKeepStr: '30'))
|
||||
}
|
||||
|
||||
parameters {
|
||||
choice(name: 'BUILD_MODE', choices: ['AUTO', 'SELECT', 'ALL'], description: 'AUTO 按 Git 变更识别;SELECT 手工指定;ALL 构建全部服务')
|
||||
string(name: 'SERVICE', defaultValue: 'gateway', description: 'SELECT 模式填写服务名,多个服务用英文逗号分隔')
|
||||
choice(name: 'ENVIRONMENT', choices: ['test', 'staging', 'production'], description: '部署环境')
|
||||
booleanParam(name: 'DEPLOY', defaultValue: false, description: '构建成功后是否部署')
|
||||
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '是否跳过测试;生产构建不建议勾选')
|
||||
string(name: 'IMAGE_REPOSITORY', defaultValue: 'registry.example.com/your-group/baiyue-firstleg', description: 'GitLab Container Registry 项目地址')
|
||||
}
|
||||
|
||||
environment {
|
||||
REGISTRY_CREDENTIALS = 'gitlab-registry-credentials'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('确定构建范围') {
|
||||
steps {
|
||||
script {
|
||||
List selected = []
|
||||
if (params.BUILD_MODE == 'ALL') {
|
||||
selected = serviceModules
|
||||
} else if (params.BUILD_MODE == 'SELECT') {
|
||||
selected = params.SERVICE.split(',').collect { it.trim() }.findAll { it }
|
||||
} else {
|
||||
String base = env.GIT_PREVIOUS_SUCCESSFUL_COMMIT?.trim()
|
||||
if (!base || sh(script: "git cat-file -e ${base}^{commit} 2>/dev/null", returnStatus: true) != 0) {
|
||||
selected = serviceModules
|
||||
} else {
|
||||
List changed = sh(script: "git diff --name-only ${base} HEAD", returnStdout: true)
|
||||
.trim().split('\\n').collect { it.trim() }.findAll { it }
|
||||
boolean commonChanged = changed.any { path -> commonPaths.any { prefix -> path == prefix || path.startsWith(prefix) } }
|
||||
selected = commonChanged ? serviceModules : serviceModules.findAll { module ->
|
||||
changed.any { it == module || it.startsWith("${module}/") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List invalid = selected.findAll { !serviceModules.contains(it) }
|
||||
if (invalid) {
|
||||
error("未知服务: ${invalid.join(', ')};允许值: ${serviceModules.join(', ')}")
|
||||
}
|
||||
selected = selected.unique()
|
||||
if (!selected) {
|
||||
currentBuild.description = '没有需要构建的业务服务'
|
||||
env.NO_SERVICE_CHANGE = 'true'
|
||||
env.SELECTED_SERVICES = ''
|
||||
} else {
|
||||
env.NO_SERVICE_CHANGE = 'false'
|
||||
env.SELECTED_SERVICES = selected.join(',')
|
||||
env.IMAGE_TAG = sh(script: 'git rev-parse --short=12 HEAD', returnStdout: true).trim()
|
||||
currentBuild.description = "${env.SELECTED_SERVICES} | ${env.IMAGE_TAG}"
|
||||
echo "本次构建服务: ${env.SELECTED_SERVICES}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Maven 测试与打包') {
|
||||
when { expression { env.NO_SERVICE_CHANGE != 'true' } }
|
||||
steps {
|
||||
script {
|
||||
String testOption = params.SKIP_TESTS ? '-DskipTests' : ''
|
||||
sh "mvn --batch-mode --errors --fail-at-end -pl ${env.SELECTED_SERVICES} -am clean package ${testOption}"
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit allowEmptyResults: true, testResults: '**/target/surefire-reports/TEST-*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('构建并推送镜像') {
|
||||
when { expression { env.NO_SERVICE_CHANGE != 'true' } }
|
||||
steps {
|
||||
script {
|
||||
String registryHost = params.IMAGE_REPOSITORY.tokenize('/')[0]
|
||||
withCredentials([usernamePassword(credentialsId: env.REGISTRY_CREDENTIALS, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASSWORD')]) {
|
||||
sh 'printf %s "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin "' + registryHost + '"'
|
||||
env.SELECTED_SERVICES.split(',').each { service ->
|
||||
String image = "${params.IMAGE_REPOSITORY}/${service}:${env.IMAGE_TAG}"
|
||||
sh "docker build -f deploy/docker/Dockerfile --build-arg JAR_FILE=${serviceJars[service]} -t ${image} ."
|
||||
sh "docker push ${image}"
|
||||
}
|
||||
sh "docker logout ${registryHost} || true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('生产部署确认') {
|
||||
when {
|
||||
allOf {
|
||||
expression { params.DEPLOY && params.ENVIRONMENT == 'production' && env.NO_SERVICE_CHANGE != 'true' }
|
||||
}
|
||||
}
|
||||
steps {
|
||||
input message: "确认将 ${env.SELECTED_SERVICES} 的镜像 ${env.IMAGE_TAG} 部署到生产环境?", ok: '确认部署'
|
||||
}
|
||||
}
|
||||
|
||||
stage('部署') {
|
||||
when {
|
||||
allOf {
|
||||
expression { params.DEPLOY && env.NO_SERVICE_CHANGE != 'true' }
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
String credentialId = "firstleg-${params.ENVIRONMENT}-env"
|
||||
withCredentials([file(credentialsId: credentialId, variable: 'DEPLOY_ENV_FILE')]) {
|
||||
sh "sh deploy/scripts/deploy-services.sh '${params.ENVIRONMENT}' '${params.IMAGE_REPOSITORY}' '${env.IMAGE_TAG}' '${env.SELECTED_SERVICES}' \"\$DEPLOY_ENV_FILE\""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always { cleanWs(deleteDirs: true, notFailBuild: true) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
name: firstleg-${DEPLOY_ENV:-test}
|
||||
|
||||
x-common: &common
|
||||
restart: unless-stopped
|
||||
networks: [firstleg-network]
|
||||
environment: &common-environment
|
||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-cn}
|
||||
NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR}
|
||||
NACOS_USERNAME: ${NACOS_USERNAME}
|
||||
NACOS_PASSWORD: ${NACOS_PASSWORD}
|
||||
NACOS_GROUP: ${NACOS_GROUP:-BAIYUE}
|
||||
NACOS_NAMESPACE: ${NACOS_NAMESPACE:-public}
|
||||
REDIS_HOST: ${REDIS_HOST}
|
||||
REDIS_PORT: ${REDIS_PORT:-6379}
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
REDIS_DATABASE: ${REDIS_DATABASE:-0}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
INTERNAL_SERVICE_TOKEN: ${INTERNAL_SERVICE_TOKEN}
|
||||
REGION_CODE: ${REGION_CODE:-CN}
|
||||
DEPLOYMENT_CELL: ${DEPLOYMENT_CELL:-test-cn}
|
||||
DATA_RESIDENCY: ${DATA_RESIDENCY:-CN}
|
||||
TZ: Asia/Shanghai
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: 100m
|
||||
max-file: "5"
|
||||
|
||||
services:
|
||||
gateway:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/gateway:${IMAGE_TAG}
|
||||
container_name: firstleg-gateway
|
||||
ports: ["${GATEWAY_PORT:-9000}:9000"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9000
|
||||
SWAGGER_ENABLED: ${SWAGGER_ENABLED:-false}
|
||||
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS}
|
||||
CORS_ALLOW_CREDENTIALS: ${CORS_ALLOW_CREDENTIALS:-false}
|
||||
|
||||
auth:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/auth:${IMAGE_TAG}
|
||||
container_name: firstleg-auth
|
||||
ports: ["${AUTH_PORT:-9100}:9100"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9100
|
||||
AUTH_DB_URL: ${AUTH_DB_URL}
|
||||
AUTH_DB_USERNAME: ${AUTH_DB_USERNAME}
|
||||
AUTH_DB_PASSWORD: ${AUTH_DB_PASSWORD}
|
||||
|
||||
iam:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/iam:${IMAGE_TAG}
|
||||
container_name: firstleg-iam
|
||||
ports: ["${IAM_PORT:-9201}:9201"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9201
|
||||
IAM_DB_URL: ${IAM_DB_URL}
|
||||
IAM_DB_USERNAME: ${IAM_DB_USERNAME}
|
||||
IAM_DB_PASSWORD: ${IAM_DB_PASSWORD}
|
||||
|
||||
firstleg:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/firstleg:${IMAGE_TAG}
|
||||
container_name: firstleg-business
|
||||
ports: ["${FIRSTLEG_PORT:-9202}:9202"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9202
|
||||
FIRSTLEG_DB_URL: ${FIRSTLEG_DB_URL}
|
||||
FIRSTLEG_DB_USERNAME: ${FIRSTLEG_DB_USERNAME}
|
||||
FIRSTLEG_DB_PASSWORD: ${FIRSTLEG_DB_PASSWORD}
|
||||
|
||||
order:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/order:${IMAGE_TAG}
|
||||
container_name: firstleg-order
|
||||
ports: ["${ORDER_PORT:-9204}:9204"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9204
|
||||
ORDER_DB_URL: ${ORDER_DB_URL}
|
||||
ORDER_DB_USERNAME: ${ORDER_DB_USERNAME}
|
||||
ORDER_DB_PASSWORD: ${ORDER_DB_PASSWORD}
|
||||
|
||||
warehouse:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/warehouse:${IMAGE_TAG}
|
||||
container_name: firstleg-warehouse
|
||||
ports: ["${WAREHOUSE_PORT:-9205}:9205"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9205
|
||||
WAREHOUSE_DB_URL: ${WAREHOUSE_DB_URL}
|
||||
WAREHOUSE_DB_USERNAME: ${WAREHOUSE_DB_USERNAME}
|
||||
WAREHOUSE_DB_PASSWORD: ${WAREHOUSE_DB_PASSWORD}
|
||||
|
||||
file:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/file:${IMAGE_TAG}
|
||||
container_name: firstleg-file
|
||||
ports: ["${FILE_PORT:-9206}:9206"]
|
||||
volumes: ["firstleg-file-data:/data/files"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9206
|
||||
FILE_DB_URL: ${FILE_DB_URL}
|
||||
FILE_DB_USERNAME: ${FILE_DB_USERNAME}
|
||||
FILE_DB_PASSWORD: ${FILE_DB_PASSWORD}
|
||||
FILE_STORAGE_ROOT: /data/files
|
||||
|
||||
print:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/print:${IMAGE_TAG}
|
||||
container_name: firstleg-print
|
||||
ports: ["${PRINT_PORT:-9207}:9207"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9207
|
||||
PRINT_DB_URL: ${PRINT_DB_URL}
|
||||
PRINT_DB_USERNAME: ${PRINT_DB_USERNAME}
|
||||
PRINT_DB_PASSWORD: ${PRINT_DB_PASSWORD}
|
||||
|
||||
integration-service:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/integration-service:${IMAGE_TAG}
|
||||
container_name: firstleg-integration
|
||||
ports: ["${INTEGRATION_PORT:-9210}:9210"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9210
|
||||
BAIDU_OCR_ENABLED: ${BAIDU_OCR_ENABLED:-false}
|
||||
BAIDU_OCR_API_KEY: ${BAIDU_OCR_API_KEY:-}
|
||||
BAIDU_OCR_SECRET_KEY: ${BAIDU_OCR_SECRET_KEY:-}
|
||||
AMAZON_SP_API_ENABLED: ${AMAZON_SP_API_ENABLED:-false}
|
||||
AMAZON_SP_API_CLIENT_ID: ${AMAZON_SP_API_CLIENT_ID:-}
|
||||
AMAZON_SP_API_CLIENT_SECRET: ${AMAZON_SP_API_CLIENT_SECRET:-}
|
||||
AMAZON_SP_API_REFRESH_TOKEN: ${AMAZON_SP_API_REFRESH_TOKEN:-}
|
||||
AMAZON_SP_API_AWS_ACCESS_KEY: ${AMAZON_SP_API_AWS_ACCESS_KEY:-}
|
||||
AMAZON_SP_API_AWS_SECRET_KEY: ${AMAZON_SP_API_AWS_SECRET_KEY:-}
|
||||
AMAZON_SP_API_REGION: ${AMAZON_SP_API_REGION:-}
|
||||
|
||||
event-bridge-service:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/event-bridge-service:${IMAGE_TAG}
|
||||
container_name: firstleg-event-bridge
|
||||
ports: ["${EVENT_BRIDGE_PORT:-9300}:9300"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9300
|
||||
EVENT_BRIDGE_DB_URL: ${EVENT_BRIDGE_DB_URL}
|
||||
EVENT_BRIDGE_DB_USERNAME: ${EVENT_BRIDGE_DB_USERNAME}
|
||||
EVENT_BRIDGE_DB_PASSWORD: ${EVENT_BRIDGE_DB_PASSWORD}
|
||||
|
||||
audit-service:
|
||||
<<: *common
|
||||
image: ${IMAGE_REPOSITORY}/audit-service:${IMAGE_TAG}
|
||||
container_name: firstleg-audit
|
||||
ports: ["${AUDIT_PORT:-9700}:9700"]
|
||||
environment:
|
||||
<<: *common-environment
|
||||
SERVER_PORT: 9700
|
||||
AUDIT_DB_URL: ${AUDIT_DB_URL}
|
||||
AUDIT_DB_USERNAME: ${AUDIT_DB_USERNAME}
|
||||
AUDIT_DB_PASSWORD: ${AUDIT_DB_PASSWORD}
|
||||
|
||||
networks:
|
||||
firstleg-network:
|
||||
name: firstleg-${DEPLOY_ENV:-test}-network
|
||||
|
||||
volumes:
|
||||
firstleg-file-data:
|
||||
name: firstleg-${DEPLOY_ENV:-test}-file-data
|
||||
@@ -0,0 +1,22 @@
|
||||
FROM eclipse-temurin:17-jre-alpine
|
||||
|
||||
RUN apk add --no-cache curl \
|
||||
&& addgroup -S app \
|
||||
&& adduser -S -G app -u 10001 app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ARG JAR_FILE
|
||||
COPY --chown=app:app ${JAR_FILE} /app/app.jar
|
||||
|
||||
USER app
|
||||
|
||||
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 -XX:+ExitOnOutOfMemoryError -Dfile.encoding=UTF-8" \
|
||||
SERVER_PORT=8080
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
|
||||
CMD curl -fsS "http://127.0.0.1:${SERVER_PORT}/actuator/health" || exit 1
|
||||
|
||||
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
if [ "$#" -ne 5 ]; then
|
||||
echo "用法: $0 <环境> <镜像仓库> <镜像标签> <逗号分隔服务> <环境文件>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
DEPLOY_ENV="$1"
|
||||
IMAGE_REPOSITORY="$2"
|
||||
IMAGE_TAG="$3"
|
||||
SERVICE_CSV="$4"
|
||||
ENV_FILE="$5"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo "环境文件不存在: $ENV_FILE" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
SERVICES=$(printf '%s' "$SERVICE_CSV" | tr ',' ' ')
|
||||
case " $SERVICES " in
|
||||
*" gateway "*) SERVICES="$SERVICES" ;;
|
||||
*) SERVICES="$SERVICES gateway" ;;
|
||||
esac
|
||||
|
||||
export DEPLOY_ENV IMAGE_REPOSITORY IMAGE_TAG
|
||||
docker compose \
|
||||
--project-name "firstleg-${DEPLOY_ENV}" \
|
||||
--env-file "$ENV_FILE" \
|
||||
-f deploy/docker-compose.yml \
|
||||
pull $SERVICES
|
||||
|
||||
docker compose \
|
||||
--project-name "firstleg-${DEPLOY_ENV}" \
|
||||
--env-file "$ENV_FILE" \
|
||||
-f deploy/docker-compose.yml \
|
||||
up -d --remove-orphans $SERVICES
|
||||
|
||||
docker compose \
|
||||
--project-name "firstleg-${DEPLOY_ENV}" \
|
||||
--env-file "$ENV_FILE" \
|
||||
-f deploy/docker-compose.yml \
|
||||
ps $SERVICES
|
||||
@@ -0,0 +1,45 @@
|
||||
services:
|
||||
gateway:
|
||||
artifactId: gateway
|
||||
jar: gateway/target/gateway-1.0.0-SNAPSHOT.jar
|
||||
port: 9000
|
||||
auth:
|
||||
artifactId: auth-service
|
||||
jar: auth/target/auth-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9100
|
||||
iam:
|
||||
artifactId: iam-service
|
||||
jar: iam/target/iam-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9201
|
||||
firstleg:
|
||||
artifactId: firstleg-service
|
||||
jar: firstleg/target/firstleg-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9202
|
||||
order:
|
||||
artifactId: order-service
|
||||
jar: order/target/order-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9204
|
||||
warehouse:
|
||||
artifactId: warehouse-service
|
||||
jar: warehouse/target/warehouse-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9205
|
||||
file:
|
||||
artifactId: file-service
|
||||
jar: file/target/file-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9206
|
||||
print:
|
||||
artifactId: print-service
|
||||
jar: print/target/print-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9207
|
||||
integration-service:
|
||||
artifactId: integration-service
|
||||
jar: integration-service/target/integration-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9210
|
||||
event-bridge-service:
|
||||
artifactId: event-bridge-service
|
||||
jar: event-bridge-service/target/event-bridge-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9300
|
||||
audit-service:
|
||||
artifactId: audit-service
|
||||
jar: audit-service/target/audit-service-1.0.0-SNAPSHOT.jar
|
||||
port: 9700
|
||||
@@ -0,0 +1,86 @@
|
||||
# 头程单仓多服务构建部署说明
|
||||
|
||||
## 一、适用范围
|
||||
|
||||
当前 `baiyue-firstleg` 是一个 Git 仓库,包含 11 个可独立运行的服务和 7 个公共模块。部署时无需拆成多个仓库,Jenkins 使用 Maven `-pl ... -am` 只构建目标服务及其依赖,并为每个服务生成独立 Docker 镜像。
|
||||
|
||||
MySQL、Redis、Nacos 是外部基础设施,不包含在业务 Compose 文件中。各业务库仍使用独立数据库,往来单位数据继续归属 `baiyue_warehouse`。
|
||||
|
||||
## 二、文件说明
|
||||
|
||||
| 文件 | 用途 |
|
||||
| --- | --- |
|
||||
| `Jenkinsfile` | 自动识别变更、测试、打包、构建镜像、推送和部署 |
|
||||
| `deploy/docker/Dockerfile` | 11 个服务共用的 Java 17 非 root 镜像模板 |
|
||||
| `deploy/service-map.yml` | 模块、JAR 和端口的唯一映射清单 |
|
||||
| `deploy/docker-compose.yml` | 11 个业务服务的统一编排文件 |
|
||||
| `deploy/env/.env.test.example` | 测试环境变量模板,不含真实密码 |
|
||||
| `deploy/scripts/deploy-services.sh` | Jenkins 节点上的增量部署脚本 |
|
||||
|
||||
## 三、Jenkins 前置条件
|
||||
|
||||
Jenkins 新建“多分支流水线”或“流水线”,脚本路径填写 `Jenkinsfile`。执行节点标签为 `docker-java17`,节点需要安装:
|
||||
|
||||
- JDK 17
|
||||
- Maven 3.8.8 以上
|
||||
- Docker Engine
|
||||
- Docker Compose V2
|
||||
- Git
|
||||
|
||||
Jenkins 执行用户必须有运行 Docker 的权限。部署脚本当前在 Jenkins 节点执行,因此该节点应位于目标服务器,或将同一标签绑定到对应环境的部署节点。
|
||||
|
||||
## 四、必须创建的 Jenkins 凭据
|
||||
|
||||
| Credentials ID | 类型 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| `gitlab-registry-credentials` | Username with password | GitLab Deploy Token 用户名和令牌,至少有 `read_registry`、`write_registry` 权限 |
|
||||
| `firstleg-test-env` | Secret file | 根据 `.env.test.example` 创建的测试环境 `.env` |
|
||||
| `firstleg-staging-env` | Secret file | 预发布环境 `.env`,使用预发布基础设施地址和密钥 |
|
||||
| `firstleg-production-env` | Secret file | 生产环境 `.env`,使用生产基础设施地址和密钥 |
|
||||
|
||||
GitLab 仓库拉取如果不是由 Jenkins GitLab 集成统一授权,还需单独创建只读仓库凭据。任何 MySQL、Redis、Nacos、JWT 和第三方平台密钥都不得直接填写在 Jenkinsfile 或提交到 Git。
|
||||
|
||||
## 五、流水线参数
|
||||
|
||||
| 参数 | 推荐值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `BUILD_MODE` | `AUTO` | 自动分析上次成功构建之后的 Git 变更 |
|
||||
| `SERVICE` | 按需 | `SELECT` 时填写,如 `order,warehouse` |
|
||||
| `ENVIRONMENT` | `test` | 可选 `test`、`staging`、`production` |
|
||||
| `DEPLOY` | 测试环境勾选 | 是否在镜像推送后执行部署 |
|
||||
| `SKIP_TESTS` | 不勾选 | 临时调试才跳过测试 |
|
||||
| `IMAGE_REPOSITORY` | GitLab 项目镜像地址 | 示例 `gitlab.example.com/logistics/baiyue-firstleg` |
|
||||
|
||||
公共模块、根 POM 或 Dockerfile 变化会构建全部 11 个服务。仅 `order/**` 变化时执行:
|
||||
|
||||
```bash
|
||||
mvn -pl order -am clean package
|
||||
```
|
||||
|
||||
`order` 与 `warehouse` 同时变化时只调用一次 Maven:
|
||||
|
||||
```bash
|
||||
mvn -pl order,warehouse -am clean package
|
||||
```
|
||||
|
||||
这样可避免多个并发 Maven 任务同时改写公共模块的 `target` 目录。
|
||||
|
||||
## 六、镜像与部署规则
|
||||
|
||||
每个镜像使用 Git 提交短 SHA 作为不可变标签,例如:
|
||||
|
||||
```text
|
||||
gitlab.example.com/logistics/baiyue-firstleg/order:a1b2c3d4e5f6
|
||||
```
|
||||
|
||||
测试和预发布环境勾选 `DEPLOY` 后自动部署。生产环境会额外出现人工确认,确认内容包含服务列表和镜像标签。增量部署会自动带上 `gateway`,保证网关配置与业务服务保持可用;不会重启未选择的其他业务服务。
|
||||
|
||||
首次部署建议使用 `BUILD_MODE=ALL`。基础设施必须先可用,并已创建以下数据库:`baiyue_auth`、`baiyue_iam`、`baiyue_firstleg`、`baiyue_order`、`baiyue_warehouse`、`baiyue_file`、`baiyue_print`、`baiyue_event_bridge`、`baiyue_audit`。
|
||||
|
||||
## 七、GitLab CI 与 Jenkins 职责
|
||||
|
||||
现有 `.gitlab-ci.yml` 仍执行全量校验和打包,适合合并请求质量检查;Jenkins 负责镜像构建与部署。正式启用 Jenkins 后,建议把 GitLab CI 的“打包服务”任务限制为合并请求或删除,避免一次提交重复进行两次完整打包。
|
||||
|
||||
## 八、回滚
|
||||
|
||||
回滚不重新编译代码。在 Jenkins 用 `SELECT` 选中服务,将部署环境 `.env` 中的镜像标签临时改为已验证的历史 SHA,或在服务器执行同一 Compose 文件指定历史 `IMAGE_TAG` 后 `up -d`。生产回滚仍应保留人工确认和操作记录。
|
||||
Reference in New Issue
Block a user