Quelle est la bonne approche pour que mes tâches Amazon ECS mettent à jour leurs images Docker, une fois que ces images ont été mises à jour dans le registre correspondant?
J'ai créé n script pour déployer les images Docker mises à jour sur un service de transfert sur ECS, de sorte que la définition de tâche correspondante fasse référence aux versions actuelles des images Docker. Je ne sais pas si je suis les meilleures pratiques, alors vos commentaires sont les bienvenus.
Pour que le script fonctionne, vous avez besoin d'une instance de sauvegarde ECS ou d'un deploymentConfiguration.minimumHealthyPercent
valeur pour qu'ECS puisse voler une instance sur laquelle déployer la définition de tâche mise à jour.
Mon algorithme est comme ça:
Mon code collé ci-dessous:
#!/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
Chaque fois que vous démarrez une tâche (par le biais d'appels d'API StartTask
et RunTask
ou démarrée automatiquement dans le cadre d'un service), l'agent ECS exécute un docker pull
du image
que vous spécifiez dans votre définition de tâche. Si vous utilisez le même nom d'image (y compris la balise) à chaque poussée dans votre base de registre, vous devriez pouvoir exécuter la nouvelle image en exécutant une nouvelle tâche. Notez que si Docker ne peut pas accéder au registre pour une raison quelconque (par exemple, problèmes de réseau ou d’authentification), l’agent ECS tentera d’utiliser une image mise en cache; Si vous souhaitez éviter que les images en cache ne soient utilisées lors de la mise à jour de votre image, vous devez insérer chaque fois une balise différente dans votre registre et mettre à jour votre définition de tâche en conséquence avant d'exécuter la nouvelle tâche.
Mise à jour: Ce comportement peut maintenant être réglé via le ECS_IMAGE_PULL_BEHAVIOR
variable d’environnement définie sur l’agent ECS. Voir la documentation pour plus de détails. Au moment de la rédaction de ce document, les paramètres suivants sont pris en charge:
Le comportement utilisé pour personnaliser le processus d'image d'extraction pour vos instances de conteneur. Ce qui suit décrit les comportements facultatifs:
Si
default
est spécifié, l'image est extraite à distance. Si l'extraction d'image échoue, le conteneur utilise l'image mise en cache sur l'instance.Si
always
est spécifié, l'image est toujours extraite à distance. Si le tirage d'image échoue, la tâche échoue. Cette option garantit que la dernière version de l'image est toujours extraite. Toutes les images mises en cache sont ignorées et sont soumises au processus de nettoyage automatique des images.Si
once
est spécifié, l'image est extraite à distance uniquement si elle n'a pas été extraite par une tâche précédente sur la même instance de conteneur ou si l'image mise en cache a été supprimée par le processus de nettoyage d'image automatisé. Sinon, l'image mise en cache sur l'instance est utilisée. Cela garantit qu'aucune extraction d'image inutile n'est tentée.Si
prefer-cached
est spécifié, l'image est extraite à distance s'il n'y a pas d'image en cache. Sinon, l'image mise en cache sur l'instance est utilisée. Le nettoyage automatique des images est désactivé pour le conteneur afin de garantir que l'image mise en cache n'est pas supprimée.
Si votre tâche est exécutée sous un service, vous pouvez forcer un nouveau déploiement. Cela oblige à réévaluer la définition de la tâche et à extraire la nouvelle image du conteneur.
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
L'enregistrement d'une nouvelle définition de tâche et la mise à jour du service pour utiliser la nouvelle définition de tâche constituent l'approche recommandée par AWS. Le moyen le plus simple de le faire est de:
Ce tutoriel contient plus de détails et décrit comment les étapes ci-dessus s'intègrent dans un processus de développement de produit de bout en bout.
Divulgation complète: Ce tutoriel présente les conteneurs de Bitnami et je travaille pour Bitnami. Cependant, les pensées exprimées ici sont les miennes et non l'opinion de Bitnami.
AWS CodePipeline.
Vous pouvez définir ECR en tant que source et ECS en tant que cible sur laquelle déployer.
En utilisant AWS cli, j'ai essayé le service de mise à jour aws ecs comme suggéré ci-dessus. N'a pas récupéré le dernier menu fixe d'ECR. En fin de compte, j'ai relancé mon livre de lecture Ansible qui a créé le cluster ECS. La version de la définition de la tâche est modifiée lors de l'exécution de ecs_taskdefinition. Alors tout va bien. La nouvelle image du menu fixe est sélectionnée.
En vérité, ne pas être sûr que la modification de la version de la tâche force le redéploiement ou que le livre de lecture utilisant ecs_service provoque le rechargement de la tâche.
Si quelqu'un est intéressé, j'aurai la permission de publier une version assainie de mon livre de jeu.
Les commandes suivantes ont fonctionné pour moi
docker build -t <repo> .
docker Push <repo>
ecs-cli compose stop
ecs-cli compose start
eh bien, j'essaie également de trouver un moyen automatisé de le faire, c’est-à-dire d’appliquer les modifications à la caisse enregistreuse électronique, puis la dernière étiquette doit être récupérée par le service. À droite, vous pouvez le faire manuellement en arrêtant la tâche de votre service à partir de votre cluster. Les nouvelles tâches vont extraire les conteneurs ECR mis à jour.