IT TIP

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

itqueen 2020. 10. 26. 21:38
반응형

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


해당 레지스트리에서 이미지가 업데이트 된 후 Amazon ECS 작업이 Docker 이미지를 업데이트 하도록하는 올바른 접근 방식은 무엇입니까 ?


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

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

때마다 당신이합니다 (통해서 작업을 시작 StartTask하고 RunTaskAPI 호출 또는 그 서비스의 일부로 자동으로 시작됩니다)는 ECS 에이전트는 수행 docker pullimage귀하의 작업 정의에 지정을. 레지스트리에 푸시 할 때마다 동일한 이미지 이름 (태그 포함)을 사용하는 경우 새 작업을 실행하여 새 이미지를 실행할 수 있어야합니다. Docker가 어떤 이유로 든 (예 : 네트워크 문제 또는 인증 문제) 레지스트리에 연결할 수없는 경우 ECS 에이전트는 캐시 된 이미지를 사용하려고 시도합니다. 이미지를 업데이트 할 때 캐시 된 이미지가 사용되지 않도록하려면 매번 다른 태그를 레지스트리에 푸시하고 새 작업을 실행하기 전에 그에 따라 작업 정의를 업데이트해야합니다.

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

컨테이너 인스턴스에 대한 가져 오기 이미지 프로세스를 사용자 지정하는 데 사용되는 동작입니다. 다음은 선택적 동작에 대해 설명합니다.

  • 경우 default지정, 이미지가 원격으로 당겨진다. 이미지 가져 오기가 실패하면 컨테이너는 인스턴스에서 캐시 된 이미지를 사용합니다.

  • 경우 always지정, 이미지는 항상 원격으로 당겨진다. 이미지 가져 오기가 실패하면 작업이 실패합니다. 이 옵션은 항상 최신 버전의 이미지를 가져 오도록합니다. 캐시 된 모든 이미지는 무시되며 자동화 된 이미지 정리 프로세스를 따릅니다.

  • 경우 once지정, 이미지는이 같은 컨테이너 인스턴스에 이전 작업에 의해 끌려되지 않았거나 캐시 된 이미지가 자동 이미지 정리 프로세스에 의해 제거 된 경우에만 원격으로 당겨진다. 그렇지 않으면 인스턴스의 캐시 된 이미지가 사용됩니다. 이렇게하면 불필요한 이미지 가져 오기가 시도되지 않습니다.

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


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

  1. 작업 정의로 이동
  2. 올바른 작업 선택
  3. 새 개정 만들기를 선택하십시오.
  4. : latest 태그와 같은 것을 사용하여 컨테이너 이미지의 최신 버전을 이미 가져오고있는 경우 만들기를 클릭하기 만하면됩니다. 그렇지 않으면 컨테이너 이미지의 버전 번호를 업데이트 한 다음 만들기를 클릭합니다.
  5. 작업 확장
  6. 업데이트 서비스 선택 (두 번)
  7. 그런 다음 서비스가 다시 시작될 때까지 기다립니다.

이 자습서 에는 더 자세한 내용이 포함되어 있으며 위 단계가 종단 간 제품 개발 프로세스에 어떻게 적용되는지 설명합니다.

전체 공개 :이 튜토리얼은 Bitnami의 컨테이너를 다루며 저는 Bitnami에서 일합니다. 그러나 여기에 표현 된 생각은 내 생각이며 Bitnami의 의견이 아닙니다.


업데이트 된 Docker 이미지를 ECS의 스테이징 서비스에 배포하기위한 스크립트생성 하여 해당 작업 정의가 현재 버전의 Docker 이미지를 참조하도록했습니다. 모범 사례를 따르고 있는지 확실하지 않으므로 피드백을 환영합니다.

스크립트가 작동하려면 deploymentConfiguration.minimumHealthyPercentECS가 업데이트 된 작업 정의를 배포 할 인스턴스를 훔칠 수 있는 예비 ECS 인스턴스 또는 이 필요합니다 .

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

  1. Git 개정으로 작업 정의의 컨테이너에 해당하는 Docker 이미지에 태그를 지정합니다.
  2. Docker 이미지 태그를 해당 레지스트리로 푸시합니다.
  3. 작업 정의 패밀리에서 이전 작업 정의를 등록 취소합니다.
  4. 이제 현재 Git 개정으로 태그가 지정된 Docker 이미지를 참조하여 새 작업 정의를 등록합니다.
  5. 새 작업 정의를 사용하도록 서비스를 업데이트합니다.

내 코드는 아래에 붙여 넣었습니다.

deploy-ecs

#!/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

AWS CodePipeline.

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


AWS cli를 사용하여 위에서 제안한대로 aws ecs update-service를 시도했습니다. ECR에서 최신 도커를 선택하지 않았습니다. 결국 ECS 클러스터를 생성 한 Ansible 플레이 북을 다시 실행합니다. 작업 정의 버전은 ecs_taskdefinition이 실행될 때 범프됩니다. 그러면 모든 것이 좋습니다. 새 도커 이미지가 선택됩니다.

작업 버전 변경으로 인해 강제로 재배포되는지 또는 ecs_service를 사용하는 플레이 북으로 인해 작업이 다시로드되는지 확실하지 않습니다.

관심이있는 사람이 있으면 내 플레이 북의 삭제 된 버전을 게시 할 수있는 권한을 얻습니다.


well i am also trying to find an automated way of doing it, That is push the changes to ECR and then latest tag should be picked up by service. Right you can do it manually by Stopping the task for your service from your cluster. New tasks will pull the updated ECR containers .


There are two ways to do this.

First, use AWS CodeDeploy. You can config Blue/Green deployment sections in ECS service definition. This includes a CodeDeployRoleForECS, another TargetGroup for switch, and a test Listener (optional). AWS ECS will create CodeDeploy application and deployment group and link these CodeDeploy resources with your ECS Cluster/Service and your ELB/TargetGroups for you. Then you can use CodeDeploy to initiate a deployment, in which you need to enter an AppSpec that specifies using what task/container to update what service. Here is where you specify your new task/container. Then, you will see new instances are spin up in the new TargetGroup and the old TargetGroup is disconnected to the ELB, and soon the old instances registered to the old TargetGroup will be terminated.

This sounds very complicated. Actually, since/if you have enabled auto scaling on your ECS service, a simple way to do it is to just force a new deployment using console or cli, like a gentleman here pointed out:

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

In this way you can still use the "polling update" deployment type, and ECS will simply spin up new instances and drain the old ones with no downtime of your service if everything is OK. The bad side is you cannot roll back to previous version if there is an error and this will break the ongoing service. But this is a really simple way to go.

BTW, don't forget to set proper numbers for Minimum healthy percent and Maximum percent, like 100 and 200.


The following commands worked for me

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

반응형