增加jenkins打包文件
This commit is contained in:
236
Jenkinsfile
vendored
Normal file
236
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(numToKeepStr: '20'))
|
||||
skipDefaultCheckout(true)
|
||||
}
|
||||
|
||||
parameters {
|
||||
string(
|
||||
name: 'ACR_REGISTRY',
|
||||
defaultValue: '',
|
||||
description: 'ACR 登录域名,不包含 https://,优先使用与 ACK 同 VPC 的内网域名'
|
||||
)
|
||||
string(name: 'ACR_NAMESPACE', defaultValue: 'wonderq', description: 'ACR 命名空间')
|
||||
string(name: 'API_REPOSITORY', defaultValue: 'wonderq-api', description: 'API 镜像仓库名')
|
||||
string(name: 'ADMIN_REPOSITORY', defaultValue: 'wonderq-admin-ui', description: 'Admin UI 镜像仓库名')
|
||||
string(name: 'ACK_NAMESPACE', defaultValue: 'wonderq-prod', description: 'ACK Kubernetes 命名空间')
|
||||
string(name: 'API_DEPLOYMENT', defaultValue: 'wonderq-api', description: 'API Deployment 名称')
|
||||
string(name: 'API_CONTAINER', defaultValue: 'api', description: 'API Deployment 中的容器名称')
|
||||
string(name: 'ADMIN_DEPLOYMENT', defaultValue: 'wonderq-admin-ui', description: 'Admin UI Deployment 名称')
|
||||
string(name: 'ADMIN_CONTAINER', defaultValue: 'admin-ui', description: 'Admin UI Deployment 中的容器名称')
|
||||
string(
|
||||
name: 'ADMIN_API_BASE_URL',
|
||||
defaultValue: '',
|
||||
description: 'Admin UI 构建时的 API 地址;ALB 使用同域名 /api 分流时保持为空'
|
||||
)
|
||||
string(
|
||||
name: 'ACR_CREDENTIALS_ID',
|
||||
defaultValue: 'acr-push-credential',
|
||||
description: 'Jenkins 中 ACR 用户名密码 Credential ID'
|
||||
)
|
||||
string(
|
||||
name: 'ACK_KUBECONFIG_CREDENTIALS_ID',
|
||||
defaultValue: 'ack-prod-kubeconfig',
|
||||
description: 'Jenkins 中 ACK kubeconfig Secret File Credential ID'
|
||||
)
|
||||
booleanParam(name: 'DEPLOY_TO_ACK', defaultValue: true, description: '推送镜像后是否更新 ACK Deployment')
|
||||
}
|
||||
|
||||
environment {
|
||||
DOCKER_BUILDKIT = '1'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
script {
|
||||
def commit = sh(script: 'git rev-parse --short=12 HEAD', returnStdout: true).trim()
|
||||
env.IMAGE_TAG = "${commit}-${env.BUILD_NUMBER}"
|
||||
env.API_IMAGE = "${params.ACR_REGISTRY}/${params.ACR_NAMESPACE}/${params.API_REPOSITORY}"
|
||||
env.ADMIN_IMAGE = "${params.ACR_REGISTRY}/${params.ACR_NAMESPACE}/${params.ADMIN_REPOSITORY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Validate') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
|
||||
if [ -z "$ACR_REGISTRY" ]; then
|
||||
echo "ACR_REGISTRY 不能为空" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$ACR_REGISTRY" in
|
||||
*://*)
|
||||
echo "ACR_REGISTRY 只填写域名,不要包含协议" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
command -v docker >/dev/null
|
||||
docker version >/dev/null
|
||||
|
||||
if [ "$DEPLOY_TO_ACK" = "true" ]; then
|
||||
command -v kubectl >/dev/null
|
||||
fi
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build API') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
docker build \
|
||||
--tag "$API_IMAGE:$IMAGE_TAG" \
|
||||
WonderQ-Admin
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Test API') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
|
||||
test_container=''
|
||||
cleanup_test_container() {
|
||||
if [ -n "$test_container" ]; then
|
||||
docker rm --force "$test_container" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cleanup_test_container EXIT
|
||||
|
||||
test_container="$(docker create \
|
||||
--entrypoint python \
|
||||
"$API_IMAGE:$IMAGE_TAG" \
|
||||
-m pytest)"
|
||||
docker cp WonderQ-Admin/tests/. "$test_container:/app/tests"
|
||||
docker start --attach "$test_container"
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Admin UI') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
docker build \
|
||||
--build-arg "VITE_API_BASE_URL=$ADMIN_API_BASE_URL" \
|
||||
--tag "$ADMIN_IMAGE:$IMAGE_TAG" \
|
||||
WonderQ-Admin-UI
|
||||
|
||||
docker run --rm \
|
||||
--entrypoint nginx \
|
||||
"$ADMIN_IMAGE:$IMAGE_TAG" \
|
||||
-t
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Push Images') {
|
||||
steps {
|
||||
withCredentials([
|
||||
usernamePassword(
|
||||
credentialsId: params.ACR_CREDENTIALS_ID,
|
||||
usernameVariable: 'ACR_USERNAME',
|
||||
passwordVariable: 'ACR_PASSWORD'
|
||||
)
|
||||
]) {
|
||||
sh '''
|
||||
set -eu
|
||||
set +x
|
||||
|
||||
docker_config="$(mktemp -d)"
|
||||
export DOCKER_CONFIG="$docker_config"
|
||||
cleanup_docker_config() {
|
||||
docker logout "$ACR_REGISTRY" >/dev/null 2>&1 || true
|
||||
rm -rf -- "$docker_config"
|
||||
}
|
||||
trap cleanup_docker_config EXIT
|
||||
|
||||
printf '%s' "$ACR_PASSWORD" | docker login \
|
||||
--username "$ACR_USERNAME" \
|
||||
--password-stdin \
|
||||
"$ACR_REGISTRY"
|
||||
|
||||
docker push "$API_IMAGE:$IMAGE_TAG"
|
||||
docker push "$ADMIN_IMAGE:$IMAGE_TAG"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy ACK') {
|
||||
when {
|
||||
expression { params.DEPLOY_TO_ACK }
|
||||
}
|
||||
steps {
|
||||
withCredentials([
|
||||
file(
|
||||
credentialsId: params.ACK_KUBECONFIG_CREDENTIALS_ID,
|
||||
variable: 'ACK_KUBECONFIG'
|
||||
)
|
||||
]) {
|
||||
sh '''
|
||||
set -eu
|
||||
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
get deployment "$API_DEPLOYMENT" >/dev/null
|
||||
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
get deployment "$ADMIN_DEPLOYMENT" >/dev/null
|
||||
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
set image "deployment/$API_DEPLOYMENT" \
|
||||
"$API_CONTAINER=$API_IMAGE:$IMAGE_TAG"
|
||||
|
||||
if ! kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
rollout status "deployment/$API_DEPLOYMENT" --timeout=300s; then
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
rollout undo "deployment/$API_DEPLOYMENT" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
set image "deployment/$ADMIN_DEPLOYMENT" \
|
||||
"$ADMIN_CONTAINER=$ADMIN_IMAGE:$IMAGE_TAG"
|
||||
|
||||
if ! kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
rollout status "deployment/$ADMIN_DEPLOYMENT" --timeout=300s; then
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
rollout undo "deployment/$ADMIN_DEPLOYMENT" || true
|
||||
kubectl --kubeconfig "$ACK_KUBECONFIG" \
|
||||
--namespace "$ACK_NAMESPACE" \
|
||||
rollout undo "deployment/$API_DEPLOYMENT" || true
|
||||
exit 1
|
||||
fi
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
echo "部署完成,镜像标签:${env.IMAGE_TAG}"
|
||||
}
|
||||
failure {
|
||||
echo '流水线失败,请查看对应阶段日志;数据库迁移不会随 Deployment 回滚自动撤销。'
|
||||
}
|
||||
}
|
||||
}
|
||||
10
WonderQ-Admin-UI/.dockerignore
Normal file
10
WonderQ-Admin-UI/.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.git/
|
||||
.idea/
|
||||
.env
|
||||
.env.*
|
||||
coverage/
|
||||
.vite/
|
||||
*.log
|
||||
|
||||
29
WonderQ-Admin-UI/Dockerfile
Normal file
29
WonderQ-Admin-UI/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM node:22-bookworm-slim AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
|
||||
# The project keeps Windows native packages for local Yarn 1 installs. The lock
|
||||
# file also contains the matching Linux packages used by this container build.
|
||||
RUN yarn install --frozen-lockfile --non-interactive --ignore-platform
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG VITE_API_BASE_URL=""
|
||||
ENV VITE_API_BASE_URL="${VITE_API_BASE_URL}"
|
||||
|
||||
RUN yarn build
|
||||
|
||||
FROM nginx:1.28-alpine AS runtime
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -q -O /dev/null http://127.0.0.1/healthz || exit 1
|
||||
|
||||
33
WonderQ-Admin-UI/nginx.conf
Normal file
33
WonderQ-Admin-UI/nginx.conf
Normal file
@@ -0,0 +1,33 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
charset utf-8;
|
||||
server_tokens off;
|
||||
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
gzip on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml;
|
||||
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "ok\n";
|
||||
}
|
||||
|
||||
location = /index.html {
|
||||
expires -1;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user