feat(terraform): AWS Fargate deployment module
Adds a terraform/ directory with an opinionated module that deploys shared-memory to ECS Fargate behind an ALB. The module assumes the operator already provides the VPC, RDS Postgres, ACM cert, ECR images, and OIDC clients, and creates everything else: ECS cluster + services, ALB, Service Connect namespace for app-embedder discovery, EFS-backed model cache for the embedder, Secrets Manager entries, IAM roles, CloudWatch log groups, and a one-shot migrator task definition. Includes examples/basic/ with a worked invocation and a README covering prerequisites, quick start, the post-apply migrator run, image updates, DNS setup, and a security note. Main README gains a short Mode C pointer to the terraform/ guide. Validated with `terraform fmt -check -recursive` and `terraform validate` against AWS provider 5.x. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# EFS for the embedder model cache.
|
||||
#
|
||||
# Without persistent storage, every cold-start embedder task re-downloads
|
||||
# the ~30 MB bge-small model from Hugging Face — slow and rate-limit-risky.
|
||||
# EFS lets us share a warm cache across replicas and across restarts.
|
||||
#
|
||||
# The access point pins ownership to UID/GID 1001, matching the
|
||||
# `node-embedder` user baked into apps/embedder/Dockerfile, so files written
|
||||
# through the access point are owned correctly.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
resource "aws_efs_file_system" "embedder_models" {
|
||||
creation_token = "${var.name_prefix}-embedder-models"
|
||||
encrypted = true
|
||||
|
||||
# General Purpose performance mode + bursting throughput is plenty for a
|
||||
# ~30 MB read-mostly cache. Don't pay for provisioned throughput.
|
||||
performance_mode = "generalPurpose"
|
||||
throughput_mode = "bursting"
|
||||
|
||||
tags = merge(local.tags, { Name = "${var.name_prefix}-embedder-models" })
|
||||
}
|
||||
|
||||
# One mount target per private subnet so any AZ the embedder lands in can
|
||||
# reach the file system.
|
||||
resource "aws_efs_mount_target" "embedder_models" {
|
||||
for_each = toset(var.private_subnet_ids)
|
||||
|
||||
file_system_id = aws_efs_file_system.embedder_models.id
|
||||
subnet_id = each.value
|
||||
security_groups = [aws_security_group.efs.id]
|
||||
}
|
||||
|
||||
# Access point gives the embedder task a chrooted view of the file system,
|
||||
# with files always owned by uid/gid 1001 regardless of which task wrote
|
||||
# them. Matches the `node-embedder` user in the Dockerfile.
|
||||
resource "aws_efs_access_point" "embedder_models" {
|
||||
file_system_id = aws_efs_file_system.embedder_models.id
|
||||
|
||||
posix_user {
|
||||
uid = 1001
|
||||
gid = 1001
|
||||
}
|
||||
|
||||
root_directory {
|
||||
path = "/models"
|
||||
|
||||
creation_info {
|
||||
owner_uid = 1001
|
||||
owner_gid = 1001
|
||||
permissions = "0755"
|
||||
}
|
||||
}
|
||||
|
||||
tags = merge(local.tags, { Name = "${var.name_prefix}-embedder-models" })
|
||||
}
|
||||
Reference in New Issue
Block a user