fix(ci): 修复 Jenkinsfile.tenant-web/ops-web 缓存穿透和错误处理
- 添加 --build-arg GIT_COMMIT 使 Docker 每次提交都重新构建源码层
- 添加 NO_CACHE 参数支持强制禁用缓存
- 修复 bat 错误处理(if %errorlevel% neq 0 exit /b 1)
- Deploy 阶段添加 lock('prod-deploy') + retry(2) 防并发失败
- Dockerfile.ops: 将 COPY ops-platform 移到 yarn install 之后,添加 ARG GIT_COMMIT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
07d6307b5d
当前提交
0572bbdd38
@ -4,13 +4,16 @@ WORKDIR /workspace
|
||||
|
||||
COPY package.json ./package.json
|
||||
COPY yarn.lock ./yarn.lock
|
||||
COPY ops-platform ./ops-platform
|
||||
|
||||
ENV YARN_CACHE_FOLDER=/var/cache/yarn
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/yarn,sharing=locked \
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
# GIT_COMMIT invalidates source-code layers on every commit, without re-running yarn install
|
||||
ARG GIT_COMMIT=unknown
|
||||
COPY ops-platform ./ops-platform
|
||||
|
||||
ARG OPS_APP_BASE=/
|
||||
ARG OPS_API_BASE_URL=/api
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ pipeline {
|
||||
parameters {
|
||||
string(name: 'IMAGE_TAG', defaultValue: 'latest', description: '镜像 Tag')
|
||||
booleanParam(name: 'DEPLOY', defaultValue: true, description: '构建后是否自动部署')
|
||||
booleanParam(name: 'NO_CACHE', defaultValue: false, description: '禁用 Docker 构建缓存(缓存错误时使用)')
|
||||
}
|
||||
|
||||
environment {
|
||||
@ -43,12 +44,20 @@ pipeline {
|
||||
withCredentials([string(credentialsId: 'ACR_PASSWORD', variable: 'ACR_PASS')]) {
|
||||
script {
|
||||
def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}"
|
||||
def cacheArgs = params.NO_CACHE
|
||||
? '--no-cache'
|
||||
: "--build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage}"
|
||||
def gitCommit = env.GIT_COMMIT ?: 'unknown'
|
||||
bat """
|
||||
docker login ${env.ACR_REGISTRY} -u ${env.ACR_USERNAME} -p %ACR_PASS%
|
||||
if %errorlevel% neq 0 exit /b 1
|
||||
docker pull --platform=linux/amd64 ${fullImage} || echo Pull failed, will build fresh
|
||||
docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage} -t ${fullImage} .
|
||||
docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg GIT_COMMIT=${gitCommit} ${cacheArgs} -t ${fullImage} .
|
||||
if %errorlevel% neq 0 exit /b 1
|
||||
docker push ${fullImage}
|
||||
docker rmi ${fullImage} || exit 0
|
||||
if %errorlevel% neq 0 exit /b 1
|
||||
docker rmi ${fullImage}
|
||||
exit /b 0
|
||||
"""
|
||||
}
|
||||
}
|
||||
@ -58,12 +67,16 @@ pipeline {
|
||||
stage('Deploy to Production') {
|
||||
when { expression { return params.DEPLOY } }
|
||||
steps {
|
||||
withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) {
|
||||
script {
|
||||
def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}"
|
||||
bat """
|
||||
ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker pull ${fullImage} && docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} && docker image prune -f"
|
||||
"""
|
||||
lock('prod-deploy') {
|
||||
withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) {
|
||||
script {
|
||||
def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}"
|
||||
retry(2) {
|
||||
bat """
|
||||
ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker image prune -f 2>/dev/null || true; docker pull ${fullImage} || exit 1; docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} || exit 1; docker image prune -f"
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ pipeline {
|
||||
parameters {
|
||||
string(name: 'IMAGE_TAG', defaultValue: 'latest', description: '镜像 Tag')
|
||||
booleanParam(name: 'DEPLOY', defaultValue: true, description: '构建后是否自动部署')
|
||||
booleanParam(name: 'NO_CACHE', defaultValue: false, description: '禁用 Docker 构建缓存(缓存错误时使用)')
|
||||
}
|
||||
|
||||
environment {
|
||||
@ -43,12 +44,20 @@ pipeline {
|
||||
withCredentials([string(credentialsId: 'ACR_PASSWORD', variable: 'ACR_PASS')]) {
|
||||
script {
|
||||
def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}"
|
||||
def cacheArgs = params.NO_CACHE
|
||||
? '--no-cache'
|
||||
: "--build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage}"
|
||||
def gitCommit = env.GIT_COMMIT ?: 'unknown'
|
||||
bat """
|
||||
docker login ${env.ACR_REGISTRY} -u ${env.ACR_USERNAME} -p %ACR_PASS%
|
||||
if %errorlevel% neq 0 exit /b 1
|
||||
docker pull --platform=linux/amd64 ${fullImage} || echo Pull failed, will build fresh
|
||||
docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage} -t ${fullImage} .
|
||||
docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg GIT_COMMIT=${gitCommit} ${cacheArgs} -t ${fullImage} .
|
||||
if %errorlevel% neq 0 exit /b 1
|
||||
docker push ${fullImage}
|
||||
docker rmi ${fullImage} || exit 0
|
||||
if %errorlevel% neq 0 exit /b 1
|
||||
docker rmi ${fullImage}
|
||||
exit /b 0
|
||||
"""
|
||||
}
|
||||
}
|
||||
@ -58,12 +67,16 @@ pipeline {
|
||||
stage('Deploy to Production') {
|
||||
when { expression { return params.DEPLOY } }
|
||||
steps {
|
||||
withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) {
|
||||
script {
|
||||
def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}"
|
||||
bat """
|
||||
ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker rm -f xuqm-${env.DEPLOY_SERVICE} 2>/dev/null || true; docker pull ${fullImage} && docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} && docker image prune -f"
|
||||
"""
|
||||
lock('prod-deploy') {
|
||||
withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) {
|
||||
script {
|
||||
def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}"
|
||||
retry(2) {
|
||||
bat """
|
||||
ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker image prune -f 2>/dev/null || true; docker pull ${fullImage} || exit 1; docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} || exit 1; docker image prune -f"
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户