使用 Terraform 将 Teleport Database Service 部署到 AWS ECS:基于 IAM Join Token 的完整示例解析
2026/9/20 21:11:04 网站建设 项目流程

使用 Terraform 将 Teleport Database Service 部署到 AWS ECS:基于 IAM Join Token 的完整示例解析

【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址: https://gitcode.com/gh_mirrors/tel/teleport

导读

本文围绕 integrations/terraform-modules/teleport/container-service/aws/examples/db-service 示例展开,讲解如何用 Terraform 把 Teleport 的数据库服务(Database Service)以容器方式部署到 AWS ECS Fargate,并通过IAM Join Token使其安全加入一个已有的 Teleport 集群。读完本文,你将掌握该示例的完整目录结构、每个 Terraform 文件的职责、teleport_config的写法要点、IAM 信任关系的自动推导逻辑,以及模块底层如何把配置渲染进 ECS 任务定义——并可以直接把这一套方案复制到自己的基础设施中。

示例概览:一键拉起一个 ECS 上的数据库代理

该示例的核心目标可以用一句话概括:用纯 Terraform 声明式地创建一个承载 Teleport Database Service 的 ECS 服务,并让这个容器代理通过 AWS IAM 身份认证(join_method = "iam")自动加入你已有的 Teleport 集群,从而为集群提供数据库访问能力,而无需在 ECS 上手工维护任何静态 Token。

从 Terraform 模块依赖看,整个示例只依赖两层:

模块SourceVersion作用
teleport_database_service../..(即本仓库模块container-service/aws当前仓库版本创建 ECS 集群、服务、任务定义、IAM 角色与安全组
vpcterraform-aws-modules/vpc/aws6.6.0创建一个带 3 个可用区、公有/私有子网的示例 VPC

它对外只暴露一个必填输入:

NameDescriptionTypeDefaultRequired
teleport_proxy_addrTeleport Proxy Service 的地址,host:port形式stringn/ayes

以及一个输出teleport_database_service,完整导出模块创建的全部资源对象,方便调用方进一步引用(例如输出 ECS 集群 ARN、任务 IAM 角色 ARN 等)。

目录结构:示例由六个文件组成

该示例目录的完整清单如下(位于 integrations/terraform-modules/teleport/container-service/aws/examples/db-service):

db-service/ ├── README.md # 本示例说明(含自动生成的 TF-Docs 表格) ├── data.tf # 可用区数据源 ├── docs_description # TF-Docs 生成用的描述文本 ├── docs_title # TF-Docs 生成用的标题文本 ├── main.tf # VPC 与 Database Service 模块、IAM Join Token ├── outputs.tf # 导出 teleport_database_service ├── providers.tf # aws / teleport 两个 provider 配置 ├── variables.tf # teleport_proxy_addr 变量声明 └── versions.tf # Terraform 与 provider 版本约束

其中docs_descriptiondocs_title只是 terraform-docs 生成README.md中表格区的输入素材,真正的业务逻辑集中在main.tfproviders.tfversions.tf三个文件里。

版本约束(versions.tf)

versions.tf 定义了整套环境的版本基线:

terraform { required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" version = "~> 6.0" } http = { source = "hashicorp/http" version = "~> 3.0" } teleport = { source = "terraform.releases.teleport.dev/gravitational/teleport" version = "~> 18.5" } } }

几个值得注意的细节:

  • teleportprovider 的 source 是terraform.releases.teleport.dev/gravitational/teleport,不是hashicorp/命名空间下的官方 registry,需要在使用前执行terraform login或在 CLI 配置中信任该私有 registry;
  • httpprovider(~> 3.0)不是直接在本示例中被声明使用,而是随teleport_database_service模块的Managed Updates功能传递引入的(见下文“版本管理”一节);
  • Terraform 版本要求>= 1.5.7,与模块自身的required_version保持一致。

Provider 配置(providers.tf)

providers.tf 同时配置了两个 provider:

provider "aws" { region = "us-east-1" default_tags { tags = { env = "example" } } } provider "teleport" { addr = var.teleport_proxy_addr profile_name = replace(var.teleport_proxy_addr, "/:[0-9]+.*/", "") }
  • awsprovider 固定使用us-east-1,并通过default_tags给所有 AWS 资源打上env = example标签——这些标签会与模块内apply_aws_tags传入的标签合并;
  • teleportprovider 使用addr指向 Proxy 地址,profile_name则通过正则replace(..., "/:[0-9]+.*/", "")host:port中剥离端口部分,得到纯主机名作为 tsh 配置文件名。这意味着使用该示例的前提是:本机已经通过tsh login登录过对应集群,Terraform 才能拿到创建teleport_provision_token所需的集群凭据。

核心编排(main.tf):从 VPC 到数据库代理

main.tf 是整个示例的灵魂,按自上而下顺序完成三件事:建 VPC → 起数据库服务模块 → 创建 IAM Join Token。

第一步:创建示例 VPC

locals { namespace = "example" } module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "6.6.0" azs = slice(data.aws_availability_zones.this.names, 0, 3) cidr = "10.0.0.0/16" name = "${local.namespace}-vpc" public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] private_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] }

配合 data.tf 中的data "aws_availability_zones" "this" {},示例从当前区域真实可用的可用区中取前 3 个(slice(..., 0, 3))来放置子网。VPC 使用10.0.0.0/16,公有子网与私有子网各 3 个,符合 ECS 多 AZ 高可用部署的最小布局。

第二步:调用 teleport_database_service 模块

module "teleport_database_service" { source = "../.." # Enable managed updates managed_updates_enabled = true managed_updates_group = "default" apply_aws_tags = { "example" = "true" } assign_public_ip = true # must be true when using public subnets ecs_cluster_name = "${local.namespace}-cluster" ecs_service_name = "${local.namespace}-svc" ecs_service_subnets = module.vpc.public_subnets ecs_task_desired_count = 1 environment_vars = { EXAMPLE_VAR = "EXAMPLE_VALUE" } vpc_id = module.vpc.vpc_id teleport_config = { version = "v3" teleport = { join_params = { token_name = "${local.namespace}-iam" method = "iam" } proxy_server = var.teleport_proxy_addr log = { severity = "DEBUG" } } auth_service = { enabled = "no" } proxy_service = { enabled = "no" } ssh_service = { enabled = "no" } discovery_service = { enabled = "no" } db_service = { enabled = "yes" resources = [ { labels = { "env" = "example" } } ] } } }

这段代码集中体现了本示例的三个设计要点:

  1. teleport_config使用原生 Terraform 语法书写,最终会被模块渲染成teleport start --config-string所需的 YAML(见下文“任务定义里发生了什么”一节)。它等价于一段标准 Teleport 配置:version: v3,通过join_params.method = "iam"token_name = "example-iam"指定 IAM 加入方式,proxy_server指向你的 Proxy 地址,日志级别设为DEBUG便于排障。
  2. 仅开启db_serviceauth_serviceproxy_servicessh_servicediscovery_service全部显式enabled = "no",这与“数据库代理”的定位完全吻合——ECS 上的容器只充当 Database Service,不承担集群控制面职责。db_service.resources[0].labels = { "env" = "example" }声明了该服务托管的数据库标签,Teleport 集群将依据这一标签把注册请求路由到本代理。
  3. assign_public_ip = true与公有子网配套:由于示例 VPC 的公网子网直连 Internet Gateway,容器需要被分配公网 IP 才能反向连接 Teleport Proxy;模块 README 明确要求——assign_public_ip = true时子网必须是公有子网,false时则必须是走 NAT 网关的私有子网。

第三步:创建 IAM Join Token

resource "teleport_provision_token" "iam" { metadata = { name = "${local.namespace}-iam" description = "Allow the Teleport ECS agent to join the cluster using AWS IAM credentials." } spec = { allow = [{ aws_arn = module.teleport_database_service.teleport_provision_token_allow_aws_arn }] join_method = "iam" roles = ["Db"] } version = "v2" }

这是整个示例最巧妙的部分:

  • 它通过teleportprovider 在你的 Teleport 集群里创建一个join_method = "iam"roles = ["Db"]的 provisioning token,作用域精确到一条 ARN;
  • 该 ARN 不是手写的,而是直接引用模块输出module.teleport_database_service.teleport_provision_token_allow_aws_arn

查看模块 outputs.tf 可知其推导公式:

value = format( "arn:%v:sts::%v:assumed-role/%v/*", one(data.aws_partition.this[*].partition), one(data.aws_caller_identity.this[*].account_id), one(aws_iam_role.ecs_task[*].name), )

arn:aws:sts::<account>:assumed-role/<ecs_task_role_name>/*。换句话说:模块自动查询当前 AWS 账号 ID 与分区,加上它创建的 ECS 任务 IAM 角色名,拼出“该任务角色可被信任”的完整 ARN 通配表达式。容器启动后带着 ECS 任务角色的临时 STS 凭据去连接 Proxy,Teleport 侧用这条 token 的allow.aws_arn与之比对,通过即放行——全程无需静态 Token,天然具备自动轮换能力。

模块输入速查:哪些参数值得关注

teleport_database_service模块(即 container-service/aws)暴露了丰富的输入,除示例中已使用的之外,下面这些在实际生产中经常被用到:

输入默认值说明
ecs_task_cpu2048任务 CPU 单元数(Fargate 计费单位)
ecs_task_memory4096任务内存(MiB)
ecs_task_desired_count2期望运行的任务副本数,示例中显式设为1
ecs_task_cloudwatch_log_group_retention_days30CloudWatch 日志保留天数
ecs_task_cloudwatch_log_group_kms_key_idnull日志组加密 KMS Key;为null时使用 CloudWatch 默认加密
createtrue全局开关,置false可只取输出不创建资源
create_security_grouptrue是否创建任务安全组,可用security_group_ids追加既有安全组
security_group_ids[]额外附加到任务的安全组
ecs_task_role_inline_policynull合并进任务 IAM 角色内联策略的 JSON,常用于给数据库代理授予访问目标数据库的权限
ecs_task_role_self_assumption_allowedtrue任务角色是否允许自我 assume(IAM Join 前置条件之一)
teleport_container_imagepublic.ecr.aws/gravitational/teleport-ent-distrolessTeleport 容器镜像,注意默认是企业版 distroless 镜像
teleport_version19.0.0-prealpha.2显式指定 Teleport 版本(一般交给 Managed Updates 决定)
managed_updates_enabled/managed_updates_grouptrue/"default"是否从 v2 Managed Updates 端点解析推荐版本

模块的常用输出包括:ecs_cluster_arnecs_service_arnecs_task_definition_arnecs_execution_role_arnecs_task_role_arnsecurity_group_idteleport_config以及上文的teleport_provision_token_allow_aws_arn,完整清单见 container-service/aws/README.md。

任务定义里发生了什么:从 Terraform 配置到容器启动

为了理解“模块如何把teleport_config变成运行中的代理”,需要看模块的 aws_ecs_task_definition.tf。其核心容器定义片段如下:

container_definitions = jsonencode([ { command = [ # rewrite SIGTERM (15) to SIGQUIT (3) so ECS stop signal triggers graceful Teleport shutdown "--rewrite", "15:3", "--", "teleport", "start", "--config-string", base64encode(yamlencode(var.teleport_config)), ] entryPoint = ["/usr/bin/dumb-init"] ... image = "${var.teleport_container_image}:${local.teleport_version}" logConfiguration = { ... awslogs ... } name = "teleport" } ])

这段实现揭示了三个底层细节:

  1. 配置传递方式teleport_config先被yamlencode序列化成 YAML,再base64encode编码,通过teleport start --config-string注入容器——所以teleport_config里的键名(如db_servicejoin_params)与 Teleport 原生 YAML 配置一一对应。
  2. 优雅停机--rewrite 15:3通过 dumb-init 把 ECS 停止容器时发送的SIGTERM(15) 改写成SIGQUIT(3),从而触发 Teleport 的优雅关闭流程,避免代理被强杀导致会话中断。
  3. 版本来源:镜像 tag 由local.teleport_version决定,其计算逻辑是——若启用了 Managed Updates,则从data.http.managed_updates拉取auto_update.agent_version,否则回退到var.teleport_version,再统一去掉前导v

同时,任务定义上的precondition还强制约束:开启 Managed Updates 时,teleport_config.teleport.proxy_server必须非空(见 aws_ecs_task_definition.tf),这是模块保证能正确解析更新源的前提,也是示例中必须传teleport_proxy_addr的原因之一。

此外,任务使用network_mode = "awsvpc"requires_compatibilities = ["FARGATE"],日志走awslogs驱动写入模块创建的 CloudWatch Log Group;任务与执行两个 IAM 角色分别承载“Teleport 自身运行所需权限”和“拉取镜像、写日志所需权限”。

部署步骤与验证

前置条件

  • 已配置好 Teleport 集群与tsh,并能以管理员身份登录(示例中创建 provisioning token 需要相应权限);
  • 本机已通过tsh login <proxy>登录,且tsh配置文件与providers.tfprofile_name推导出的主机名一致;
  • 已配置 AWS 凭据(Access Key 或 SSO),且账号有创建 VPC、ECS、IAM 的权限;
  • 已信任terraform.releases.teleport.dev私有 registry(首次执行terraform init时按提示完成登录)。

操作步骤

# 1. 初始化,拉取模块与 provider terraform init # 2. 确认执行计划,重点检查 teleport_provision_token 与 ECS 资源 terraform plan -var="teleport_proxy_addr=proxy.example.com:443" # 3. 应用 terraform apply -var="teleport_proxy_addr=proxy.example.com:443"

应用成功后,可以按以下顺序验证:

  1. terraform output teleport_database_service查看模块导出的完整资源(ECS 集群/服务/任务 ARN、角色 ARN、安全组 ID、CloudWatch 日志组);
  2. 在 Teleport 侧运行tsh status确认集群连接正常,再用tsh db ls(或 Web UI 的 Databases 页面)查看是否出现env=example标签的数据库服务实例;
  3. 登录 AWS 控制台查看 ECS 服务是否处于RUNNING、任务是否通过健康检查,并到 CloudWatch Log Group 中检查teleport start的启动日志(示例配置了DEBUG级别,会输出 IAM Join 的完整握手过程)。

变更与清理

  • 调整ecs_task_desired_count即可水平扩缩容代理副本;
  • 需要数据库代理访问具体数据库时,通过ecs_task_role_inline_policy给任务角色授予对应数据库的访问策略,并在db_service.resources中补充数据库标签或静态资源清单;
  • 不再需要时执行terraform destroy即可回收全部资源(注意 provisioning token 是创建在 Teleport 集群内的资源,也会一并删除)。

小结

db-service示例是一个“麻雀虽小、五脏俱全”的 ECS 化 Teleport 代理模板:它用最少的外部依赖(一个 VPC 模块 + 一个自研模块),演示了从网络环境搭建、teleport_config编写、IAM Join Token 创建到版本托管的全流程。其核心可复用资产有三点:

  • 纯声明式的teleport_config写法,与 Teleport 原生 YAML 语义一致,可平移到任意容器平台;
  • teleport_provision_token_allow_aws_arn自动推导,让“ECS 任务角色 ↔ Teleport IAM Join Token”的信任关系零手写、随模块自动闭环;
  • Managed Updates 机制,把 Teleport 版本选择权交给官方更新端点,配合proxy_server校验前置条件,降低长期维护成本。

如果你正在把 Teleport 的数据库访问能力容器化落地到 AWS,可以直接以本示例为基线:先原样部署跑通链路,再按上文“模块输入速查”中的参数逐项替换成生产配置即可。

【免费下载链接】teleportThe easiest, and most secure way to access and protect all of your infrastructure.项目地址: https://gitcode.com/gh_mirrors/tel/teleport

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询