code

업데이트된 도커 이미지를 Amazon ECS 작업에 배포하려면 어떻게 해야 합니까?

starcafe 2023. 10. 15. 17:30
반응형

업데이트된 도커 이미지를 Amazon ECS 작업에 배포하려면 어떻게 해야 합니까?

이미지가 해당 레지스트리에서 업데이트되었다고 말하면 아마존 ECS 작업이 도커 이미지를 업데이트하도록 하는 올바른 방법은 무엇입니까?

서비스에서 작업이 실행 중인 경우 새 배포를 강제로 수행할 수 있습니다.이렇게 하면 작업 정의가 재평가되고 새 컨테이너 이미지가 당겨집니다.

aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment

(마다)를)StartTask그리고.RunTask됨), ECS합니다를 합니다.docker pullimage작업 정의에 지정합니다.레지스트리에 푸시할 때마다 동일한 이미지 이름(태그 포함)을 사용하는 경우 새 작업을 실행하여 새 이미지를 실행할 수 있습니다.가 어떤 네트워크 문제)로할 수 할 때 되지 않도록 유예:제) ECS합니다를우합니다. 이미지를 업데이트할 때 캐시된 이미지가 사용되지 않도록 하려,에 매번 를 그에 새 작업을 실행하기 전에 매번 다른 태그를 레지스트리에 푸시하고 작업 정의를 상응하게 업데이트할 수 있습니다.

를 이 할 수 .ECS_IMAGE_PULL_BEHAVIORECS 에이전트에 설정된 환경 변수.자세한 내용은 설명서를 참조하십시오.작성 시점을 기준으로 다음 설정이 지원됩니다.

컨테이너 인스턴스의 풀 이미지 프로세스를 사용자 지정하는 데 사용되는 동작입니다.다음은 옵션 동작에 대해 설명합니다.

  • 한다면default지정되고 이미지가 원격으로 당겨집니다.이미지 풀이 실패하면 컨테이너는 인스턴스의 캐시된 이미지를 사용합니다.

  • 한다면always가 지정되며, 이미지는 항상 원격으로 당겨집니다.이미지 꺼내기가 실패하면 작업이 실패합니다.이 옵션을 사용하면 최신 버전의 이미지가 항상 당겨집니다.캐시된 모든 이미지는 무시되며 자동 이미지 정리 프로세스의 대상이 됩니다.

  • 한다면once지정되며, 동일한 컨테이너 인스턴스에서 이전 작업에 의해 풀링되지 않았거나 캐시된 이미지가 자동 이미지 정리 프로세스에 의해 제거된 경우에만 이미지가 원격으로 풀링됩니다.그렇지 않으면 인스턴스의 캐시된 이미지가 사용됩니다.이를 통해 불필요한 이미지 끌어오기를 시도하지 않습니다.

  • 한다면prefer-cached지정되며, 캐시된 이미지가 없는 경우 이미지를 원격으로 꺼냅니다.그렇지 않으면 인스턴스의 캐시된 이미지가 사용됩니다.캐시된 이미지가 제거되지 않도록 컨테이너에 대해 자동 이미지 정리가 비활성화됩니다.

AWS에서 권장하는 방법은 새 작업 정의를 등록하고 서비스를 업데이트하여 새 작업 정의를 사용하는 것입니다.가장 쉬운 방법은 다음과 같습니다.

  1. 작업 정의로 이동
  2. 올바른 태스크를 선택합니다.
  3. 새 리비전 만들기 선택
  4. :latest 태그와 같은 최신 버전의 컨테이너 이미지를 이미 끌어오는 경우 만들기를 클릭합니다.그렇지 않으면 컨테이너 이미지의 버전 번호를 업데이트한 다음 만들기를 클릭합니다.
  5. 액션 펼치기
  6. 업데이트 서비스 선택(2회)
  7. 그런 다음 서비스가 다시 시작될 때까지 기다립니다.

본 자습서에는 위의 단계들이 엔드 투 엔드 제품 개발 프로세스에 어떻게 적용되는지에 대해 보다 상세하게 설명되어 있습니다.

전체 공개:이 튜토리얼은 빛나미의 컨테이너들을 특징으로 하고 있으며 저는 빛나미에서 일하고 있습니다.그러나 여기에 표현된 생각은 나만의 것이지 빛나미의 생각이 아닙니다.

이렇게 하는 방법은 두 가지가 있습니다.

먼저 AWS CodeDeploy를 사용합니다.ECS 서비스 정의에서 Blue/Green 배포 섹션을 구성할 수 있습니다.여기에는 CodeDeployRoleForECS, 스위치에 대한 다른 TargetGroup, 테스트 Listener(선택 사항)가 포함됩니다.AWS ECS는 CodeDeploy 응용 프로그램 및 배포 그룹을 생성하고 이러한 CodeDeploy 리소스를 ECS 클러스터/서비스 및 ELB/TargetGroups와 연결합니다.그런 다음 CodeDeploy를 사용하여 배포를 시작할 수 있습니다. 여기서 어떤 작업/컨테이너를 사용하여 어떤 서비스를 업데이트할지 지정하는 AppSpec을 입력해야 합니다.여기서 새 작업/컨테이너를 지정합니다.그러면 새 TargetGroup에 새 인스턴스가 스핀업되고 이전 TargetGroup이 ELB에 연결되지 않는 것을 볼 수 있으며, 곧 이전 TargetGroup에 등록된 이전 인스턴스가 종료됩니다.

굉장히 복잡한 것 같네요.사실, ECS 서비스에서 자동 확장을 활성화한 경우 간단한 방법은 콘솔이나 CLI를 사용하여 새 배포를 강제하는 것입니다. 여기 신사분께서 지적하신 것처럼 말입니다.

aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment

이렇게 하면 "롤링 업데이트" 배포 유형을 계속 사용할 수 있으며, ECS는 문제가 없을 경우 서비스 다운타임 없이 새로운 인스턴스를 생성하고 이전 인스턴스를 삭제하기만 하면 됩니다.단점은 배포에 대한 통제력이 떨어지고 오류가 발생하면 이전 버전으로 롤백할 수 없으며 이로 인해 현재 진행 중인 서비스가 중단된다는 것입니다.하지만 이것은 정말 간단한 방법입니다.

그건 그렇고, 최소 건강 퍼센트와 최대 퍼센트의 적절한 숫자를 100과 200과 같이 설정하는 것을 잊지 마세요.

같은 문제에 부딪혔습니다.몇 시간을 보낸 후, 업데이트된 이미지의 자동 배포를 위한 다음과 같은 간소화된 단계를 마쳤습니다.

1.ECS 작업 정의 변경 사항:이해를 돕기 위해 다음과 같은 세부 정보로 작업 정의를 생성했다고 가정합니다(참고: 작업 정의에 따라 이 숫자가 바뀝니다).

launch_type = EC2

desired_count = 1

그런 다음 다음을 변경해야 합니다.

deployment_minimum_healthy_percent = 0  //this does the trick, if not set to zero the force deployment wont happen as ECS won't allow to stop the current running task

deployment_maximum_percent = 200  //for allowing rolling update

2. 이미지를 <your-image-name>:latest로 태그합니다.최신 키는 각각의 ECS 작업에 의해 당겨지는 것을 처리합니다.

sudo docker build -t imageX:master .   //build your image with some tag
sudo -s eval $(aws ecr get-login --no-include-email --region us-east-1)  //login to ECR
sudo docker tag imageX:master <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest    //tag your image with latest tag

3. 영상을 ECR로 푸시

sudo docker push  <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest

4. apply력 투입

sudo aws ecs update-service --cluster <your-cluster-name> --service <your-service-name> --force-new-deployment --region us-east-1

참고: 지역을 us-east-1로 가정하고 모든 명령을 작성했습니다.구현하는 동안 각자의 지역으로 교체하기만 하면 됩니다.

도커 이미지 태그가 동일한 경우 다음이 저를 위해 작동했습니다.

  1. 클러스터로 이동하여 서비스합니다.
  2. service를 선택하고 update를 클릭합니다.
  3. 작업 수를 0으로 설정하고 업데이트합니다.
  4. 배포가 완료된 후 작업 수를 1로 다시 확장합니다.

다음 api도 작동합니다.

aws ecs update-service --cluster <cluster_name> --service <service_name> --force-new-deployment

업데이트된 도커 이미지를 ECS의 스테이징 서비스에 배포하기 위한 스크립트를 만들어 해당 작업 정의가 도커 이미지의 현재 버전을 참조하도록 했습니다.베스트 프랙티스를 잘 따르고 있는지 잘 모르니 피드백을 받아도 좋습니다.

스크립트가 작동하려면 예비 ECS 인스턴스 또는deploymentConfiguration.minimumHealthyPercentECS가 업데이트된 작업 정의를 배포할 인스턴스를 가져올 수 있도록 값을 지정합니다.

제 알고리즘은 다음과 같습니다.

  1. Git 리비전과 함께 작업 정의의 컨테이너에 해당하는 도커 이미지에 태그를 지정합니다.
  2. 도커 영상 태그를 해당 레지스트리로 누릅니다.
  3. 작업 정의 패밀리의 이전 작업 정의 등록을 취소합니다.
  4. 새 작업 정의를 등록합니다. 이제 현재 Git 수정사항으로 태그된 도커 이미지를 참조합니다.
  5. 새 작업 정의를 사용하도록 서비스를 업데이트합니다.

아래에 붙여넣은 내 코드:

ec를 전개하다

#!/usr/bin/env python3
import subprocess
import sys
import os.path
import json
import re
import argparse
import tempfile

_root_dir = os.path.abspath(os.path.normpath(os.path.dirname(__file__)))
sys.path.insert(0, _root_dir)
from _common import *


def _run_ecs_command(args):
    run_command(['aws', 'ecs', ] + args)


def _get_ecs_output(args):
    return json.loads(run_command(['aws', 'ecs', ] + args, return_stdout=True))


def _tag_image(tag, qualified_image_name, purge):
    log_info('Tagging image \'{}\' as \'{}\'...'.format(
        qualified_image_name, tag))
    log_info('Pulling image from registry in order to tag...')
    run_command(
        ['docker', 'pull', qualified_image_name], capture_stdout=False)
    run_command(['docker', 'tag', '-f', qualified_image_name, '{}:{}'.format(
        qualified_image_name, tag), ])
    log_info('Pushing image tag to registry...')
    run_command(['docker', 'push', '{}:{}'.format(
        qualified_image_name, tag), ], capture_stdout=False)
    if purge:
        log_info('Deleting pulled image...')
        run_command(
            ['docker', 'rmi', '{}:latest'.format(qualified_image_name), ])
        run_command(
            ['docker', 'rmi', '{}:{}'.format(qualified_image_name, tag), ])


def _register_task_definition(task_definition_fpath, purge):
    with open(task_definition_fpath, 'rt') as f:
        task_definition = json.loads(f.read())

    task_family = task_definition['family']

    tag = run_command([
        'git', 'rev-parse', '--short', 'HEAD', ], return_stdout=True).strip()
    for container_def in task_definition['containerDefinitions']:
        image_name = container_def['image']
        _tag_image(tag, image_name, purge)
        container_def['image'] = '{}:{}'.format(image_name, tag)

    log_info('Finding existing task definitions of family \'{}\'...'.format(
        task_family
    ))
    existing_task_definitions = _get_ecs_output(['list-task-definitions', ])[
        'taskDefinitionArns']
    for existing_task_definition in [
        td for td in existing_task_definitions if re.match(
            r'arn:aws:ecs+:[^:]+:[^:]+:task-definition/{}:\d+'.format(
                task_family),
            td)]:
        log_info('Deregistering task definition \'{}\'...'.format(
            existing_task_definition))
        _run_ecs_command([
            'deregister-task-definition', '--task-definition',
            existing_task_definition, ])

    with tempfile.NamedTemporaryFile(mode='wt', suffix='.json') as f:
        task_def_str = json.dumps(task_definition)
        f.write(task_def_str)
        f.flush()
        log_info('Registering task definition...')
        result = _get_ecs_output([
            'register-task-definition',
            '--cli-input-json', 'file://{}'.format(f.name),
        ])

    return '{}:{}'.format(task_family, result['taskDefinition']['revision'])


def _update_service(service_fpath, task_def_name):
    with open(service_fpath, 'rt') as f:
        service_config = json.loads(f.read())
    services = _get_ecs_output(['list-services', ])[
        'serviceArns']
    for service in [s for s in services if re.match(
        r'arn:aws:ecs:[^:]+:[^:]+:service/{}'.format(
            service_config['serviceName']),
        s
    )]:
        log_info('Updating service with new task definition...')
        _run_ecs_command([
            'update-service', '--service', service,
            '--task-definition', task_def_name,
        ])


parser = argparse.ArgumentParser(
    description="""Deploy latest Docker image to staging server.
The task definition file is used as the task definition, whereas
the service file is used to configure the service.
""")
parser.add_argument(
    'task_definition_file', help='Your task definition JSON file')
parser.add_argument('service_file', help='Your service JSON file')
parser.add_argument(
    '--purge_image', action='store_true', default=False,
    help='Purge Docker image after tagging?')
args = parser.parse_args()

task_definition_file = os.path.abspath(args.task_definition_file)
service_file = os.path.abspath(args.service_file)

os.chdir(_root_dir)

task_def_name = _register_task_definition(
    task_definition_file, args.purge_image)
_update_service(service_file, task_def_name)

_common.py

import sys
import subprocess


__all__ = ['log_info', 'handle_error', 'run_command', ]


def log_info(msg):
    sys.stdout.write('* {}\n'.format(msg))
    sys.stdout.flush()


def handle_error(msg):
    sys.stderr.write('* {}\n'.format(msg))
    sys.exit(1)


def run_command(
        command, ignore_error=False, return_stdout=False, capture_stdout=True):
    if not isinstance(command, (list, tuple)):
        command = [command, ]
    command_str = ' '.join(command)
    log_info('Running command {}'.format(command_str))
    try:
        if capture_stdout:
            stdout = subprocess.check_output(command)
        else:
            subprocess.check_call(command)
            stdout = None
    except subprocess.CalledProcessError as err:
        if not ignore_error:
            handle_error('Command failed: {}'.format(err))
    else:
        return stdout.decode() if return_stdout else None

모든 IAC 도구를 사용하여 테라폼과 같은 ECS 작업을 설정하는 경우 작업 정의에서 항상 이미지 버전을 업데이트할 수 있습니다.테라폼은 기본적으로 기존 작업 정의를 대체하고 새로운 작업 정의를 만들고 ECS 서비스는 새로운 작업 정의를 업데이트된 이미지로 사용하기 시작합니다.

반대로 파이프라인에 항상 awsecs update 명령을 사용하면 ECS 작업에 사용할 이미지가 구축되고 이미지를 구축하는 즉시 강제 배포를 수행할 수 있습니다.

aws ecs update-service --cluster clusterName --service serviceName --force-new-deployment

AWS측에서 아무런 진전이 없었기 때문입니다.Dima와 Samuel Karp의 높은 평가를 받은 답변에 설명된 단계를 정확히 수행하는 간단한 파이썬 스크립트를 드리겠습니다.

먼저 이미지를 AWS 레지스트리 ECR에 밀어 넣은 다음 스크립트를 실행합니다.

import boto3, time

client = boto3.client('ecs')
cluster_name = "Example_Cluster"
service_name = "Example-service"
reason_to_stop = "obsolete deployment"

# Create new deployment; ECS Service forces to pull from docker registry, creates new task in service
response = client.update_service(cluster=cluster_name, service=service_name, forceNewDeployment=True)

# Wait for ecs agent to start new task
time.sleep(10)

# Get all Service Tasks
service_tasks = client.list_tasks(cluster=cluster_name, serviceName=service_name)

# Get meta data for all Service Tasks
task_meta_data = client.describe_tasks(cluster=cluster_name, tasks=service_tasks["taskArns"])

# Extract creation date
service_tasks = [(task_data['taskArn'], task_data['createdAt']) for task_data in task_meta_data["tasks"]]

# Sort according to creation date
service_tasks = sorted(service_tasks, key= lambda task: task[1])

# Get obsolete task arn
obsolete_task_arn = service_tasks[0][0]
print("stop ", obsolete_task_arn)

# Stop obsolete task
stop_response = client.stop_task(cluster=cluster_name, task=obsolete_task_arn, reason=reason_to_stop)

이 코드는 다음을 수행합니다.

  1. 서비스의 새 이미지를 사용하여 새 작업 생성
  2. 서비스의 오래된 이미지로 오래된 작업 중지

AWS 코드 파이프라인.

ECR을 소스로 설정하고 ECS를 배포할 대상으로 설정할 수 있습니다.

AWS cli를 이용하여 위에서 제안한 awsec update-service를 시도했습니다.ECR에서 최신 도커를 선택하지 않았습니다.결국 ECS 클러스터를 만든 Ansible 플레이북을 다시 실행했습니다.작업 정의의 버전은 ecs_task 정의가 실행될 때 부딪힙니다.그럼 다 좋습니다.새 도커 이미지가 픽업됩니다.

작업 버전이 변경되어 다시 배포가 강제로 수행되는지 아니면 ecs_service를 사용하는 플레이북이 작업을 다시 로드하게 하는지 확실히 알 수 없습니다.

관심 있는 사람이 있다면 제 플레이북을 위생적으로 출판할 수 있도록 허락을 받도록 하겠습니다.

다음 명령이 나에게 효과가 있었습니다.

docker build -t <repo> . 
docker push <repo>
ecs-cli compose stop
ecs-cli compose start

언급URL : https://stackoverflow.com/questions/34840137/how-do-i-deploy-updated-docker-images-to-amazon-ecs-tasks

반응형