hub aws docker docker-registry amazon-ecs

aws - ¿Cómo implemento las imágenes actualizadas de Docker en las tareas de Amazon ECS?



docker aws (8)

AWS CodePipeline.

Puede establecer ECR como fuente y ECS como destino para implementar.

¿Cuál es el enfoque correcto para que mis tareas de Amazon ECS actualicen sus imágenes de Docker, una vez que dichas imágenes se hayan actualizado en el registro correspondiente?


Bueno, también estoy tratando de encontrar una forma automatizada de hacerlo, es decir, impulsar los cambios a ECR y luego la última etiqueta debe ser recogida por el servicio. Bien, puede hacerlo manualmente deteniendo la tarea para su servicio desde su clúster. Las nuevas tareas extraerán los contenedores de ECR actualizados.


Cada vez que inicia una tarea (ya sea a través de las llamadas API StartTask y RunTask o que se inicia automáticamente como parte de un Servicio), el Agente ECS realizará una docker pull de la image que especifique en su definición de tarea. Si usa el mismo nombre de imagen (incluida la etiqueta) cada vez que ingresa a su registro, debería poder ejecutar la nueva imagen ejecutando una nueva tarea. Tenga en cuenta que si Docker no puede acceder al registro por algún motivo (por ejemplo, problemas de red o problemas de autenticación), el Agente ECS intentará usar una imagen en caché; si desea evitar que se usen imágenes almacenadas en caché cuando actualice su imagen, querrá insertar una etiqueta diferente en su registro cada vez y actualizar la definición de tarea correspondientemente antes de ejecutar la nueva tarea.

Actualización: Este comportamiento ahora se puede ajustar a través de la variable de entorno ECS_IMAGE_PULL_BEHAVIOR establecida en el agente ECS. Consulte la documentación para más detalles. En el momento de la escritura, se admiten las siguientes configuraciones:

El comportamiento utilizado para personalizar el proceso de extracción de imágenes para sus instancias de contenedor. A continuación se describen los comportamientos opcionales:

  • Si se especifica el default predeterminado, la imagen se extrae de forma remota. Si falla la extracción de la imagen, el contenedor usa la imagen en caché en la instancia.

  • Si always se especifica, la imagen siempre se extrae de forma remota. Si la extracción de la imagen falla, entonces la tarea falla. Esta opción garantiza que siempre se extraiga la última versión de la imagen. Las imágenes almacenadas en caché se ignoran y están sujetas al proceso automatizado de limpieza de imágenes.

  • Si se especifica once , la imagen se extrae de forma remota solo si no ha sido extraída por una tarea anterior en la misma instancia del contenedor o si la imagen en caché fue eliminada por el proceso automatizado de limpieza de imagen. De lo contrario, se utiliza la imagen en caché en la instancia. Esto asegura que no se intenten extracciones de imagen innecesarias.

  • Si se especifica prefer-cached , la imagen se extrae de forma remota si no hay imagen en caché. De lo contrario, se utiliza la imagen en caché en la instancia. La limpieza automática de imágenes está deshabilitada para el contenedor para garantizar que la imagen en caché no se elimine.


Creé un script para implementar imágenes Docker actualizadas en un servicio de preparación en ECS, de modo que la definición de tarea correspondiente se refiera a las versiones actuales de las imágenes Docker. No sé con certeza si estoy siguiendo las mejores prácticas, por lo que cualquier comentario será bienvenido.

Para que el script funcione, necesita una instancia de ECS de repuesto o un valor de deploymentConfiguration.minimumHealthyPercent configuración.minimumHealthyPercent para que ECS pueda robar una instancia para implementar la definición de tarea actualizada.

Mi algoritmo es así:

  1. Etiquete las imágenes de Docker correspondientes a los contenedores en la definición de la tarea con la revisión Git.
  2. Empuje las etiquetas de imagen de Docker a los registros correspondientes.
  3. Anule el registro de las definiciones de tareas antiguas en la familia de definiciones de tareas.
  4. Registre una nueva definición de tarea, ahora refiriéndose a las imágenes de Docker etiquetadas con las revisiones actuales de Git.
  5. Servicio de actualización para usar la nueva definición de tarea.

Mi código pegado a continuación:

deploy-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


El enfoque recomendado por AWS es registrar una nueva definición de tarea y actualizar el servicio para usar la nueva definición de tarea. La forma más fácil de hacer esto es:

  1. Navegue a las definiciones de tareas
  2. Seleccione la tarea correcta
  3. Elija crear nueva revisión
  4. Si ya está sacando la última versión de la imagen del contenedor con algo como la última etiqueta, simplemente haga clic en Crear. De lo contrario, actualice el número de versión de la imagen del contenedor y luego haga clic en Crear.
  5. Expandir acciones
  6. Elija el Servicio de actualización (dos veces)
  7. Luego espere a que se reinicie el servicio

Este tutorial tiene más detalles y describe cómo los pasos anteriores encajan en un proceso de desarrollo de producto de extremo a extremo.

Divulgación completa: este tutorial presenta contenedores de Bitnami y yo trabajo para Bitnami. Sin embargo, los pensamientos expresados ​​aquí son míos y no de la opinión de Bitnami.


Los siguientes comandos me funcionaron

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


Si su tarea se ejecuta bajo un servicio, puede forzar una nueva implementación. Esto obliga a volver a evaluar la definición de la tarea y extraer la nueva imagen del contenedor.

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


Usando AWS cli probé el servicio de actualización aws ecs como se sugirió anteriormente. No recogió la última ventana acoplable de ECR. Al final, volví a ejecutar mi libro de jugadas Ansible que creó el clúster ECS. La versión de la definición de tarea aparece cuando se ejecuta ecs_taskdefinition. Entonces todo está bien. Se recoge la nueva imagen del acoplador.

Verdaderamente no estoy seguro de si el cambio de versión de la tarea fuerza la nueva implementación, o si el libro de jugadas que usa ecs_service hace que la tarea se vuelva a cargar.

Si alguien está interesado, obtendré permiso para publicar una versión desinfectada de mi libro de jugadas.