2-2同步

This commit is contained in:
2026-02-02 00:56:16 +08:00
parent 03e175b47c
commit 8ffe3e0e15
5 changed files with 99 additions and 69 deletions

View File

@@ -1,18 +1,45 @@
# ==================== 安装依赖阶段(构建镜像)====================
# 基础镜像3.12-slim轻量化小镜像 # 基础镜像3.12-slim轻量化小镜像
FROM python:3.12-slim as builder FROM python:3.12-slim AS builder
# 构建阶段:隔离构建依赖,最终镜像仅保留运行时,镜像瘦身 # 构建阶段:隔离构建依赖,最终镜像仅保留运行时,镜像瘦身
WORKDIR /app WORKDIR /app
# 默认测试环境可通过env修改 development/production
ENV PYTHONUNBUFFERED=1 \ ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \ PYTHONDONTWRITEBYTECODE=1 \
UV_SYSTEM_PYTHON=1 \ UV_SYSTEM_PYTHON=1 \
UV_HTTP_TIMEOUT=600 \ UV_HTTP_TIMEOUT=600 \
UV_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \ UV_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \
UV_EXTRA_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \ UV_EXTRA_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
# 安装构建依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
g++ \
build-essential \
python3-dev \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# 复制uv和依赖文件
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml uv.lock ./
# 安装依赖:--frozen锁定版本--no-dev默认安装生产依赖
# 依赖安装到venv保证隔离性同时支持uv run调用
RUN uv sync --frozen --no-dev
# ==================== 运行时阶段(最终镜像)====================
FROM python:3.12-slim AS runtime
# 默认测试环境,可通过 env 修改
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
# 可外部覆盖的核心启动参数 # 可外部覆盖的核心启动参数
APP_ENV=test \ ENV=test \
APP_PORT=8031 \ APP_PORT=8031 \
APP_HOST=0.0.0.0 \ APP_HOST=0.0.0.0 \
# 是否开启热重载:默认关闭(生产/测试环境不需要开发环境可设为1 # 是否开启热重载:默认关闭(生产/测试环境不需要开发环境可设为1
@@ -20,32 +47,6 @@ ENV PYTHONUNBUFFERED=1 \
# 日志配置文件默认使用json配置可设为空关闭 # 日志配置文件默认使用json配置可设为空关闭
APP_LOG_CONFIG=logging_config.json APP_LOG_CONFIG=logging_config.json
# 安装构建依赖仅builder阶段需要运行时镜像会剔除
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
g++ \
build-essential \
python3-dev \
&& rm -rf /var/lib/apt/lists/* \
# 清理apt缓存进一步减小构建层体积
&& apt-get clean
# 复制uv和依赖文件**核心缓存层**仅pyproject.toml/uv.lock变更时才重新安装依赖
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml uv.lock ./
# 安装依赖:--frozen锁定版本--no-dev默认安装生产依赖开发环境可通过uv sync --dev覆盖
# 依赖安装到venv保证隔离性同时支持uv run调用
RUN uv sync --frozen --no-dev
# ==================== 运行时阶段(最终镜像)====================
FROM python:3.12-slim as runtime
# 继承构建阶段的环境变量(可被外部传参覆盖)
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
UV_SYSTEM_PYTHON=1
# 设置工作目录,与构建阶段一致 # 设置工作目录,与构建阶段一致
WORKDIR /app WORKDIR /app
@@ -62,9 +63,5 @@ EXPOSE ${APP_PORT}
# 启动脚本用shell脚本解析环境变量动态生成启动命令 # 启动脚本用shell脚本解析环境变量动态生成启动命令
CMD ["/bin/sh", "-c", \ CMD ["/bin/sh", "-c", \
"if [ $APP_RELOAD -eq 1 ]; then \ "exec uv run uvicorn app.main:app --host ${APP_HOST} --port ${APP_PORT} --log-config logging_config.json" \
uv run uvicorn app.main:app --host $APP_HOST --port $APP_PORT --reload ${APP_LOG_CONFIG:+-log-config $APP_LOG_CONFIG}; \
else \
uv run uvicorn app.main:app --host $APP_HOST --port $APP_PORT ${APP_LOG_CONFIG:+-log-config $APP_LOG_CONFIG}; \
fi" \
] ]

View File

@@ -1,10 +1,54 @@
# 使用 Nginx 官方轻量镜像 # # 使用 Nginx 官方轻量镜像
FROM nginx:1.25-alpine # FROM nginx:1.25-alpine
# 拷贝前端构建产物 # # 拷贝前端构建产物
COPY dist/ /usr/share/nginx/html/ # COPY dist/ /usr/share/nginx/html/
# 暴露端口 # # 暴露端口
# EXPOSE 80
# CMD ["nginx", "-g", "daemon off;"]
# 第一阶段:构建阶段 - 使用指定node20.15.0版本安装pnpm
FROM node:20.15.0-alpine AS builder
# 设定工作目录(容器内的项目目录)
WORKDIR /app
# 配置pnpm源可选加速国内安装
ENV PNPM_REGISTRY=https://registry.npmmirror.com/
# 全局安装pnpmnode20+推荐corepack管理pnpm更适配
RUN corepack enable && corepack prepare pnpm@latest --activate
# 复制包管理文件先复制lock文件利用docker层缓存避免代码变动重复装包
COPY package.json pnpm-lock.yaml* ./
# 安装项目依赖(--frozen-lockfile 锁定依赖版本,保证构建一致性)
RUN pnpm install --frozen-lockfile
# 复制整个项目代码到容器
COPY . .
# 构建参数指定构建环境默认im可在build时覆盖为test/im/prod等
ARG BUILD_ENV=im
# 执行对应环境的构建命令拼接为pnpm build:xxx
RUN pnpm build:${BUILD_ENV}
RUN mv /app/dist/main/index.html /app/dist/
# 第二阶段:运行阶段 - 使用官方Nginx稳定版仅保留dist目录减小镜像体积
FROM nginx:stable-alpine
# 暴露Nginx默认端口前端项目常规端口
EXPOSE 80 EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] # 可选替换Nginx默认配置解决前端路由刷新404、开启gzip压缩优化静态资源
# COPY ./nginx.conf /etc/nginx/conf.d/default.conf
# 从构建阶段builder复制构建后的dist目录到Nginx的静态资源根目录
COPY --from=builder /app/dist /usr/share/nginx/html
# Nginx镜像默认会启动nginx无需额外CMD/ENTRYPOINT

View File

@@ -38,7 +38,7 @@ pipeline {
) )
choice( choice(
name: 'BUILD_ENV', name: 'BUILD_ENV',
choices: ['im', 's2', 'prod'], choices: ['im', 'prod'],
description: '选择构建的环境配置, 默认为 pnpm build:im 构建' description: '选择构建的环境配置, 默认为 pnpm build:im 构建'
) )
string( string(
@@ -97,6 +97,8 @@ pipeline {
stage('获取信息') { stage('获取信息') {
steps { steps {
script { script {
def cause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
env.ACTUAL_USER = cause ? cause.userName[0] : "系统/自动触发"
// 获取分支名 // 获取分支名
env.Code_branch = "${params.Code_branch}" env.Code_branch = "${params.Code_branch}"
// 获取最近一次提交的哈希值短格式前8位 // 获取最近一次提交的哈希值短格式前8位
@@ -170,23 +172,6 @@ pipeline {
} }
} }
stage('pnpm i&b') {
steps {
script {
def buildEnv = params.BUILD_ENV // 获取参数
sh """
export PATH="/data/nvm/versions/node/v20.15.0/bin:$PATH"
echo "开始安装依赖包"
cd ${WORKSPACE}/ && rm -rf node_modules && pnpm install
echo "开始构建"
pnpm build:${buildEnv}
mv dist/main/index.html dist/
chmod -R 755 dist/
"""
}
}
}
stage('登录容器') { stage('登录容器') {
steps { steps {
withCredentials([usernamePassword( withCredentials([usernamePassword(
@@ -206,7 +191,7 @@ pipeline {
script { script {
// 构建镜像,添加标签信息 // 构建镜像,添加标签信息
sh """ sh """
docker build -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} \ docker build -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} --build-arg BUILD_ENV=${params.BUILD_ENV} \
--label "git-branch='${Code_branch}'" \ --label "git-branch='${Code_branch}'" \
--label "git-commit='${GIT_COMMIT_SHORT}'" \ --label "git-commit='${GIT_COMMIT_SHORT}'" \
--label "git-author='${GIT_AUTHOR}'" \ --label "git-author='${GIT_AUTHOR}'" \

View File

@@ -16,7 +16,7 @@ pipeline {
name: 'IMAGE_NAME', name: 'IMAGE_NAME',
description: 'sit 仓库镜像 (除非输入 CUSTOM_IMAGE, 否则使用这里的)', description: 'sit 仓库镜像 (除非输入 CUSTOM_IMAGE, 否则使用这里的)',
registry: 'https://uswccr.ccs.tencentyun.com', registry: 'https://uswccr.ccs.tencentyun.com',
image: 'lessiesit/lessie-review-service', image: 'lessiesit/lessie-email',
credentialId: 'dxin_img_hub_auth', credentialId: 'dxin_img_hub_auth',
filter: '.*', filter: '.*',
defaultTag: '', defaultTag: '',
@@ -26,7 +26,7 @@ pipeline {
string( string(
name: 'CUSTOM_IMAGE', name: 'CUSTOM_IMAGE',
defaultValue: '', defaultValue: '',
description: '手输完整镜像<registry>/<namespace>/<repo>:<tag>,例如: uswccr.ccs.tencentyun.com/lessiesit/lessie-sourcing-agents:v0.0.1 (填充后,则忽略镜像仓库选择)' description: '手输完整镜像<registry>/<namespace>/<repo>:<tag>,例如: uswccr.ccs.tencentyun.com/lessiesit/lessie-email:v0.0.1 (填充后,则忽略镜像仓库选择)'
) )
booleanParam( booleanParam(
name: 'ROLLBACK_VERSION', name: 'ROLLBACK_VERSION',
@@ -42,11 +42,11 @@ pipeline {
environment { environment {
LARK_ROBOT = "4b8d66d0-c0f0-4587-b0e5-cff772cb3046" // ID LARK_ROBOT = "4b8d66d0-c0f0-4587-b0e5-cff772cb3046" // ID
KUBECONFIG = credentials('k8s-test-config-admin') // k8s ID, Jenkins KUBECONFIG = credentials('k8s-test-config-admin') // k8s ID, Jenkins
Deployment_yaml = "${WORKSPACE}/lessie-ai/sit/s1/lessie-review-service.yaml" Deployment_yaml = "${WORKSPACE}/lessie-ai/sit/s1/lessie-email.yaml"
Deployment_name = "sit-lessie-review-service" Deployment_name = "s1-lessie-email"
K8s_namespace = "sit" K8s_namespace = "sit"
Pod_container_name = "lessie-review-service" Pod_container_name = "lessie-email"
Pod_environment = "sit" Pod_environment = "s1"
} }
stages { stages {
stage('回滚上版') { stage('回滚上版') {
@@ -86,7 +86,7 @@ pipeline {
def matcher = (customImage =~ imageRegex) def matcher = (customImage =~ imageRegex)
if (!matcher.matches()) { if (!matcher.matches()) {
error "CUSTOM_IMAGE 格式不正确,必须是 registry/namespace/repository:tag 格式\n" + error "CUSTOM_IMAGE 格式不正确,必须是 registry/namespace/repository:tag 格式\n" +
"示例: uswccr.ccs.tencentyun.com/lessiesit/lessie-sourcing-agents:v1.0.0\n" + "示例: uswccr.ccs.tencentyun.com/lessie-ai-web:v1.0.0\n" +
"当前输入: ${customImage}" "当前输入: ${customImage}"
} }
@@ -188,21 +188,25 @@ pipeline {
sh """ sh """
echo "--- Deployment 状态 ---" echo "--- Deployment 状态 ---"
kubectl describe deployment ${Deployment_name} -n ${K8s_namespace} || true kubectl describe deployment ${Deployment_name} -n ${K8s_namespace} || true
echo " "
echo '--- Pod 列表 ---' echo '--- Pod 列表 ---'
kubectl get pods -n ${K8s_namespace} -l "app=${Pod_container_name},environment=${Pod_environment}" -o wide || true kubectl get pods -n ${K8s_namespace} -l "app=${Pod_container_name},environment=${Pod_environment}" -o wide || true
echo " "
echo "--- Pod 描述 (describe) ---" echo "--- Pod 描述 (describe) ---"
for pod in \$(kubectl get pods -n ${K8s_namespace} -l "app=${Pod_container_name},environment=${Pod_environment}" -o jsonpath='{.items[*].metadata.name}'); do for pod in \$(kubectl get pods -n ${K8s_namespace} -l "app=${Pod_container_name},environment=${Pod_environment}" -o jsonpath='{.items[*].metadata.name}'); do
echo "-- Pod 描述 \$pod --" echo "-- Pod 描述 \$pod --"
kubectl describe pod \$pod -n ${K8s_namespace} || true kubectl describe pod \$pod -n ${K8s_namespace} || true
done done
echo " "
echo "--- 最近 200 行 Pod 日志 ---" echo "--- 最近 200 行 Pod 日志 ---"
for pod in \$(kubectl get pods -n ${K8s_namespace} -l "app=${Pod_container_name},environment=${Pod_environment}" -o jsonpath='{.items[*].metadata.name}'); do for pod in \$(kubectl get pods -n ${K8s_namespace} -l "app=${Pod_container_name},environment=${Pod_environment}" -o jsonpath='{.items[*].metadata.name}'); do
echo "-- logs \$pod --" echo "-- logs \$pod --"
kubectl logs \$pod -n ${K8s_namespace} --tail=200 || true kubectl logs \$pod -n ${K8s_namespace} --tail=200 || true
done done
echo " "
""" """
error("=== Deployment 发布失败,请检查以上输出定位问题 ===") error("=== Deployment 发布失败,请检查以上输出定位问题 ===")
} }