3

使用 ECS Exec 直通 ECS 容器会话(适用于 Fargate 和 EC2)

 1 year ago
source link: https://yanbin.blog/ecs-exec-connect-ecs-container-on-fargate-and-ec2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

使用 ECS Exec 直通 ECS 容器会话(适用于 Fargate 和 EC2)

2023-01-24 | 阅读(6)

基于 EC2 的 ECS 服务,要看看容器内的状态,一直以来都是先 SSM(Simple System Manager) 或 SSH 进到 EC2 实例,然后再 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 中。

session-manager-4.png =〉 ecs-exec-1.jpg

由于 AWS Cli 是通过 Session Manager 来连接容器的,所以在客户端也必须安装 Session Manager 插件,参见 Install the Session Manager plugin for the AWS CLI.

接下来我们用 Terraform 来创建 Fargate 的 ECS 集群,并启动一个容器(任务),并由 ECS Exce 进入到它的交互界面。

ecs-fargate.tf

resource "aws_ecs_cluster" ecs-exec-demo-cluster {
  name = "ecs-exec-demo-cluster"
resource "aws_ecs_service" demo-service {
  name = "demo-service"
  cluster = aws_ecs_cluster.ecs-exec-demo-cluster.name
  task_definition = aws_ecs_task_definition.demo-task-definition.arn
  launch_type = "FARGATE"
  desired_count = 1   # 设置为 1 来启动一个 task
  enable_execute_command = true   # 这是必须的
  network_configuration {
    subnets = ["subnet-cf034d94"]
resource "aws_ecs_task_definition" "demo-task-definition" {
  family = "demo-task-definition"
  network_mode = "awsvpc"
  requires_compatibilities = [
    "FARGATE"
  cpu = 256
  memory = 512
#  该 role 是 EC2 或 Fargate 用来 pull 和 运行 Docker 容器的
  execution_role_arn = aws_iam_role.task-execution-role.arn
  task_role_arn = aws_iam_role.task-role.arn # 这是容器内部使用的 role
  container_definitions = <<EOF
    "name": "demo-container",
    "image": "busybox",
    "essential": true,
    "command": ["sh", "-c", "echo hello world, sleep...; sleep 3600"],
    "linuxParameters": {
      "initProcessEnabled": true
resource "aws_iam_role" task-execution-role {
  name = "demo-task-execution-role"
  assume_role_policy = local.ecs_assume_role_policy
  managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
resource "aws_iam_role" "task-role" {
  name = "demo-task-role"
  assume_role_policy = local.ecs_assume_role_policy
#  容器内使用的 task role 必须有以下的权限来启动容器内的 SSM Agent
  inline_policy {
    name = "SSM_agent_permissions"
    policy = <<EOF
   "Version": "2012-10-17",
   "Statement": [
       "Effect": "Allow",
       "Action": [
            "ssmmessages:CreateControlChannel",
            "ssmmessages:CreateDataChannel",
            "ssmmessages:OpenControlChannel",
            "ssmmessages:OpenDataChannel"
      "Resource": "*"
locals {
  ecs_assume_role_policy = <<EOF
  "Version": "2012-10-17",
  "Statement": [
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "ecs-tasks.amazonaws.com"
      "Action": "sts:AssumeRole"

简单说明:

  1. task execution role 是 EC2 或 Fargate 用来 pull 和运行 docker 容器的,所以只需要 AWS 现成的 AmazonECSTaskExecutionRolePolicy
  2. task role 是容器内部使用的,它用来启动容器内的 SSM Agent 和运行应用程序,所以必须给予它应用程序所需的权限和启动 SSM Agent 的权限,如 ssmmessages:*
  3. 创建 ECS service 时必须指定 enable_execute_command 为 true, 否则无法直连容器
  4. 任务定义中推荐设置 initProcessEnabled: true,为 false 时容器也是可被连接的,但会造成容器退出时 ECS Exec 执行的命令进程仍在 

在本地准备好 AWS 的 provider, 我们运行 terraform 命令

terraform init
terraform apply -auto-approve

然后,我们找到 task ID 来,从 AWS Web 界面或用 aws cli 命令找都行

➜  / aws ecs list-tasks --cluster ecs-exec-demo-cluster
    "taskArns": [
        "arn:aws:ecs:us-east-1:913903414417:task/ecs-exec-demo-cluster/62d6110c212f4849962deaa761852dd8"
➜  / aws ecs describe-tasks --cluster ecs-exec-demo-cluster --tasks 62d6110c212f4849962deaa761852dd8 | grep enableExecuteCommand
            "enableExecuteCommand": true,

有了 task/container ID 后就可以直接连接到该容器会话了

➜  / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "sh" --interactive --task 62d6110c212f4849962deaa761852dd8
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Starting session with SessionId: ecs-execute-command-0574b7d8566ba9da7
PID   USER     TIME  COMMAND
    1 root      0:00 sh -c echo hello world, sleep...; sleep 3600
    7 root      0:00 /managed-agents/execute-command/amazon-ssm-agent
   20 root      0:00 /managed-agents/execute-command/ssm-agent-worker
  109 root      0:00 /managed-agents/execute-command/ssm-session-worker ecs-execute-command-0574b7d8566ba9da7
  117 root      0:00 sh
  118 root      0:00 ps
/ # netstat -na|grep 443
tcp        0    490 10.255.60.141:59228     209.54.181.251:443      ESTABLISHED
tcp        0      0 10.255.60.141:39104     52.46.156.29:443        ESTABLISHED
tcp        0      0 10.255.60.141:59216     209.54.181.251:443      ESTABLISHED

EC2 或 Fargate 在运行容器时会把 SSM Agent 放到 /managed-agents 目录中并执行. 如果任务包含多个容器时必须用参数 --container 指定容器名。

这实际上是使用了 Session Manager 连接容器的,回忆一下用 Session Manager 连接 EC2 实例的情形

➜  ~ aws ssm start-session --target i-08e7fb57079b1ac48
Starting session with SessionId: [email protected]
sh-4.2$ ps -ef |grep ssm-agent
root      3460     1  0 Jan11 ?        00:00:39 /usr/bin/amazon-ssm-agent
root      3868  3460  0 Jan11 ?        00:07:50 /usr/bin/ssm-agent-worker
ssm-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  参数则报错

➜  / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "sh" --task 62d6110c212f4849962deaa761852dd8
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Parameter validation failed:
Missing required parameter in input: "interactive"

你可能会觉得 sh 是交互界面,其实换成非交互的程序也不行,比如试图执行 echo 123

➜  / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "echo 123" --task 62d6110c212f4849962deaa761852dd8
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Parameter validation failed:
Missing required parameter in input: "interactive"

如果是非交互的命令执行完后直接退出

➜  / aws ecs execute-command --cluster ecs-exec-demo-cluster --command "echo 123" --interactive --task 2f1cb44b02f8454cbaf5ec317c6b16ec
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Starting session with SessionId: ecs-execute-command-05b5f8e4f4d871bd6
Exiting session with sessionId: ecs-execute-command-05b5f8e4f4d871bd6.

--interactive 参数少不得

ECS Exec 的命令输出可记录到 CloudWatch 或 S3 Bucket 中,默认是会写入到定义任务时的 awslogs 指示的 LogGroup/LogStream, 也可在定义 ECS cluster 时指定 ECS Exec 的命令输出目的地,如我们在创建 ECS Cluster 时的 Terraform 要变成这样

resource "aws_ecs_cluster" ecs-exec-demo-cluster {
  name = "ecs-exec-demo-cluster"
  configuration {
    execute_command_configuration {
      log_configuration {
        cloud_watch_log_group_name = "ecs-exec-log"
        s3_bucket_name = "ecs-exec-log-bucket"
        s3_key_prefix = "demo"

相应的 Task Role 要有相应的 CloudWatch  或 S3 写的权限。

先前一直还苦于无法连接 Fargate 或 Fargate 中的容器,如今试用一番之后反而有点鸡肋

如果是基于 EC2 的容器,还是倾向于先 SSM 登陆 EC2 实例,再用 docker 命令来观察和诊断容器。因为 EC2 比容器的生命周期要长,用 docker logs <container-id> 对存活的或已死的容器都能查看它们的日志。

如果是直连容器的话,容器因某种原因快速消亡后便来不急建立会话,或是要频繁的建立会话; 且已死的容器依然需要通过 AWS 控制台或 CloudWatch 日志来确定问题

Fargate 的容器如果运行比较稳定,大可不必接入它的会话,基本上观察程序执行日志就行了

Categories: AWS

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK