使用 ECS Exec 直通 ECS 容器会话(适用于 Fargate 和 EC2)
基于 EC2 的 ECS 服务,要看看容器内的状态,一直以来都是先 SSM(Simple System Manager) 或 SSH 进到 EC2 实例,然后再
Amazon 在 2021-03-15 推出了一个新的特性
ECS Exec 的实现原理是以往在 EC2 实例上启动的 SSM Agent,也在容器内部启动一份,然后命令
由于 AWS Cli 是通过 Session Manager 来连接容器的,所以在客户端也必须安装 Session Manager 插件,参见 Install the Session Manager plugin for the AWS CLI.
接下来我们用 Terraform 来创建 Fargate 的 ECS 集群,并启动一个容器(任务),并由 ECS Exce 进入到它的交互界面。
ecs-fargate.tf
简单说明:
在本地准备好 AWS 的 provider, 我们运行 terraform 命令
有了 task/container ID 后就可以直接连接到该容器会话了
EC2 或 Fargate 在运行容器时会把 SSM Agent 放到 /managed-agents 目录中并执行. 如果任务包含多个容器时必须用参数
这实际上是使用了 Session Manager 连接容器的,回忆一下用 Session Manager 连接 EC2 实例的情形
无论是 EC2 还是 Fargate 启动的容器,我们都可以一步进到容器中,这对于 EC2 的容器确实是省了一个中间环节。不过一旦到了容器内部,反而无法查看当前容器的日志了,在容器外还能用
注意到上面执行
你可能会觉得
如果是非交互的命令执行完后直接退出
但
ECS Exec 的命令输出可记录到 CloudWatch 或 S3 Bucket 中,默认是会写入到定义任务时的
相应的 Task Role 要有相应的 CloudWatch 或 S3 写的权限。
如果是基于 EC2 的容器,还是倾向于先 SSM 登陆 EC2 实例,再用 docker 命令来观察和诊断容器。因为 EC2 比容器的生命周期要长,用
如果是直连容器的话,容器因某种原因快速消亡后便来不急建立会话,或是要频繁的建立会话, 很多时候最是需要了解容器是怎么死的。因此已死的容器依然需要通过 AWS 控制台或 CloudWatch 日志来确定问题
Fargate 的容器如果运行比较稳定,大可不必接入它的会话,基本上观察程序执行日志就行了
[2023-12-23]
如果连接不上出现类似如下错误信息的话
参考:
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
docker exec -it <container-id> sh, 查看容器的控制台日志则用 docker logs <container-id> [--follow]. 但是对使用 Farget 的 ECS 服务就无能为力了,因为找不到 SSM 或 SSH 的主体, 只能通过程序日志来大概了解容器内发生的事了。Amazon 在 2021-03-15 推出了一个新的特性
ECS Exec 允许我们直接连接 Fargate 或 EC2 中的容器会话,见 Amazon ECS now allows you to run commands in a container running on Amazon EC2 or AWS Fargate. ECS Exec 支持 Container Agent 版本为 1.50.2 及以上的 ECS Optimized AMI 系列,和 Fargate Platform Version 1.4.0(Linux) 或 1.0.0(Windows) 及以上。ECS Exec 的实现原理是以往在 EC2 实例上启动的 SSM Agent,也在容器内部启动一份,然后命令
aws ecs execute-command 直指容器本身。参考本人写过的一篇 AWS Session Manager 管理 EC2 实例,连接过程中唯一的不同就是容器中也运行了一个 SSM Agent, 所以这个容器也就无所谓是在 EC2 实例还是在 Fargate 中。![]() | =〉 | ![]() |
由于 AWS Cli 是通过 Session Manager 来连接容器的,所以在客户端也必须安装 Session Manager 插件,参见 Install the Session Manager plugin for the AWS CLI.
接下来我们用 Terraform 来创建 Fargate 的 ECS 集群,并启动一个容器(任务),并由 ECS Exce 进入到它的交互界面。
ecs-fargate.tf
1resource "aws_ecs_cluster" ecs-exec-demo-cluster {
2 name = "ecs-exec-demo-cluster"
3}
4
5resource "aws_ecs_service" demo-service {
6 name = "demo-service"
7 cluster = aws_ecs_cluster.ecs-exec-demo-cluster.name
8 task_definition = aws_ecs_task_definition.demo-task-definition.arn
9 launch_type = "FARGATE"
10 desired_count = 1 # 设置为 1 来启动一个 task
11 enable_execute_command = true # 这是必须的
12 network_configuration {
13 subnets = ["subnet-cf034d94"]
14 }
15}
16
17
18resource "aws_ecs_task_definition" "demo-task-definition" {
19 family = "demo-task-definition"
20 network_mode = "awsvpc"
21 requires_compatibilities = [
22 "FARGATE"
23 ]
24 cpu = 256
25 memory = 512
26
27# 该 role 是 EC2 或 Fargate 用来 pull 和 运行 Docker 容器的
28 execution_role_arn = aws_iam_role.task-execution-role.arn
29 task_role_arn = aws_iam_role.task-role.arn # 这是容器内部使用的 role
30
31 container_definitions = <<EOF
32[
33 {
34 "name": "demo-container",
35 "image": "busybox",
36 "essential": true,
37 "command": ["sh", "-c", "echo hello world, sleep...; sleep 3600"],
38 "linuxParameters": {
39 "initProcessEnabled": true
40 }
41 }
42]
43EOF
44}
45
46resource "aws_iam_role" task-execution-role {
47 name = "demo-task-execution-role"
48 assume_role_policy = local.ecs_assume_role_policy
49 managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
50}
51
52resource "aws_iam_role" "task-role" {
53 name = "demo-task-role"
54 assume_role_policy = local.ecs_assume_role_policy
55# 容器内使用的 task role 必须有以下的权限来启动容器内的 SSM Agent
56 inline_policy {
57 name = "SSM_agent_permissions"
58 policy = <<EOF
59{
60 "Version": "2012-10-17",
61 "Statement": [
62 {
63 "Effect": "Allow",
64 "Action": [
65 "ssmmessages:CreateControlChannel",
66 "ssmmessages:CreateDataChannel",
67 "ssmmessages:OpenControlChannel",
68 "ssmmessages:OpenDataChannel"
69 ],
70 "Resource": "*"
71 }
72 ]
73}
74EOF
75 }
76}
77
78locals {
79 ecs_assume_role_policy = <<EOF
80{
81 "Version": "2012-10-17",
82 "Statement": [
83 {
84 "Effect": "Allow",
85 "Principal": {
86 "Service": [
87 "ecs-tasks.amazonaws.com"
88 ]
89 },
90 "Action": "sts:AssumeRole"
91 }
92 ]
93}
94EOF
95}简单说明:
- task execution role 是 EC2 或 Fargate 用来 pull 和运行 docker 容器的,所以只需要 AWS 现成的 AmazonECSTaskExecutionRolePolicy
- task role 是容器内部使用的,它用来启动容器内的 SSM Agent 和运行应用程序,所以必须给予它应用程序所需的权限和启动 SSM Agent 的权限,如
ssmmessages:* - 创建 ECS service 时必须指定
enable_execute_command为 true, 否则无法直连容器 - 任务定义中推荐设置
initProcessEnabled: true,为false时容器也是可被连接的,但会造成容器退出时 ECS Exec 执行的命令进程仍在
在本地准备好 AWS 的 provider, 我们运行 terraform 命令
terraform init然后,我们找到 task ID 来,从 AWS Web 界面或用 aws cli 命令找都行
terraform apply -auto-approve
1➜ / aws ecs list-tasks --cluster ecs-exec-demo-cluster
2{
3 "taskArns": [
4 "arn:aws:ecs:us-east-1:913903414417:task/ecs-exec-demo-cluster/62d6110c212f4849962deaa761852dd8"
5 ]
6}
7➜ / aws ecs describe-tasks --cluster ecs-exec-demo-cluster --tasks 62d6110c212f4849962deaa761852dd8 | grep enableExecuteCommand
8 "enableExecuteCommand": true, 1➜ / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "sh" --interactive --task 62d6110c212f4849962deaa761852dd8
2
3The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
4
5
6Starting session with SessionId: ecs-execute-command-0574b7d8566ba9da7
7/ #
8/ # ps
9PID USER TIME COMMAND
10 1 root 0:00 sh -c echo hello world, sleep...; sleep 3600
11 7 root 0:00 /managed-agents/execute-command/amazon-ssm-agent
12 20 root 0:00 /managed-agents/execute-command/ssm-agent-worker
13 109 root 0:00 /managed-agents/execute-command/ssm-session-worker ecs-execute-command-0574b7d8566ba9da7
14 117 root 0:00 sh
15 118 root 0:00 ps
16/ # netstat -na|grep 443
17tcp 0 490 10.255.60.141:59228 209.54.181.251:443 ESTABLISHED
18tcp 0 0 10.255.60.141:39104 52.46.156.29:443 ESTABLISHED
19tcp 0 0 10.255.60.141:59216 209.54.181.251:443 ESTABLISHEDEC2 或 Fargate 在运行容器时会把 SSM Agent 放到 /managed-agents 目录中并执行. 如果任务包含多个容器时必须用参数
--container 指定容器名。这实际上是使用了 Session Manager 连接容器的,回忆一下用 Session Manager 连接 EC2 实例的情形
1➜ ~ aws ssm start-session --target i-08e7fb57079b1ac48
2
3Starting session with SessionId: yanbin@example.com-01ed9ebeaf557b145
4sh-4.2$ ps -ef |grep ssm-agent
5root 3460 1 0 Jan11 ? 00:00:39 /usr/bin/amazon-ssm-agent
6root 3868 3460 0 Jan11 ? 00:07:50 /usr/bin/ssm-agent-worker
7ssm-user 12192 12123 0 20:00 pts/0 00:00:00 grep ssm-agent无论是 EC2 还是 Fargate 启动的容器,我们都可以一步进到容器中,这对于 EC2 的容器确实是省了一个中间环节。不过一旦到了容器内部,反而无法查看当前容器的日志了,在容器外还能用
docker logs <containter-id>, 但在容器内就只能感叹身在此山中了。注意到上面执行
aws ecs execute-command 是指了 --interactive 即进到交互界面,不过目前也只能支持交互界面,去掉 --interactive 参数则报错1➜ / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "sh" --task 62d6110c212f4849962deaa761852dd8
2
3The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
4
5
6Parameter validation failed:
7Missing required parameter in input: "interactive"你可能会觉得
sh 是交互界面,其实换成非交互的程序也不行,比如试图执行 echo 1231➜ / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "echo 123" --task 62d6110c212f4849962deaa761852dd8
2
3The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
4
5
6Parameter validation failed:
7Missing required parameter in input: "interactive"如果是非交互的命令执行完后直接退出
1➜ / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "echo 123" --interactive --task 2f1cb44b02f8454cbaf5ec317c6b16ec
2
3The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
4
5
6Starting session with SessionId: ecs-execute-command-05b5f8e4f4d871bd6
7123
8
9
10Exiting session with sessionId: ecs-execute-command-05b5f8e4f4d871bd6.但
--interactive 参数少不得ECS Exec 的命令输出可记录到 CloudWatch 或 S3 Bucket 中,默认是会写入到定义任务时的
awslogs 指示的 LogGroup/LogStream, 也可在定义 ECS cluster 时指定 ECS Exec 的命令输出目的地,如我们在创建 ECS Cluster 时的 Terraform 要变成这样 1resource "aws_ecs_cluster" ecs-exec-demo-cluster {
2 name = "ecs-exec-demo-cluster"
3 configuration {
4 execute_command_configuration {
5 log_configuration {
6 cloud_watch_log_group_name = "ecs-exec-log"
7 s3_bucket_name = "ecs-exec-log-bucket"
8 s3_key_prefix = "demo"
9 }
10 }
11 }
12}相应的 Task Role 要有相应的 CloudWatch 或 S3 写的权限。
最后感受
先前一直还苦于无法连接 Fargate 或 Fargate 中的容器,如今试用一番之后反而有点鸡肋如果是基于 EC2 的容器,还是倾向于先 SSM 登陆 EC2 实例,再用 docker 命令来观察和诊断容器。因为 EC2 比容器的生命周期要长,用
docker logs <container-id> 对存活的或已死的容器都能查看它们的日志。如果是直连容器的话,容器因某种原因快速消亡后便来不急建立会话,或是要频繁的建立会话, 很多时候最是需要了解容器是怎么死的。因此已死的容器依然需要通过 AWS 控制台或 CloudWatch 日志来确定问题
Fargate 的容器如果运行比较稳定,大可不必接入它的会话,基本上观察程序执行日志就行了
[2023-12-23]
如果连接不上出现类似如下错误信息的话
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.最好的检查办法就是运行 check-ecs-exec.sh 命令,它能帮我们检测 Task 为什么不能被连接的具体原因,从而进行快速修复。
An error occurred (TargetNotConnectedException) when calling the ExecuteCommand operation: The execute command failed due to an internal error. Try again later.
参考:
- NEW - Using Amazon ECS Exec to access your containers on AWS Fargate and Amazon EC2
- Using Amazon ECS Exec for debugging
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
