あしたのチーム Tech Blog

はたらくすべての人に「ワクワク」を届けるべく、日々奮闘するエンジニアの日常をお伝えします。

サッと ECS Exec でシェルを起動したいときに使えるスクリプト

Photo by Kari Shea on Unsplash

engineer.ashita-team.com

(この記事はあしたのクラウド のインフラ移行に関する連載記事です)

こんにちは。最近、副業を始めたこともあり ( 弊社は副業OKです ) 何かと忙しくなりブログ更新が滞ってましたが、改めてアウトプットを継続していく気持ちの @snaka です。

ECS 上で動作しているアプリケーションの調査を行う際に ECS Exec は何かと便利ですよね。このブログの別の記事でも紹介しました。

engineer.ashita-team.com

今回はその ECS Exec をより便利に使うためのスクリプトをご紹介します。

🙏 Acknowledgements

この記事で紹介するスクリプトの原形は (私ではなく) 社内メンバーが作ってくれました。 初版から環境の変更等への対応を経て現在の状態になりました。

この場を借りて感謝を捧げます 🙏

😥 ECS Exec に渡すオプションを調べるのがめんどくさい

ECS Exec 便利ですが、ただ... ちょっと指定するオプションの数が多くて面倒くさくないですか?

たとえば、オプションとして --cluster, --task, --container を指定する必要があります。

ただ、 --task で指定する Task ID は起動する毎に採番されるのでその都度 Task ID を調べる必要があります。そうして調べた Task ID を使って以下のように AWS CLI でコマンドを実行します。

$ aws ecs execute-command --cluster hoge --task abcdefg0123456789 --container app --interactive --command '/bin/bash'

この「現在稼働している Task の ID を取得する」という作業もひっくるめて面倒を見てくれるスクリプトを用意しました。

⚙️ ecs exec + shell 起動 = ecsh

スクリプトの名前は ecsh としました。 ecs execshell を足して ecsh です。

前提条件

スクリプトの動作的には AWS CLI を叩いているだけなので、AWS CLI がインストールされていてクレデンシャルなどが設定されていることが必要です。

スクリプト

以下にスクリプトを掲載します。

#!/usr/bin/env sh

check_cmd() {
  which session-manager-plugin 1>/dev/null 2>&1
  if [ $? -ne 0 ]; then
    echo "Not found Session Manager Plugin"
    echo "Install please"
    exit 1
  fi

  which aws 1>/dev/null 2>&1
  if [ $? -ne 0 ]; then
    echo "Not found aws-cli"
    echo "Install please"
    exit 1
  fi
}

usage() {
  cmd=`basename "$0"`
  echo "Usage: ${cmd} (OPTION cluster|profile) [OPTION service|container|region] ..."
  echo "-C, --cluster:   Part of the cluster name (REQUIRED)"
  echo "-s, --service:   Service name (default 'app')"
  echo "-c, --container: Container name (default 'app')"
  echo "-p, --profile:   AWS profile name (use environment variable 'AWS_PROFILE' as the default value)"
  echo "-r, --region:    AWS region name (default 'ap-northeast-1')"
}

check_cmd

service='app'
container='app'
region='ap-northeast-1'
profile=$AWS_PROFILE

while [ "$#" -gt 0 ]; do
  option=$1
  shift
  arg=$1
  shift

  case $option in
    -C | --cluster)
      cluster=$arg
      ;;
    -s | --service)
      service=$arg
      ;;
    -c | --container)
      container=$arg
      ;;
    -p | --profile)
      profile=$arg
      ;;
    -r | --region)
      region=$arg
      ;;
    -h | --help)
      usage
      exit 0
      ;;
    *)
      usage
      exit 1
      ;;
  esac
done

if [ -z $cluster ] || [ -z $profile ]; then
  usage
  exit 1
fi
profile_region="--profile ${profile} --region ${region}"

# cluster
cluster_arn=`aws ecs list-clusters --output text ${profile_region} | grep ${cluster} | ruby -ne 'puts $_.split[-1]'`
if [ $? -ne 0 ]; then
  exit 1
fi

cluster_name=`echo $cluster_arn | ruby -ne 'puts $_.split("/")[-1]'`
if [ -z $cluster_name ]; then
  echo "Cannot find a cluster name that containes '${cluster}' as part of it."
  exit 1
fi
echo "cluster_name: $cluster_name"

# task
task=`aws ecs list-tasks --cluster ${cluster_name} --service-name ${service} --output text ${profile_region} | ruby -ne 'puts $_.split[-1]'`
if [ $? -ne 0 ]; then
  exit 1
fi

task_id=`echo $task | ruby -ne 'puts $_.split("/")[-1]'`
if [ -z $task_id ]; then
  echo "Cannot find the runnnig tasks in the service '${service}'."
  exit 1
fi
echo "service: ${service}"
echo "task_id: ${task_id}"

aws ecs execute-command --cluster ${cluster_name} --task $task_id --container ${container} --interactive --command '/bin/bash' ${profile_region}

🕵️‍♂️ 使い方

Usage の内容を見れば、だいたい使い方は想像がつくと思います。

Usage: ecsh (OPTION cluster|profile) [OPTION service|container|region] ...
-C, --cluster:   Part of the cluster name (REQUIRED)
-s, --service:   Service name (default 'app')
-c, --container: Container name (default 'app')
-p, --profile:   AWS profile name (use environment variable 'AWS_PROFILE' as the default value)
-r, --region:    AWS region name (default 'ap-northeast-1')

クラスタ名 (--cluster) は必須*1です。指定された文字列をクラスタ名の一覧から部分一致で検索し、該当するクラスタ名の中から最後尾のクラスタに接続します。

使用例1

hoge-cluster クラスタで稼働している app サービスの app コンテナに接続するする例です。

ecsh --cluster hoge-cluster

サービス名およびコンテナ名のデフォルトは app なので、そのように配置されているコンテナには --cluster の指定だけで接続できます。

使用例2

hoge-cluster クラスタで稼働している batch サービスの sidekiq コンテナに接続する例です。

ecsh --cluster hoge-cluster --service batch --container sidekiq

サービス名およびコンテナ名が app 以外の場合は、オプションによる指定が可能です。

👋 さいごに

エンジニアが開発や運用を行う上で「ちょっとめんどくさい」手順ってよくあることです。

そして、そういう「ちょっとめんどくさい」はつい放置されがちで、そのうち「めんどくさい」自分自身が慣れてしまってそれが当たり前になってしまうことがあると思います。

そういった「めんどくさい」作業 ( トイル*2 ) を潰していくことは地味ですが SRE として大切な活動だなぁ、と改めて感じました。

ゆくゆくは SRE 活動の中から生まれた、このように運用をちょっとだけ楽にするツールを OSS で公開できたらなー、と妄想しております。

あしたのチームではプロダクトの成長と品質を支える SRE を募集中です。 一緒にトイルの撲滅に協力したい方いらっしゃったら 求人一覧 のページよりご応募ください 🙋‍♂️

herp.careers

*1:Usage で 'OPTION' と言いつつ '必須' というのは変なので、そのうち直します...

*2:SRE の原則に沿ったトイルの洗い出しとトラッキング | Google Cloud Blog