-
Notifications
You must be signed in to change notification settings - Fork 98
feat: Migrate fastly-exporter to AWS ECS Express
#830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,17 @@ | ||
| # Prometheus Exporter for Fastly | ||
|
|
||
| This module deploys a Prometheus exporter for Fastly using the official | ||
| [fastly/fastly-exporter] Docker image. The implementation uses the [`ecs-task`] | ||
| and [`ecs-service`] modules to deploy the exporter to ECS. | ||
| [fastly/fastly-exporter] Docker image. | ||
|
|
||
| ## ECS Express Migration | ||
|
|
||
| **Note**: This service has been migrated to **AWS ECS Express** (November 2025), | ||
| which simplifies deployment by automatically managing load balancing, networking, | ||
| and scaling infrastructure. See [MIGRATION_TO_ECS_EXPRESS.md](MIGRATION_TO_ECS_EXPRESS.md) | ||
| for details about the migration. | ||
|
|
||
| The implementation now uses `aws_ecs_express_gateway_service` instead of the | ||
| traditional `ecs-task` and `ecs-service` modules, reducing complexity while | ||
| maintaining the same functionality. | ||
|
|
||
| [`ecs-service`]: ../../terragrunt/modules/ecs-service | ||
| [`ecs-task`]: ../../terragrunt/modules/ecs-task | ||
| [fastly/fastly-exporter]: https://github.com/fastly/fastly-exporter |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,3 +1,7 @@ | ||||||
| # ECS Express migration for fastly-exporter | ||||||
| # This simplifies the configuration by using aws_ecs_express_gateway_service | ||||||
| # which automatically manages load balancing, networking, and scaling. | ||||||
|
|
||||||
| locals { | ||||||
| name = "fastly-exporter" | ||||||
| } | ||||||
|
|
@@ -6,104 +10,144 @@ data "aws_ssm_parameter" "fastly_api_token" { | |||||
| name = "/prod/fastly-exporter/fastly/api-token" | ||||||
| } | ||||||
|
|
||||||
| resource "aws_iam_policy" "read_fastly_api_token" { | ||||||
| name = "ecs--${local.name}" | ||||||
| # CloudWatch log group for the service | ||||||
| resource "aws_cloudwatch_log_group" "fastly_exporter" { | ||||||
| name = "/ecs/${local.name}" | ||||||
| retention_in_days = 7 | ||||||
| } | ||||||
|
|
||||||
| # IAM role for ECS task execution (pulling images, writing logs, accessing secrets) | ||||||
| resource "aws_iam_role" "execution" { | ||||||
| name = "ecs-express-execution--${local.name}" | ||||||
| assume_role_policy = jsonencode({ | ||||||
| Version = "2012-10-17" | ||||||
| Statement = [ | ||||||
| { | ||||||
| Effect = "Allow" | ||||||
| Principal = { | ||||||
| Service = "ecs-tasks.amazonaws.com" | ||||||
| } | ||||||
| Action = "sts:AssumeRole" | ||||||
| } | ||||||
| ] | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| resource "aws_iam_role_policy" "execution" { | ||||||
| role = aws_iam_role.execution.name | ||||||
| policy = jsonencode({ | ||||||
| Version = "2012-10-17" | ||||||
| Statement = [ | ||||||
| { | ||||||
| Sid = "AllowReadingFastlyApiToken" | ||||||
| Sid = "AllowParameterStore" | ||||||
| Effect = "Allow" | ||||||
| Action = "ssm:GetParameters" | ||||||
| Resource = data.aws_ssm_parameter.fastly_api_token.arn | ||||||
| }, | ||||||
| { | ||||||
| Sid = "AllowLogs" | ||||||
| Effect = "Allow" | ||||||
| Action = [ | ||||||
| "logs:PutLogEvents", | ||||||
| "logs:CreateLogStream" | ||||||
| ] | ||||||
| Resource = "${aws_cloudwatch_log_group.fastly_exporter.arn}:*" | ||||||
| }, | ||||||
| { | ||||||
| Sid = "ECRAuthentication" | ||||||
| Effect = "Allow" | ||||||
| Action = "ecr:GetAuthorizationToken" | ||||||
| Resource = "*" | ||||||
| } | ||||||
| ] | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| resource "aws_iam_role_policy_attachment" "read_fastly_api_token" { | ||||||
| role = module.ecs_task.execution_role_name | ||||||
| policy_arn = aws_iam_policy.read_fastly_api_token.arn | ||||||
| # IAM role for ECS Express infrastructure management (ALB, target groups, scaling) | ||||||
| resource "aws_iam_role" "infrastructure" { | ||||||
| name = "ecs-express-infrastructure--${local.name}" | ||||||
| assume_role_policy = jsonencode({ | ||||||
| Version = "2012-10-17" | ||||||
| Statement = [ | ||||||
| { | ||||||
| Effect = "Allow" | ||||||
| Principal = { | ||||||
| Service = "ecs.amazonaws.com" | ||||||
| } | ||||||
| Action = "sts:AssumeRole" | ||||||
| } | ||||||
| ] | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| module "ecs_task" { | ||||||
| source = "../shared/modules/ecs-task" | ||||||
|
|
||||||
| name = local.name | ||||||
| cpu = 256 | ||||||
| memory = 512 | ||||||
| resource "aws_iam_role_policy_attachment" "infrastructure" { | ||||||
| role = aws_iam_role.infrastructure.name | ||||||
| policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRolePolicyForVolumes" | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is incorrect
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| log_retention_days = 7 | ||||||
| ecr_repositories_arns = [ | ||||||
| # This repository does not exist, since we're pulling the imge directly from GitHub. | ||||||
| # But the task module cannot be applied without providing at least one ARN here. | ||||||
| "arn:aws:ecr:us-west-1:890664054962:repository/fastly-exporter" | ||||||
| # Wait for IAM roles to propagate | ||||||
| resource "time_sleep" "wait_for_iam" { | ||||||
| depends_on = [ | ||||||
| aws_iam_role_policy.execution, | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is redundant - if you are waiting on the attachment then you are already implicitly waiting on the role. more importantly, you shouldn't need this |
||||||
| aws_iam_role_policy_attachment.infrastructure | ||||||
| ] | ||||||
|
|
||||||
| containers = <<EOF | ||||||
| [ | ||||||
| { | ||||||
| "name": "${local.name}", | ||||||
| "image": "ghcr.io/fastly/fastly-exporter:v7.4.0", | ||||||
| "essential": true, | ||||||
| "portMappings": [ | ||||||
| { | ||||||
| "containerPort": 8080, | ||||||
| "hostPort": 8080 | ||||||
| } | ||||||
| ], | ||||||
| "logConfiguration": { | ||||||
| "logDriver": "awslogs", | ||||||
| "options": { | ||||||
| "awslogs-group": "/ecs/${local.name}", | ||||||
| "awslogs-region": "us-west-1", | ||||||
| "awslogs-stream-prefix": "${local.name}" | ||||||
| } | ||||||
| }, | ||||||
| "environment": [], | ||||||
| "secrets": [ | ||||||
| { | ||||||
| "name": "FASTLY_API_TOKEN", | ||||||
| "valueFrom": "${data.aws_ssm_parameter.fastly_api_token.arn}" | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
| ] | ||||||
| EOF | ||||||
| create_duration = "10s" | ||||||
| } | ||||||
|
|
||||||
| # Security group for the service | ||||||
| resource "aws_security_group" "fastly_exporter" { | ||||||
| name = "fastly-exporter" | ||||||
| description = "Allow Prometheus to scrape the fastly-exporter" | ||||||
| name = "ecs-express-fastly-exporter" | ||||||
| description = "Allow Prometheus to scrape the fastly-exporter via ECS Express" | ||||||
| vpc_id = data.terraform_remote_state.shared.outputs.ecs_cluster_config.vpc_id | ||||||
|
|
||||||
| ingress { | ||||||
| from_port = 8080 | ||||||
| to_port = 8080 | ||||||
| protocol = "tcp" | ||||||
| # Elastic IP of the monitoring server | ||||||
| from_port = 8080 | ||||||
| to_port = 8080 | ||||||
| protocol = "tcp" | ||||||
| cidr_blocks = ["52.9.166.219/32"] | ||||||
| # Load balancer security group, required for health checks | ||||||
| security_groups = ["sg-0ddcb954525a46b70"] | ||||||
| description = "Elastic IP of the monitoring server" | ||||||
| } | ||||||
|
|
||||||
| egress { | ||||||
| from_port = 0 | ||||||
| to_port = 0 | ||||||
| protocol = "-1" | ||||||
| cidr_blocks = ["0.0.0.0/0"] | ||||||
| description = "Allow all outbound traffic" | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| module "ecs_service" { | ||||||
| source = "../shared/modules/ecs-service" | ||||||
| # ECS Express Gateway Service | ||||||
| resource "aws_ecs_express_gateway_service" "fastly_exporter" { | ||||||
| service_name = local.name | ||||||
| execution_role_arn = aws_iam_role.execution.arn | ||||||
| infrastructure_role_arn = aws_iam_role.infrastructure.arn | ||||||
|
|
||||||
| cluster_config = data.terraform_remote_state.shared.outputs.ecs_cluster_config | ||||||
| platform_version = "1.4.0" | ||||||
| primary_container { | ||||||
| image = "ghcr.io/fastly/fastly-exporter:v7.4.0" | ||||||
| container_port = 8080 | ||||||
|
|
||||||
| name = local.name | ||||||
| task_arn = module.ecs_task.arn | ||||||
| tasks_count = 1 | ||||||
| aws_logs_configuration { | ||||||
| log_group = aws_cloudwatch_log_group.fastly_exporter.name | ||||||
| } | ||||||
|
|
||||||
| http_container = local.name | ||||||
| http_port = 8080 | ||||||
| secrets { | ||||||
| name = "FASTLY_API_TOKEN" | ||||||
| value_from = data.aws_ssm_parameter.fastly_api_token.arn | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| domains = ["fastly-exporter.infra.rust-lang.org"] | ||||||
| network_configuration { | ||||||
| subnets = data.terraform_remote_state.shared.outputs.ecs_cluster_config.subnet_ids | ||||||
| security_groups = [aws_security_group.fastly_exporter.id] | ||||||
| } | ||||||
|
|
||||||
| additional_security_group_ids = [ | ||||||
| aws_security_group.fastly_exporter.id, | ||||||
| ] | ||||||
| scaling_target { | ||||||
| min_task_count = 1 | ||||||
| max_task_count = 2 | ||||||
| auto_scaling_metric = "CPU" | ||||||
| auto_scaling_target_value = 70 | ||||||
| } | ||||||
|
|
||||||
| depends_on = [time_sleep.wait_for_iam] | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is incorrect/invalid - the
aws_ecs_express_gateway_serviceresource was added inv6.23.0of the AWS provider which means, thats the minimum required version (I haven't checked other resources, but just using that for reference)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh goodness - theres a whole bunch more custom modules here https://github.com/rust-lang/simpleinfra/tree/master/terragrunt/modules. again, most of our modules are setup to support Terragrunt natively https://github.com/terraform-aws-modules