I’ve used rexray with ECS before, and I didn’t like it very much (your milage may vary of course), primarily because with EBS volume being tied to AZ I just found it rather janky.
I’ve attached our terraform definition for the reader and writer services below (excluding the auto scaling parts). We aren’t using the new backend
component yet, but will probably move to that soon.
reader:
resource "aws_service_discovery_service" "loki_reader" {
name = "${var.loki_cluster_identifier}-reader"
dns_config {
namespace_id = aws_service_discovery_private_dns_namespace.service_discovery.id
routing_policy = "MULTIVALUE"
dns_records {
ttl = 5
type = "A"
}
}
health_check_custom_config {
failure_threshold = 3
}
}
resource "aws_ecs_service" "loki_reader" {
name = "${var.loki_cluster_identifier}-reader"
cluster = module.ecs-cluster-loki.ecs_cluster_arn
task_definition = aws_ecs_task_definition.loki_reader.arn
desired_count = var.loki_reader_ecs_desired_count
deployment_minimum_healthy_percent = 50
scheduling_strategy = "REPLICA"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.loki_ecs_service.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.loki_reader_external_80.arn
container_name = "nginx"
container_port = 80
}
load_balancer {
target_group_arn = aws_lb_target_group.loki_reader_internal_80.arn
container_name = "nginx"
container_port = 80
}
service_registries {
registry_arn = aws_service_discovery_service.loki_reader.arn
container_name = "${var.loki_cluster_identifier}-reader"
}
capacity_provider_strategy {
capacity_provider = module.ecs-cluster-loki.capacity_provider_name
weight = 1
}
placement_constraints {
type = "memberOf"
expression = "attribute:loki-instance-type == reader"
}
depends_on = [aws_lb.loki_internal_alb]
lifecycle {
ignore_changes = [desired_count]
}
}
resource "aws_ecs_task_definition" "loki_reader" {
family = "${var.loki_cluster_identifier}-reader"
task_role_arn = aws_iam_role.loki_ecs.arn
execution_role_arn = aws_iam_role.loki_ecs.arn
network_mode = "awsvpc"
volume {
name = "nginx-config"
host_path = "/opt/nginx/etc/nginx.conf"
}
volume {
name = "nginx-htpasswd"
host_path = "/opt/nginx/etc/.htpasswd"
}
volume {
name = "loki-config"
host_path = "/opt/loki/etc/loki.yml"
}
volume {
name = "loki-config-limits-overrides"
host_path = "/opt/loki/etc/limits-overrides.yml"
}
container_definitions = jsonencode([
{
"name" = "nginx",
"image" = var.nginx_image_version,
"cpu" = 256,
"memoryReservation" = 128,
"essential" = true,
"mountPoints" = [
{
"sourceVolume" = "nginx-config",
"containerPath" = "/etc/nginx/nginx.conf",
"readOnly" = true,
},
{
"sourceVolume" = "nginx-htpasswd",
"containerPath" = "/etc/nginx/.htpassword",
"readOnly" = true,
}
],
"portMappings" = [
{
"hostPort" = 80,
"containerPort" = 80,
"protocol" = "tcp"
}
],
"logConfiguration": {
"logDriver": "json-file",
"options": {
"max-size": "1g",
"max-file": "5",
"tag": "nginx/{{.FullID}}"
}
}
},
{
"name" = "${var.loki_cluster_identifier}-reader",
"image" = var.loki_docker_image_version,
"command" = [
"-config.file=/etc/loki/loki.yml",
"-target=read"
],
"environment" = [
{
"name" = "ansible_playbook_md5",
"value" = data.archive_file.ansible_playbook.output_md5
},
{
"name" = "ansible_vars_md5",
"value" = md5(local.ansible_vars)
}
],
"essential" = true,
"cpu" = var.loki_reader_ecs_cpu_reservation,
"memoryReservation" = var.loki_reader_ecs_memory_reservation,
"mountPoints" = [
{
"sourceVolume" = "loki-config",
"containerPath" = "/etc/loki/loki.yml",
"readOnly" = true
},
{
"sourceVolume" = "loki-config-limits-overrides",
"containerPath" = "/etc/loki/limits-overrides.yml",
"readOnly" = true
}
],
"portMappings" = [
{
"hostPort" = 3100,
"containerPort" = 3100,
"protocol" = "tcp"
},
{
"hostPort" = var.loki_ring_gossip_port,
"containerPort" = var.loki_ring_gossip_port,
"protocol" = "tcp"
}
],
"privileged" = true,
"ulimits" = [
{
"name" = "nofile",
"softLimit" = 65536,
"hardLimit" = 65536
}
],
"logConfiguration": {
"logDriver": "json-file",
"options": {
"max-size": "1g",
"max-file": "5",
"tag": "loki-reader/{{.FullID}}"
}
}
}
])
depends_on = [null_resource.playbook_delay]
}
writer:
resource "aws_service_discovery_service" "loki_writer" {
name = "${var.loki_cluster_identifier}-writer"
dns_config {
namespace_id = aws_service_discovery_private_dns_namespace.service_discovery.id
routing_policy = "MULTIVALUE"
dns_records {
ttl = 5
type = "A"
}
}
health_check_custom_config {
failure_threshold = 3
}
}
resource "aws_ecs_service" "loki_writer" {
name = "${var.loki_cluster_identifier}-writer"
cluster = module.ecs-cluster-loki.ecs_cluster_arn
task_definition = aws_ecs_task_definition.loki_writer.arn
deployment_minimum_healthy_percent = 50
scheduling_strategy = "DAEMON"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.loki_ecs_service.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.loki_writer_external_80.arn
container_name = "nginx"
container_port = 80
}
load_balancer {
target_group_arn = aws_lb_target_group.loki_writer_internal_80.arn
container_name = "nginx"
container_port = 80
}
service_registries {
registry_arn = aws_service_discovery_service.loki_writer.arn
container_name = "${var.loki_cluster_identifier}-writer"
}
placement_constraints {
type = "memberOf"
expression = "attribute:loki-instance-type == writer"
}
depends_on = [aws_lb.loki_internal_alb]
}
resource "aws_ecs_task_definition" "loki_writer" {
family = "${var.loki_cluster_identifier}-writer"
task_role_arn = aws_iam_role.loki_ecs.arn
execution_role_arn = aws_iam_role.loki_ecs.arn
network_mode = "awsvpc"
volume {
name = "nginx-config"
host_path = "/opt/nginx/etc/nginx.conf"
}
volume {
name = "nginx-htpasswd"
host_path = "/opt/nginx/etc/.htpasswd"
}
volume {
name = "loki-config"
host_path = "/opt/loki/etc/loki.yml"
}
volume {
name = "loki-config-limits-overrides"
host_path = "/opt/loki/etc/limits-overrides.yml"
}
volume {
name = "loki-writer-local-storage"
host_path = var.loki_writer_local_storage
}
container_definitions = jsonencode([
{
"name" = "nginx",
"image" = var.nginx_image_version,
"cpu" = 256,
"memoryReservation" = 256,
"essential" = true,
"mountPoints" = [
{
"sourceVolume" = "nginx-config",
"containerPath" = "/etc/nginx/nginx.conf",
"readOnly" = true,
},
{
"sourceVolume" = "nginx-htpasswd",
"containerPath" = "/etc/nginx/.htpassword",
"readOnly" = true,
}
],
"portMappings" = [
{
"hostPort" = 80,
"containerPort" = 80,
"protocol" = "tcp"
}
],
"logConfiguration": {
"logDriver": "json-file",
"options": {
"max-size": "1g",
"max-file": "5",
"tag": "nginx/{{.FullID}}"
}
}
},
{
"name" = "${var.loki_cluster_identifier}-writer",
"image" = var.loki_docker_image_version,
"cpu" = 256,
"memoryReservation" = 2048,
"essential" = true,
"command" = [
"-config.file=/etc/loki/loki.yml",
"-target=write"
],
"mountPoints" = [
{
"sourceVolume" = "loki-config",
"containerPath" = "/etc/loki/loki.yml",
"readOnly" = true
},
{
"sourceVolume" = "loki-config-limits-overrides",
"containerPath" = "/etc/loki/limits-overrides.yml",
"readOnly" = true
},
{
"sourceVolume" = "loki-writer-local-storage",
"containerPath" = var.loki_writer_local_storage,
"readOnly" = false
}
],
"portMappings" = [
{
"hostPort" = 3100,
"containerPort" = 3100,
"protocol" = "tcp"
},
{
"hostPort" = var.loki_ring_gossip_port,
"containerPort" = var.loki_ring_gossip_port,
"protocol" = "tcp"
},
{
"hostPort" = 9095,
"containerPort" = 9095,
"protocol" = "tcp"
}
],
"privileged" = true,
"ulimits" = [
{
"name" = "nofile",
"softLimit" = 65536,
"hardLimit" = 65536
}
],
"logConfiguration": {
"logDriver": "json-file",
"options": {
"max-size": "1g",
"max-file": "5",
"tag": "loki-writer/{{.FullID}}"
}
}
}
])
}