Lambda(golang)에서 Fargate Spot의 종료 알림을 받는 ECS 작업을 NLB에서 중단합니다.

개시하다


나는 생산기술부에서 제품 검사 공정을 책임지는 엔지니어다.이번에는 Fargate spot에서 ECS가 중단 통지를 받았을 때 ELB에 대한 Deregister Target 수행을 장담할 수 없는 과제에 대해 노력했다.
Fargate Spot의 목적은 다음과 같이 비용을 절감할 수 있다는 것입니다.
또 팜게이트에서 시작된 ECS의 캐피티 프로바이더에 팜게이트와 팜게이트 스팟을 함께 사용함으로써 시스템의 안정성과 비용 절감을 양립하는 구조를 구현했다.
https://d1.awsstatic.com/webinars/jp/pdf/services/202109_AWS_Black_Belt_Containers303-ECS-Spot-Fargate.pdf

Capacity Provider 가져오기


설치 참조:
https://github.com/TomoyukiSugiyama/ElasticStack/pull/35/files#diff-e294b70d2a4a1e225aa9f6a19dc7fff8942e8fa32698e9b7ca742e1703862c89R39
Cluster의 기본값, 서비스에서 실제로 사용되는 Capacity Provider를 설정합니다.기본은 최소 실행 작업 수를 나타냅니다. 다음 예에서 두 작업은 Fargate로 실행됩니다.Weight는 전체 작업 수행 횟수에 대한 상대적 비율을 나타내며, 크기를 조절하더라도 Fargate 및 Fargate Spot에서 작업을 1대1로 구성합니다.AWS 콘솔에서 각 임무의 설정을 확인하면 어떤 것을 사용했는지 확인할 수 있다.
이 서비스에서는 Capacity Provider Strategy와 LaunchType을 동시에 설정할 수 없습니다.
  Cluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: ecs
      CapacityProviders:
        - FARGATE
        - FARGATE_SPOT
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Base: 2
          Weight: 1
        - CapacityProvider: FARGATE_SPOT
          Base: 0
          Weight: 1
  Service:
    Type: AWS::ECS::Service
    Properties:
      CapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Base: 2
          Weight: 1
        - CapacityProvider: FARGATE_SPOT
          Base: 0
          Weight: 1
기본적으로 SIGTERM의 30초 후에 SIGKILL이 시작되며 StopTimeout 설정을 통해 120초로 변경할 수 있습니다.
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Name: logstash
          StopTimeout: 120

Lambda로 Fargate Spot의 종료 알림 처리


설치 참조:
https://github.com/TomoyukiSugiyama/ElasticStack/pull/35/files#diff-5151c8e56ed376b508fdfd4b764031a5b206765554fa7715598262df048e2f57R1
Event Bridge에서 종료 통지를 받고 NLB 대상에서 객체 작업을 해제합니다.
  • Event Bridge에서 이벤트를 수신하여 미리 준비된 패브릭에 필요한 데이터를 저장
  • StopCode가 종료 알림이 아닌 경우 종료 Lambda
  • 대상 작업의 IP 확보
  • 환경 변수로 설정된 AWS: Elastic Load Balanceng V2: Load Balancer의 Amazon Resource Name(이하 ARN), AWS: Elastic Load Balancig V2: Target Group의 ARN 가져오기
  • aws-lambda-go,aws-sdk-go-v2를 사용하는 라이브러리의 초기 설정
  • LoadBalancer의 ARN에서 LoadBalancer
  • 를 취득
  • Target Group ARN에서 Target Group
  • 가져오기
  • TargetGroup에 개체 작업의 IP가 있는 경우 등록 해제
  • type NetworkInterface struct {
    	PrivateIpv4Address string `json:"privateIpv4Address"`
    }
    type Container struct {
    	NetworkInterfaces []NetworkInterface
    	Name              string `json:"name"`
    }
    type EcsEvent struct {
    	StopCode   string `json:"stopCode"`
    	Containers []Container
    }
    
    func HandleLambdaEvent(_ context.Context, event events.CloudWatchEvent) {
    	// 1. Event Bridgeからイベントを受け取る
    	var ecsEvent EcsEvent
    	if err := json.Unmarshal(event.Detail, &ecsEvent); err != nil {
    		os.Exit(1)
    	}
    	// 2. 終了通知かチェック
    	fmt.Printf("stopCode = %s\n", ecsEvent.StopCode)
    	if ecsEvent.StopCode != "TerminationNotice" {
    		return
    	}
    	// 3. 対象タスクのIP取得
    	var ecsIp string
    	for _, contaier := range ecsEvent.Containers {
    		if contaier.Name == "logstash" {
    			for _, ni := range contaier.NetworkInterfaces {
    				ecsIp = ni.PrivateIpv4Address
    			}
    		}
    	}
    	fmt.Printf("ip v4 = %s\n", ecsIp)
    	// 4. 環境変数取得
    	nlbId := os.Getenv("NlbId")
    	nlbTargetGroupId := os.Getenv("NlbTargetGroupId")
    	fmt.Printf("GET ENV AlbId: %s AlbTargetGroupId: %s\n", nlbId, nlbTargetGroupId)
    	// 5. 初期設定
    	svc := Init()
    	// 6. 指定したLoadbalancerを取得
    	lb := GetSpecifiedLoadbalancer(svc, nlbId)
    	fmt.Printf("GET LoadbalancerName: %s LoadbalancerArn: %s\n", *lb.LoadBalancerName, *lb.LoadBalancerArn)
    	// 7. 指定したLoadbalancerのTargetGroupを取得
    	tg := GetSpecifiedTargetGroup(svc, lb, nlbTargetGroupId)
    	fmt.Printf("GET TargetGroupName: %s TargetGroupArn: %s\n", *tg.TargetGroupName, *tg.TargetGroupArn)
    	// 8. TargetGroupからTargetの登録を解除
    	if HasTarget(svc, tg, ecsIp) {
    		const tcpPort = 5044
    		DeregisterSpecifiedTarget(svc, tg, ecsIp, tcpPort)
    		fmt.Println("DEREGISTER")
    	}
    }
    

    Cloudformation에 Lambda, Event Bridge 규칙 추가


    설치 참조:
    https://github.com/TomoyukiSugiyama/ElasticStack/pull/35/files#diff-54c53a21ae7349d2d6c3e6031e07f720b140ebdba7d993c88c9a159d245f7de9R21
    ECS 문서에 기재된 대로 규칙을 설정합니다.
    https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/fargate-capacity-providers.html
      EventRule:
        Type: AWS::Events::Rule
        Properties:
          Description: detach ecs task that received terminate notification from nlb
          Name: detach-task-to-be-terminated-from-nlb
          EventPattern:
            source:
              - aws.ecs
            detail-type:
              - ECS Task State Change
            detail:
              clusterArn:
                - !Ref ClusterId
          State: ENABLED
          Targets:
            - Arn: !GetAtt Function.Arn
              Id: lambda
    

    최후


    Lambda 테스트를 사용하여 대상 작업이 NLB에서 제거됨을 확인했습니다.ECS에서 실행되는 응용 프로그램의Graceful Shutdown에 대해서는 사용하는 응용 프로그램의 문서를 참조하여 처리하는 것이 좋다.이번에 사용한 Logo stash는 아래에 기재되어 있습니다.
    https://www.elastic.co/guide/en/logstash/current/shutdown.html

    좋은 웹페이지 즐겨찾기