在Amazon ECS中更新服务的容器


32

建议使用哪种方法来更新在Amazon ECS中运行的服务的容器?

AWS文档说:“如果你已经更新了应用程序的泊坞图像,你可以创建一个新的任务定义与图像,并将其部署到您的服务,一次一个任务。” 当前(2015年4月13日)文档中目前几乎包含所有内容。

我是否正确理解,在Amazon ECS中更新我的应用程序容器的唯一方法是创建一个新任务,然后停止旧任务并启动新任务?

我已经在Core OS和Fleetctl中成功使用了“最新”标签。这样做的好处是不需要为新的更新而更改Docker映像的标签,因为重新加载服务将看到新的更改并更新容器(使用相同的标签“最新”)。

您使用哪种方法通过Amazon ECS中的更新的docker镜像来更新服务?


另外,我们也试图弄清楚这一点,因为我们希望使用ECS来部署需要在生产中连续运行的各种后台驻留程序。
parent5446

1
只是为了确认,您说过重启ecs服务会拉低映像的最新版本?我一直在寻找有关此文档,但找不到任何地方。
mmilleruva

1
对此有任何确认吗?
Lior Ohana

@LiorOhana不幸的是,这是事实。请参阅我的答案以获取详细信息。
hamx0r

我在下面发布了一个新的详细答案,但请在此处进行说明:您的服务将始终尝试根据您设置的标签从存储库中提取容器的新副本。如果一个任务被杀害,当服务重新部署它,它没有什么回忆在回购协议中,只有什么在回购协议。
MrDuk '18

Answers:


18

不确定是否将其视为已遗弃的问题-在解决我的问题并在解决后立即添加我的解决方案时偶然发现了这个问题。

要使用新容器更新服务,您需要:

  1. 将新容器上传到存储库;
  2. 触发任务定义更新;
  3. 触发容器更新;
  4. 重要:确保服务规则允许启动新版本的任务。

如果服务任务未更新到最新版本,请检查“事件”选项卡是否有错误。例如,ECS可能无法启动您的服务的新版本:群集中只有一个ec2实例,并且主机上已使用了应用程序端口。在这种情况下,请将“最小运行状况/最大运行状况”限制设置为“ 0%,100%”-这样,ECS将选择在部署新容器之前杀死旧容器。这也在几分钟内发生-如果您看不到即时反馈,请不要着急。

下面是一个示例部署脚本,用于更新预配置群集和服务中的容器。请注意,如果您只是想“使用家族中的最新版本”,则无需指定版本。

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

2
这迫使我将任务定义保存为本地文件,如果我理解正确的话,那是我唯一可以定义环境变量的地方。有没有本地没有环境变量的方法?理想情况下,我想发出命令以指向新的docker image标签,而不发送有关任务/服务/容器/等的任何其他信息。
rmac '16

1
的评论set "min health/max health" limits to "0%, 100%"是金色的。非常感谢!
sivabudh's

1
提醒您,如果将设置min0%,则当您更改要部署的服务的任务定义时,实际上是赋予它完全权限同时撤消该部署的所有任务。
MrDuk


1

我使用了经过改进的ecs-deploy脚本的一部分(它从每个容器描述中获取图像,并用$ 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"

建议从答案中的链接中提供有用的信息,以提供链接腐烂。你能这样做吗?
BE77Y

1
更新了我的答案
ForeverYoung,2016年

1

上载新的Docker映像后,即使它具有与Task使用的相同的标签,也必须复制最新任务,然后将Service配置为使用该新Task。可选地,一个人可以简单地拥有2个重复的任务,并配置服务以在每次Docker镜像更新时在它们之间交换。

基本上,为了使ECS创建新的Docker容器,必须对服务进行更新才能触发它,而使服务触发的唯一方法就是以某种方式对其进行更新-就像告诉它使用一个不同的任务号。

请注意,仅由于服务已更新,现有的正在运行的容器可能不会自动停止-您可能需要查看“任务”列表并手动停止它们。


这实际上不是事实-您始终可以手动终止任务,而不是依靠服务来执行任务。当服务部门检测到它被杀死时,它将尝试再次将其提出来,并强制将其再次拉出tag
-MrDuk

1

适用于我的方法与上述类似。创建服务和任务并开始一切操作之后,请编辑Auto-Scaling组,并确保将minmax期望值设置为1

该组可能是默认组;如果不确定,则可以通过选择集群中的“ ECS实例”选项卡来访问它,然后从“ 操作”下拉列表中选择“ 集群资源”,然后单击打开的对话框底部附近的链接。

一切就绪后,只要您想部署更新的容器映像,请转到集群的Task区域并停止 Task。您会收到警告,但是只要设置了自动缩放功能,服务就会以最新的推送再次开始运行。

无需创建服务或任务的新版本。

请注意,服务/任务会随时随地更新到一分钟左右。如果您迫切需要等待,则可以手动运行“新任务”。该服务不会拥有它,因此它不是理想的,但是如果它死了,它将仍然启动一个新的服务。


1

我知道这是一个旧线程,但是解决方案比这里给出的大多数答案要容易得多。

如何分两个步骤更新运行中的容器:

下面假设您有一个服务正在运行一个任务,该任务正在引用已标记容器latest(或在容器更新期间不会更改的任何其他静态标记)的容器。

  1. 将您的新容器上传到存储库
  2. 手动终止任务

如果目标是让我们的产品重获新生,那么我们实际上就不需要依靠我们的服务了(我认为,我们不应该依靠它)。如果您终止了任务,该服务将识别出该Desired Count任务没有正在运行的任务,只需启动一个新任务即可。这将基于相同标签触发容器的重新拉动。

ECS服务HA防盗网,不会为您的CD / CI管线的替代品。


奖励:如果目标是让服务识别一个新容器已被推送(无论标签如何),我们需要考虑其含义。我们是否真的需要基本服务来为我们控制部署管道?可能不会。理想情况下,您将使用不同的标签(基于发行版本等)来推送容器。在这种情况下,部署的障碍是必须通知服务新的东西-再次,这是该服务的安全网,仅此而已。


如何通过三个步骤部署新标签:

  1. 将您的新文件上传container:tag到存储库
  2. 创建引用新任务定义的新任务 tag
  3. 更新您的服务以引用新的任务定义
    • 小心点!如果您已minimum healthy设置为0%一些其他的答案表明,你给AWS全权杀死你的整个服务以部署新的任务定义。如果您希望采用滚动/渐进式部署,请将最小值设置为>0%
    • 另外,设置你minimum healthy100%和你maximum healthy的东西>100%,让你的服务部署杀死旧的(最小化用户影响)之前的任务。

从这一点开始,您的服务将自动识别您已指定了新任务,并根据您配置的minimum/ maximum正常阈值进行部署。


很好,谢谢,比其他答案要好
Olegzandr Denman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.