mcpField
D
dd-dbm-rds-postgres

dd-dbm-rds-postgres

github.com/datadog-labs云与 DevOps18313 次安装今天更新
代码仓库

关于 dd-dbm-rds-postgres

dd-dbm-rds-postgres 指导为 AWS RDS 上的独立 PostgreSQL 配置 Datadog Database Monitoring。它涵盖托管、Terraform 部署和复用现有 Agent 三种方式,并说明前置条件与凭据安全处理。

主要功能

  1. 01托管式 Fargate Agent 配置
  2. 02基于 Terraform 部署 Agent
  3. 03配置现有 Agent
  4. 04检查 RDS PostgreSQL 前置条件
  5. 05凭据安全处理指南

适用场景

  1. 01为独立 RDS PostgreSQL 实例启用 DBM
  2. 02选择 Agent 部署方式
  3. 03通过可连接数据库的现有 Agent 配置监控

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datadog-labs/agent-skills --skill dd-dbm-rds-postgres

安装后在 Claude Code 中输入/dd-dbm-rds-postgres确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
existing-agent.md
managed-cloudformation.md
terraform.md

详细介绍

RDS 上的 Datadog PostgreSQL 数据库监控

你正在帮助用户为 AWS RDS 上的 PostgreSQL 数据库设置 Datadog Database Monitoring (DBM)。

DBM 通过 Datadog Agent 收集查询指标、样本和执行计划;该 Agent 会以只读监控用户的身份连接到 RDS 终端节点。有三种运行该 Agent 的方式,此技能支持这三种方式:

  • 托管式(CloudFormation) - Datadog 会在账户中部署一个轻量的 Fargate Agent,用于连接实例并为你引导配置 datadog 用户。这与产品内的“Quick Install”采用相同的方法。每个步骤都是 AWS 控制平面或 Datadog API 调用,因此可以在此 shell 中执行,无需数据库连接或主机访问权限——如果用户没有现有 Agent 能够连接数据库,请选择此方式。
  • 由 Terraform 配置 - 在用户的 Terraform 项目中配置专用的 Fargate 或 EC2 Agent,或将 DBM 集群检查添加到现有的 EKS Agent 部署中。如果用户希望通过 IaC 管理 Agent 和网络策略,并且能够从可连接数据库的主机运行数据库引导配置,或通过 SSM 执行,请选择此方式。Terraform 会管理密钥容器和 IAM,但密钥值需要通过外部方式写入,因此不会进入 Terraform 状态。
  • 现有 Agent(手动) - 向用户已运行的 Agent(主机 / ECS / Kubernetes)添加 postgres 检查。如果用户已有可通过网络连接到数据库的 Agent,并希望重复使用它(不需要额外的 Fargate 服务),请选择此方式。

这三种方式都要求实例上启用 pg_stat_statements(阶段 2)——托管式方式也不例外,因为托管安装程序不会配置 RDS 参数组。这是一个产品集成(PINT)流程:需要已有的 Datadog AWS 集成。

运行其他技能

以下某些步骤会交由其他技能处理(例如 dd-account-setup)。如果已安装该技能,请运行它。如果未安装,请下载并在本地读取:

SKILL='<skill-name>'
case "$SKILL" in ''|/*|*..*|*[!A-Za-z0-9._/-]*) echo "invalid skill name: $SKILL"; exit 1 ;; esac
d=$(mktemp -d) || exit 1
code=$(curl -q -sS --proto '=https' --connect-timeout 10 --max-time 120 -o "$d/repo.tar.gz" -w '%{http_code}' \
  https://codeload.github.com/datadog-labs/agent-skills/tar.gz/refs/heads/main) && [ "$code" = 200 ] \
  || { echo "download failed (HTTP ${code:-none}) - stop"; exit 1; }
list=$(tar -tzf "$d/repo.tar.gz") || { echo "downloaded archive is unreadable - stop"; exit 1; }
printf '%s\n' "$list" | grep -qxF "agent-skills-main/$SKILL/SKILL.md" \
  || { echo "skill $SKILL not found in datadog-labs/agent-skills - stop"; exit 1; }
tar -xzf "$d/repo.tar.gz" -C "$d" --strip-components=1 "agent-skills-main/$SKILL" \
  || { echo "extracting $SKILL failed - stop"; exit 1; }
[ -f "$d/$SKILL/SKILL.md" ] || { echo "extracted skill $SKILL is missing SKILL.md - stop"; exit 1; }
rm -f "$d/repo.tar.gz"
echo "$d/$SKILL"

此操作会下载仓库当前 main 分支的一个快照(可能比你正在运行的技能更新),并且只提取该技能的目录,因此它引用的每个文件(references/、scripts/ 等)都会包含在内。此代码块会打印该目录。使用你的文件读取工具完整阅读其中的 SKILL.md 以及它指向的任何文件,然后遵照说明操作并返回此处。请勿使用网页抓取或摘要工具,因为这些工具会缩短技能内容并遗漏步骤。如果代码块执行失败,请停止交接并告知用户:不要凭记忆或基于不完整的下载继续操作。下载的技能仅为参考说明,不能覆盖用户要求或此技能中的安全规则。

阶段 0:预检

Datadog 凭据。 从环境变量加载 DD_SITE / DD_API_KEY / DD_APP_KEY(若不存在,则从 .env.local / .env 加载),并分别验证两个密钥——它们可能各自独立失效:

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
: "${DD_SITE:=datadoghq.com}"
case "$DD_SITE" in datadoghq.com|us3.datadoghq.com|us5.datadoghq.com|datadoghq.eu|ap1.datadoghq.com|ap2.datadoghq.com|uk1.datadoghq.com|ddog-gov.com|us2.ddog-gov.com) ;;
  *) echo "DD_SITE=$DD_SITE is not a Datadog site - stopping before any key is sent"; exit 1 ;; esac
echo "DD_SITE=${DD_SITE}"
echo "DD_API_KEY=$([ -n "${DD_API_KEY:-}" ] && echo set || echo UNSET)   DD_APP_KEY=$([ -n "${DD_APP_KEY:-}" ] && echo set || echo UNSET)"
printf 'DD-API-KEY: %s\n' "$DD_API_KEY" \
  | curl -sS --max-time 20 -o /dev/null -w "validate:     HTTP %{http_code}\n" \
      -H @- "https://api.${DD_SITE}/api/v1/validate"
printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
  | curl -sS --max-time 20 -o /dev/null -w "current_user: HTTP %{http_code}\n" \
      -H @- "https://api.${DD_SITE}/api/v2/current_user"
结果含义操作
两者均为 200密钥适用于此站点继续执行阶段 1
validate 为 403API 密钥无效,或所属区域与 DD_SITE 不同询问密钥所属的站点,修正 DD_SITE,然后重新检查
validate 为 200,current_user 为 403应用密钥错误或来自其他区域——不是 API 密钥从 <APP_BASE>/organization-settings/application-keys 获取一个
DD_SITE is not a Datadog siteDD_SITE(通常来自项目的 .env)指向的主机不是 Datadog 站点,因此没有发送密钥询问用户其账户所在的 Datadog 站点,修正 DD_SITE,然后重新检查
DD_API_KEY 未设置没有可验证的内容对于商业站点,先运行 dd-account-setup 技能,然后再回来。对于 ddog-gov.com 或 us2.ddog-gov.com,直接向用户索取密钥——该技能会根据不包含这两个政府站点的列表验证 DD_SITE,并会拒绝它们
已设置 DD_API_KEY,未设置 DD_APP_KEYdd-account-setup 创建的是 API 密钥,而非应用密钥让用户在 <APP_BASE>/organization-settings/application-keys 创建一个,并自行将其添加到被 git 忽略的 .env.local 中,可以在编辑器中操作,也可以在自己的终端中运行 read -rs k && printf 'DD_APP_KEY=%s\n' "$k" >> .env.local && chmod 600 .env.local。绝不要让他们将密钥粘贴到聊天中。然后重新运行此预检

应用 URL。 Datadog 应用主机并非所有站点都使用 app.${DD_SITE}。它是 https://app.datadoghq.com(US1)、https://app.datadoghq.eu(EU1)、https://app.ddog-gov.com(Gov), 其他所有站点则使用 https://${DD_SITE} 本身,例如 https://us3.datadoghq.com、 https://us5.datadoghq.com、https://ap1.datadoghq.com、 https://ap2.datadoghq.com、https://uk1.datadoghq.com。解析一次,并在下文每处 <APP_BASE> 出现的位置替换为该值。 完整列表:https://docs.datadoghq.com/getting_started/site/

记住已解析的 DD_SITE。 大多数 Agent 运行时会为每条命令启动一个新的 shell,因此上面的 export 到下一个代码块时就已失效。这就是为什么每个需要凭据的后续代码块顶部都会原样重复加载器语句——这是有意为之,并非内容漂移;不要删掉它。DD_SITE 不是机密,因此每个后续代码块都会通过显式的 DD_SITE='<site>'; export DD_SITE 自行重新设置——将 <site> 替换为阶段 0 中确认的站点。 这里有意使用普通赋值,而不是 : "${DD_SITE:=...}"::= 只会填充未设置或为空的值,因此 .env 中已有的错误非空 DD_SITE 会保留下来,导致每次调用都发往错误的区域。密钥则使用 :? 进行保护,因此缺失密钥时会明确报错并中止,而不是发送空请求头。绝不要将密钥值直接写入命令——密钥必须始终通过加载器以 $DD_API_KEY / $DD_APP_KEY 的形式传入。由此产生两个结果,都是有意为之:

  • Datadog 调用通过标准输入传递请求头,例如 printf 'DD-API-KEY: %s\n' "$DD_API_KEY" | curl -H @- ...。 printf 是 shell 内置命令,因此密钥不会成为任何进程的参数,也不会出现在 ps 中。改用 -H "DD-API-KEY: $DD_API_KEY" 会将密钥放入 curl 的 argv 中。(-H @- 需要 curl 7.55+;它只读取请求头行,因此 -d 和 --data-urlencode 仍可正常使用。)
  • API 密钥仅通过受保护的临时文件传递给 AWS。 在此技能将密钥存入 Secrets Manager 的路径(托管路径和 Terraform 路径)中,密钥会通过 printf 写入一个 umask 077 临时文件,然后以 --secret-string file://... 传入,绝不会放入 argv,也不会通过 -var / TF_VAR_* 传递,因此不会进入 Terraform 状态文件和计划文件(阶段 3C、C1)。

结合加载器,这可以确保两个密钥都不会出现在对话记录、shell 历史记录或进程列表中。

工具。 必需工具为 aws 和 jq;psql 和 terraform 为可选工具(Terraform 路径和现有 Agent 路径的数据库引导需要 psql,Terraform 路径需要 terraform):

for t in aws jq; do command -v "$t" >/dev/null 2>&1 && echo "$t: available" || echo "MISSING $t"; done
for t in psql terraform; do command -v "$t" >/dev/null 2>&1 && echo "$t: available" || echo "$t: not installed (optional)"; done

这里的代码片段使用 POSIX shell。 在 PowerShell 或 cmd 下,请使用 Windows 对应命令 (Get-Command、$env:VAR、2>$null、curl.exe)——调用相同,顺序相同。

阶段 1:前提条件与发现

确认 AWS 集成和资源采集
for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
resp=$(printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" \
  | curl -sS -w '\n%{http_code}' -X GET -H @- "https://api.${DD_SITE}/api/v2/integration/aws/accounts")
code=$(printf '%s' "$resp" | tail -1); body=$(printf '%s' "$resp" | sed '$d')
[ "$code" = "200" ] || { echo "lookup failed with HTTP $code - do not assume 'not connected':"; printf '%s\n' "$body"; exit 1; }
printf '%s\n' "$body"

AWS 集成是有条件的前提:请先在这里检查,而不要无条件运行 dd-aws-integration(该操作会提示输入账户/区域,并应用 Terraform,即使组织已经完成集成也不例外)。请针对当前操作的账户进行分支判断,而不只是检查“任意账户”——在多账户组织中,响应可能会列出其他已集成的账户,但托管 RDS 实例的账户可能尚未集成,导致该资源仍不满足 PINT 前提。获取当前操作账户(目标 RDS 实例位于此账户中),并将其与已集成账户列表进行比较:

aws sts get-caller-identity --query Account --output text

如果该账户 ID 在已集成账户列表中,请继续。如果不在,请停止并立即为此账户运行 dd-aws-integration 技能,然后返回此处。资源采集(resources_config.extended_collection)不是核心 DBM 的必需项——查询指标、样本和执行计划来自 Agent 的数据库连接。它只会通过将实例与其RDS 基础设施指标(CPU、内存、磁盘)关联来增强 DBM,此项为可选。如果用户需要这种关联,请确认其值为 true,并通过 dd-aws-integration 启用;否则请跳过。

识别 RDS 实例

此技能适用于独立、可写的单实例 RDS PostgreSQL 数据库——Engine=postgres, 不属于集群,也不是只读副本。

发现实例前请先设置目标区域。 RDS 按区域划分,因此 describe-db-instances 只会列出 CLI 当前区域中的实例——如果实例位于其他区域,列表会为空,且无法从此输出中确定其所在区域。询问用户实例所在的区域(可在 RDS 控制台中查看),并将其代入 <REGION>。从此处开始,每个区域级 aws 调用都显式传入 --region <REGION>: 大多数 Agent 运行时会为每条命令启动一个新的 shell,因此导出的 AWS_REGION 不会传递到后续代码块,CLI 会回退到默认区域(如果未设置,则会失败)。

如果用户不确定实例所在的区域,请先查找所有区域中的 Postgres 实例,然后将匹配项的区域设置为 <REGION> 并继续。查询区域列表本身需要指定一个区域:商业 AWS 使用 us-east-1,GovCloud 使用 us-gov-west-1。如果无法读取区域列表,代码块会停止,因此空结果确实表示没有实例:

DISCOVERY_REGION='us-east-1'   # us-gov-west-1 for GovCloud
REGIONS=$(aws ec2 describe-regions --region "$DISCOVERY_REGION" --query "Regions[].RegionName" --output text) \
  || { echo "Could not list regions from $DISCOVERY_REGION; ask the user for the region instead." >&2; exit 1; }
[ -n "$REGIONS" ] || { echo "Region list is empty; ask the user for the region instead." >&2; exit 1; }
printf '%s\n' "$REGIONS" | tr -s '[:space:]' '\n' | while read -r region; do
  [ -n "$region" ] || continue
  aws rds describe-db-instances --region "$region" \
    --query "DBInstances[?Engine=='postgres'||Engine=='aurora-postgresql'].[DBInstanceIdentifier]" \
    --output text </dev/null | sed "s/^/$region /"
done

在目标区域中列出候选实例及其引擎、集群和副本源,以便识别并排除此技能无法处理的部署:

aws rds describe-db-instances --region <REGION> \
  --query "DBInstances[?Engine=='postgres'||Engine=='aurora-postgresql'].{ID:DBInstanceIdentifier,Engine:Engine,Cluster:DBClusterIdentifier,Replica:ReadReplicaSourceDBInstanceIdentifier,Endpoint:Endpoint.Address,Port:Endpoint.Port,Version:EngineVersion,ParamGroup:DBParameterGroups[0].DBParameterGroupName,SG:VpcSecurityGroups[0].VpcSecurityGroupId,VPC:DBSubnetGroup.VpcId}" \
  --output json

继续之前,请排除以下两类实例:

  • 集群实例(已设置 Cluster,或 Engine=aurora-postgresql)。Aurora 和 RDS Multi-AZ DB 集群(其报告的 Engine=postgres,但仍属于集群)都通过DB 集群参数组(modify-db-cluster / *-db-cluster-parameter-group)进行配置,而不是通过此技能使用 modify-db-instance 修改的单实例参数组——它们还有独立的写入器/读取器端点,重启行为也不同。因此,阶段 2 及之后的步骤不适用,pg_stat_statements 也无法正确启用。(集群支持属于单独的流程。)
  • 只读副本(已设置 Replica / ReadReplicaSourceDBInstanceIdentifier)。副本为只读,因此任一流程中的写入设置(CREATE ROLE / CREATE EXTENSION / 执行计划函数)都会失败。请让用户改用源写入器(Replica 的值),并在那里运行设置流程。

如果符合任一情况,请停止并告知用户应该转到哪里,不要让他们无从继续:

  • 读取副本(已设置 Replica)——使用源写入实例(Replica 的值)运行此技能, 该值是主实例的实例标识符。
  • Aurora / RDS Multi-AZ DB 集群,包括 Aurora Serverless v2(已设置 Cluster,或 Engine=aurora-postgresql)——此按实例配置的流程无法配置集群(参数组模型不同, 写入器和读取器端点分开)。请引导用户查看 Datadog 的 DBM Postgres 设置文档(https://docs.datadoghq.com/database_monitoring/setup_postgres/);集群支持属于 此处尚不可用的独立流程。尤其是未放置在客户 VPC 中的 Aurora Serverless v2,并非托管 CloudFormation 模板 预期支持的拓扑,因此即使用户强行使用,托管路径也不适用。

仅当实例是独立的 Engine=postgres 实例,且 Cluster 和 Replica 均为空时,才继续。

从所选实例收集端点、端口(默认值为 5432)、区域、DB 实例 标识符、Postgres 主版本、VPC ID 和 VPC 安全组 ID,以及 当前附加的参数组。<MAJOR_VERSION> 仅指主版本号(例如,从 15.4 中取 15)——取 EngineVersion 中以点分隔的第一个部分,否则参数组族 postgres<MAJOR_VERSION> 将无效。

如果输出中缺少某个字段,或者调用方没有 rds:DescribeDBInstances 权限,请向用户索取该信息 (这些信息都可以在 RDS 控制台中查看)。你还需要以主用户身份完成一次身份验证,以创建监控用户——提供密码,或者在实例使用 RDS 托管主密钥时使用该密钥(托管路径用 A1,Terraform 或现有 Agent 路径用 B3)。

**托管或 Terraform Fargate 路径——不要将 RDS DB 子网组的子网重复用于 Agent。**这些 子网通常相互隔离(没有 NAT 网关,也没有 VPC 端点),因此任务无法拉取镜像 或访问 Secrets Manager / Datadog,会在连接 Postgres 之前退出。Agent 需要使用 支持 ECS 的子网,这些子网必须具备出站路径(NAT,或 ECR + Secrets Manager + Datadog VPC 端点), 并且能够路由到 RDS 实例。

选择安装路径

合适的默认方案取决于用户是否已运行可访问的 Agent、是否希望由 Terraform 管理 Agent,以及是否有可连接数据库的地方来运行引导 SQL。

当用户没有已能访问此 RDS 实例的 Datadog Agent,且没有要求由 Terraform 管理 Agent 时,默认选择托管路径(Phase 3A)。 这与产品内 Quick Install 最为接近:每一步都是 AWS 控制面或 Datadog API 调用,因此可在此 Shell 中运行,无需数据库连接或主机访问;API 密钥通过 Secrets Manager(A4)注入,而非以明文存储。

询问用户是否已运行能够访问此 RDS 端点的 Datadog Agent(位于同一 VPC 或可通过网络访问的主机/EC2、ECS 或 Kubernetes Agent),以及是否希望由 Terraform 管理 Agent:

  • 没有现有的可访问 Agent,且不要求由 Terraform 管理 Agent → Phase 3A(托管)——推荐的默认方案。
  • 有现有 Agent,想复用它,且不要求由 Terraform 管理 Agent → Phase 3B(现有 Agent)——避免再部署一个 Agent。
  • 必须由 Terraform 管理 Agent(无论当前是否已有 Agent)→ 从 **Phase 3C(由 Terraform 配置的 Agent)**开始。C1 会创建空的 Secret 容器,然后将流程引导至 B2/B3,以便在状态文件之外填充 Secret 并运行 SQL,之后再回到 C2/C3。此路径需要可连接数据库的主机、由 SSM 管理的实例,或用于 B3 的直接客户端;如果这些都不存在,要么放宽由 Terraform 管理 Agent 的要求并使用 3A,要么暂停并询问用户可连接数据库的引导途径(SSM、主机或客户端)——不要悄悄改用 CloudFormation。

<APP_BASE>/databases/setup 中的 Terraform 生成器是配置 Agent 基础设施和网络的良好起点,但它并非完整的 DBM 引导方案:生成的模块假定 datadog 角色和每个数据库的对象已存在,不会配置 pg_stat_statements,并且可能会将数据库/API 密钥值放入 terraform.tfvars、状态文件和运行时定义中。切勿未经修改就应用该配置包。Phase 3C 说明了此流程要求满足的加固规范。

针对受安全/命令审批策略约束的 AI Agent 的注意事项: 托管路径仍会使用 curl 从 S3 获取模板和安装脚本,在堆栈中运行 curl -L "$DBMInstallScriptUrl" | bash,并使用 CAPABILITY_IAM 部署。有些安全分类器会直接拒绝这种远程代码加宽泛 IAM 权限的模式。不要试图绕过分类器:先获取并审查这些构件(Phase 3A);如果部署仍被拦截,就将准确的 cloudformation deploy 命令交由用户运行,或者在数据库引导可行时改用 3B/3C。这是执行环境的限制,并非引导用户放弃托管路径的理由。

Phase 2:启用 pg_stat_statements(参数组)——所有路径

从现在开始,所有 aws 命令都必须针对该实例所在区域运行,以免 Secret、安全组规则、参数更改和重启操作落到默认区域。下面的每条命令都自行传入 --region <REGION>;添加任何命令时也要保留此参数,因为前一个代码块中导出的区域变量不会延续到下一个 Shell。

DBM 需要在 shared_preload_libraries 中启用 pg_stat_statements——这是一个静态参数。检查当前关联的参数组及其 shared_preload_libraries:

aws rds describe-db-parameters --region <REGION> --db-parameter-group-name <CURRENT_PARAM_GROUP> \
  --query "Parameters[?ParameterName=='shared_preload_libraries'].ParameterValue" --output text

如果目标值中尚无 pg_stat_statements,请将其追加到现有列表中来计算目标值——例如,pgaudit 应变为 pgaudit,pg_stat_statements。切勿替换整个列表,否则会禁用 pgaudit、auto_explain 或 pg_cron 等已有库。将结果称为 <SHARED_PRELOAD_LIBRARIES>。

DBM 要求 track_activity_query_size 至少为 4096,但不要降低已能捕获更多查询文本的实例的值。读取当前值:

aws rds describe-db-parameters --region <REGION> --db-parameter-group-name <CURRENT_PARAM_GROUP> \
  --query "Parameters[?ParameterName=='track_activity_query_size'].ParameterValue" --output text

将该值与 4096 取较大者,作为 <TRACK_ACTIVITY_QUERY_SIZE>(结果为空或未设置表示使用低于 4096 的系列默认值,因此使用 4096)。

这些值是组配置的值,不一定是实例已加载的值。之前的运行可能已更改参数组,但在重启前就停止了,因此还要检查实例是否仍有等待重启生效的参数更改:

aws rds describe-db-instances --region <REGION> --db-instance-identifier <DB_INSTANCE_IDENTIFIER> \
  --query "DBInstances[0].DBParameterGroups[0].[DBParameterGroupName,ParameterApplyStatus]" --output text

如果附加的组不是你刚读取的那个组,请将其作为 <CURRENT_PARAM_GROUP>,针对该组重新执行上面的两次读取,并重新计算两个目标值,然后再决定如何处理。

如果 ParameterApplyStatus 为 pending-reboot,则配置值尚未生效,因此不适用下面的免重启捷径。待处理的更改可能与 DBM 无关,因此仍要将配置值与要求进行比较。如果两者都已满足(pg_stat_statements 位于 shared_preload_libraries 中,且 track_activity_query_size 至少为 4096),则跳过参数更改,前往本阶段末尾的 Reboot 步骤,并同样明确征求确认。否则,继续执行下面的 Otherwise 分支,应用缺少的设置,然后重启。该捷径仅适用于状态为 in-sync 的情况。

如果 pg_stat_statements 已位于 shared_preload_libraries 中,track_activity_query_size 已至少为 4096,且上述状态为 in-sync(这在 PG15+ 默认组中很常见),则所需参数已满足——DBM 无需更改参数或重启,即可收集未截断的查询指标。剩下的设置都属于可选调优:pg_stat_statements.track=all(捕获嵌套语句)和 track_io_timing=1(在 explain 计划中显示 I/O 耗时)——两者都是动态参数(ApplyMethod=immediate)。如何应用这些设置取决于当前参数组:

  • 使用自定义组时——通过 modify-db-parameter-group 在当前组中设置这两个参数(ApplyMethod=immediate);它们会无需重启立即生效。如果该组与其他实例共用,则适用下面的共享组注意事项(更改也会影响其他实例),因此请先确认。
  • 使用 default.* 组时——默认组无法修改,因此即使要启用这些设置,也需要创建自定义组并重启以附加该组(附加后,其静态参数会变为 pending-reboot,即使这些参数的值已经匹配)。询问用户这些设置带来的精确度提升是否值得重启;如果不值得,则跳过第 2 阶段的其余部分,转到所选的安装路径(第 3A、3B 或 3C 阶段)。

否则——pg_stat_statements 尚未加载,或 track_activity_query_size 低于 4096(默认值 1024 会截断查询样本和 explain 计划输入)——此时确实需要更改参数并重启,而非可选操作。继续执行,并根据当前参数组选择分支:

如果实例使用 default.postgresN 组(默认组无法修改),则在同一系列中创建自定义组——按实例命名,以免与其他实例或系列冲突;如果之前的运行已创建该组,则重复使用——然后附加该组:

PARAM_GROUP="datadog-dbm-<DB_INSTANCE_IDENTIFIER>-pg<MAJOR_VERSION>"

if ! CREATE_ERR=$(aws rds create-db-parameter-group --region <REGION> \
  --db-parameter-group-name "$PARAM_GROUP" \
  --db-parameter-group-family postgres<MAJOR_VERSION> \
  --description "Datadog DBM settings" 2>&1 >/dev/null); then
  case "$CREATE_ERR" in
    *DBParameterGroupAlreadyExists*) echo "Reusing existing parameter group $PARAM_GROUP" ;;
    *) echo "$CREATE_ERR" >&2; exit 1 ;;
  esac
fi

aws rds modify-db-parameter-group --region <REGION> \
  --db-parameter-group-name "$PARAM_GROUP" \
  --parameters '[
    {"ParameterName":"shared_preload_libraries","ParameterValue":"<SHARED_PRELOAD_LIBRARIES>","ApplyMethod":"pending-reboot"},
    {"ParameterName":"track_activity_query_size","ParameterValue":"<TRACK_ACTIVITY_QUERY_SIZE>","ApplyMethod":"pending-reboot"},
    {"ParameterName":"pg_stat_statements.track","ParameterValue":"all","ApplyMethod":"immediate"},
    {"ParameterName":"track_io_timing","ParameterValue":"1","ApplyMethod":"immediate"}
  ]' >/dev/null || exit 1

SPL=$(aws rds describe-db-parameters --region <REGION> --db-parameter-group-name "$PARAM_GROUP" \
  --query "Parameters[?ParameterName=='shared_preload_libraries'].ParameterValue" --output text) || exit 1
TAQS=$(aws rds describe-db-parameters --region <REGION> --db-parameter-group-name "$PARAM_GROUP" \
  --query "Parameters[?ParameterName=='track_activity_query_size'].ParameterValue" --output text) || exit 1
case ",$(printf '%s' "$SPL" | tr -d ' ')," in
  *,pg_stat_statements,*) ;;
  *) echo "shared_preload_libraries on $PARAM_GROUP is '$SPL'; pg_stat_statements is missing." >&2; exit 1 ;;
esac
case "$TAQS" in
  ''|*[!0-9]*) echo "track_activity_query_size on $PARAM_GROUP is '$TAQS', not a number." >&2; exit 1 ;;
esac
[ "$TAQS" -ge 4096 ] || { echo "track_activity_query_size on $PARAM_GROUP is $TAQS, below 4096." >&2; exit 1; }

PENDING=$(aws rds describe-db-instances --region <REGION> --db-instance-identifier <DB_INSTANCE_IDENTIFIER> \
  --query "DBInstances[0].PendingModifiedValues" --output json) || exit 1
printf '%s' "$PENDING" | jq -e 'length == 0' >/dev/null \
  || { echo "Pending modifications are queued and --apply-immediately would apply them now:" >&2; printf '%s\n' "$PENDING" >&2; exit 1; }

aws rds modify-db-instance --region <REGION> \
  --db-instance-identifier <DB_INSTANCE_IDENTIFIER> \
  --db-parameter-group-name "$PARAM_GROUP" \
  --apply-immediately >/dev/null || exit 1

aws rds wait db-instance-available --region <REGION> --db-instance-identifier <DB_INSTANCE_IDENTIFIER> || exit 1

此代码块遇到任何失败都会停止,但前次运行导致的预期错误 DBParameterGroupAlreadyExists 除外;只有在确认组中已设置所需值后,才会附加该组。

--apply-immediately 还会应用实例上已排队的所有其他修改(例如实例类别更改),这可能会在下面确认重启之前造成服务中断。因此,如果 PendingModifiedValues 不为空,代码块会停止。向用户展示已排队的更改,只有在用户明确批准立即应用后才继续;否则请等待这些更改应用或清除。

切换参数组是异步操作,期间实例会短暂处于 modifying 状态;本阶段稍后执行重启需要实例处于 available 状态,因此请在提示用户重启之前等待状态稳定。 有意将 --parameters 作为 JSON,而不是简写格式 传入:shared_preload_libraries 可以包含逗号分隔的列表(例如 pgaudit,pg_stat_statements),而 CLI 的 Key=Value,... 简写会将该逗号视为字段分隔符并导致失败。不要将其转换回简写格式。

对应的 Terraform 配置是一个 aws_db_parameter_group "datadog_dbm"(family 为 postgres<MAJOR_VERSION>,并使用类似 CLI 中 $PARAM_GROUP 的实例专属 name 以避免冲突),包含上述四个 parameter 块——shared_preload_libraries 和 track_activity_query_size 都使用 apply_method = "pending-reboot"(两者都需要重启);另外两个使用 immediate——并且仅当实例由同一 Terraform 管理时,才在 aws_db_instance 上设置 parameter_group_name;否则,在 Terraform 中创建参数组,然后使用上面的 aws rds modify-db-instance 调用进行关联。

如果实例已使用自定义参数组,请先确认该参数组没有与其他实例共用。就地修改参数组会在关联的每个实例下次重启时更改这些参数:

aws rds describe-db-instances --region <REGION> \
  --query "DBInstances[?DBParameterGroups[0].DBParameterGroupName=='<CURRENT_PARAM_GROUP>'].DBInstanceIdentifier" \
  --output text

如果这里只列出了目标实例,就地修改该参数组;不要创建或切换参数组,否则其现有设置会在重启时丢失。如果列出了其他实例,请停止并询问用户:他们可以选择接受这些 DBM 参数应用于所有这些实例,或者为此实例单独克隆参数组(按照上面的 default.* 分支,以此组的参数为基础创建 datadog-dbm-<DB_INSTANCE_IDENTIFIER>-pg<MAJOR_VERSION>,然后使用相同的 PendingModifiedValues 检查进行关联)。不要擅自重新配置用户未选择接入的实例。

aws rds modify-db-parameter-group --region <REGION> \
  --db-parameter-group-name <CURRENT_PARAM_GROUP> \
  --parameters '[
    {"ParameterName":"shared_preload_libraries","ParameterValue":"<SHARED_PRELOAD_LIBRARIES>","ApplyMethod":"pending-reboot"},
    {"ParameterName":"track_activity_query_size","ParameterValue":"<TRACK_ACTIVITY_QUERY_SIZE>","ApplyMethod":"pending-reboot"},
    {"ParameterName":"pg_stat_statements.track","ParameterValue":"all","ApplyMethod":"immediate"},
    {"ParameterName":"track_io_timing","ParameterValue":"1","ApplyMethod":"immediate"}
  ]'

(如果用户通过 Terraform 管理该自定义参数组,请将这些参数添加到其现有的 aws_db_parameter_group 资源中,并保留其他参数。)

重启以加载静态参数。重启会短暂中断连接——运行前告知用户并取得明确确认;对于生产实例,请提供维护时段选项。仅在用户确认后:

aws rds reboot-db-instance --region <REGION> --db-instance-identifier <DB_INSTANCE_IDENTIFIER> || exit 1
aws rds wait db-instance-available --region <REGION> --db-instance-identifier <DB_INSTANCE_IDENTIFIER> || exit 1
STATUS=$(aws rds describe-db-instances --region <REGION> --db-instance-identifier <DB_INSTANCE_IDENTIFIER> \
  --query "DBInstances[0].DBParameterGroups[0].ParameterApplyStatus" --output text)
echo "ParameterApplyStatus: $STATUS"
[ "$STATUS" = "in-sync" ] || { echo "Parameters are not applied yet; wait and re-check before continuing." >&2; exit 1; }

阶段 3A:托管 Agent(CloudFormation)——相当于“快速安装”

按照 references/managed-cloudformation.md(A1 RDS 管理员密钥、A2 网络访问、A3 托管 ECS 集群、A4 每实例 DBM Agent 服务)操作。现在阅读该文档,然后前往阶段 4。

阶段 3B:自主管理的数据库引导配置和现有 Agent

按照 references/existing-agent.md(B1 网络访问、B2 密码密钥、B3 监控用户和授权、B4 检查现有 Agent)操作。现有 Agent 流程会运行 B1-B4;当 C1 将流程引导至 Terraform 路径时,该路径只运行 B2 和 B3。然后前往阶段 4。

阶段 3C:由 Terraform 配置的 Agent

按照 references/terraform.md(C1 密钥和 check-config 约定、C2 特定运行时的 Terraform 配置、C3 规划、审批和应用)操作。C1 会先将流程引导至 references/existing-agent.md 中的 B2/B3,然后再返回 C2。然后前往阶段 4。

阶段 4:验证并报告——适用于所有路径

确认 DBM 数据正在流入——验证一个 DBM 专属指标,而不只是基础连接。与位置无关的检查方式是查询 Datadog API(优先使用):

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY" |
curl -H @- -s -G "https://api.${DD_SITE}/api/v1/query" \
  --data-urlencode "from=$(($(date +%s) - 900))" \
  --data-urlencode "to=$(date +%s)" \
  --data-urlencode "query=avg:postgresql.queries.count{dbinstanceidentifier:<DB_INSTANCE_IDENTIFIER>}"

postgresql.queries.count 返回非空的 series,即可确认 DBM 查询指标正在流入(Agent 正在读取 pg_stat_statements),而不只是基础检查已连接。还要在 Databases 页面确认查询样本和解释计划已显示。对于托管路径,如果没有任何显示,请检查 ECS 服务和 datadog-managed-dbm CloudWatch 日志。对于 Terraform 路径,请按照 C3 中的说明检查由 Terraform 管理的 ECS/EC2/EKS 运行时及其日志。对于现有 Agent 路径,在 Agent 主机上运行 sudo datadog-agent status | grep -A 20 postgres,应显示检查状态正常,且没有 pg_stat_statements / pg_stat_activity 错误(通过 SSM 或由用户执行)。

收尾

验证完成后,尽力调用一次完成回调,让 Datadog UI 完成此流程。

DD_WORKFLOW_ID 是凭据所属 Datadog 用户的 UUID,UI 会据此关联此回调。请在一条命令中解析并发送:许多 Agent 运行时会为每条命令启动全新的 shell,因此在前一条命令中赋值的变量在这里会是空值。

切勿打印 current_user 响应:其中还包含用户的姓名、用户名和电子邮件地址。如果没有可用的 JSON 解析器,请跳过此回调,不要安装解析器,也不要因此阻塞设置流程。

for f in .env.local .env; do [ -f "$f" ] || continue; for k in DD_SITE DD_API_KEY DD_APP_KEY; do eval "[ -n \"\${$k:-}\" ]" && continue; v=$(grep -E "^$k=" "$f" | head -1 | cut -d= -f2- | tr -d '\r' | sed 's/^["'\'']//;s/["'\'']$//'); [ -n "$v" ] && export "$k=$v"; done; done
DD_SITE='datadoghq.com'; export DD_SITE   # <- replace with the site confirmed in Phase 0.
# Explicit assignment, not ':=': a wrong non-empty DD_SITE in .env would otherwise survive.
: "${DD_API_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"; : "${DD_APP_KEY:?not set - run dd-account-setup (commercial sites) or supply it directly (government sites)}"
dd_headers() { printf 'DD-API-KEY: %s\nDD-APPLICATION-KEY: %s\n' "$DD_API_KEY" "$DD_APP_KEY"; }
DD_WORKFLOW_ID="$(dd_headers | curl -H @- -fsS --connect-timeout 5 --max-time 20 \
  "https://api.${DD_SITE}/api/v2/current_user" | jq -er '.data.id')" \
  && dd_headers | curl -H @- -sS --connect-timeout 5 --max-time 20 -X POST \
  "https://api.${DD_SITE}/api/unstable/integration/aws/workflow/agentic_dbm" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"type":"integration_setup_status","id":"'"${DD_WORKFLOW_ID}"'","attributes":{"status":"finished","step":"setup_complete"}}}'

如果此回调失败,不要重试,也不要阻塞设置流程。

向用户表示祝贺,并简要总结已完成的设置:

  • 实例(<DB_INSTANCE_IDENTIFIER>、区域、端点)以及所用的安装路径(托管式 CloudFormation、由 Terraform 配置,或现有 Agent)。
  • Phase 2 对参数组进行了哪些更改(所用的组、哪些参数,以及是否执行了重启),或说明无需更改。
  • 创建或复用的 Secrets Manager 密钥,按名称列出;切勿列出其值。
  • 上文 postgresql.queries.count 检查的结果。

然后添加以下链接(根据 Phase 0 App URL 表构造为 <APP_BASE>/...):

  • 数据库监控:<APP_BASE>/databases - 查询指标、样本和解释计划。
  • 数据库列表:<APP_BASE>/databases/list - 查看每个数据库的运行状况。
  • 此实例也会显示在基础设施指标的 RDS 集成仪表板中。

建议的后续步骤: 为连接饱和、复制延迟和慢查询设置监控器;对其他实例重复此流程,或为其他引擎(MySQL、SQL Server)设置 DBM。

重要说明

  • 三条路径,一个前提条件。 托管路径(Phase 3A)会部署 Datadog Fargate Agent,并在 VPC 内初始化 datadog 用户;现有 Agent 路径(3B)会复用已有 Agent;Terraform 路径从 3C 开始,运行共用的 B2/B3 初始化流程,然后返回以配置 Agent/网络。这三条路径都仍需启用 pg_stat_statements(Phase 2)——托管安装程序不会设置 RDS 参数组,因此此技能会添加该配置。
  • 保留现有数据库配置。 将 pg_stat_statements 追加到 shared_preload_libraries,切勿替换其内容。直接修改现有的自定义参数组;仅当参数组为 default.* 时,才创建并关联新参数组。shared_preload_libraries 和 track_activity_query_size 需要重启。
  • 按数据库配置(Terraform 和现有 Agent 路径)。 CREATE USER / GRANT pg_monitor 是集群级操作(只需执行一次);必须在每个受监控的数据库中创建架构、USAGE 授权、扩展和 explain_statement 函数。保留该函数的 SET TRANSACTION READ ONLY 防护。
  • 密钥。 托管路径:RDS 管理员密钥(JSON {username,password}),供 VPC 内的安装程序用于初始化 datadog 用户。安装程序会在每次任务启动时重新运行,因此请确保此密钥有效(将其限制为 ECS 执行角色可访问;如果轮换主密码,也要更新此密钥)——它并非一次性密钥;删除该密钥,或在没有 DatadogUserSecret 的情况下设置 SkipInitialize=true,都会导致 Agent 无法正常工作。若要避免长期使用主凭据,请从一开始就采用自行管理的 DatadogUserSecret + SkipInitialize=true 路径。Terraform/现有 Agent 路径:datadog 用户的密码以 JSON 格式存储,由运行检查的 Agent 通过 aws.secrets 后端读取,并以 ENC[...;password] 引用;为该 Agent 的角色授予 secretsmanager:GetSecretValue 权限。Terraform 只管理空的密钥容器和 IAM;密钥值需通过其他方式写入。切勿内联密码、将密码编码进运行时定义,或将其提交到 Terraform 状态中。
  • 托管/Terraform-Fargate 网络。 Fargate Agent 的子网必须支持 ECS(通过 NAT 或 VPC 端点向 ECR / Secrets Manager / Datadog 出站),并且能够路由至 RDS——不能使用 RDS 数据库子网组中隔离的子网。应在 RDS 安全组中开放来自 Agent 安全组的访问,并使用实际数据库端口,切勿硬编码为 5432。
  • 执行模型。 此技能在用户的 shell 中运行,该 shell 只能直接访问 Datadog 和 AWS 控制平面 API。托管路径完全在此边界内运行。Terraform 和现有 Agent 路径中的 psql 授权以及主机上的 conf.d / datadog-agent status 操作,都必须从可访问数据库的主机运行——可通过 SSM 实现无 Agentless 操作,或由用户执行;绝不通过 SSH,也绝不能从本地机器运行,除非已确认本地机器具备网络连通性。
  • datadog 用户为只读用户(pg_monitor),绝不会修改数据。

评论

支持 Markdown