🕌

【ecsh】ECS execでのコンテナログインを楽にするツールを作りました

2022/06/08に公開

ecsh

ECS Execを使用したコンテナへのログインを対話形式で実行するツールです。
すべて対話形式で進むのでコマンドを実行するだけで使えます。

https://github.com/kishii4726/ecsh

ecsh_v0 0 3

install

バイナリファイルを取得してパスの通っているディレクトリに配置すれば使えます。

Mac

$ ECSH_VERSION=0.0.3
$ curl -OL https://github.com/kishii4726/ecsh/releases/download/v${ECSH_VERSION}/ecsh_v${ECSH_VERSION}_darwin_amd64.zip

$ unzip ecsh_v${ECSH_VERSION}_darwin_amd64.zip ecsh

$ sudo cp ecsh /usr/local/bin

Linux

$ ECSH_VERSION=0.0.3
$ curl -OL https://github.com/kishii4726/ecsh/releases/download/v${ECSH_VERSION}/ecsh_v${ECSH_VERSION}_linux_amd64.zip

$ unzip ecsh_v${ECSH_VERSION}_linux_amd64.zip ecsh

$ sudo cp ecsh /usr/local/bin

作った理由

CLIでECS Execでコンテナにログインする際は以下のようにコマンドを実行しますが、実行するためにクラスター名タスクidコンテナ名コマンドを指定する必要があります。

execute-commandの例
aws ecs execute-command --cluster cluster-name \
    --task task-id \
    --container container-name \
    --interactive \
    --command "/bin/sh"

都度必要なパラメータを揃えて上記のコマンドを実行するのは中々辛いので以下のようなシェルスクリプトを作って使っていましたが、雑に作っていていつか直したいなと思っていたので、勉強も兼ねてGoで作り直しました。

ECS Execを実行するために使っていたシェルスクリプト
#!/bin/bash
AWS_PROFILE=$1
AWS_VAULT_COMMAND="aws-vault exec ${AWS_PROFILE} --"

function usage() {
  echo "[ERROR]: Please put the AWS profile name in the first argument."
  exit 1
}

if [ -z $AWS_PROFILE ]; then
  usage
  exit 1
fi

select ECS_CLUSTER in $(${AWS_VAULT_COMMAND} aws ecs list-clusters --query 'clusterArns[*]' | cut -f 2 -d "/" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,//g' -e 's/"//g' -e '/^$/d')
do
    echo -e "Selected ECS Cluster: ${ECS_CLUSTER}"
    break
done

select ECS_SERVICE in $(${AWS_VAULT_COMMAND} aws ecs list-services --cluster "${ECS_CLUSTER}" --query 'serviceArns[*]' | cut -f 3 -d "/" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,//g' -e 's/"//g' -e '/^$/d')
do
    echo -e "Selected ECS Service: ${ECS_SERVICE}"
    break
done

select ECS_TASK_ID in $(${AWS_VAULT_COMMAND} aws ecs list-tasks --cluster "${ECS_CLUSTER}" --service-name "${ECS_SERVICE}" --query 'taskArns[*]' | cut -f 3 -d "/" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,//g' -e 's/"//g' -e '/^$/d')
do
    echo -e "Selected Task: ${ECS_TASK_ID}"
    break
done

select ECS_CONTAINER_NAME in $(${AWS_VAULT_COMMAND} aws ecs describe-tasks --cluster "${ECS_CLUSTER}" --tasks "${ECS_TASK_ID}" --query 'tasks[*][containers][][].name' | sed -e 's/\[//g' -e 's/\]//g' -e 's/,//g' -e 's/"//g' -e '/^$/d')
do
    echo -e "Selected Container: ${ECS_CONTAINER_NAME}"
    break
done

select ECS_EXEC_SHELL in 'sh' 'bash'
do
    echo -e "Selected Shell: ${ECS_EXEC_SHELL}"
    break
done

echo -e 'Execute ECS exec'
${AWS_VAULT_COMMAND} aws ecs execute-command \
    --cluster "${ECS_CLUSTER}" \
    --task "${ECS_TASK_ID}" \
    --container "${ECS_CONTAINER_NAME}" \
    --interactive \
    --command "${ECS_EXEC_SHELL}"

実装について

文字入力・キーによる選択

入力・選択部分にはpromptuiを使用しました。promptuiに関しては以下の記事を参考にさせていただきました🙏

https://zenn.dev/ktechb/articles/golung-promptui

ExecuteCommandとStartSession

CLIのようにExecuteCommandで接続出来るのかと思ったのですが、実際にはExecuteCommandの返り値を使ってsession-manager-pluginStartSessionを実行する必要があり、少しハマりました。

	out, err := client.ExecuteCommand(context.TODO(), &ecs.ExecuteCommandInput{
		Command:     aws.String(ecs_shell),
		Interactive: true,
		Task:        aws.String(ecs_task_id),
		Cluster:     aws.String(ecs_cluster),
		Container:   aws.String(ecs_container),
	})
	sess, _ := json.Marshal(out.Session)
	target := fmt.Sprintf("ecs:%s_%s_%s", ecs_cluster, ecs_task_id, ecs_runtime_id)
	ssm_target := ssm.StartSessionInput{
		Target: &target,
	}
	targetJSON, err := json.Marshal(ssm_target)

	// StartSession実行時の引数にJSON化したExecuteCommandの返り値を与える
	cmd := exec.Command(
		"session-manager-plugin",
		string(sess),
		aws_region,
		"StartSession",
		"",
		string(targetJSON),
		"https://ssm."+aws_region+".amazonaws.com",
	)

やっていないこと

  • paginationの処理
    • クラスターやクラスター内のサービスが100以上という環境を扱うことが今のところないので対応してないです
  • リージョンを入力ではなく選択肢から選べるようにする
    • リージョンを切り替えてECS Execする機会がほぼないのでわざわざ選択方式にする必要もないなということで入力方式にしました
  • ECS Execの実行可否確認
  • 実行環境のsession-manager-pluginの有無確認
    • amazon-ecs-exec-checkerで確認できるので入れませんでしたがこれは入れても良かったかも?

Discussion