From 193af53735000780de2f3c0fd0242607eed2d802 Mon Sep 17 00:00:00 2001 From: dxinn <1554389441@qq.com> Date: Tue, 27 Jan 2026 18:21:54 +0800 Subject: [PATCH] =?UTF-8?q?1-27=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile/python/apex/apex_dockerfile | 53 +-- Dockerfile/python/lessie-email/Dockerfile | 66 +++ .../v4/build_image_flymoon_admin_v4.groovy | 6 +- .../v4/build_image_flymoon_agent_v4.groovy | 9 +- .../v4/build_image_flymoon_payment_v4.groovy | 3 + .../v4/build_image_lessie_email_v4.groovy | 405 ++++++++++++++++++ .../v4/build_image_lessie_go_api_v4.groovy | 5 +- k8s_yaml/ELK/filebast/filebeat.yaml | 4 +- 8 files changed, 511 insertions(+), 40 deletions(-) create mode 100644 Dockerfile/python/lessie-email/Dockerfile create mode 100644 SCM/构建镜像/v4/build_image_lessie_email_v4.groovy diff --git a/Dockerfile/python/apex/apex_dockerfile b/Dockerfile/python/apex/apex_dockerfile index 77220a7..6d9d23a 100644 --- a/Dockerfile/python/apex/apex_dockerfile +++ b/Dockerfile/python/apex/apex_dockerfile @@ -1,54 +1,45 @@ # =============================== # 1) 构建依赖阶段 # =============================== -FROM python:3.12.9-alpine AS build +FROM uswccr.ccs.tencentyun.com/lessie/python:3.12-slim AS build WORKDIR /app -# 替换 Alpine 国内源 -RUN echo "https://mirrors.aliyun.com/alpine/v3.21/main/" > /etc/apk/repositories && \ - echo "https://mirrors.aliyun.com/alpine/v3.21/community/" >> /etc/apk/repositories +# 使用 uv 官方镜像中的二进制文件(不用 pip install uv ) +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uv/bin/uv -# 构建依赖 -RUN apk add --no-cache \ - gcc \ - musl-dev \ - libffi-dev \ - openssl-dev \ - curl - -# 安装 uv -RUN pip install -i https://mirrors.aliyun.com/pypi/simple/ --no-cache-dir uv - -# uv 使用的 PyPI 镜像 -ENV UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ +# APT 源替换 + 安装依赖(合并为一个 RUN,最大化缓存) +RUN set -eux; \ + sed -i 's@deb.debian.org@mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list.d/debian.sources; \ + apt-get update; \ + apt-get install -y --no-install-recommends build-essential git; \ + rm -rf /var/lib/apt/lists/* # 拷贝依赖定义(用于缓存) COPY uv.lock pyproject.toml ./ -# 安装依赖到 .venv -RUN uv sync --no-dev +# 使用 uv 安装依赖(--frozen 确保锁定版本) +RUN /uv/bin/uv sync --frozen --no-dev --no-install-project # =============================== # 2) 运行阶段 # =============================== -FROM python:3.12.9-alpine AS runtime +FROM uswccr.ccs.tencentyun.com/lessie/python:3.12-slim AS runtime WORKDIR /app -# 替换 Alpine 国内源 -RUN echo "https://mirrors.aliyun.com/alpine/v3.21/main/" > /etc/apk/repositories && \ - echo "https://mirrors.aliyun.com/alpine/v3.21/community/" >> /etc/apk/repositories +# 设置时区 (Debian 方式) +RUN set -eux; \ + sed -i 's@deb.debian.org@mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list.d/debian.sources; \ + apt-get update; \ + apt-get install -y --no-install-recommends tzdata; \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime; \ + echo "Asia/Shanghai" > /etc/timezone; \ + rm -rf /var/lib/apt/lists/* -# 时区 -RUN apk add --no-cache tzdata && \ - ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ - echo "Asia/Shanghai" > /etc/timezone - -# 拷贝 uv、虚拟环境、Python 运行库 -COPY --from=build /usr/local/bin/uv /usr/local/bin/uv +# 拷贝 uv、虚拟环境 +COPY --from=build /uv/bin/uv /usr/local/bin/uv COPY --from=build /app/.venv /app/.venv -COPY --from=build /usr/local/lib/python3.12 /usr/local/lib/python3.12 # 拷贝代码 COPY . . diff --git a/Dockerfile/python/lessie-email/Dockerfile b/Dockerfile/python/lessie-email/Dockerfile new file mode 100644 index 0000000..1dc66fa --- /dev/null +++ b/Dockerfile/python/lessie-email/Dockerfile @@ -0,0 +1,66 @@ +# ==================== 构建阶段 ==================== +FROM python:3.12-slim AS builder + +# 设置工作目录 +WORKDIR /app + +# 设置环境变量 +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_SYSTEM_PYTHON=1 \ + UV_HTTP_TIMEOUT=600 \ + UV_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 \ + && rm -rf /var/lib/apt/lists/* + +# 使用 uv 官方镜像中的二进制文件(不用 pip install uv ) +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +# 复制依赖文件(利用 Docker 缓存) +COPY pyproject.toml uv.lock ./ + +# 安装项目依赖(不包括开发依赖,使用 frozen 锁定版本) +RUN uv sync --frozen --no-dev + +# ==================== 运行阶段 ==================== +FROM python:3.12-slim AS runtime + +# 设置工作目录 +WORKDIR /app + +# 设置环境变量 +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_SYSTEM_PYTHON=1 \ + PATH="/app/.venv/bin:$PATH" + +# 从构建阶段复制依赖 +COPY --from=builder /app/.venv /app/.venv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +# 创建非 root 用户 +RUN groupadd -r appuser && useradd -r -g appuser appuser \ + && chown -R appuser:appuser /app + +# 复制项目文件 +COPY --chown=appuser:appuser . . + +# 切换到非 root 用户 +USER appuser + +# 暴露端口 +EXPOSE 8031 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD python -c "from urllib.request import urlopen; urlopen('http://localhost:8031/health', timeout=5).read()" || exit 1 + +# 启动命令 +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8031", "--workers", "1"] diff --git a/SCM/构建镜像/v4/build_image_flymoon_admin_v4.groovy b/SCM/构建镜像/v4/build_image_flymoon_admin_v4.groovy index 40ec7fa..a5764eb 100644 --- a/SCM/构建镜像/v4/build_image_flymoon_admin_v4.groovy +++ b/SCM/构建镜像/v4/build_image_flymoon_admin_v4.groovy @@ -241,7 +241,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "admin镜像构建成功", + title: "${IMAGE_NAME}镜像构建成功", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", @@ -373,7 +373,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "admin镜像构建失败", + title: "${IMAGE_NAME}镜像构建失败", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", @@ -392,7 +392,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "admin镜像构建取消", + title: "${IMAGE_NAME}镜像构建取消", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", diff --git a/SCM/构建镜像/v4/build_image_flymoon_agent_v4.groovy b/SCM/构建镜像/v4/build_image_flymoon_agent_v4.groovy index b8c64a9..3c61653 100644 --- a/SCM/构建镜像/v4/build_image_flymoon_agent_v4.groovy +++ b/SCM/构建镜像/v4/build_image_flymoon_agent_v4.groovy @@ -94,6 +94,9 @@ pipeline { def formattedBranch = branchName.replace('/', '-').replace('_', '-') // 替换分支名中的/和_为- // 构建时间(格式:202510181215,年-月-日-时-分,无分隔符) def buildTime = sh(script: 'date +%Y%m%d%H%M', returnStdout: true).trim() + // 当前时间(格式:YYYY-MM-DD HH:MM:SS) + env.currentTime = sh(script: 'date "+%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + def defaultTag = "v${buildNumber}_${formattedBranch}_${GIT_COMMIT_SHORT}_${buildTime}" def customTag = params.CUSTOM_TAG?.trim() @@ -237,7 +240,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "flymoon-agent镜像构建成功", + title: "${IMAGE_NAME}镜像构建成功", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", @@ -368,7 +371,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "flymoon-agent镜像构建失败", + title: "${IMAGE_NAME}镜像构建失败", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", @@ -387,7 +390,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "apex镜像构建取消", + title: "${IMAGE_NAME}镜像构建取消", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", diff --git a/SCM/构建镜像/v4/build_image_flymoon_payment_v4.groovy b/SCM/构建镜像/v4/build_image_flymoon_payment_v4.groovy index ae430d8..ce400ae 100644 --- a/SCM/构建镜像/v4/build_image_flymoon_payment_v4.groovy +++ b/SCM/构建镜像/v4/build_image_flymoon_payment_v4.groovy @@ -94,6 +94,9 @@ pipeline { def formattedBranch = branchName.replace('/', '-').replace('_', '-') // 替换分支名中的/和_为- // 构建时间(格式:202510181215,年-月-日-时-分,无分隔符) def buildTime = sh(script: 'date +%Y%m%d%H%M', returnStdout: true).trim() + // 当前时间(格式:YYYY-MM-DD HH:MM:SS) + env.currentTime = sh(script: 'date "+%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + def defaultTag = "v${buildNumber}_${formattedBranch}_${GIT_COMMIT_SHORT}_${buildTime}" def customTag = params.CUSTOM_TAG?.trim() diff --git a/SCM/构建镜像/v4/build_image_lessie_email_v4.groovy b/SCM/构建镜像/v4/build_image_lessie_email_v4.groovy new file mode 100644 index 0000000..1877c55 --- /dev/null +++ b/SCM/构建镜像/v4/build_image_lessie_email_v4.groovy @@ -0,0 +1,405 @@ +// --- 辅助函数:深拷贝对象以确保可序列化 --- +def deepCopyForSerialization(obj) { + if (obj instanceof Map) { + // 创建新的 LinkedHashMap,递归拷贝值 + return obj.collectEntries { k, v -> [(k): deepCopyForSerialization(v)] } + } else if (obj instanceof List) { + // 创建新的 ArrayList,递归拷贝元素 + return obj.collect { item -> deepCopyForSerialization(item) } + } else if (obj instanceof String || obj instanceof Number || obj instanceof Boolean || obj == null) { + return obj + } else { + return obj.toString() + } +} +// --- 结束辅助函数 --- + +pipeline { + agent any + + parameters { + gitParameter( + branchFilter: 'origin/(.*)', + defaultValue: 'dxin', + name: 'Code_branch', + type: 'PT_BRANCH_TAG', + selectedValue: 'DEFAULT', + sortMode: 'NONE', + description: '选择代码分支: ', + quickFilterEnabled: true, + tagFilter: '*', + listSize: "1" + ) + choice( + name: 'NAME_SPACES', + choices: ['sit', 'test', 'prod'], + description: '选择存放镜像的仓库命名空间:' + ) + string( + name: 'CUSTOM_TAG', + defaultValue: '', + description: '可选:自定义镜像 Tag (字母、数字、点、下划线、短横线), 留空则自动生成 “ v+构建次数_分支名_短哈希_构建时间 ”' + ) + booleanParam( + name: 'DEPLOY_TO_S1', + defaultValue: false, + description: '可选:构建成功后部署到 S1 环境 (触发 job: DM_s1_lessie_email)' + ) + } + environment { + LARK_ROBOT = "4b8d66d0-c0f0-4587-b0e5-cff772cb3046" // 飞书机器人ID,用于发送构建通知 + REGISTRY = "uswccr.ccs.tencentyun.com" // 镜像仓库地址 + NAMESPACE = "lessie${params.NAME_SPACES}" // 命名空间根据choices的选择拼接 + IMAGE_NAME = "lessie-email" // 镜像名(固定前缀) + CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID + } + + stages { + stage('拉取代码') { + steps { + git branch: "${params.Code_branch}", + credentialsId: 'fly_gitlab_auth', + url: 'http://172.24.16.20/python/lessie-email.git' + } + } + stage('获取信息') { + steps { + script { + // 获取构建者 + def cause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause') + env.ACTUAL_USER = cause ? cause.userName[0] : "系统/自动触发" + // 获取分支名 + env.Code_branch = "${params.Code_branch}" + // 获取最近一次提交的哈希值(短格式,前8位) + env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD',returnStdout: true).trim() + // 获取最近一次提交的哈希值(全格式) + env.GIT_COMMIT_LONG = sh(script: 'git rev-parse HEAD', returnStdout: true).trim() + // 获取最近一次提交的作者 + env.GIT_AUTHOR = sh(script: 'git log -1 --pretty=format:%an',returnStdout: true).trim() + // 获取最近一次提交的时间(格式化) + env.GIT_COMMIT_TIME = sh( + script: 'git log -1 --pretty=format:%ct | xargs -I {} date -d @{} +%Y%m%d-%H%M%S', + returnStdout: true + ).trim() + // 获取最近一次提交的备注信息(转义特殊字符,避免构建失败) + env.GIT_COMMIT_MSG = sh(script: 'git log -1 --pretty=format:%s | sed -e \'s/"/\\"/g\'', returnStdout: true).trim() + + // Jenkins构建次数 + def buildNumber = env.BUILD_NUMBER // Jenkins内置变量,直接获取当前Job的构建序号 + // 当前分支名(处理/为-,如feature/docker_1015 → feature-docker_1015) + def branchName = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim() + def formattedBranch = branchName.replace('/', '-').replace('_', '-') // 替换分支名中的/和_为- + // 构建时间(格式:202510181215,年-月-日-时-分,无分隔符) + def buildTime = sh(script: 'date +%Y%m%d%H%M', returnStdout: true).trim() + // 当前时间(格式:YYYY-MM-DD HH:MM:SS) + env.currentTime = sh(script: 'date "+%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + + def defaultTag = "v${buildNumber}_${formattedBranch}_${GIT_COMMIT_SHORT}_${buildTime}" + + def customTag = params.CUSTOM_TAG?.trim() + def tagPattern = ~/^[a-zA-Z0-9._-]+$/ + + // 判断最终Tag + if (customTag && customTag ==~ tagPattern) { + echo "✅ 使用自定义镜像 Tag: ${customTag}" + env.IMAGE_TAG = customTag + } else if (customTag) { + echo "⚠️ 自定义 Tag '${customTag}' 不符合规范,将使用默认生成的 Tag: ${defaultTag}" + + def confirmed = true + timeout(time: 1, unit: 'MINUTES') { + try { + input( + message: """⚠️ Tag 命名不规范: + ${customTag} + + 将使用自动生成的 Tag: + ${defaultTag} + + 是否继续构建?""", + ok: '确认' + ) + } catch (err) { + // 用户点击“取消”或中断 + echo "🚫 用户取消构建" + confirmed = false + } + } + + if (confirmed) { + echo "✅ 用户确认使用自动生成的 Tag:${defaultTag}" + env.IMAGE_TAG = defaultTag + } else { + error("流水线已终止。") + } + } else { + env.IMAGE_TAG = defaultTag + echo "未输入自定义 Tag, 使用自动生成规则: ${env.IMAGE_TAG}" + } + } + } + } + stage('登录仓库') { + steps { + withCredentials([usernamePassword( + credentialsId: env.CREDENTIALS_ID, + usernameVariable: 'REGISTRY_USER', + passwordVariable: 'REGISTRY_PWD' + )]) { + sh ''' + echo "$REGISTRY_PWD" | docker login ${REGISTRY} -u ${REGISTRY_USER} --password-stdin + ''' + } + } + } + stage('构建镜像') { + steps { + script { + // 构建镜像,添加标签信息 + sh """ + docker build -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} \ + --label "git-branch='${Code_branch}'" \ + --label "git-commit='${GIT_COMMIT_SHORT}'" \ + --label "git-author='${GIT_AUTHOR}'" \ + --label "git-message='${GIT_COMMIT_MSG}'" \ + --label "build-time='${GIT_COMMIT_TIME}'" \ + . + """ + } + } + } + stage('推送镜像') { + steps { + script { + // 推送镜像(带唯一 Tag) + sh "docker push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "推送镜像成功:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + } + } + } + } + + post { + always { + script { + def keepCount = 3 + echo "开始清理本地旧镜像,仅保留最近 ${keepCount} 个构建版本" + def imagePrefix = "${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}" + + // 获取所有镜像(按创建时间排序,越新的越前) + // 格式:Repository:Tag ImageID CreatedAt + def allImagesRaw = sh(script: "docker images ${imagePrefix} --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.CreatedAt}}' | sort -rk3", returnStdout: true).trim() + if (!allImagesRaw) { + echo "未找到任何镜像,无需清理" + return + } + + def allImages = allImagesRaw.split('\n') + if (allImages.size() <= keepCount) { + echo "当前镜像数未超过 ${keepCount} 个,无需清理" + return + } + + def oldImages = allImages.drop(keepCount) + echo "发现 ${oldImages.size()} 个旧镜像需要清理" + oldImages.each { line -> + echo " ${line}" + } + + oldImages.each { line -> + def parts = line.split(' ') + def imageTag = parts[0] + def imageId = parts.size() > 1 ? parts[1] : "" + + // 对于标签为的无效镜像,使用镜像ID删除 + if (imageTag.contains("") && imageId) { + echo "删除无效镜像: ${imageId}" + sh(returnStatus: true, script: "docker rmi -f ${imageId} || true") + } else if (imageId) { + // 对于有标签的有效镜像,优先使用镜像ID删除 + echo "删除旧镜像: ${imageTag} (${imageId})" + sh(returnStatus: true, script: "docker rmi -f ${imageId} || true") + } else { + // 兜底方案,使用标签删除 + echo "删除旧镜像: ${imageTag}" + sh(returnStatus: true, script: "docker rmi -f ${imageTag} || true") + } + } + + echo "清理完成,当前镜像状态:" + sh """ + docker images ${imagePrefix} --format 'table {{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}\\t{{.Size}}' + """ + } + } + success { + script { + lark ( + robot: "${LARK_ROBOT}", + type: "CARD", + title: "${IMAGE_NAME}镜像构建成功", + text: [ + "📅 **构建时间**: ${currentTime}", + "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", + "🔢 **任务编号**: [${BUILD_DISPLAY_NAME}](${BUILD_URL})", + "🔀 **代码分支**: ${params.Code_branch}", + "🌟 **构建状态**: 构建成功", + "⏱️ **构建用时**: ${currentBuild.duration.intdiv(1000)} 秒", + "📦 **镜像名称**: ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}", + "📝 **提交摘要**: ${env.GIT_COMMIT_MSG}", + "👤 **执行账号**: ${env.ACTUAL_USER}", + ] + ) + // 1. 准备元数据 (转换所有环境变量为 String) + def metadataDir = '/var/lib/jenkins/metadata' + def metadataFileRelativePath = "${env.NAMESPACE}-${env.IMAGE_NAME}.json" // 相对于 metadataDir 的文件名 + def fullMetadataPath = "${metadataDir}/${metadataFileRelativePath}" + + // --- 转换为 String --- + def registry = env.REGISTRY as String + def namespace = env.NAMESPACE as String + def imageName = env.IMAGE_NAME as String + def imageTag = env.IMAGE_TAG as String + def codeBranch = params.Code_branch as String // 使用 params,因为 Code_branch 是参数 + def gitCommit = env.GIT_COMMIT_LONG as String + def gitAuthor = env.GIT_AUTHOR as String + def gitCommitMsg = env.GIT_COMMIT_MSG as String + def gitCommitTime = env.GIT_COMMIT_TIME as String + def buildNumber = env.BUILD_NUMBER as String + // --- 转换为 String --- + + // 2. 准备新数据 + def newImageData = [ + image_tag: imageTag, // 使用转换后的变量 + full_image_name: "${registry}/${namespace}/${imageName}:${imageTag}", // 使用转换后的变量 + labels: [ + "git-branch": codeBranch, + "git-commit": gitCommit, + "git-author": gitAuthor, + "git-message": gitCommitMsg, + "build-time": gitCommitTime + ], + build_job_number: buildNumber, + build_time: new Date().format('yyyy-MM-dd HH:mm:ss') // Jenkins 构建完成时间 + ] + + // 2. 读取现有数据(如果文件存在) + def existingDataList = [] + try { + // 使用 readJSON 步骤读取文件内容 (readJSON 会自动处理 LazyMap 问题) + def rawExistingData = readJSON file: fullMetadataPath, default: [] // 如果文件不存在,则返回空列表 [] + + // --- ✅ 修复:深拷贝 rawExistingData (修正内联代码) --- + if (rawExistingData instanceof List) { + existingDataList = rawExistingData.collect { item -> + if (item instanceof Map) { + // 递归深拷贝 Map (使用辅助函数) + return deepCopyForSerialization(item) + } else { + return item + } + } + } else { + echo "警告: 元数据文件 ${fullMetadataPath} 格式不正确(非 List 类型),将被覆盖。" + existingDataList = [] + } + // --- 结束修复 --- + + } catch (Exception e) { + // readJSON 在文件不存在时通常会返回 default 值,但如果文件存在但格式错误,会抛出异常 + echo "警告: 读取元数据文件 ${fullMetadataPath} 失败或格式错误: ${e.getMessage()},将被覆盖。" + // 确保目录存在 + sh "mkdir -p ${metadataDir}" + existingDataList = [] // 重置为新列表 + } + + // 3. 将新数据添加到列表开头(最新的在前) + existingDataList.add(0, newImageData) + + // 4. 限制列表大小为 20 + if (existingDataList.size() > 20) { + existingDataList = existingDataList.take(20) + } + + // 5. 使用 writeJSON 步骤写入文件 (writeJSON 会自动处理 Map 的序列化) + writeJSON file: fullMetadataPath, json: existingDataList, pretty: 2 // pretty: 2 表示格式化 JSON (2 个空格缩进) + + echo "镜像元数据已存储到: ${fullMetadataPath}" + + // 输出构建结果 + echo """ + 镜像地址:${registry}/${namespace}/${imageName}:${imageTag} + 对应代码提交哈希:${gitCommit} + 对应代码分支:${codeBranch} + 代码提交者:${gitAuthor} + 提交备注:${gitCommitMsg} + """.stripIndent() + } + // 构建成功后,若选择则触发部署到 S1 环境 + script { + // 定义部署映射:参数名 → 部署 Job 名 + def deployMap = [ + 'DEPLOY_TO_S1' : 'DM_s1_lessie_email', + ] + + // 获取完整镜像名(从已有 env 变量拼接) + def fullImageName = "${env.REGISTRY}/${env.NAMESPACE}/${env.IMAGE_NAME}:${env.IMAGE_TAG}" + + echo "构建完成镜像:${fullImageName}" + echo "检查部署触发项..." + + deployMap.each { paramKey, jobName -> + if (params."${paramKey}" == true) { + echo "触发部署:${jobName} ← ${fullImageName}" + // 异步触发 + build job: jobName, + parameters: [ + string(name: 'CUSTOM_IMAGE', value: fullImageName), + string(name: 'ACTUAL_USER', value: ACTUAL_USER), + ], + wait: false // ← 异步(此 Job 结束,不等待被调用的 Job 结束) + } else { + echo "未勾选 ${paramKey}, 跳过触发部署:${jobName}" + } + } + } + } + failure { + lark ( + robot: "${LARK_ROBOT}", + type: "CARD", + title: "${IMAGE_NAME}镜像构建失败", + text: [ + "📅 **构建时间**: ${currentTime}", + "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", + "🔢 **任务编号**: [${BUILD_DISPLAY_NAME}](${BUILD_URL})", + "🔀 **代码分支**: ${params.Code_branch}", + "🌟 **构建状态**: 构建失败", + "⏱️ **构建用时**: ${currentBuild.duration.intdiv(1000)} 秒", + "📦 **镜像名称**: ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}", + "📝 **提交摘要**: ${env.GIT_COMMIT_MSG}", + "👤 **执行账号**: ${env.ACTUAL_USER}", + ] + ) + echo "有步骤出错!" + } + aborted { + lark ( + robot: "${LARK_ROBOT}", + type: "CARD", + title: "${IMAGE_NAME}镜像构建取消", + text: [ + "📅 **构建时间**: ${currentTime}", + "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", + "🔢 **任务编号**: [${BUILD_DISPLAY_NAME}](${BUILD_URL})", + "🔀 **代码分支**: ${params.Code_branch}", + "🌟 **构建状态**: 构建取消", + "⏱️ **构建用时**: ${currentBuild.duration.intdiv(1000)} 秒", + "📦 **镜像名称**: ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}", + "📝 **提交摘要**: ${env.GIT_COMMIT_MSG}", + "👤 **取消账号**: ${env.ACTUAL_USER}", + ] + ) + } + } +} \ No newline at end of file diff --git a/SCM/构建镜像/v4/build_image_lessie_go_api_v4.groovy b/SCM/构建镜像/v4/build_image_lessie_go_api_v4.groovy index fd480bf..1d0081d 100644 --- a/SCM/构建镜像/v4/build_image_lessie_go_api_v4.groovy +++ b/SCM/构建镜像/v4/build_image_lessie_go_api_v4.groovy @@ -117,6 +117,9 @@ pipeline { def formattedBranch = branchName.replace('/', '-').replace('_', '-') // 替换分支名中的/和_为- // 构建时间(格式:202510181215,年-月-日-时-分,无分隔符) def buildTime = sh(script: 'date +%Y%m%d%H%M', returnStdout: true).trim() + // 当前时间(格式:YYYY-MM-DD HH:MM:SS) + env.currentTime = sh(script: 'date "+%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + def defaultTag = "v${buildNumber}_${formattedBranch}_${GIT_COMMIT_SHORT}_${buildTime}" def customTag = params.CUSTOM_TAG?.trim() @@ -273,7 +276,7 @@ pipeline { lark ( robot: "${LARK_ROBOT}", type: "CARD", - title: "lessie-go-api镜像构建成功", + title: "${IMAGE_NAME}镜像构建成功", text: [ "📅 **构建时间**: ${currentTime}", "📋 **任务名称**: [${JOB_NAME}](${JOB_URL})", diff --git a/k8s_yaml/ELK/filebast/filebeat.yaml b/k8s_yaml/ELK/filebast/filebeat.yaml index 77260ee..52360fe 100644 --- a/k8s_yaml/ELK/filebast/filebeat.yaml +++ b/k8s_yaml/ELK/filebast/filebeat.yaml @@ -212,12 +212,12 @@ output.elasticsearch: password: "G7ZSKFM4AQwHQpwA" indices: - - index: "k8s-%{[kubernetes.labels.environment]}-%{[kubernetes.labels.app]}-%{+yyyy.MM.dd}" + - index: "k8s-%{[kubernetes.labels.environment]}-%{[kubernetes.labels.app]}-%{+yyyy.MM}" when: regexp: kubernetes.labels.app: "(lessie-go-api|flymoon-admin|flymoon-agent|flymoon-payment|flymoon-email|lessie-agents|apex)" - - index: "apex-python-%{+yyyy.MM.dd}" + - index: "apex-python-%{+yyyy.MM}" when: equals: kubernetes.labels.apex: "lessie-agents"