서비스 검색을 사용하여 ECS 작업 부하 분산

소개



ECS + Fargate 에서의 부하 분산은 다음과 같이 하는 것이 기초의 기초로서 가르치는 곳이라고 생각하지만, 그 밖에도 부하 분산의 방법이 있다.



ECS에서 서비스를 만들 때 설정하는 "서비스 검색"을 사용하면 DNS를 사용하여 부하 분산이 가능합니다.
본 기사에서는, 내부 통신에 대해서 프라이빗 호스트 존을 사용해 부하 분산을 실시하는 것을 전제로 한다.



ECS + Fargate의 Terraform HCL은 다음 기사를 참조하십시오.

Terraform의 초보자가 Amazon EC2에 실행 환경을 만들어 ECS Fargate 앱의 자동 구축을 해본다

또, 컨테이너의 내용은 무엇이든 좋지만, 나중에 정상성 확인을 하기 쉽도록, 표준 출력으로 로그를 내도록 해 두자.

Terraform



Terraform의 수정 포인트는 이하다.
  • 서비스 검색 리소스 정의
  • 백엔드 서비스에 대한 ELB, Listener, TargetGroup 삭제
  • aws_ecs_service에서 load_balancer를 삭제하고 service_registries를 정의
  • aws_ecs_task_definition 에서 스스로 헬스 체크를 정의

  • 이후에서는, 백엔드측의 서비스를 service_b 로서 정의하고 있다.
    프론트 측은 코드 중에 등장하지 않지만 service_a로 한다.

    서비스 검색 리소스 정의


    local.service_discovery_namespace 에 대해서는, 내부 통신이므로 좋아하는 이름을 붙여 문제 없다.
    여기서 health_check_custom_config 를 정의하기 위해, 나중에 컨테이너 측에서 헬스 체크를 해 준다.
    ################################################################################
    # Service Discovery                                                            #
    ################################################################################
    resource "aws_service_discovery_private_dns_namespace" "internal" {
      name        = local.service_discovery_namespace
      description = "Service Discovery Example"
      vpc         = data.aws_vpc.my.id
    }
    
    resource "aws_service_discovery_service" "service_b" {
      name = local.service_b_service_discovery_nane
    
      dns_config {
        namespace_id = aws_service_discovery_private_dns_namespace.internal.id
    
        dns_records {
          ttl  = 10
          type = "A"
        }
    
        routing_policy = "MULTIVALUE"
      }
    
      health_check_custom_config {
        failure_threshold = 1
      }
    }
    

    ECS 기반 리소스 수정



    이 후의 정상성 확인을 알기 쉽게 하기 위해, desired_count 는 2 이상을 설정해 두자.
    resource "aws_ecs_task_definition" "service_b_ecsfargate" {
      family             = local.service_b_ecs_task_family_name
      task_role_arn      = aws_iam_role.ecs.arn
      execution_role_arn = aws_iam_role.ecstaskexecution.arn
      network_mode       = "awsvpc"
      cpu                = "256"
      memory             = "512"
      requires_compatibilities = [
        "FARGATE",
      ]
    
      container_definitions = data.template_file.service_b_ecsfargate.rendered
    }
    
    data "template_file" "service_b_ecsfargate" {
      template = file("${path.module}/service_b_taskdef.json")
    
      vars = {
        service_b_ecs_container_name     = local.service_b_ecs_container_name
        service_b_image                  = "${aws_ecr_repository.service_b_image.repository_url}:latest"
        service_b_ecstask_log_group_name = aws_cloudwatch_log_group.service_b_ecstask_log_group.name
        service_b_region_name            = data.aws_region.current.name
      }
    }
    
    resource "aws_ecs_service" "service_b_ecsfargate_service" {
      name            = local.service_b_ecs_service_name
      cluster         = aws_ecs_cluster.service_b_ecsfargate.id
      launch_type     = "FARGATE"
      task_definition = aws_ecs_task_definition.service_b_ecsfargate.arn
      desired_count   = 2
    
      network_configuration {
        subnets = flatten([data.aws_subnet_ids.my_vpc.ids])
    
        security_groups = [
          data.aws_security_group.http.id,
        ]
    
        assign_public_ip = "true"
      }
    
      service_registries {
        registry_arn = aws_service_discovery_service.service_b.arn
      }
    }
    

    태스크 정의 수정



    태스크 정의에서는 ELB에 의한 헬스 체크가 행해지지 않기 때문에 자전의 헬스 체크를 실시한다.

    service_b_taskdef.json
    [
      {
        "name" : "${service_b_ecs_container_name}",
        "image": "${service_b_image}",
        "cpu": 0,
        "memoryReservation": 512,
        "portMappings": [
          {
            "containerPort": 80,
            "hostPort": 80,
            "protocol": "tcp"
          }
        ],
        "logConfiguration": {
          "logDriver": "awslogs",
          "secretOptions": null,
          "options": {
            "awslogs-group": "${service_b_ecstask_log_group_name}",
            "awslogs-region": "${service_b_region_name}",
            "awslogs-stream-prefix": "ecs"
          }
        },
        "healthCheck": {
          "command": [
            "CMD-SHELL",
            "curl http://localhost/ || exit 1"
          ],
          "interval": 5,
          "retries": 3,
          "startPeriod": 60,
          "timeout": 5
        }
      }  
    ]
    

    이제 여기까지 설정하면 Route53에서 다음과 같이 여러 A 레코드가 설정됩니다.



    CloudMap에서 만든 작업은 상태 확인 OK 상태에 있어야 합니다.



    이 상태에서 service_a 전단의 ELB에 대해 curl을 하여 CloudWatch Logs를 확인해 보면 제대로 두 태스크에서 액세스 로그가 출력되고 있는 것을 알 수 있을 것이다.

    이것으로 NLB를 사용하지 않고도 나눌 수 있게 되었다.
    사실 이것은 AppMesh를 사용할 때 필요하기 때문에 알아두면 나중에 편리합니다.

    좋은 웹페이지 즐겨찾기