Подтвердить что ты не робот

Как развернуть обновленные изображения Docker для задач Amazon ECS?

Каков правильный подход, чтобы мои задачи Amazon ECS обновляли их изображения Docker, как только указанные изображения были обновлены в соответствующем реестре?

4b9b3361

Ответ 1

Я создал a script для развертывания обновленных изображений Docker в промежуточную службу в ECS, так что соответствующее определение задачи относится к текущие версии изображений Docker. Я не знаю точно, если я буду следовать лучшим методам, поэтому отзывы будут приветствоваться.

Для работы script вам нужен либо запасной экземпляр ECS, либо значение deploymentConfiguration.minimumHealthyPercent, чтобы ECS могла украсть экземпляр для развертывания обновленного определения задачи.

Мой алгоритм выглядит следующим образом:

  • Изображения тегов Docker, соответствующие контейнерам в определении задачи с версией Git.
  • Нажмите теги изображения Docker на соответствующие реестры.
  • Отмените старые определения задач в семействе определения задачи.
  • Зарегистрируйте новое определение задачи, теперь ссылаясь на изображения Docker, помеченные текущими версиями Git.
  • Обновить службу, чтобы использовать новое определение задачи.

Мой код, вставленный ниже:

развертывают-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

Ответ 2

Если ваша задача выполняется в службе, вы можете форсировать новое развертывание. Это заставляет определение задачи переоценивать и получать новый образ контейнера.

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

Ответ 3

Каждый раз, когда вы запускаете задачу (либо с помощью StartTask API StartTask и RunTask либо она запускается автоматически как часть службы), агент ECS будет выполнять docker pull image из image указанного вами в определении задачи. Если вы используете одно и то же имя образа (включая тег) каждый раз, когда вы нажимаете на реестр, вы сможете запустить новый образ, запустив новую задачу. Обратите внимание, что если Docker не может связаться с реестром по какой-либо причине (например, из-за проблем с сетью или из-за проблем с аутентификацией), агент ECS попытается использовать кэшированный образ; Если вы хотите избежать использования кэшированных изображений при обновлении изображения, вам нужно будет каждый раз вставлять в свой реестр новый тег и соответственно обновлять определение задачи перед запуском новой задачи.

Обновление: теперь это поведение можно настроить с ECS_IMAGE_PULL_BEHAVIOR переменной среды ECS_IMAGE_PULL_BEHAVIOR установленной в агенте ECS. Смотрите документацию для деталей. На момент написания статьи поддерживаются следующие настройки:

Поведение, используемое для настройки процесса извлечения изображения для ваших экземпляров контейнера. Ниже описаны необязательные варианты поведения:

  • Если указано default, изображение извлекается удаленно. Если при извлечении изображения происходит сбой, контейнер использует кэшированное изображение в экземпляре.

  • Если always указано, изображение всегда вытягивается удаленно. Если вытащить изображение не удается, значит, задача не выполнена. Эта опция гарантирует, что самая последняя версия изображения всегда вытягивается. Все кэшированные изображения игнорируются и подлежат автоматической очистке изображения.

  • Если указан once, изображение извлекается удаленно, только если оно не было извлечено предыдущей задачей в том же экземпляре контейнера или если кэшированное изображение было удалено в процессе автоматической очистки изображения. В противном случае используется кэшированное изображение в экземпляре. Это гарантирует, что никакие ненужные изображения не будут предприняты.

  • Если указано prefer-cached, изображение извлекается удаленно, если кэшированное изображение отсутствует. В противном случае кэшированное изображение в экземпляре используется. Автоматическая очистка изображения отключена для контейнера, чтобы гарантировать, что кэшированное изображение не будет удалено.

Ответ 4

Регистрация нового определения задачи и обновление службы для использования определения новой задачи - это подход, рекомендованный AWS. Самый простой способ сделать это:

  • Перейдите к определениям задач
  • Выберите правильную задачу
  • Выберите создать новую версию
  • Если вы уже вытаскиваете последнюю версию изображения контейнера с чем-то вроде последнего тега, просто нажмите "Создать". В противном случае обновите номер версии изображения контейнера и нажмите "Создать".
  • Развернуть действия
  • Выберите службу обновления (дважды)
  • Затем подождите, пока служба будет перезагружена.

В этом руководстве более подробно описано, как приведенные выше шаги вписываются в сквозной процесс разработки продукта.

Полное раскрытие: В этом учебнике представлены контейнеры от Bitnami, и я работаю над Bitnami. Однако мысли, выраженные здесь, являются моими собственными, а не мнением Битнами.

Ответ 5

AWS CodePipeline.

Вы можете установить ECR в качестве источника и ECS в качестве цели для развертывания.

Ответ 6

Используя AWS cli, я попробовал сервис обновления aws ecs, как предложено выше. Не забрал последний докер из ECR. В конце концов, я перезапустил свою пьесу Ansible, которая создала кластер ECS. Версия определения задачи поднимается при запуске ecs_taskdefinition. Тогда все хорошо. Новый образ докера поднят.

Честно говоря, не уверен, что изменение версии задачи вызывает повторное развертывание, или если книга воспроизведения, использующая ecs_service, вызывает перезагрузку задачи.

Если кому-то будет интересно, я получу разрешение на публикацию продезинфицированной версии моей пьесы.

Ответ 7

Ну, я также пытаюсь найти автоматизированный способ сделать это, то есть подтолкнуть изменения к ECR, и тогда последний тег должен быть выбран службой. Право, вы можете сделать это вручную, остановив задачу для вашего сервиса из вашего кластера. Новые задачи будут тянуть обновленные контейнеры ECR.

Ответ 8

Есть два способа сделать это.

Во-первых, используйте AWS CodeDeploy. Вы можете настроить разделы развертывания Blue/Green в определении сервиса ECS. Это включает CodeDeployRoleForECS, другую TargetGroup для коммутатора и тестовый прослушиватель (необязательно). 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.

Ответ 9

Следующие команды помогли мне

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