Який правильний підхід, щоб змусити мої завдання Амазонки ECS оновлювати свої зображення Docker після того, як зазначені зображення були оновлені у відповідному реєстрі?
Який правильний підхід, щоб змусити мої завдання Амазонки ECS оновлювати свої зображення Docker після того, як зазначені зображення були оновлені у відповідному реєстрі?
Відповіді:
Якщо ваше завдання працює під службою, ви можете примусити нове розгортання. Це змушує повторно оцінити визначення завдання та витягнути нове зображення контейнера.
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
Кожен раз , коли ви запускаєте завдання (або через StartTask
та RunTask
виклики API або що автоматично запускається як частина служби), то ECS Агент виконає docker pull
з image
вас вказати у визначенні завдань. Якщо ви використовуєте одне й те саме ім’я зображення (включаючи тег) кожного разу, коли ви натискаєте на свій реєстр, вам слід мати можливість запускати нове зображення, виконуючи нове завдання. Зверніть увагу, що якщо Docker не може отримати доступ до реєстру з будь-якої причини (наприклад, проблеми з мережею або проблеми автентифікації), агент ECS спробує використати кешоване зображення; якщо ви хочете уникнути використання кешованих зображень при використанні під час оновлення зображення, вам потрібно буде щоразу натискати на ваш реєстр інший тег і відповідно оновити визначення завдання перед запуском нового завдання.
Оновлення: тепер ця поведінка може бути налаштована за допомогою ECS_IMAGE_PULL_BEHAVIOR
змінної середовища, встановленої на агенті ECS. Детальну інформацію див. У документації . На час написання підтримуються наступні налаштування:
Поведінка, яка використовується для налаштування процесу витягування зображень для екземплярів контейнера. Далі описано необов'язкове поведінку:
Якщо
default
вказано, зображення витягується віддалено. Якщо витяг зображення не вдається, контейнер використовує кешоване зображення на екземплярі.Якщо
always
вказано, зображення завжди витягується віддалено. Якщо витягнути зображення не вдалося, то завдання не вдасться. Ця опція гарантує, що остання версія зображення завжди буде витягнута. Будь-які кешовані зображення ігноруються та підлягають автоматичному очищенню зображення.Якщо
once
вказано, зображення витягується віддалено, лише якщо воно не було витягнуто попереднім завданням у тому ж екземплярі контейнера або якщо кешоване зображення було видалено автоматичним процесом очищення зображення. В іншому випадку використовується кешоване зображення в екземплярі. Це гарантує, що ніяких зайвих знімків зображень не буде здійснено.Якщо
prefer-cached
вказано, зображення витягується віддалено, якщо немає кешованого зображення. В іншому випадку використовується кешоване зображення в екземплярі. Автоматичне очищення зображення вимкнено для контейнера, щоб запобігти видаленню кешованого зображення.
/var/log/ecs
.
Реєстрація нового визначення завдання та оновлення служби для використання нового визначення завдання - це підхід, рекомендований AWS. Найпростіший спосіб зробити це:
У цьому підручнику є більш докладно і описано, як вищезазначені кроки вписуються в процес розробки продукту, що завершується.
Повне розкриття: У цьому підручнику представлені контейнери з Bitnami, і я працюю в Bitnami. Однак думки, висловлені тут, є моєю власною, а не думкою Бітнамі.
Є два способи зробити це.
По-перше, використовуйте AWS CodeDeploy. Ви можете налаштувати розділи синього / зеленого розгортання у визначенні служби ECS. Сюди входить CodeDeployRoleForECS, ще одна TargetGroup для комутатора та тестовий слухач (необов’язково). AWS ECS створить групу додатків та розгортань CodeDeploy та зв’яже ці ресурси CodeDeploy із вашим кластером / службою ECS та вашими ELB / TargetGroups. Тоді ви можете використовувати CodeDeploy для ініціювання розгортання, в якому потрібно ввести AppSpec, який вказує використання завдання / контейнера для оновлення тієї служби. Тут ви вказуєте нове завдання / контейнер. Потім ви побачите, що нові екземпляри з'являються в новій TargetGroup, а стара TargetGroup відключається до ELB, і незабаром старі екземпляри, зареєстровані в старій TargetGroup, будуть припинені.
Це звучить дуже складно. Насправді, оскільки / якщо ви ввімкнули автоматичне масштабування на вашій службі ECS, простий спосіб зробити це просто змусити нове розгортання за допомогою консолі чи кліпу, як тут зазначив джентльмен:
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
Таким чином, ви все ще можете використовувати тип розгортання "прокатного оновлення", і ECS просто розкручує нові екземпляри та зливає старі без простою вашої служби, якщо все добре. Погана сторона полягає в тому, що ви втрачаєте тонкий контроль над розгортанням, і не можете повернутися до попередньої версії, якщо сталася помилка, і це порушить поточну послугу. Але це дійсно простий шлях.
До речі, не забудьте встановити відповідні числа для Мінімального здорового відсотка та Максимального відсотка, наприклад 100 і 200.
Я створив сценарій для розгортання оновлених зображень Docker в службі інсценізації на ECS, так що відповідне визначення завдання стосується поточних версій зображень Docker. Я точно не знаю, чи я дотримуюся найкращих практик, тому відгуки будуть вітатися.
Щоб сценарій працював, вам потрібен або запасний екземпляр ECS, або deploymentConfiguration.minimumHealthyPercent
значення, щоб 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)
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
Наступний працював для мене у випадку, якщо тег зображення docker є таким же:
Побіг у тому ж випуску. Витративши години, зробили наступні спрощені кроки для автоматичного розгортання оновленого зображення:
Зміни визначення завдання 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.Тегніть зображення як < своє-зображення-ім'я>: останнє . Остання клавіша забезпечує потягнення за відповідним завданням 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
Примітка. Я написав усі команди, припускаючи, що цей регіон є нами схід-1 . Просто замініть його на відповідний регіон під час впровадження.
За допомогою AWS cli я спробував послугу оновлення aws ecs, як було запропоновано вище. Не забрав останній докер з ECR. Врешті-решт я перезапустив мою програму Ansible, яка створила кластер ECS. Версія визначення завдання нарікається під час запуску ecs_taskdefinition. Тоді все добре. Зображено нове зображення докера.
Поправді не впевнений, чи зміна версії задачі змушує перевстановитись, або якщо ігрова книга, що використовує службу ecs_service, спричинить перезавантаження завдання.
Якщо хтось зацікавиться, я отримаю дозвіл на публікацію санізованої версії своєї ігрової книги.
добре, я також намагаюся знайти автоматизований спосіб зробити це, тобто натиснути зміни на ECR, і тоді останній тег повинен бути підібраний сервісом. Правильно ви можете це зробити вручну, зупинивши завдання для своєї служби зі свого кластеру. Нові завдання потягнуть оновлені контейнери ECR.
Наступні команди працювали на мене
docker build -t <repo> .
docker push <repo>
ecs-cli compose stop
ecs-cli compose start