AmazonECS/Fargate로 카나리아 배포 구현

얼마 전 AmazonECS / Fargate 프로덕션 운영을 위한 구축 및 배포 방법 요약이라는 제목으로 ECS/Fargate를 프로덕션 운용하기 위한 구성과 배포에 대한 기사를 쓰고 히로 무스다 / 에스에서 p ぉ y이라는 배포용 스크립트를 공개했습니다.



그러자, ECS/Fargate를 이용했을 때의 카나리아 배포의 수법이 의외로 화제가 되었기 때문에, 전회는 간단하게 소개해 버린 카나리아 배포를, 이번은 조금 목을 돌진해 소개하고 싶습니다.

전체 아키텍처



전체상은 이쪽입니다.



ECS/Fargate는 무엇입니까? 라고 하는 분이나, 아직 전 기사를 읽지 않은 분은 우선은 이쪽을 봐 주세요.

AmazonECS / Fargate 프로덕션 운영을 위한 구축 및 배포 방법 요약

카나리아 배포를 ECS로 실현



service내에서 task가 2대 움직이고 있는 상태로부터, 카나리아 배포를 해 33% 전달을 실현한다고 하는 예를 소개합니다.



또한 배포용 스크립트는 여기에서 공개하고 있습니다.

히로 무스다 / 에스에서 p ぉ y

1. 새 버전의 이미지를 build하고 push한다





버전을 식별하기 위해 타임 스탬프와 git 커밋 해시를 태그로 빌드 한 이미지에 ECR로 푸시합니다.
def push_latest_image
  tag_timestamp = Time.now.strftime("%Y%m%d_%H%M")
  tag_git_commit_hash = `git rev-parse HEAD`
  tags = [tag_timestamp, tag_git_commit_hash]
  puts "-----> Push latest image. Tag: #{tags.join(", ")}"

  cmd_build = `docker build -t #{@ecr_name} #{@dockerfile_path}`
  tags.each do |tag|
    cmd = `
      docker tag #{@ecr_name}:latest #{get_ecr_image_name(tag)}
      docker push #{get_ecr_image_name(tag)}`
  end

  return get_ecr_image_name(tag_timestamp)
end

2. push한 이미지를 참조하는 TaskDefinition의 새로운 리비전을 작성합니다.





최신 TaskDefinition을 가져오고 ContainerDefinition에서 참조하는 이미지 버전을 새 버전으로 변경하고 개정을 업데이트합니다.
def update_task_definition(image_name)
  puts "-----> Update task definition"
  task_definition = get_latest_task_definition_description
  container_definitions = task_definition["containerDefinitions"]

  new_container_definitions = []
  container_definitions.each do |container|
    container["image"] = image_name if container["name"] == "#{@container_name}"
    new_container_definitions << container
  end

  new_revision = `aws ecs register-task-definition \
    --family #{@task} \
    --task-role-arn #{task_definition["taskRoleArn"]} \
    --execution-role-arn #{task_definition["executionRoleArn"]} \
    --network-mode #{task_definition["networkMode"]} \
    --volumes '#{task_definition["volumes"].to_json}' \
    --cpu #{task_definition["cpu"]} \
    --memory #{task_definition["memory"]} \
    --requires-compatibilities #{task_definition["requiresCompatibilities"][0]} \
    --container-definitions '#{new_container_definitions.to_json}'`

  new_task_definition_arn = JSON.parse(new_revision)["taskDefinition"]["taskDefinitionArn"]
  puts "-----> New task definition arn: #{new_task_definition_arn}"

  return new_task_definition_arn
end

3. 작성한 TaskDefinition을 바탕으로 1대만 Task를 생성한다





2에서 만든 TaskDefinition을 기반으로 task를 새로 만듭니다. 이때 위 그림과 같이 service 밖으로 만듭니다. 그렇게 함으로써 위의 두 task는 이전 리비전의 TaskDefinition에서, 아래의 하나는 새로운 리비전의 TaskDefinition을 참조하게 됩니다. 또, 지금 작성한 task가 카나리아 배포에 의한 것이라고 판별이 되도록(듯이), started_by 라고 하는 스페이스에 태그를 추가합니다. 이 스크립트는 canary라는 문자열을 지정합니다.
def run_task(task_definition, started_by_tag)
  puts "-----> Run new task"
  service_desc = get_service_description
  conf = service_desc["networkConfiguration"]["awsvpcConfiguration"]
  cmd = `aws ecs run-task \
    --cluster #{@cluster} \
    --task-definition #{task_definition} \
    --network-configuration "awsvpcConfiguration={\
      subnets=[#{conf["subnets"].join(",")}],\
      securityGroups=[#{conf["securityGroups"].join(",")}],\
      assignPublicIp="DISABLED"}" \
    --launch-type FARGATE \
    --started-by #{started_by_tag}`
  return JSON.parse(cmd)
end

4. 생성된 Task의 PrivateIP를 로드 밸런서의 대상 그룹에 추가





먼저 방금 만든 task에서 privateIP를 가져옵니다. task 가 작성되고 나서 몇 초 하지 않으면 privateIP 가 생성되지 않기 때문에 , 취득할 때까지 루프를 돌리고 있습니다.
def canary_deploy
  ...
  task_arn = new_task["tasks"][0]["taskArn"]
  puts "-----> task ARN: #{task_arn}"

  # take several seconds to get IP
  while true
    private_ip = get_task_private_ip(task_arn)
    if !private_ip.nil?
      puts "-----> private IP: #{private_ip}"
      break
    end
    sleep(1)
  end

  add_task_to_target_group(private_ip)
  ...
end

그런 다음 얻은 privateIP를 ELB의 대상 그룹에 추가합니다. 이렇게 하면 요청이 카나리아 배포된 하나의 태스크에도 분산되어 흐릅니다.
def add_task_to_target_group(private_ip)
  puts "-----> Add #{private_ip} to the target group"
  cmd = `aws elbv2 register-targets \
    --target-group-arn #{@target_group_arn} \
    --targets Id=#{private_ip},Port=8000`
  return cmd
end

이상과 같이 카나리아 배포를 실현했습니다.

요약



카나리아 배포를 스크립팅할 수 있어 배포로 인한 장애의 영향을 줄일 수 있습니다. 꼭 시도해보세요.

또, Twitter에서는 항상 기술계・근육 트레이닝계의 아웃풋을 하고 있으므로 꼭 팔로우해 주세요! 👉 htps : // 라고 해서 r. 코m/히로무_bdy

좋은 웹페이지 즐겨찾기