Secrets Manager Agent 升级:预取密钥 + IAM 角色切换,冷启动延迟直降 90%

"Lambda 冷启动 800ms,其中 600ms 在等 Secrets Manager 返回数据库密码。"

如果你的应用在启动时需要从 Secrets Manager 拉密钥,大概率踩过这个坑——API 调用延迟在冷启动路径上特别刺眼。正常运行时 Secrets Manager 响应几十毫秒不是问题,但冷启动时累积三四个密钥请求就是大几百毫秒。

亚马逊云科技上周给 Secrets Manager Agent 加了两个新能力:

  1. Pre-fetching — Agent 启动时预加载你配置好的密钥,应用启动时密钥已在本地缓存
  2. IAM Role Assumption — Agent 可以 assume 不同的 IAM 角色去拉不同作用域的密钥

这两个功能组合起来,解决了密钥获取在延迟敏感场景下的性能问题。

Secrets Manager Agent 是什么

先说背景。Secrets Manager Agent 是一个轻量级本地进程,跑在你的计算实例上(EC2、ECS、EKS Pod),充当应用和 Secrets Manager 服务之间的代理层。

它的核心价值:

  • 本地缓存:密钥拉一次缓存在内存里,不用每次都调 API
  • 自动轮转:密钥更新时自动刷新缓存
  • HTTP 接口:应用通过 http://localhost:2773 拉密钥,不用集成 AWS SDK

之前的限制:Agent 启动后要等第一次请求才去拉密钥(被动缓存)。现在有了 pre-fetching,变成主动预加载。

Pre-fetching 配置

基本配置

# /etc/secrets-manager-agent/config.yaml
cache:
  ttl: 300  # 缓存 5 分钟
  max_size: 100

# 新增:预取配置
prefetch:
  enabled: true
  secrets:
    - secret_id: "prod/database/mysql"
    - secret_id: "prod/api-keys/stripe"
    - secret_id: "prod/api-keys/sendgrid"
    - secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:prod/redis-abc123"

server:
  port: 2773
  bind: "127.0.0.1"

效果对比

# 不用 Pre-fetching(老方式):
# App 启动 → 请求 Secret A → 等 API 返回 (80ms)
#           → 请求 Secret B → 等 API 返回 (75ms)
#           → 请求 Secret C → 等 API 返回 (90ms)
# 总延迟: ~245ms

# 用 Pre-fetching(新方式):
# Agent 启动 → 后台并行预取 A/B/C (一次性 ~100ms)
# App 启动 → 请求 Secret A → 本地缓存命中 (< 1ms)
#           → 请求 Secret B → 本地缓存命中 (< 1ms)
#           → 请求 Secret C → 本地缓存命中 (< 1ms)
# App 可见延迟: < 3ms

从 245ms 降到 3ms,延迟降了 90%+。

在 ECS Task Definition 中使用

{
  "containerDefinitions": [
    {
      "name": "secrets-agent",
      "image": "public.ecr.aws/aws-secrets-manager/secrets-manager-agent:latest",
      "essential": true,
      "portMappings": [
        {"containerPort": 2773, "protocol": "tcp"}
      ],
      "environment": [
        {"name": "SECRETS_MANAGER_AGENT_CONFIG", "value": "/config/config.yaml"}
      ],
      "mountPoints": [
        {"sourceVolume": "agent-config", "containerPath": "/config"}
      ]
    },
    {
      "name": "my-app",
      "image": "my-app:latest",
      "dependsOn": [
        {"containerName": "secrets-agent", "condition": "HEALTHY"}
      ],
      "environment": [
        {"name": "SECRETS_ENDPOINT", "value": "http://localhost:2773"}
      ]
    }
  ]
}

关键点:dependsOn + HEALTHY 确保 Agent 完成预取后应用才启动。

IAM Role Assumption

之前 Agent 只能用宿主实例的 IAM 角色拉密钥。如果一台机器跑多个微服务,每个服务需要不同密钥(不同权限边界),就得跑多个 Agent 实例。

现在可以配置多个角色:

# 多角色配置
roles:
  - name: "payments-role"
    role_arn: "arn:aws:iam::123456789:role/payments-secrets-reader"
    session_name: "payments-agent"
    secrets:
      - "prod/payments/stripe-key"
      - "prod/payments/bank-credentials"

  - name: "notifications-role"  
    role_arn: "arn:aws:iam::123456789:role/notifications-secrets-reader"
    session_name: "notifications-agent"
    secrets:
      - "prod/notifications/ses-credentials"
      - "prod/notifications/push-certs"

  - name: "analytics-role"
    role_arn: "arn:aws:iam::987654321:role/cross-account-analytics"
    external_id: "analytics-2026"
    secrets:
      - "prod/analytics/redshift-password"

跨账号场景

数据团队的密钥在账号 B,应用在账号 A:

import requests

# 应用代码不变,Agent 自动 assume 对应角色
# 请求带上角色标识
response = requests.get(
    'http://localhost:2773/secretsmanager/get',
    params={
        'secretId': 'prod/analytics/redshift-password',
        'role': 'analytics-role'  # 指定用哪个角色
    },
    headers={'X-Aws-Parameters-Secrets-Token': os.environ['SECRETS_TOKEN']}
)

secret = response.json()['SecretString']

IAM 策略示例

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAgentToAssumeRoles",
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": [
        "arn:aws:iam::123456789:role/payments-secrets-reader",
        "arn:aws:iam::123456789:role/notifications-secrets-reader",
        "arn:aws:iam::987654321:role/cross-account-analytics"
      ]
    }
  ]
}

完整实战:EKS Pod 中使用

# Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    spec:
      serviceAccountName: order-service-sa  # IRSA 绑定 IAM Role
      initContainers:
        - name: secrets-agent-init
          image: public.ecr.aws/aws-secrets-manager/secrets-manager-agent:latest
          command: ["sh", "-c", "secrets-manager-agent --prefetch --wait-ready"]
          volumeMounts:
            - name: agent-socket
              mountPath: /var/run/secrets-agent
      containers:
        - name: order-service
          image: order-service:latest
          env:
            - name: DB_HOST
              value: "prod-mysql.xxx.us-east-1.rds.amazonaws.com"
            - name: DB_PASSWORD_SECRET
              value: "prod/orders/db-password"
            - name: SECRETS_ENDPOINT
              value: "http://localhost:2773"
        - name: secrets-agent
          image: public.ecr.aws/aws-secrets-manager/secrets-manager-agent:latest
          ports:
            - containerPort: 2773
          volumeMounts:
            - name: agent-config
              mountPath: /config
      volumes:
        - name: agent-config
          configMap:
            name: secrets-agent-config
        - name: agent-socket
          emptyDir: {}

性能数据

根据官方公告和实测估算:

场景 无 Agent Agent(无预取) Agent(预取)
首次获取密钥 80-120ms 80-120ms < 1ms
后续获取 80-120ms < 1ms < 1ms
冷启动 3 密钥 ~300ms ~300ms < 3ms
P99 延迟 200ms 5ms 2ms

我的判断

这个更新看起来不起眼,但对延迟敏感的应用来说是实打实的改进。

之前的最佳实践是在应用代码里自己做预加载(启动时主动调 Secrets Manager API),但这意味着每个应用都要写一遍这个逻辑。现在 Agent 层面解决了,应用代码可以更干净。

IAM 角色切换也是个实用功能——之前一台机器跑多个服务要么共享一个大权限角色(安全风险),要么跑多个 Agent(资源浪费)。现在一个 Agent 支持多角色,最小权限原则落地更自然了。

唯一要注意的:Agent 本身变成了关键路径。如果 Agent 挂了,所有依赖它的应用都拉不到密钥。建议配合健康检查和自动重启策略。


相关链接:

posted @ 2026-05-28 20:00  亚马逊云开发者  阅读(3)  评论(0)    收藏  举报