Shipyard:Slack 如何推进下一代 EC2 平台现代化

2026-07-15 39 预计阅读时间: 1 分钟
来源: slack.engineering AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

Slack 正在持续改造其 Amazon EC2 实例的运行方式。此前,他们已经把单一 Chef 栈演进为具备容错能力的多栈架构,并引入版本化 cookbook 部署与更安全的晋级流程;名为 Shipyard 的下一代 EC2 平台,则延续了这条现代化路线:把实例交付从零散的配置操作,逐步变成可版本化、可验证、可回退的平台流程。

需要说明的是,来源摘要没有披露 Shipyard 的完整架构、内部 API 或具体 AWS 资源组合。下面的分析以摘要明确提到的演进方向为依据,实践示例则展示团队可以如何落地类似思路,并不代表 Slack 的实际实现。

从“维护服务器”转向“晋级制品”

Chef 多栈、cookbook 版本化和安全晋级流程解决了一个核心问题:生产环境不应该直接接收未经验证的配置变化。

同样的原则可以扩展到 EC2 平台。一次实例变更通常不只涉及 Chef cookbook,还可能包括:

  • 基础 AMI 和操作系统补丁;
  • 启动模板、实例类型与磁盘配置;
  • IAM 实例角色和安全组;
  • 启动阶段运行的 bootstrap 脚本;
  • 应用包、监控代理及日志采集组件。

如果这些元素分别由不同脚本修改,团队很难回答“当前实例究竟运行哪个版本”。下一代平台更值得追求的交付单元,是一个带版本的实例规格:它引用固定 AMI、固定配置版本和可审计的基础设施定义,然后依次晋级到测试、预生产和生产环境。

这也改变了故障处理方式。与其登录实例修复状态,不如构建新版本、通过自动检查,再让 Auto Scaling Group 逐批替换旧实例。旧版本仍然存在,因此回退变成重新指向上一个已知可用版本,而不是现场撤销一串命令。

多栈的价值不只是高可用

摘要提到 Slack 已经从单一 Chef 栈迁移到具备韧性的多栈结构。多栈除了降低单点故障风险,还能建立清晰的故障域和发布边界。

例如,可以按 AWS 区域、环境或业务关键度拆分控制面。每个栈独立接受新版本,并通过健康指标决定是否继续扩大范围。一个典型的晋级路径可以是:

  1. 在测试栈启动少量实例并执行配置收敛。
  2. 检查实例启动时间、Chef 执行结果和服务健康探针。
  3. 将同一版本晋级到预生产栈,观察真实流量下的错误率。
  4. 对生产栈执行分批替换,并设置自动停止条件。

这里的关键不是栈越多越好。每增加一个栈,团队都要承担证书、权限、监控、容量和升级成本。合理的拆分标准应当是故障隔离需求,而不是组织架构本身。

可以这样实践:用不可变版本驱动实例刷新

下面是一个可改造的最小示例。假设团队已经构建了 AMI,并将待发布的 AMI ID 写入 AWS Systems Manager Parameter Store。脚本会创建新的 Launch Template 版本,再触发 Auto Scaling Instance Refresh。

运行前需要修改 REGIONAMI_PARAMETERLAUNCH_TEMPLATE_IDASG_NAME,并确保当前 AWS 身份拥有读取参数、创建启动模板版本和刷新 Auto Scaling Group 的权限。

#!/usr/bin/env bash
set -euo pipefail

REGION="us-east-1"
AMI_PARAMETER="/shipyard/example/staging/ami-id"
LAUNCH_TEMPLATE_ID="lt-0123456789abcdef0"
ASG_NAME="example-staging-web"

AMI_ID=$(aws ssm get-parameter \
  --region "$REGION" \
  --name "$AMI_PARAMETER" \
  --query 'Parameter.Value' \
  --output text)

SOURCE_VERSION=$(aws ec2 describe-launch-templates \
  --region "$REGION" \
  --launch-template-ids "$LAUNCH_TEMPLATE_ID" \
  --query 'LaunchTemplates[0].LatestVersionNumber' \
  --output text)

NEW_VERSION=$(aws ec2 create-launch-template-version \
  --region "$REGION" \
  --launch-template-id "$LAUNCH_TEMPLATE_ID" \
  --source-version "$SOURCE_VERSION" \
  --version-description "ami-${AMI_ID}" \
  --launch-template-data "{\"ImageId\":\"${AMI_ID}\"}" \
  --query 'LaunchTemplateVersion.VersionNumber' \
  --output text)

aws ec2 modify-launch-template \
  --region "$REGION" \
  --launch-template-id "$LAUNCH_TEMPLATE_ID" \
  --default-version "$NEW_VERSION"

REFRESH_ID=$(aws autoscaling start-instance-refresh \
  --region "$REGION" \
  --auto-scaling-group-name "$ASG_NAME" \
  --preferences '{"MinHealthyPercentage":90,"InstanceWarmup":300,"AutoRollback":true}' \
  --query 'InstanceRefreshId' \
  --output text)

echo "Started refresh: $REFRESH_ID"

部署后可以持续查询刷新状态:

aws autoscaling describe-instance-refreshes \
  --region us-east-1 \
  --auto-scaling-group-name example-staging-web \
  --instance-refresh-ids REPLACE_WITH_REFRESH_ID \
  --query 'InstanceRefreshes[0].{Status:Status,Progress:PercentageComplete,Reason:StatusReason}'

这个示例体现了三个重要约束:AMI ID 是明确的版本输入;实例通过替换而不是原地修改完成升级;平台保留旧启动模板版本,为回退提供基础。不过,生产环境还应在刷新前验证 AMI 签名、漏洞扫描结果、容量余量和目标组健康检查。

平台必须同时管理发布与证据

只封装 AWS API 并不足以构成下一代平台。平台还需要记录每次晋级的证据,例如:

  • 谁批准了哪个版本进入生产;
  • AMI、cookbook 和启动模板之间的版本关系;
  • 测试栈通过了哪些健康检查;
  • 实例刷新期间错误率和容量是否越过阈值;
  • 回退究竟恢复了哪些资源版本。

Chef 收敛成功也不等于服务可用。实例可能正确安装了软件,却因为依赖不可达、证书错误或负载均衡注册失败而无法处理请求。因此,发布门禁至少应结合配置执行结果、EC2 状态检查、目标组健康度和应用层探针。

采用时应守住的边界

类似 Shipyard 的平台化改造适合从一条低风险服务链路开始,而不是一次迁移全部 EC2 工作负载。先建立版本模型、晋级规则和回退路径,再逐步接入更多团队。

落地前可以检查以下事项:

  • 每台实例能否追溯到唯一的 AMI、配置和启动模板版本;
  • 是否禁止或至少审计生产实例上的手工变更;
  • 预生产与生产是否使用同一个制品,而不是分别构建;
  • 分批发布是否设置最小健康容量、预热时间和停止条件;
  • 控制面故障时,现有实例能否继续提供服务;
  • 回退流程是否经过演练,而不只是写在文档里。

这条现代化路线的重点并非给 EC2 再套一层界面,而是把实例生命周期变成可重复的工程流程。版本化制品、多栈隔离和受控晋级组合起来,才能让大规模 EC2 变更既有速度,也保留可审计与可恢复的边界。


相关推荐