Actualice el contenedor de un servicio en Amazon ECS

32

¿Qué tipo de enfoque se recomienda para actualizar el contenedor de un servicio que se ejecuta en Amazon ECS?

La documentación de AWS dice: "Si ha actualizado la imagen de Docker de su aplicación, puede crear una nueva definición de tarea con esa imagen e implementarla en su servicio, una tarea a la vez". Esto es prácticamente todo lo que está actualmente disponible en la documentación actualmente (13 de abril de 2015).

¿Comprendí correctamente que la única forma de actualizar mi contenedor de aplicaciones en Amazon ECS es crear una nueva tarea, luego detener la tarea anterior y comenzar la nueva tarea?

He estado usando con éxito una etiqueta "más reciente" con Core OS y Fleetctl. Esto tiene el beneficio de no necesitar cambiar la etiqueta de la imagen de Docker para nuevas actualizaciones, ya que al recargar el servicio verá nuevos cambios y actualizará el contenedor (usando la misma etiqueta "más reciente").

¿Qué tipo de enfoques ha utilizado para actualizar su servicio con la imagen acoplada actualizada en Amazon ECS?

Repos Petrus
fuente
También estamos tratando de resolver esto, ya que esperamos usar ECS para implementar una variedad de demonios que necesitan ejecutarse continuamente en la producción.
parent5446
1
Solo para confirmar, ¿dijiste que reiniciar un servicio ecs eliminará la última versión de una imagen? He estado buscando documentación sobre esto y no puedo encontrarla en ningún lado.
mmilleruva
1
¿Alguna confirmación sobre este?
Lior Ohana
@LiorOhana Lamentablemente es verdad. Vea mi respuesta para más detalles.
hamx0r
Publiqué una nueva respuesta detallada a continuación, pero para aclarar aquí: su servicio siempre intentará extraer una copia nueva de su contenedor del repositorio, en función de la etiqueta que haya establecido. Si se elimina una tarea, cuando el servicio la despliega nuevamente, no recuerda lo que estaba en el repositorio, solo lo que está en el repositorio.
MrDuk

Respuestas:

18

No estoy seguro de si esto se considera una pregunta abandonada: tropecé con esto al solucionar mi problema y ahora agrego mi solución ahora que está resuelta.

Para actualizar el servicio con un nuevo contenedor, debe:

  1. cargar un nuevo contenedor al repositorio;
  2. desencadenar actualización de definición de tarea;
  3. desencadenar la actualización del contenedor;
  4. importante: asegúrese de que las reglas de servicio permitan iniciar una nueva versión de la tarea.

Si la tarea de servicio no se actualiza a la última versión, revise la pestaña "eventos" para ver si hay errores. Por ejemplo, quizás ECS no pudo iniciar una nueva versión de su servicio: solo tiene una instancia de ec2 en el clúster y el puerto de la aplicación ya está en uso en el host. En este caso, establezca los límites de "salud mínima / salud máxima" en "0%, 100%"; de esta forma, ECS elegirá matar el contenedor antiguo antes de implementar uno nuevo. Esto también sucede en unos pocos minutos: no se apresure si no ve comentarios inmediatos.

A continuación se muestra un script de implementación de ejemplo para actualizar el contenedor en un clúster y servicio preconfigurado. Tenga en cuenta que no hay necesidad de especificar versiones si solo quiere decir "usar lo último de la familia".

awsRegion=us-east-1
containerName=..
containerRepository=..
taskDefinitionFile=...
taskDefinitionName=...
serviceName=...


echo 'build docker image...'
docker build -t $containerName .

echo 'upload docker image...'
docker tag $containerName:latest $containerRepository:$containerName
docker push $containerRepository:$containerName

echo 'update task definition...'
aws ecs register-task-definition --cli-input-json file://$taskDefinitionFile --region $awsRegion > /dev/null

echo 'update our service with that last task..'
aws ecs update-service --service $serviceName --task-definition $taskDefinitionName --region $awsRegion  > /dev/null
uiron
fuente
2
Esto me obliga a tener una definición de tarea como un archivo local, y si lo entiendo correctamente, ese es el único lugar donde puedo definir variables de entorno. ¿Hay alguna manera de hacer esto sin tener las variables de entorno localmente? Idealmente, me gustaría emitir un comando para apuntar a una nueva etiqueta de imagen acoplable sin enviar ninguna otra información sobre la tarea / servicio / contenedor / etc.
rmac
1
Los comentarios set "min health/max health" limits to "0%, 100%"son dorados. Muchas gracias!
sivabudh
1
Palabra de precaución aquí, si configura el minque 0%, al cambiar la definición de tarea despliega sus servicios, básicamente estás dándole plena autoridad para derribar todas las tareas al mismo tiempo para que el despliegue.
MrDuk
1

Utilizo parte de la secuencia de comandos ecs-deploy con mis mejoras (toma imágenes de cada descripción de contenedor y reemplaza su parte de etiqueta con $ TAG_PURE): https://gist.github.com/Forever-Young/e939d9cc41bc7a105cdcf8cd7ab9d714

# based on ecs-deploy script
TASK_DEFINITION_NAME=$(aws ecs describe-services --services $SERVICE --cluster $CLUSTER | jq -r .services[0].taskDefinition)
TASK_DEFINITION=$(aws ecs describe-task-definition --task-def "$TASK_DEFINITION_NAME" | jq '.taskDefinition')
NEW_CONTAINER_DEFINITIONS=$(echo "$TASK_DEFINITION" | jq --arg NEW_TAG $TAG_PURE 'def replace_tag: if . | test("[a-zA-Z0-9.]+/[a-zA-Z0-9]+:[a-zA-Z0-9]+") then sub("(?<s>[a-zA-Z0-9.]+/[a-zA-Z0-9]+:)[a-zA-Z0-9]+"; "\(.s)" + $NEW_TAG) else . end ; .containerDefinitions | [.[] | .+{image: .image | replace_tag}]')
TASK_DEFINITION=$(echo "$TASK_DEFINITION" | jq ".+{containerDefinitions: $NEW_CONTAINER_DEFINITIONS}")
# Default JQ filter for new task definition
NEW_DEF_JQ_FILTER="family: .family, volumes: .volumes, containerDefinitions: .containerDefinitions"
# Some options in task definition should only be included in new definition if present in
# current definition. If found in current definition, append to JQ filter.
CONDITIONAL_OPTIONS=(networkMode taskRoleArn)
for i in "${CONDITIONAL_OPTIONS[@]}"; do
  re=".*${i}.*"
  if [[ "$TASK_DEFINITION" =~ $re ]]; then
    NEW_DEF_JQ_FILTER="${NEW_DEF_JQ_FILTER}, ${i}: .${i}"
  fi
done

# Build new DEF with jq filter
NEW_DEF=$(echo $TASK_DEFINITION | jq "{${NEW_DEF_JQ_FILTER}}")
NEW_TASKDEF=`aws ecs register-task-definition --cli-input-json "$NEW_DEF" | jq -r .taskDefinition.taskDefinitionArn`

echo "New task definition registered, $NEW_TASKDEF"

aws ecs update-service --cluster $CLUSTER --service $SERVICE --task-definition "$NEW_TASKDEF" > /dev/null

echo "Service updated"
Joven para siempre
fuente
Se recomienda proporcionar la información útil de los enlaces en su respuesta, para proporcionar la rotura de enlaces. ¿Podrías hacerlo, por favor?
BE77Y
1
Actualicé
1

Después de cargar una nueva imagen de Docker, incluso si tiene la misma etiqueta que la utilizada por una Tarea, se debe copiar la última tarea y luego configurar el Servicio para usar esa nueva Tarea. Opcionalmente, uno podría simplemente tener 2 tareas duplicadas y configurar el Servicio para intercambiarlas cada vez que se actualice la Imagen Docker.

Básicamente, para hacer que ECS haga un nuevo Contenedor Docker, una actualización del Servicio debe activarlo, y la única forma de activarlo es Actualizarlo de alguna manera, como diciéndole que use un Número de tarea diferente.

Tenga en cuenta que los Contenedores en ejecución existentes pueden no detenerse automáticamente solo porque el Servicio se actualizó; es posible que deba mirar su lista de Tareas y detenerlos manualmente.

hamx0r
fuente
Esto no es realmente cierto: siempre puede matar manualmente una tarea en lugar de confiar en su servicio para hacerlo. Cuando el servicio detecte que ha sido asesinado, intentará volver a mencionarlo, forzando un nuevo tirón del mismotag
MrDuk
1

El enfoque que funciona para mí es similar al anterior. Después de crear su servicio y tarea, y de comenzar todo, edite el Grupo de Auto-Scaling y asegúrese de que min , max y deseado estén configurados en 1 .

El grupo puede ser el predeterminado; si no está seguro, puede hacerlo seleccionando la pestaña Instancias ECS en su clúster, luego en el menú desplegable Acciones elija Recursos del clúster y haga clic en el enlace cerca de la parte inferior del cuadro de diálogo que se abre.

Cuando todo esté en su lugar, cada vez que desee implementar una imagen de contenedor actualizada, vaya al área Tarea del clúster y detenga la tarea . Recibirá una advertencia, pero siempre que la escala automática esté configurada, el servicio comenzará a funcionar nuevamente con el último impulso.

No es necesario crear nuevas versiones ni del servicio ni de la tarea.

Tenga en cuenta que el servicio / tarea se actualiza a sí mismo en cualquier lugar desde instantáneamente hasta dentro de un minuto aproximadamente. Si espera desesperadamente, puede simplemente ejecutar Nueva tarea manualmente. El servicio no será el propietario, por lo que no es ideal, pero seguirá generando uno nuevo si muere.

K Cartlidge
fuente
1

Sé que este es un hilo viejo, pero la solución es mucho más fácil de lo que la mayoría de las respuestas aquí hacen.

Cómo actualizar el contenedor en ejecución en dos pasos:

A continuación, se supone que tiene un servicio que ejecuta una tarea que hace referencia a un contenedor etiquetado latest(o cualquier otra etiqueta estática que no cambie entre las actualizaciones del contenedor).

  1. Sube tu nuevo contenedor al repositorio
  2. Mata manualmente tus tareas

Si el objetivo es que tengamos una nueva construcción en la naturaleza, realmente no necesitamos confiar en nuestro servicio para eso (y diría que no deberíamos confiar en él). Si elimina su tarea, el servicio reconocerá que no tiene las Desired Counttareas en ejecución y simplemente activará una nueva. Esto activará una nueva extracción de su contenedor, basado en la misma etiqueta.

Los servicios ECS son una red de seguridad HA, no un reemplazo para su canalización de CD / CI.


Bonificación: si el objetivo es que un servicio reconozca que se ha enviado un nuevo contenedor (independientemente de las etiquetas), debemos considerar las implicaciones de eso. ¿Realmente queremos un servicio básico que controle nuestro canal de implementación para nosotros? Probablemente no. Idealmente, empujará sus contenedores con diferentes etiquetas (basadas en versiones de lanzamiento o algo así). En este caso, la barrera para la implementación es que el servicio debe ser notificado de algo nuevo; nuevamente, es una red de seguridad para el servicio, y nada más.


Cómo implementar nuevas etiquetas en tres pasos:

  1. Sube tu nuevo container:tagal repositorio
  2. Cree una nueva definición de tarea haciendo referencia a la nueva tag
  3. Actualice su servicio para hacer referencia a la nueva definición de tarea
    • ¡Cuidado aquí! Si se ha minimum healthyconfigurado 0%como sugieren algunas otras respuestas, le está dando a AWS plena autoridad para eliminar todo su servicio a fin de implementar la nueva definición de tarea. Si prefiere una implementación gradual / gradual, establezca su mínimo en algo >0%.
    • Como alternativa, establecer su minimum healthya 100%y su maximum healthyalgo >100%para permitir su servicio para desplegar las nuevas tareas antes de matar a los viejos (minimizar el impacto en los usuarios).

A partir de este punto, su servicio reconocerá automáticamente que ha especificado una nueva tarea y trabajará en implementarla en función de los umbrales minimum/ maximumsaludables que haya configurado.

MrDuk
fuente
agradable, gracias, mejor que otras respuestas
Olegzandr Denman