Каков правильный подход, чтобы мои задачи Amazon ECS обновляли их изображения Docker, как только указанные изображения были обновлены в соответствующем реестре?
Как развернуть обновленные изображения Docker для задач Amazon ECS?
Ответ 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