企业实战:Go/Node 构建、扫描、审批与 EKS 发布
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
把已有订单服务的 Go 或 Node.js 仓库接入 Jenkins:测试 → 构建镜像 → 漏洞扫描 → 推送 ECR → 预生产业务验证 → 审批 → 生产发布 → 验证/回退。ECR 是 Amazon Elastic Container Registry(镜像仓库),EKS 是 Amazon Elastic Kubernetes Service(托管 Kubernetes)。
本实验使用真实业务服务和两个隔离环境,不用 Hello World 代替业务验证;应用开发与集群创建沿用已有文档。首次运行只接预生产,失败演练通过后再开放生产审批。
1. 前置条件与验收目标#
- 已完成 Controller 部署、Agent 和 权限隔离。只读验证 Controller 与可信发布 Controller 分离。
- 两套可访问的 EKS 集群,各有
orders命名空间及orders-apiDeployment,容器名api,当前镜像固定 digest。就绪探针、优雅退出、滚动升级容量与数据库兼容参考 企业基线。 - 真实应用仓库提供 Dockerfile、锁定依赖、业务测试;Go 运行
go test -race ./...与go vet ./...,Node 运行npm ci、npm run lint、npm test。Go 的 race 检查需要相应平台及 C 编译工具链。 - 三台独立 EC2 Agent,每台 1 个执行器:
trusted-build、staging-release、production-release。分别附加专属 instance profile;不能共用管理员实例角色。 - 构建 Agent 安装固定版本 Java、Git、Go/Node、rootless Podman、Trivy、AWS CLI;发布 Agent 只需 Java、Python 3、AWS CLI、兼容集群的 kubectl。
验收:生产镜像摘要与预生产完全相同;测试/扫描失败禁止推送;预生产失败禁止审批;错误生产版本会触发受保护的回退;任何身份均不能读取无关秘密。
2. 准备仓库及身份#
由平台管理员设置实际区域、角色和集群变量。下面初始化仅执行一次;资源已存在时先检查,不覆盖既有策略:
export AWS_REGION='ap-east-1'
export ECR_REPOSITORY='ci/orders-api'
export STAGING_CLUSTER='REPLACE_STAGING_CLUSTER'
export PRODUCTION_CLUSTER='REPLACE_PRODUCTION_CLUSTER'
export BUILD_ROLE_NAME='REPLACE_BUILD_INSTANCE_ROLE'
export STAGING_ROLE_NAME='REPLACE_STAGING_INSTANCE_ROLE'
export PRODUCTION_ROLE_NAME='REPLACE_PRODUCTION_INSTANCE_ROLE'
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
aws ecr create-repository --region "$AWS_REGION" --repository-name "$ECR_REPOSITORY" --image-tag-mutability IMMUTABLE三个角色由企业已有 EC2 provisioning 流程建立并附到对应实例;信任主体为 EC2 服务,发布节点不要给构建任务使用。使用以下脚本生成最小权限文件,不手抄账户号:
python3 - <<'PY'
import json, os
from pathlib import Path
r,a=os.environ['AWS_REGION'],os.environ['AWS_ACCOUNT_ID']
def write(name, statements):
Path(name).write_text(json.dumps({'Version':'2012-10-17','Statement':statements},indent=2))
write('build-policy.json',[
{'Effect':'Allow','Action':['ecr:GetAuthorizationToken'],'Resource':'*'},
{'Effect':'Allow','Action':['ecr:DescribeRepositories','ecr:DescribeImages',
'ecr:BatchCheckLayerAvailability','ecr:InitiateLayerUpload','ecr:UploadLayerPart',
'ecr:CompleteLayerUpload','ecr:PutImage'],
'Resource':f"arn:aws:ecr:{r}:{a}:repository/{os.environ['ECR_REPOSITORY']}"}])
for env in ['STAGING','PRODUCTION']:
write(env.lower()+'-policy.json',[{'Effect':'Allow','Action':['eks:DescribeCluster'],
'Resource':f"arn:aws:eks:{r}:{a}:cluster/{os.environ[env+'_CLUSTER']}"}])
PY
aws iam put-role-policy --role-name "$BUILD_ROLE_NAME" --policy-name JenkinsBuild --policy-document file://build-policy.json
aws iam put-role-policy --role-name "$STAGING_ROLE_NAME" --policy-name JenkinsRelease --policy-document file://staging-policy.json
aws iam put-role-policy --role-name "$PRODUCTION_ROLE_NAME" --policy-name JenkinsRelease --policy-document file://production-policy.json这些策略是附加权限,不能抵消已有宽权限,必须同时审查角色其他策略与权限边界。实例应强制 IMDSv2(Instance Metadata Service v2,实例元数据服务第二版),阻止其他信任域访问元数据。使用官方非中国区、同账户 ECR;跨账户或其他分区要修改仓库地址、策略和拉取授权。
集群管理员为两个角色建立 Access Entry(访问身份映射),并按 发布权限 分别应用 Role/RoleBinding:
# 前提:集群 authenticationMode 已为 API 或 API_AND_CONFIG_MAP。
# 从 CONFIG_MAP 迁移需单独评审,不能在流水线临时切换认证模式。
for ENV_NAME in STAGING PRODUCTION; do
CLUSTER_VAR="${ENV_NAME}_CLUSTER"
ROLE_VAR="${ENV_NAME}_ROLE_NAME"
ROLE_ARN=$(aws iam get-role --role-name "${!ROLE_VAR}" --query Role.Arn --output text)
aws eks create-access-entry --region "$AWS_REGION" --cluster-name "${!CLUSTER_VAR}" --principal-arn "$ROLE_ARN" --type STANDARD --kubernetes-groups orders-release
done以上循环用 Bash 执行。不要额外关联集群管理员访问策略。集群工作节点需有 ECR 拉取权限及网络,发布角色不需要推镜像权限。EKS Access Entry 说明
在三台 Agent 上执行 aws sts get-caller-identity,核对分别是预期角色;发布节点生成临时 kubeconfig 后执行 权限正反检查。禁止保存 AWS access key。
3. 安装项目文件#
下载到应用仓库:
| 本地位置 | 下载 |
|---|---|
| Jenkinsfile | 流水线 |
| ci/build-image.sh | 构建与扫描 |
| ci/deploy.py | 部署与回退 |
| ci/verify-business.sh | 真实 API 验证 |
应用 Dockerfile 使用固定基础镜像、非 root 运行用户;.dockerignore 排除 .git、环境文件和密钥,构建秘密不得通过 ARG/COPY 固化到层。应用容器基础示例见 Go 与 Node.js,本项目必须换成实际业务服务。私有基础镜像另授予其仓库的最小拉取权限。
修改 Jenkinsfile 的 APP_KIND(go/node)、区域、仓库名及两个集群名;这些是受审查常量,不向任意构建者开放生产目标参数。保留镜像摘要与审计文件的传递方式,不重新构建生产镜像。
扫描使用 Trivy 的 HIGH/CRITICAL 阈值,数据库下载失败也会失败;接受风险时通过有负责人和到期时间的审查修改规则,不直接忽略全部漏洞。构建节点预留镜像磁盘空间,缓存仅在同一信任域内使用。
4. 接入真实业务门禁#
脚本定义的接口契约:POST /orders 支持 Idempotency-Key 并返回 id;GET /orders/{id} 返回 id、tenant、status,状态为 created 或 accepted。若你的实际接口不同,按其契约修改脚本并审查;不要改成只检查 HTTP 200。
在两个发布 Agent 的节点配置 → Node Properties → Environment variables 分别设置:
| 变量 | 值 |
|---|---|
| VERIFY_ENVIRONMENT | staging 或 production,与节点一致 |
| VERIFY_BASE_URL | 该环境真实 HTTPS 业务入口,不带末尾 / |
| VERIFY_TENANT | 已授权的合成测试租户,例如 release-verification |
| VERIFY_TOKEN_FILE | Agent 本地受控文件路径,内容为该租户 API token |
| VERIFY_PAYLOAD_FILE | 已评审业务请求 JSON 文件路径 |
秘密由企业秘密系统投递,文件仅 Agent 用户可读;不将 token 写入节点环境变量、仓库或归档。业务 token 定期轮换,范围只限测试租户。请求文件示意如下,字段需满足你的服务契约:
{"tenant":"release-verification","synthetic":true,"sku":"REPLACE_APPROVED_TEST_SKU","quantity":1}synthetic 字段本身不提供隔离:先确认服务端把该租户路由到测试库存、支付沙箱及消息消费者,不触发真实扣款/发货,并设置测试数据留存与清理。禁止在未实现这种隔离的生产系统执行创建订单测试。
手工在预生产发布节点运行 bash ci/verify-business.sh staging:必须完成创建、同键重复创建不产生新订单、持久化读回。生产节点先由业务负责人确认隔离机制,再执行对应测试。
5. 建任务并完整运行#
- 仓库保护 main,Jenkinsfile、ci、Dockerfile、依赖锁文件修改必须经过指定维护者审查。部署任务只读取该仓库受保护的 main。
- 在可信 Controller 新建 Pipeline → Pipeline script from SCM → Git,填写固定仓库、只读凭据、分支
*/main、路径 Jenkinsfile。不允许开发者 Configure/Replay 该发布任务。 - 三个节点分别设对应标签和专用执行模式;身份组中建立真实 release-managers。先禁用自动触发,人工发起首轮。
- Build once 成功后下载 audit/image.txt;确认内容是
仓库@sha256:...,不是可变标签。 - 预生产通过后审阅 staging.json、扫描结果、监控与数据库兼容性,再由发布经理批准;首次可直接拒绝审批验证阻断路径。
- 开放生产后核对两个环境镜像摘要、production.json、业务指标和变更记录。错误率、延迟、队列积压等指标的评估窗口与阈值写入变更单;本脚本的少量业务调用不代替负载测试或持续观测。
同一 ECR 仓库由这一个发布任务写入,标签使用提交号加 BUILD_NUMBER;迁移任务时保留构建号或调整受审查的命名规则,避免不可变标签碰撞。不要直接重跑已经发布的摘要;脚本会拒绝制造“新发布”。
6. 故障演练与退出#
| 演练(先在预生产) | 必须观察到的结果 |
|---|---|
| 提交失败测试 | 不构建、不推送、不部署 |
| 扫描命中阈值或扫描器不可用 | 不推送;保留扫描/失败记录 |
| 候选版本业务验证失败 | 恢复旧摘要并重新验证;流水线失败 |
| 验证期间由管理员更改镜像 | 回退条件检查失败,不覆盖他人版本 |
| 审批超时/非发布经理操作 | 不触发生产部署 |
| Agent 在部署后掉线 | 人工按归档与实际状态恢复,不能宣称自动回退成功 |
演练结束停用测试触发器,删除临时 Access Entry/RoleBinding 及专用实例身份授权,按留存策略清理测试镜像和订单。不要删除共享仓库、生产工作负载或仍用于恢复的旧镜像。后续镜像签名与准入验证参考 软件供应链。
可直接运行的配套工程#
完整配套业务见 企业订单参考工程。复制其源码和 ci/test.sh 到应用仓库,再复制本文 Jenkins 资产;APP_KIND=python,构建节点配置 CONTAINER_ENGINE=podman、批准的 BASE_IMAGE/POSTGRES_IMAGE 摘要并安装 podman compose provider。测试用固定回环端口 8088,可信构建 Agent 限单执行器;该工程与 Go/Node 路径二选一。设置 VERIFY_ASYNC=true,测试正文使用 {“tenant”:“release-verification”,“synthetic”:true,“amount_cents”:1200},门禁会等待账本及投影完成。