Как сказал однажды, как правильно заставить мои задачи Amazon ECS обновлять свои образы Docker образы обновлялись в соответствующем реестре?
Как развернуть обновленные образы Docker в задачах Amazon ECS?
Ответы (12)
Если ваша задача выполняется в рамках службы, вы можете принудительно выполнить новое развертывание. Это приводит к повторной оценке определения задачи и извлечению нового образа контейнера.
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
--region <region>
- person shwz; 11.01.2019
AWS::ECS::Service
с DeploymentConfiguration: MinimumHealthyPercent: 50 MaximumPercent: 200
- person Wayne; 07.03.2021
Каждый раз, когда вы запускаете задачу (либо через вызовы StartTask
и RunTask
API, либо запускается автоматически как часть службы), агент ECS будет выполнять docker pull
из image
, указанных в определении задачи. Если вы используете одно и то же имя образа (включая тег) каждый раз, когда вы отправляете его в реестр, вы сможете запустить новый образ, запустив новую задачу. Обратите внимание: если Docker не может получить доступ к реестру по какой-либо причине (например, проблемы с сетью или проблемы с аутентификацией), агент ECS попытается использовать кэшированный образ; если вы хотите избежать использования кэшированных изображений при обновлении образа, вам нужно каждый раз вставлять в реестр разные теги и соответственно обновлять определение задачи перед запуском новой задачи.
Обновление: теперь это поведение можно настроить с помощью переменной среды ECS_IMAGE_PULL_BEHAVIOR
, заданной в агенте ECS. Дополнительные сведения см. В документации. На момент написания поддерживаются следующие настройки:
Поведение, используемое для настройки процесса извлечения изображения для ваших экземпляров контейнера. Ниже описаны дополнительные варианты поведения:
Если указано
default
, изображение извлекается удаленно. Если получение изображения не удается, контейнер использует кэшированное изображение в экземпляре.Если указано
always
, изображение всегда извлекается удаленно. Если получение изображения не удается, задача не выполняется. Эта опция гарантирует, что всегда будет извлекаться последняя версия образа. Любые кэшированные изображения игнорируются и подлежат автоматической очистке.Если указано
once
, изображение извлекается удаленно, только если оно не было извлечено предыдущей задачей в том же экземпляре контейнера или если кэшированное изображение было удалено в процессе автоматической очистки образа. В противном случае используется кэшированное изображение в экземпляре. Это гарантирует, что не будут предприниматься попытки ненужного извлечения изображения.Если указано
prefer-cached
, изображение извлекается удаленно, если нет кэшированного изображения. В противном случае используется кэшированное изображение в экземпляре. Автоматическая очистка образа отключена для контейнера, чтобы гарантировать, что кешированный образ не будет удален.
/var/log/ecs
.
- person Samuel Karp; 15.10.2017
AWS рекомендует зарегистрировать новое определение задачи и обновить сервис для использования нового определения задачи. Самый простой способ сделать это:
- Перейти к определениям задач
- Выберите правильную задачу
- Выберите создать новую ревизию
- Если вы уже загружаете последнюю версию образа контейнера с чем-то вроде тега: latest, просто нажмите Create. В противном случае обновите номер версии образа контейнера и нажмите «Создать».
- Развернуть Действия
- Выберите службу обновления (дважды)
- Затем дождитесь перезапуска службы
Это руководство содержит более подробную информацию и описывает, как вышеуказанные шаги вписываются в непрерывный процесс разработки продукта.
Полное раскрытие информации: в этом руководстве представлены контейнеры от Bitnami, и я работаю на Bitnami. Однако мысли, выраженные здесь, являются моими собственными, а не мнением Bitnami.
Есть два способа сделать это.
Сначала используйте AWS CodeDeploy. Вы можете настроить сине-зеленые разделы развертывания в определении службы 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.
Я создал скрипт для развертывания обновленных образов Docker в промежуточной service на ECS, так что соответствующее определение задачи относится к текущим версиям образов Docker. Я не знаю наверняка, следую ли я лучшим практикам, поэтому обратная связь будет приветствоваться.
Чтобы сценарий работал, вам нужен либо запасной экземпляр 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
Попался в ту же проблему. Потратив несколько часов, вы завершили эти упрощенные шаги для автоматического развертывания обновленного образа:
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. примените принудительное развертывание
sudo aws ecs update-service --cluster <your-cluster-name> --service <your-service-name> --force-new-deployment --region us-east-1
Примечание. Я написал все команды, предполагая, что регион будет us-east-1. Просто замените его на свой регион при внедрении.
Следующее сработало для меня, если тег изображения докера такой же:
- Заходим в кластер и сервис.
- Выберите услугу и нажмите «Обновить».
- Установите количество задач как 0 и обновите.
- После завершения развертывания масштабируйте количество задач до 1.
Также работает следующий api:
aws ecs update-service --cluster <cluster_name> --service <service_name> --force-new-deployment
AWS CodePipeline.
Вы можете установить ECR в качестве источника и ECS в качестве цели для развертывания.
поскольку на стороне AWS не было никакого прогресса. Я дам вам простой скрипт python, который точно выполняет шаги, описанные в высоко оцененных ответах Димы и Самуэля Карпа.
Сначала нажмите изображение в ECR реестра AWS, затем запустите сценарий:
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)
Этот код делает:
- создать новую задачу с новым изображением в сервисе
- остановить устаревшую старую задачу старым образом в сервисе
Используя AWS cli, я попробовал сервис обновления aws ecs, как было предложено выше. Не забирал последний докер из ECR. В конце концов, я перезапускаю свою книгу пьес Ansible, в которой был создан кластер ECS. Версия определения задачи изменяется при запуске ecs_taskdefinition. Тогда все хорошо. Подбирается новый образ докера.
По правде говоря, не уверен, вызывает ли изменение версии задачи повторное развертывание или playbook, использующий ecs_service, вызывает перезагрузку задачи.
Если кому-то интересно, я получу разрешение на публикацию очищенной версии моего плейбука.
ну, я также пытаюсь найти автоматический способ сделать это, то есть внести изменения в ECR, а затем последний тег должен быть получен службой. Правильно, вы можете сделать это вручную, остановив задачу для вашей службы из вашего кластера. Новые задачи потянут обновленные контейнеры ECR.
Следующие команды работали для меня
docker build -t <repo> .
docker push <repo>
ecs-cli compose stop
ecs-cli compose start