From a7c7be8452f0a302bf59bf9605ae430f0610ff77 Mon Sep 17 00:00:00 2001 From: dxin Date: Thu, 13 Nov 2025 11:59:04 +0800 Subject: [PATCH] zenjia --- SCM/s1_flymoon_admin.groovy | 305 +++++++++++++++++ SCM/s1_flymoon_agent.groovy | 303 +++++++++++++++++ SCM/s1_flymoon_payment.groovy | 301 +++++++++++++++++ SCM/s1_go_lessie_api.groovy | 2 +- SCM/s1_lessie_ai_web.groovy | 314 ++++++++++++++++++ SCM/s1_python_lessie_agents.groovy | 7 +- .../{test-default.conf => default.conf} | 18 + k8s_yaml/s1/TkeServiceConfig.yaml | 44 +++ k8s_yaml/s1/network-test-pod.yaml | 4 +- k8s_yaml/s1/s1-flymoon-admin.yaml | 36 +- ...ymoon-agent.yaml => s1-flymoon-agent.yaml} | 57 ++-- k8s_yaml/s1/s1-flymoon-payment.yaml | 113 +++++++ ...ssie-ai-web.yaml => s1-lessie-ai-web.yaml} | 44 ++- .../s1/sit-ingress-auto-ingress-config.yaml | 185 +++++++++++ k8s_yaml/s1/sit-ingress.yaml | 179 ++++++++++ k8s_yaml/s1/test-flymoon-payment.yaml | 90 ----- k8s_yaml/s1/test-lessie-ingress.yaml | 89 ----- 17 files changed, 1856 insertions(+), 235 deletions(-) create mode 100644 SCM/s1_flymoon_admin.groovy create mode 100644 SCM/s1_flymoon_agent.groovy create mode 100644 SCM/s1_flymoon_payment.groovy create mode 100644 SCM/s1_lessie_ai_web.groovy rename k8s_yaml/public/ConfigMap/{test-default.conf => default.conf} (54%) create mode 100644 k8s_yaml/s1/TkeServiceConfig.yaml rename k8s_yaml/s1/{test-flymoon-agent.yaml => s1-flymoon-agent.yaml} (50%) create mode 100644 k8s_yaml/s1/s1-flymoon-payment.yaml rename k8s_yaml/s1/{test-lessie-ai-web.yaml => s1-lessie-ai-web.yaml} (67%) create mode 100644 k8s_yaml/s1/sit-ingress-auto-ingress-config.yaml create mode 100644 k8s_yaml/s1/sit-ingress.yaml delete mode 100644 k8s_yaml/s1/test-flymoon-payment.yaml delete mode 100644 k8s_yaml/s1/test-lessie-ingress.yaml diff --git a/SCM/s1_flymoon_admin.groovy b/SCM/s1_flymoon_admin.groovy new file mode 100644 index 0000000..626aa7e --- /dev/null +++ b/SCM/s1_flymoon_admin.groovy @@ -0,0 +1,305 @@ +pipeline { + agent any + tools{ + maven 'mvn3.8.8' + jdk 'jdk21' + } + 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_AFTER_BUILD', + defaultValue: true, + description: '是否构建完镜像后部署?' + ) + } + environment { + KUBECONFIG = credentials('k8s-test-config-admin') // k8s 凭证 ID, Jenkins 中配置的凭证名称 + + REGISTRY = "uswccr.ccs.tencentyun.com" // 镜像仓库地址 + NAMESPACE = "lessie${params.NAME_SPACES}" // 命名空间根据choices的选择拼接 + IMAGE_NAME = "flymoon-admin" // 镜像名(固定前缀) + CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID + + Deployment_name = "s1-flymoon-admin-deployment" // 工作负载名 + Pod_container_name = "s1-flymoon-admin" // pod内运行的容器名 + K8s_namespace = "sit" // 这是k8s集群的命名空间 + } + + stages { + stage('拉取代码') { + steps { + git branch: "${params.Code_branch}", credentialsId: 'fly_gitlab_auth', url: 'http://106.53.194.199/root/fly_moon_admin.git' + } + } + + stage('获取信息') { + steps { + script { + // 获取最近一次提交的哈希值(短格式,前8位) + env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short 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() + 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('Maven 编译') { + steps { + sh "cd ${WORKSPACE}/ && mvn clean install -Dmaven.test.skip=true" + } + } + + 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='${params.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}" + } + } + } + + stage('部署到K8S') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + sh """ + echo "=== 更新 Deployment 镜像 ===" + kubectl set image deployment/${Deployment_name} ${Pod_container_name}=${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} -n ${K8s_namespace} + echo "=== 添加注解 ===" + kubectl annotate deployment/${Deployment_name} kubernetes.io/change-cause="${GIT_COMMIT_MSG}" --overwrite -n ${K8s_namespace} + echo "=== 查看历史版本 ===" + kubectl rollout history deployment/${Deployment_name} -n ${K8s_namespace} + echo "=== 查看所使用的镜像 ===" + kubectl get deployment ${Deployment_name} -n ${K8s_namespace} -o=jsonpath='{.spec.template.spec.containers[*].image}' + """ + } + } + + stage('检查部署情况') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + echo "检测部署状态并验证新版本运行情况" + sh """ + echo "=== 检查 Deployment 滚动更新状态 ===" + kubectl rollout status deployment/${Deployment_name} -n ${K8s_namespace} --timeout=180s + + if [ \$? -ne 0 ]; then + echo "❌ 部署超时或失败,开始收集诊断信息..." + echo "=== 查看当前 Pods 状态 ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 查看最近的事件 ===" + kubectl get events -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp | tail -n 20 + + echo "=== 查看最近一个失败 Pod 的详细描述 ===" + FAILED_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --field-selector=status.phase!=Running -o jsonpath='{.items[0].metadata.name}') + if [ ! -z "\$FAILED_POD" ]; then + kubectl describe pod \$FAILED_POD -n ${K8s_namespace} || true + kubectl logs \$FAILED_POD -n ${K8s_namespace} --tail=50 || true + fi + + echo "=== 回滚到上一个版本 ===" + kubectl rollout undo deployment/${Deployment_name} -n ${K8s_namespace} + + exit 1 + fi + + echo "=== 检查 Pods 是否全部 Ready ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 获取最新 Pod 名称 ===" + NEW_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}') + + echo "=== 新 Pod 启动日志(最近20行) ===" + kubectl logs \$NEW_POD -n ${K8s_namespace} --tail=20 || true + + echo "✅ 部署成功:\$NEW_POD 已正常运行" + """ + } + } + + } + + post { + always { + script { + def keepCount = 2 + 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}}' + """ + + sh "docker logout ${REGISTRY}" + echo "容器仓库已登出,本地凭证已清理" + } + } + success { + // 输出构建结果 + echo "成功!" + echo "镜像地址:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "对应代码提交:${GIT_COMMIT_SHORT}(${GIT_COMMIT_MSG})" + } + failure { + // 输出构建结果 + echo "有步骤失败,请检查!" + } + } +} + + diff --git a/SCM/s1_flymoon_agent.groovy b/SCM/s1_flymoon_agent.groovy new file mode 100644 index 0000000..545d6f6 --- /dev/null +++ b/SCM/s1_flymoon_agent.groovy @@ -0,0 +1,303 @@ +pipeline { + agent any + tools{ + maven 'mvn3.8.8' + jdk 'jdk21' + } + 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_AFTER_BUILD', + defaultValue: true, + description: '是否构建完镜像后部署?' + ) + } + environment { + KUBECONFIG = credentials('k8s-test-config-admin') // k8s 凭证 ID, Jenkins 中配置的凭证名称 + + REGISTRY = "uswccr.ccs.tencentyun.com" // 镜像仓库地址 + NAMESPACE = "lessie${params.NAME_SPACES}" // 命名空间根据choices的选择拼接 + IMAGE_NAME = "flymoon-agent" // 镜像名(固定前缀) + CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID + + Deployment_name = "s1-flymoon-agent-deployment" // 工作负载名 + Pod_container_name = "s1-flymoon-agent" // pod内运行的容器名 + K8s_namespace = "sit" // 这是k8s集群的命名空间 + } + + stages { + stage('拉取代码') { + steps { + git branch: "${params.Code_branch}", + credentialsId: 'fly_gitlab_auth', + url: 'http://106.53.194.199/root/fly_moon_agent.git' + } + } + + stage('获取信息') { + steps { + script { + // 获取最近一次提交的哈希值(短格式,前8位) + env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short 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() + 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('Maven 编译') { + steps { + sh "cd ${WORKSPACE}/ && mvn clean install -Dmaven.test.skip=true" + } + } + + 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='${params.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 { + sh "docker push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "推送镜像成功:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + } + } + } + + stage('部署到K8S') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + sh """ + echo "=== 更新 Deployment 镜像 ===" + kubectl set image deployment/${Deployment_name} ${Pod_container_name}=${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} -n ${K8s_namespace} + echo "=== 添加注解 ===" + kubectl annotate deployment/${Deployment_name} kubernetes.io/change-cause="${GIT_COMMIT_MSG}" --overwrite -n ${K8s_namespace} + echo "=== 查看历史版本 ===" + kubectl rollout history deployment/${Deployment_name} -n ${K8s_namespace} + echo "=== 查看所使用的镜像 ===" + kubectl get deployment ${Deployment_name} -n ${K8s_namespace} -o=jsonpath='{.spec.template.spec.containers[*].image}' + """ + } + } + + stage('检查部署情况') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + echo "检测部署状态并验证新版本运行情况" + sh """ + echo "=== 检查 Deployment 滚动更新状态 ===" + kubectl rollout status deployment/${Deployment_name} -n ${K8s_namespace} --timeout=180s + + if [ \$? -ne 0 ]; then + echo "❌ 部署超时或失败,开始收集诊断信息..." + echo "=== 查看当前 Pods 状态 ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 查看最近的事件 ===" + kubectl get events -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp | tail -n 20 + + echo "=== 查看最近一个失败 Pod 的详细描述 ===" + FAILED_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --field-selector=status.phase!=Running -o jsonpath='{.items[0].metadata.name}') + if [ ! -z "\$FAILED_POD" ]; then + kubectl describe pod \$FAILED_POD -n ${K8s_namespace} || true + kubectl logs \$FAILED_POD -n ${K8s_namespace} --tail=50 || true + fi + + echo "=== 回滚到上一个版本 ===" + kubectl rollout undo deployment/${Deployment_name} -n ${K8s_namespace} + + exit 1 + fi + + echo "=== 检查 Pods 是否全部 Ready ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 获取最新 Pod 名称 ===" + NEW_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}') + + echo "=== 新 Pod 启动日志(最近20行) ===" + kubectl logs \$NEW_POD -n ${K8s_namespace} --tail=20 || true + + echo "✅ 部署成功:\$NEW_POD 已正常运行" + """ + } + } + } + + post { + always { + script { + def keepCount = 2 + 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}}' + """ + + sh "docker logout ${REGISTRY}" + echo "容器仓库已登出,本地凭证已清理" + } + } + success { + // 输出构建结果 + echo "镜像构建成功!" + echo "镜像地址:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "对应代码提交:${GIT_COMMIT_SHORT}(${GIT_COMMIT_MSG})" + } + failure { + // 输出构建结果 + echo "有步骤出错!" + } + } +} \ No newline at end of file diff --git a/SCM/s1_flymoon_payment.groovy b/SCM/s1_flymoon_payment.groovy new file mode 100644 index 0000000..a6fe3b2 --- /dev/null +++ b/SCM/s1_flymoon_payment.groovy @@ -0,0 +1,301 @@ +pipeline { + agent any + tools{ + maven 'mvn3.8.8' + jdk 'jdk21' + } + 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_AFTER_BUILD', + defaultValue: true, + description: '是否构建完镜像后部署?' + ) + } + environment { + KUBECONFIG = credentials('k8s-test-config-admin') // k8s 凭证 ID, Jenkins 中配置的凭证名称 + + REGISTRY = "uswccr.ccs.tencentyun.com" // 镜像仓库地址 + NAMESPACE = "lessie${params.NAME_SPACES}" // 命名空间根据choices的选择拼接 + IMAGE_NAME = "flymoon-payment" // 镜像名(固定前缀) + CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID + + Deployment_name = "s1-flymoon-payment-deployment" // 工作负载名 + Pod_container_name = "s1-flymoon-payment" // pod内运行的容器名 + K8s_namespace = "sit" // 这是k8s集群的命名空间 + } + + stages { + stage('拉取代码') { + steps { + git branch: "${params.Code_branch}", credentialsId: 'fly_gitlab_auth', url: 'http://106.53.194.199/root/fly_moon_payment.git' + } + } + + stage('获取信息') { + steps { + script { + // 获取最近一次提交的哈希值(短格式,前8位) + env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short 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() + 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('Maven 编译') { + steps { + sh "cd ${WORKSPACE}/ && mvn clean install -Dmaven.test.skip=true" + } + } + + 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='${params.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 { + sh "docker push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "推送镜像成功:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + } + } + } + + stage('部署到K8S') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + sh """ + echo "=== 更新 Deployment 镜像 ===" + kubectl set image deployment/${Deployment_name} ${Pod_container_name}=${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} -n ${K8s_namespace} + echo "=== 添加注解 ===" + kubectl annotate deployment/${Deployment_name} kubernetes.io/change-cause="${GIT_COMMIT_MSG}" --overwrite -n ${K8s_namespace} + echo "=== 查看历史版本 ===" + kubectl rollout history deployment/${Deployment_name} -n ${K8s_namespace} + echo "=== 查看所使用的镜像 ===" + kubectl get deployment ${Deployment_name} -n ${K8s_namespace} -o=jsonpath='{.spec.template.spec.containers[*].image}' + """ + } + } + + stage('检查部署情况') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + echo "检测部署状态并验证新版本运行情况" + sh """ + echo "=== 检查 Deployment 滚动更新状态 ===" + kubectl rollout status deployment/${Deployment_name} -n ${K8s_namespace} --timeout=180s + + if [ \$? -ne 0 ]; then + echo "❌ 部署超时或失败,开始收集诊断信息..." + echo "=== 查看当前 Pods 状态 ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 查看最近的事件 ===" + kubectl get events -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp | tail -n 20 + + echo "=== 查看最近一个失败 Pod 的详细描述 ===" + FAILED_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --field-selector=status.phase!=Running -o jsonpath='{.items[0].metadata.name}') + if [ ! -z "\$FAILED_POD" ]; then + kubectl describe pod \$FAILED_POD -n ${K8s_namespace} || true + kubectl logs \$FAILED_POD -n ${K8s_namespace} --tail=50 || true + fi + + echo "=== 回滚到上一个版本 ===" + kubectl rollout undo deployment/${Deployment_name} -n ${K8s_namespace} + + exit 1 + fi + + echo "=== 检查 Pods 是否全部 Ready ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 获取最新 Pod 名称 ===" + NEW_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}') + + echo "=== 新 Pod 启动日志(最近20行) ===" + kubectl logs \$NEW_POD -n ${K8s_namespace} --tail=20 || true + + echo "✅ 部署成功:\$NEW_POD 已正常运行" + """ + } + } + } + + post { + always { + script { + def keepCount = 2 + 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}}' + """ + + sh "docker logout ${REGISTRY}" + echo "容器仓库已登出,本地凭证已清理" + } + } + success { + // 输出构建结果 + echo "镜像构建成功!" + echo "镜像地址:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "对应代码提交:${GIT_COMMIT_SHORT}(${GIT_COMMIT_MSG})" + } + failure { + // 输出构建结果 + echo "有片段失败!" + } + } +} \ No newline at end of file diff --git a/SCM/s1_go_lessie_api.groovy b/SCM/s1_go_lessie_api.groovy index 979579e..2ab72d4 100644 --- a/SCM/s1_go_lessie_api.groovy +++ b/SCM/s1_go_lessie_api.groovy @@ -40,7 +40,7 @@ pipeline { IMAGE_NAME = "go_lessie-sourcing-api" // 镜像名(固定前缀,这里是指构建后的镜像名) CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID - Deployment_name = "s1-lessie-go-api-deployment" // 工作负载资源清单文件名 + Deployment_name = "s1-lessie-go-api-deployment" // 工作负载名 Pod_container_name = "s1-lessie-go-api" // pod内运行的容器名 K8s_namespace = "sit" // 这是k8s集群的命名空间 diff --git a/SCM/s1_lessie_ai_web.groovy b/SCM/s1_lessie_ai_web.groovy new file mode 100644 index 0000000..8c53003 --- /dev/null +++ b/SCM/s1_lessie_ai_web.groovy @@ -0,0 +1,314 @@ +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: '选择存放镜像的仓库命名空间:' + ) + choice( + name: 'BUILD_ENV', + choices: ['im', 's2', 'prod'], + description: '选择构建的环境配置, 默认为 pnpm build:im 构建' + ) + string( + name: 'CUSTOM_TAG', + defaultValue: '', + description: '可选:自定义镜像 Tag (字母、数字、点、下划线、短横线), 如 v0.0.1, 留空则自动生成 “ v+构建次数_分支名_短哈希_构建时间 ”' + ) + booleanParam( + name: 'DEPLOY_AFTER_BUILD', + defaultValue: true, + description: '是否构建完镜像后部署?' + ) + } + environment { + KUBECONFIG = credentials('k8s-test-config-admin') // k8s 凭证 ID, Jenkins 中配置的凭证名称 + + REGISTRY = "uswccr.ccs.tencentyun.com" // 镜像仓库地址 + NAMESPACE = "lessie${params.NAME_SPACES}" // 命名空间根据choices的选择拼接 + IMAGE_NAME = "lessie-ai-web" // 镜像名(固定前缀) + CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID + + Deployment_name = "s1-lessie-ai-web-deployment" // 工作负载名 + Pod_container_name = "s1-lessie-ai-web" // pod内运行的容器名 + K8s_namespace = "sit" // 这是k8s集群的命名空间 + } + + stages { + stage('拉取代码') { + steps { + git branch: "${params.Code_branch}", + credentialsId: 'fly_gitlab_auth', + url: 'http://106.53.194.199/web/jennie.git' + } + } + + stage('获取信息') { + steps { + script { + // 获取最近一次提交的哈希值(短格式,前8位) + env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short 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() + 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('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('登录容器仓库') { + 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='${params.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 { + sh "docker push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "推送镜像成功:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + } + } + } + stage('部署到K8S') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + sh """ + echo "=== 更新 Deployment 镜像 ===" + kubectl set image deployment/${Deployment_name} ${Pod_container_name}=${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} -n ${K8s_namespace} + echo "=== 添加注解 ===" + kubectl annotate deployment/${Deployment_name} kubernetes.io/change-cause="${GIT_COMMIT_MSG}" --overwrite -n ${K8s_namespace} + echo "=== 查看历史版本 ===" + kubectl rollout history deployment/${Deployment_name} -n ${K8s_namespace} + echo "=== 查看所使用的镜像 ===" + kubectl get deployment ${Deployment_name} -n ${K8s_namespace} -o=jsonpath='{.spec.template.spec.containers[*].image}' + """ + } + } + stage('检查部署情况') { + when { + expression { return params.DEPLOY_AFTER_BUILD } + } + steps { + echo "检测部署状态并验证新版本运行情况" + sh """ + echo "=== 检查 Deployment 滚动更新状态 ===" + kubectl rollout status deployment/${Deployment_name} -n ${K8s_namespace} --timeout=180s + + if [ \$? -ne 0 ]; then + echo "❌ 部署超时或失败,开始收集诊断信息..." + echo "=== 查看当前 Pods 状态 ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 查看最近的事件 ===" + kubectl get events -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp | tail -n 20 + + echo "=== 查看最近一个失败 Pod 的详细描述 ===" + FAILED_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --field-selector=status.phase!=Running -o jsonpath='{.items[0].metadata.name}') + if [ ! -z "\$FAILED_POD" ]; then + kubectl describe pod \$FAILED_POD -n ${K8s_namespace} || true + kubectl logs \$FAILED_POD -n ${K8s_namespace} --tail=50 || true + fi + + echo "=== 回滚到上一个版本 ===" + kubectl rollout undo deployment/${Deployment_name} -n ${K8s_namespace} + + exit 1 + fi + + echo "=== 检查 Pods 是否全部 Ready ===" + kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide + + echo "=== 获取最新 Pod 名称 ===" + NEW_POD=\$(kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}') + + echo "=== 新 Pod 启动日志(最近20行) ===" + kubectl logs \$NEW_POD -n ${K8s_namespace} --tail=20 || true + + echo "✅ 部署成功:\$NEW_POD 已正常运行" + """ + } + } + } + + post { + always { + script { + def keepCount = 2 + 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}}' + """ + + sh "docker logout ${REGISTRY}" + echo "容器仓库已登出,本地凭证已清理" + } + } + success { + // 输出构建结果 + echo "镜像构建成功!" + echo "镜像地址:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" + echo "对应代码提交:${GIT_COMMIT_SHORT}(${GIT_COMMIT_MSG})" + } + failure { + // 输出构建结果 + echo "部署有错误,请检查!" + } + } +} \ No newline at end of file diff --git a/SCM/s1_python_lessie_agents.groovy b/SCM/s1_python_lessie_agents.groovy index 0393d77..9407bb6 100644 --- a/SCM/s1_python_lessie_agents.groovy +++ b/SCM/s1_python_lessie_agents.groovy @@ -175,10 +175,13 @@ pipeline { steps { echo "检测部署状态并验证新版本运行情况" sh """ + set +e + echo "=== 检查 Deployment 滚动更新状态 ===" kubectl rollout status deployment/${Deployment_name} -n ${K8s_namespace} --timeout=180s + ROLLOUT_RESULT=\$? - if [ \$? -ne 0 ]; then + if [ \$ROLLOUT_RESULT -ne 0 ]; then echo "❌ 部署超时或失败,开始收集诊断信息..." echo "=== 查看当前 Pods 状态 ===" kubectl get pods -l app=${Pod_container_name} -n ${K8s_namespace} -o wide @@ -272,7 +275,7 @@ pipeline { } success { // 输出构建结果 - echo "镜像构建成功!" + echo "部署成功!" echo "镜像地址:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" echo "对应代码提交:${GIT_COMMIT_SHORT}(${GIT_COMMIT_MSG})" } diff --git a/k8s_yaml/public/ConfigMap/test-default.conf b/k8s_yaml/public/ConfigMap/default.conf similarity index 54% rename from k8s_yaml/public/ConfigMap/test-default.conf rename to k8s_yaml/public/ConfigMap/default.conf index e7a9c49..d7a389c 100644 --- a/k8s_yaml/public/ConfigMap/test-default.conf +++ b/k8s_yaml/public/ConfigMap/default.conf @@ -1,7 +1,24 @@ +map $msec $sample_200 { + default 0; + "~00$" 1; # 每 200 次大约打印 1 次 +} + +map $http_user_agent $loggable { + default 1; + "clb-healthcheck" $sample_200; +} + +log_format custom_main '$remote_addr - $remote_user [$time_local+0800] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" rt=$request_time ua="$upstream_addr" ' + 'us="$upstream_status" ut="$upstream_response_time"'; + server { listen 80; server_name _; + access_log /var/log/nginx/access.log custom_main if=$loggable; + # 前端静态文件 location / { root /usr/share/nginx/html; @@ -9,6 +26,7 @@ server { try_files $uri $uri/ /index.html; } + # 精确匹配 index.html,禁用缓存 location = /index.html { root /usr/share/nginx/html; diff --git a/k8s_yaml/s1/TkeServiceConfig.yaml b/k8s_yaml/s1/TkeServiceConfig.yaml new file mode 100644 index 0000000..29f167b --- /dev/null +++ b/k8s_yaml/s1/TkeServiceConfig.yaml @@ -0,0 +1,44 @@ +apiVersion: cloud.tencent.com/v1alpha1 +kind: TkeServiceConfig +metadata: + name: jetty-ingress-config + namespace: sit +spec: + loadBalancer: + l7Listeners: + - protocol: HTTP + port: 80 + snatEnable: false # 监听器透传客户端源 IP(非 snat 白名单用户,请勿声明该字段)。当 snatEnable 为 true 时,关闭透传客户端源 IP 选项。当 snatEnable 为 false 时,打开透传客户端源 IP 选项。当设置 keepaliveEnable 为 1 时,snatEnable 不能为 false。 + domains: + - domain: "" # domain为空表示使用VIP作为域名 + rules: + - url: "/health" + forwardType: HTTP # 指定后端协议为 HTTP,目前支持 HTTP/HTTPS/GRPC。 + healthCheck: + enable: false + - protocol: HTTPS + port: 443 + defaultServer: "sample.tencent.com" # 默认域名 + keepaliveEnable: 1 # 监听器开启长连接(非 keepalive 白名单用户,请勿声明该字段) + domains: + - domain: "sample.tencent.com" + http2: true # 启用 HTTP 2.0 + rules: + - url: "/" + forwardType: HTTPS # 指定后端协议为 HTTPS,目前支持 HTTP/HTTPS/GRPC。 + session: + enable: true + sessionExpireTime: 3600 + healthCheck: + enable: true + intervalTime: 10 # intervalTime 要大于 timeout,否则会出错 + timeout: 5 # timeout 要小于 intervalTime,否则会出错 + healthNum: 2 + unHealthNum: 2 + httpCheckPath: "/checkHealth" + httpCheckDomain: "sample.tencent.com" #注意:健康检查必须使用固定域名进行探测,如果您在.spec.loadBalancer.l7Listeners.protocol.domains.domain 里填写的是泛域名,一定要使用 httpCheckDomain 字段明确具体需要健康检查的域名,否则泛域名不支持健康检查。 + httpCheckMethod: HEAD + httpCode: 31 # 可选值:1~31,默认 31。 1 表示探测后返回值 1xx 代表健康,2 表示返回 2xx 代表健康,4 表示返回 3xx 代表健康,8 表示返回 4xx 代表健康,16 表示返回 5xx 代表健康。若希望多种返回码都可代表健康,则将相应的值相加。 + sourceIpType: 0 # 可选值:0或1,设定健康检查源ip。0 表示负载均衡VIP,1 表示 100.64.0.0/10 网段ip。对于域名化clb默认值为1且只能为1,对于非域名化的clb默认值不一定,可在clb控制台配置页面看能否看到VIP探测方式,如能看到默认值为0,否则为1,更多详情查看 https://cloud.tencent.com/document/product/214/86666。 + checkType: "HTTPS" # 可选值:HTTP,HTTPS,TCP 或 GRPC,默认 HTTP。2024.06之后新建的集群支持改字段,存量集群如有需要可提工单升级后台组件来支持。当 forwardType 为 HTTPS 时,checkType 只能为 TCP 或 HTTPS。当 forwardType 为 GRPC 时,checkType 只能为 TCP 或 GRPC。 + scheduler: WRR # 可选值:WRR、LEAST_CONN、IP_HASH \ No newline at end of file diff --git a/k8s_yaml/s1/network-test-pod.yaml b/k8s_yaml/s1/network-test-pod.yaml index db437e8..796cbae 100644 --- a/k8s_yaml/s1/network-test-pod.yaml +++ b/k8s_yaml/s1/network-test-pod.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Pod metadata: name: network-test-pod - namespace: lessie-sit + namespace: sit labels: app: network-test spec: @@ -25,7 +25,7 @@ apiVersion: v1 kind: Service metadata: name: network-test-pod-svc - namespace: test-lessie + namespace: sit labels: app: network-test spec: diff --git a/k8s_yaml/s1/s1-flymoon-admin.yaml b/k8s_yaml/s1/s1-flymoon-admin.yaml index 1c2dcb4..468d35f 100644 --- a/k8s_yaml/s1/s1-flymoon-admin.yaml +++ b/k8s_yaml/s1/s1-flymoon-admin.yaml @@ -5,7 +5,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: s1-flymoon-admin-deployment - namespace: test-lessie + namespace: sit labels: app: s1-flymoon-admin environment: s1 @@ -38,7 +38,7 @@ spec: type: DirectoryOrCreate containers: - name: s1-flymoon-admin # 容器名称 - image: uswccr.ccs.tencentyun.com/lessiesit/flymoon-admin:v0.0.1 # 容器镜像 + image: uswccr.ccs.tencentyun.com/lessiesit/flymoon-admin:v1_dxin_467169b_202511101605 # 容器镜像 imagePullPolicy: Always # 镜像拉取策略 拉 env: - name: POD_NAME @@ -60,25 +60,48 @@ spec: - name: flymoon-admin-logs-volume mountPath: /app/logs/ subPathExpr: flymoon-admin-log-$(POD_NAME) + startupProbe: # 启动探针,用于判断容器是否已启动完成 + httpGet: + path: /sit-api/health + port: 8080 + initialDelaySeconds: 10 #容器启动后等待多少秒才开始第一次探测 + periodSeconds: 10 # 启动探测的间隔秒数,每隔多少秒执行一次探测 + failureThreshold: 30 + readinessProbe: # 就绪探针,用于判断容器是否已准备好接收流量 + httpGet: + path: /sit-api/health + port: 8080 + initialDelaySeconds: 20 # 就绪探测在容器启动后等待多少秒才开始第一次探测(避免应用启动未完成即被判为不就绪) + periodSeconds: 10 # 就绪探测的间隔秒数,每隔多少秒执行一次探测 + timeoutSeconds: 5 # 单次就绪探测的超时时间(秒),超过则该次探测视为失败 + failureThreshold: 3 # 连续失败多少次后认为就绪探测失败(Pod 不再被视为就绪) + livenessProbe: # 存活探针,用于判断容器是否仍然健康,失败会触发重启 + httpGet: + path: /sit-api/health + port: 8080 + initialDelaySeconds: 10 # 存活探测在容器启动后等待多少秒才开始第一次探测 + periodSeconds: 30 # 存活探测的间隔秒数 + timeoutSeconds: 5 # 单次存活探测的超时时间(秒) + failureThreshold: 3 # 连续失败多少次后认为容器不健康并触发重启 + --- # ---------------------------- # Service -# 集群内部:http://s1-flymoon-admin-svc.s1-lessie.svc.cluster.local:8080 -# 集群外部:http://:30808 +# 集群内部:http://s1-flymoon-admin-svc.sit.svc.cluster.local:8080 # ---------------------------- apiVersion: v1 kind: Service metadata: name: s1-flymoon-admin-svc - namespace: test-lessie + namespace: sit labels: app: s1-flymoon-admin environment: s1 project: flymoon spec: - type: NodePort + type: ClusterIP selector: # 必须匹配 Deployment 的 labels 才能关联 Pod app: s1-flymoon-admin environment: s1 @@ -87,5 +110,4 @@ spec: - name: http port: 8080 # ClusterIP 内部端口 targetPort: 8080 # 容器端口 - nodePort: 30808 # 节点对外端口(30000-32767) diff --git a/k8s_yaml/s1/test-flymoon-agent.yaml b/k8s_yaml/s1/s1-flymoon-agent.yaml similarity index 50% rename from k8s_yaml/s1/test-flymoon-agent.yaml rename to k8s_yaml/s1/s1-flymoon-agent.yaml index f36c133..36c03a7 100644 --- a/k8s_yaml/s1/test-flymoon-agent.yaml +++ b/k8s_yaml/s1/s1-flymoon-agent.yaml @@ -4,18 +4,18 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: test-flymoon-agent-deployment - namespace: test-lessie + name: s1-flymoon-agent-deployment + namespace: sit labels: - app: test-flymoon-agent - environment: test + app: s1-flymoon-agent + environment: s1 project: flymoon spec: replicas: 1 selector: matchLabels: - app: test-flymoon-agent - environment: test + app: s1-flymoon-agent + environment: s1 project: flymoon strategy: type: RollingUpdate # 滚动更新策略 @@ -25,8 +25,8 @@ spec: template: metadata: labels: - app: test-flymoon-agent - environment: test + app: s1-flymoon-agent + environment: s1 project: flymoon spec: imagePullSecrets: @@ -37,8 +37,8 @@ spec: path: /data/logs/flymoon-agent/ type: DirectoryOrCreate containers: - - name: test-flymoon-agent # 容器名称 - image: uswccr.ccs.tencentyun.com/lessietest/flymoon-agent:v0.0.5 # 容器镜像 + - name: s1-flymoon-agent # 容器名称 + image: uswccr.ccs.tencentyun.com/lessiesit/flymoon-agent:v0.0.5 # 容器镜像 imagePullPolicy: Always # 镜像拉取策略,拉 env: - name: POD_NAME @@ -55,37 +55,52 @@ spec: memory: "1Gi" # 容器请求分配1Gi内存(这会实际预留) limits: cpu: "1" # 最多可以使用1个CPU核心 - memory: "3Gi" # 容器最多可以使用3Gi内存 + memory: "4Gi" # 容器最多可以使用3Gi内存 volumeMounts: - name: flymoon-agent-logs-volume mountPath: /app/logs/ subPathExpr: flymoon-agent-log-$(POD_NAME) + readinessProbe: # 就绪探针,用于判断容器是否已准备好接收流量 + httpGet: + path: /sit-api/health + port: 8070 + initialDelaySeconds: 20 # 就绪探测在容器启动后等待多少秒才开始第一次探测(避免应用启动未完成即被判为不就绪) + periodSeconds: 10 # 就绪探测的间隔秒数,每隔多少秒执行一次探测 + timeoutSeconds: 5 # 单次就绪探测的超时时间(秒),超过则该次探测视为失败 + failureThreshold: 3 # 连续失败多少次后认为就绪探测失败(Pod 不再被视为就绪) + livenessProbe: # 存活探针,用于判断容器是否仍然健康,失败会触发重启 + httpGet: + path: /sit-api/health + port: 8070 + initialDelaySeconds: 10 # 存活探测在容器启动后等待多少秒才开始第一次探测 + periodSeconds: 30 # 存活探测的间隔秒数 + timeoutSeconds: 5 # 单次存活探测的超时时间(秒) + failureThreshold: 3 # 连续失败多少次后认为容器不健康并触发重启 + --- # ---------------------------- # Service -# 集群内部:http://test-flymoon-agent-svc.test-lessie.svc.cluster.local:8070 -# 集群外部:http://:30807 +# 集群内部:http://s1-flymoon-agent-svc.sit.svc.cluster.local:8070 # ---------------------------- apiVersion: v1 kind: Service metadata: - name: test-flymoon-agent-svc - namespace: test-lessie + name: s1-flymoon-agent-svc + namespace: sit labels: - app: test-flymoon-agent - environment: test + app: s1-flymoon-agent + environment: s1 project: flymoon spec: - type: NodePort + type: ClusterIP selector: # 必须匹配 Deployment 的 labels 才能关联 Pod - app: test-flymoon-agent - environment: test + app: s1-flymoon-agent + environment: s1 project: flymoon ports: - name: http port: 8070 # ClusterIP 内部端口 targetPort: 8070 # 容器端口 - nodePort: 30807 # 节点对外端口(30000-32767) diff --git a/k8s_yaml/s1/s1-flymoon-payment.yaml b/k8s_yaml/s1/s1-flymoon-payment.yaml new file mode 100644 index 0000000..d843ecc --- /dev/null +++ b/k8s_yaml/s1/s1-flymoon-payment.yaml @@ -0,0 +1,113 @@ +# ---------------------------- +# Deployment +# ---------------------------- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: s1-flymoon-payment-deployment + namespace: sit + labels: + app: s1-flymoon-payment + environment: s1 + project: flymoon +spec: + replicas: 1 + selector: + matchLabels: + app: s1-flymoon-payment + environment: s1 + project: flymoon + strategy: + type: RollingUpdate # 滚动更新策略 + rollingUpdate: + maxSurge: 1 # 最大新增副本数 + maxUnavailable: 0 # 最大不可用副本数 + template: + metadata: + labels: + app: s1-flymoon-payment + environment: s1 + project: flymoon + spec: + imagePullSecrets: + - name: dxin-image-repository # 镜像仓库凭证Secret + volumes: + - name: flymoon-payment-logs-volume + hostPath: + path: /data/logs/flymoon-payment/ + type: DirectoryOrCreate + containers: + - name: s1-flymoon-payment # 容器名称 + image: uswccr.ccs.tencentyun.com/lessiesit/flymoon-payment:v2_dxin_d408919_202511121501 # 容器镜像 + imagePullPolicy: Always # 镜像拉取策略,总是拉取 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SPRING_PROFILES_ACTIVE + value: "s1" + ports: + - containerPort: 8090 # 容器暴露的端口 + resources: + requests: + cpu: "100m" # 容器请求分配0.1个CPU核心(这不是实际占用,但调度会以这里进行参考) + memory: "1Gi" # 容器请求分配1Gi内存(这会实际预留) + limits: + cpu: "1" # 最多可以使用1个CPU核心 + memory: "3Gi" # 容器最多可以使用3Gi内存 + volumeMounts: + - name: flymoon-payment-logs-volume + mountPath: /app/logs/ + subPathExpr: flymoon-payment-log-$(POD_NAME) + startupProbe: # 启动探针,用于判断容器是否已启动完成 + httpGet: + path: /health + port: 8090 + initialDelaySeconds: 10 #容器启动后等待多少秒才开始第一次探测 + periodSeconds: 10 # 启动探测的间隔秒数,每隔多少秒执行一次探测 + failureThreshold: 30 + readinessProbe: # 就绪探针,用于判断容器是否已准备好接收流量 + httpGet: + path: /health + port: 8090 + initialDelaySeconds: 20 # 就绪探测在容器启动后等待多少秒才开始第一次探测(避免应用启动未完成即被判为不就绪) + periodSeconds: 10 # 就绪探测的间隔秒数,每隔多少秒执行一次探测 + timeoutSeconds: 5 # 单次就绪探测的超时时间(秒),超过则该次探测视为失败 + failureThreshold: 3 # 连续失败多少次后认为就绪探测失败(Pod 不再被视为就绪) + livenessProbe: # 存活探针,用于判断容器是否仍然健康,失败会触发重启 + httpGet: + path: /health + port: 8090 + initialDelaySeconds: 10 # 存活探测在容器启动后等待多少秒才开始第一次探测 + periodSeconds: 30 # 存活探测的间隔秒数 + timeoutSeconds: 5 # 单次存活探测的超时时间(秒) + failureThreshold: 3 # 连续失败多少次后认为容器不健康并触发重启 + + +--- +# ---------------------------- +# Service +# 集群内部:http://s1-flymoon-payment-svc.sit.svc.cluster.local:8090 +# 集群外部:http://:30809 +# ---------------------------- + +apiVersion: v1 +kind: Service +metadata: + name: s1-flymoon-payment-svc + namespace: sit + labels: + app: s1-flymoon-payment + environment: s1 + project: flymoon +spec: + type: ClusterIP + selector: # 必须匹配 Deployment 的 labels 才能关联 Pod + app: s1-flymoon-payment + environment: s1 + project: flymoon + ports: + - name: http + port: 8090 # ClusterIP 内部端口 + targetPort: 8090 # 容器端口 diff --git a/k8s_yaml/s1/test-lessie-ai-web.yaml b/k8s_yaml/s1/s1-lessie-ai-web.yaml similarity index 67% rename from k8s_yaml/s1/test-lessie-ai-web.yaml rename to k8s_yaml/s1/s1-lessie-ai-web.yaml index 173bb6e..64e2821 100644 --- a/k8s_yaml/s1/test-lessie-ai-web.yaml +++ b/k8s_yaml/s1/s1-lessie-ai-web.yaml @@ -4,18 +4,18 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: test-lessie-ai-web-deployment - namespace: test-lessie + name: s1-lessie-ai-web-deployment + namespace: sit labels: - app: test-lessie-ai-web - environment: test + app: s1-lessie-ai-web + environment: s1 project: lessie spec: replicas: 1 selector: matchLabels: - app: test-lessie-ai-web - environment: test + app: s1-lessie-ai-web + environment: s1 project: lessie strategy: type: RollingUpdate # 滚动更新策略 @@ -25,18 +25,18 @@ spec: template: metadata: labels: - app: test-lessie-ai-web - environment: test + app: s1-lessie-ai-web + environment: s1 project: lessie spec: imagePullSecrets: - name: dxin-image-repository volumes: - - name: test-default-conf-volume + - name: s1-default-conf-volume configMap: - name: test-default-conf + name: default-conf containers: - - name: test-lessie-ai-web + - name: s1-lessie-ai-web image: uswccr.ccs.tencentyun.com/lessiesit/lessie-ai-web:latest imagePullPolicy: Always ports: @@ -49,33 +49,31 @@ spec: cpu: "500m" # 最多可以使用0.5个CPU核心 memory: "512Mi" # 容器最多可以使用8Gi内存 volumeMounts: - - name: test-default-conf-volume + - name: s1-default-conf-volume mountPath: /etc/nginx/conf.d/default.conf subPath: default.conf --- # ---------------------------- # Service -# 集群内部:http://test-lessie-ai-web-svc.test-lessie.svc.cluster.local:8000 -# 集群外部:http://:30080 +# 集群内部:http://s1-lessie-ai-web-svc.sit.svc.cluster.local:8000 # ---------------------------- apiVersion: v1 kind: Service metadata: - name: test-lessie-ai-web-svc - namespace: test-lessie + name: s1-lessie-ai-web-svc + namespace: sit labels: - app: test-lessie-ai-web - environment: test + app: s1-lessie-ai-web + environment: s1 project: lessie spec: - type: NodePort + type: ClusterIP selector: - app: test-lessie-ai-web - environment: test + app: s1-lessie-ai-web + environment: s1 project: lessie ports: - name: http port: 80 - targetPort: 80 - nodePort: 30080 \ No newline at end of file + targetPort: 80 \ No newline at end of file diff --git a/k8s_yaml/s1/sit-ingress-auto-ingress-config.yaml b/k8s_yaml/s1/sit-ingress-auto-ingress-config.yaml new file mode 100644 index 0000000..f49516a --- /dev/null +++ b/k8s_yaml/s1/sit-ingress-auto-ingress-config.yaml @@ -0,0 +1,185 @@ +apiVersion: cloud.tencent.com/v1alpha1 +kind: TkeServiceConfig +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"cloud.tencent.com/v1alpha1","kind":"TkeServiceConfig","metadata":{"annotations":{},"creationTimestamp":"2025-11-10T10:30:17Z","generation":4,"name":"sit-ingress-auto-ingress-config","namespace":"sit","resourceVersion":"2206754346","uid":"a64d0729-c7b3-416d-8eab-921eaa9c6c9d"},"spec":{"loadBalancer":{"l7Listeners":[{"defaultServer":"test.jennie.im","domains":[{"domain":"test.jennie.im","http2":true,"rules":[{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"GET","httpCheckPath":"/health","httpCode":31,"intervalTime":10,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/api/"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/api/chat"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/api/conversation"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/api/searches"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/api/shares"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/api/showcases"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/prod-api/agent/"},{"forwardType":"HTTP","healthCheck":{"checkType":"HTTP","enable":true,"extendedCode":"","healthNum":3,"httpCheckDomain":"test.jennie.im","httpCheckMethod":"HEAD","httpCheckPath":"/","httpCode":31,"intervalTime":5,"sourceIpType":1,"timeout":2,"unHealthNum":3},"scheduler":"WRR","session":{"enable":false},"url":"/prod-api/system"}]}],"keepaliveEnable":0,"port":443,"protocol":"HTTPS"}]}}} + creationTimestamp: "2025-11-10T10:30:17Z" + generation: 5 + name: sit-ingress-auto-ingress-config + namespace: sit + resourceVersion: "2206893793" + uid: a64d0729-c7b3-416d-8eab-921eaa9c6c9d +spec: + loadBalancer: + l7Listeners: + - defaultServer: test.jennie.im + domains: + - domain: test.jennie.im + http2: true + rules: + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: HEAD + httpCheckPath: / + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: / + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /health + httpCode: 31 + intervalTime: 10 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /api/ + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /api/chat + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /api/conversation + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /api/searches + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /api/shares + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /api/showcases + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /sit-api/health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /prod-api/agent/ + - forwardType: HTTP + healthCheck: + checkType: HTTP + enable: true + extendedCode: "" + healthNum: 3 + httpCheckDomain: test.jennie.im + httpCheckMethod: GET + httpCheckPath: /sit-api/health + httpCode: 31 + intervalTime: 5 + sourceIpType: 1 + timeout: 2 + unHealthNum: 3 + scheduler: WRR + session: + enable: false + url: /prod-api/system + keepaliveEnable: 0 + port: 443 + protocol: HTTPS diff --git a/k8s_yaml/s1/sit-ingress.yaml b/k8s_yaml/s1/sit-ingress.yaml new file mode 100644 index 0000000..668ab4a --- /dev/null +++ b/k8s_yaml/s1/sit-ingress.yaml @@ -0,0 +1,179 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + ingress.cloud.tencent.com/client-token: dc603203-6dcd-4f15-8357-a9bf648c70a3 + ingress.cloud.tencent.com/direct-access: "true" + ingress.cloud.tencent.com/status.conditions: '[{"type":"Ready","status":"True","lastTransitionTime":"2025-11-12T07:56:00Z","reason":"Success","message":""}]' + ingress.cloud.tencent.com/tke-service-config-auto: "true" + kubernetes.io/ingress.class: qcloud + kubernetes.io/ingress.extensiveParameters: '{"AddressIPVersion":"IPV4","InternetAccessible":{"InternetChargeType":"TRAFFIC_POSTPAID_BY_HOUR","InternetMaxBandwidthOut":100}}' + kubernetes.io/ingress.http-rules: "null" + kubernetes.io/ingress.https-rules: '[{"host":"test.jennie.im","path":"/","backend":{"serviceName":"s1-lessie-ai-web-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/api/chat","backend":{"serviceName":"s1-lessie-go-api-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/api/conversation","backend":{"serviceName":"s1-lessie-go-api-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/api/shares","backend":{"serviceName":"s1-lessie-go-api-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/api/showcases","backend":{"serviceName":"s1-lessie-go-api-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/api/searches","backend":{"serviceName":"s1-lessie-go-api-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/api/","backend":{"serviceName":"s1-lessie-agents-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/prod-api/agent/","backend":{"serviceName":"s1-flymoon-agent-svc","servicePort":"http"}},{"host":"test.jennie.im","path":"/prod-api/system","backend":{"serviceName":"s1-flymoon-agent-svc","servicePort":"http"}}]' + kubernetes.io/ingress.qcloud-loadbalance-id: lb-2ro8hoe0 + kubernetes.io/ingress.rule-mix: "true" + creationTimestamp: "2025-11-10T10:28:54Z" + finalizers: + - ingress.k8s.tencent/resources + generation: 2 + labels: + ingress.cloud.tencent.com/loadbalance-type: OPEN + managedFields: + - apiVersion: networking.k8s.io/v1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + f:loadBalancer: + f:ingress: {} + manager: service-controller + operation: Update + subresource: status + time: "2025-11-10T10:29:12Z" + - apiVersion: networking.k8s.io/v1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + f:ingress.cloud.tencent.com/tke-service-config-auto: {} + manager: tke-cloud-gw + operation: Update + time: "2025-11-10T10:30:16Z" + - apiVersion: networking.k8s.io/v1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:ingress.cloud.tencent.com/direct-access: {} + f:kubernetes.io/ingress.class: {} + f:kubernetes.io/ingress.extensiveParameters: {} + f:kubernetes.io/ingress.http-rules: {} + f:kubernetes.io/ingress.https-rules: {} + f:kubernetes.io/ingress.rule-mix: {} + f:spec: + f:rules: {} + f:tls: {} + manager: tke-platform-api + operation: Update + time: "2025-11-10T11:20:12Z" + - apiVersion: networking.k8s.io/v1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + f:ingress.cloud.tencent.com/client-token: {} + f:ingress.cloud.tencent.com/status.conditions: {} + f:kubernetes.io/ingress.qcloud-loadbalance-id: {} + f:finalizers: + .: {} + v:"ingress.k8s.tencent/resources": {} + f:labels: + .: {} + f:ingress.cloud.tencent.com/loadbalance-type: {} + manager: service-controller + operation: Update + time: "2025-11-12T07:56:00Z" + name: sit-ingress + namespace: sit + resourceVersion: "2234515988" + uid: ec5b21d8-4386-495d-a5c2-998ae6258f1a +spec: + rules: + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-ai-web-svc + port: + name: http + path: / + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-go-api-svc + port: + name: http + path: /api/chat + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-go-api-svc + port: + name: http + path: /api/conversation + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-go-api-svc + port: + name: http + path: /api/shares + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-go-api-svc + port: + name: http + path: /api/showcases + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-go-api-svc + port: + name: http + path: /api/searches + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-lessie-agents-svc + port: + name: http + path: /api/ + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-flymoon-agent-svc + port: + name: http + path: /prod-api/agent/ + pathType: ImplementationSpecific + - host: test.jennie.im + http: + paths: + - backend: + service: + name: s1-flymoon-agent-svc + port: + name: http + path: /prod-api/system + pathType: ImplementationSpecific + tls: + - hosts: + - test.jennie.im + secretName: jennie-im-sbydostx +status: + loadBalancer: + ingress: + - hostname: lb-2ro8hoe0-iadyek218pl4vqn0.clb.usw-tencentclb.cloud \ No newline at end of file diff --git a/k8s_yaml/s1/test-flymoon-payment.yaml b/k8s_yaml/s1/test-flymoon-payment.yaml deleted file mode 100644 index 7338a64..0000000 --- a/k8s_yaml/s1/test-flymoon-payment.yaml +++ /dev/null @@ -1,90 +0,0 @@ -# ---------------------------- -# Deployment -# ---------------------------- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-flymoon-payment-deployment - namespace: test-lessie - labels: - app: test-flymoon-payment - environment: test - project: flymoon -spec: - replicas: 1 - selector: - matchLabels: - app: test-flymoon-payment - environment: test - project: flymoon - strategy: - type: RollingUpdate # 滚动更新策略 - rollingUpdate: - maxSurge: 1 # 最大新增副本数 - maxUnavailable: 0 # 最大不可用副本数 - template: - metadata: - labels: - app: test-flymoon-payment - environment: test - project: flymoon - spec: - imagePullSecrets: - - name: dxin-image-repository # 镜像仓库凭证Secret - volumes: - - name: flymoon-payment-logs-volume - hostPath: - path: /data/logs/flymoon-payment/ - type: DirectoryOrCreate - containers: - - name: test-flymoon-payment # 容器名称 - image: uswccr.ccs.tencentyun.com/lessietest/flymoon-payment:v0.0.7 # 容器镜像 - imagePullPolicy: Always # 镜像拉取策略,总是拉取 - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: SPRING_PROFILES_ACTIVE - value: "s1" - ports: - - containerPort: 8090 # 容器暴露的端口 - resources: - requests: - cpu: "100m" # 容器请求分配0.1个CPU核心(这不是实际占用,但调度会以这里进行参考) - memory: "1Gi" # 容器请求分配1Gi内存(这会实际预留) - limits: - cpu: "1" # 最多可以使用1个CPU核心 - memory: "3Gi" # 容器最多可以使用3Gi内存 - volumeMounts: - - name: flymoon-payment-logs-volume - mountPath: /app/logs/ - subPathExpr: flymoon-payment-log-$(POD_NAME) - ---- -# ---------------------------- -# Service -# 集群内部:http://test-flymoon-payment-svc.test-lessie.svc.cluster.local:8090 -# 集群外部:http://:30809 -# ---------------------------- - -apiVersion: v1 -kind: Service -metadata: - name: test-flymoon-payment-svc - namespace: test-lessie - labels: - app: test-flymoon-payment - environment: test - project: flymoon -spec: - type: NodePort - selector: # 必须匹配 Deployment 的 labels 才能关联 Pod - app: test-flymoon-payment - environment: test - project: flymoon - ports: - - name: http - port: 8090 # ClusterIP 内部端口 - targetPort: 8090 # 容器端口 - nodePort: 30809 # 节点对外端口(30000-32767) diff --git a/k8s_yaml/s1/test-lessie-ingress.yaml b/k8s_yaml/s1/test-lessie-ingress.yaml deleted file mode 100644 index d8770e4..0000000 --- a/k8s_yaml/s1/test-lessie-ingress.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - ingress.cloud.tencent.com/auto-rewrite: "false" - ingress.cloud.tencent.com/client-token: 454eb954-4094-43fc-9cff-05f3f7f307c7 - ingress.cloud.tencent.com/healthcheck-interval: "5" - ingress.cloud.tencent.com/healthcheck-method: GET - ingress.cloud.tencent.com/healthcheck-path: /health - ingress.cloud.tencent.com/healthcheck-timeout: "3" - ingress.cloud.tencent.com/healthy-threshold: "2" - ingress.cloud.tencent.com/status.conditions: '[{"type":"Ready","status":"True","lastTransitionTime":"2025-11-05T09:47:06Z","reason":"Success","message":""}]' - ingress.cloud.tencent.com/unhealthy-threshold: "3" - kubernetes.io/ingress.class: qcloud - kubernetes.io/ingress.extensiveParameters: '{"AddressIPVersion":"IPV4","InternetAccessible":{"InternetChargeType":"TRAFFIC_POSTPAID_BY_HOUR","InternetMaxBandwidthOut":100}}' - kubernetes.io/ingress.http-rules: "null" - kubernetes.io/ingress.https-rules: '[{"host":"test.jennie.im","path":"/","backend":{"serviceName":"test-lessie-ai-web-svc","servicePort":"80"}},{"host":"test.jennie.im","path":"/payment/webhook","backend":{"serviceName":"test-flymoon-payment-svc","servicePort":"8090"}},{"host":"test.jennie.im","path":"/sit-api/agent/","backend":{"serviceName":"test-flymoon-agent-svc","servicePort":"8070"}},{"host":"test.jennie.im","path":"/sit-api/system/","backend":{"serviceName":"test-flymoon-agent-svc","servicePort":"8070"}},{"host":"test.jennie.im","path":"/api/chat","backend":{"serviceName":"test-lessie-go-api-svc","servicePort":"8100"}}]' - kubernetes.io/ingress.qcloud-loadbalance-id: lb-ifri829c - kubernetes.io/ingress.rule-mix: "true" - creationTimestamp: "2025-10-31T06:28:10Z" - finalizers: - - ingress.k8s.tencent/resources - generation: 14 - labels: - ingress.cloud.tencent.com/loadbalance-type: OPEN - name: test-lessie-ingress - namespace: test-lessie - resourceVersion: "2052378552" - uid: d196795e-711d-4b08-a5f7-22613becfc13 -spec: - rules: - - host: test.jennie.im - http: - paths: - - backend: - service: - name: test-lessie-ai-web-svc - port: - number: 80 - path: / - pathType: ImplementationSpecific - - host: test.jennie.im - http: - paths: - - backend: - service: - name: test-flymoon-payment-svc - port: - number: 8090 - path: /payment/webhook - pathType: ImplementationSpecific - - host: test.jennie.im - http: - paths: - - backend: - service: - name: test-flymoon-agent-svc - port: - number: 8070 - path: /sit-api/agent/ - pathType: ImplementationSpecific - - host: test.jennie.im - http: - paths: - - backend: - service: - name: test-flymoon-agent-svc - port: - number: 8070 - path: /sit-api/system/ - pathType: ImplementationSpecific - - host: test.jennie.im - http: - paths: - - backend: - service: - name: test-lessie-go-api-svc - port: - number: 8100 - path: /api/chat - pathType: ImplementationSpecific - tls: - - hosts: - - test.jennie.im - secretName: jennie-im-sbydostx -status: - loadBalancer: - ingress: - - hostname: lb-ifri829c-6dku12mwxpf5ivcs.clb.usw-tencentclb.cloud