電通総研 テックブログ

電通総研が運営する技術ブログ

AWS FargateでECS Exec、session managerモロモロのTIPS

はじめに

ISID X(クロス)イノベーション本部 の三浦です。

筆者の関わってる案件では、コンテナ利用、AWS Fargate利用を進めております。 AWS Fargateのお手軽さは非常に重宝しております。

しかし、そこで問題になってくるのが、管理接続、踏み台系をどうするかです。 アプリケーション本体をせっかくAWS Fargateでやっているので、管理系接続、踏み台的な作業もできるだけ非EC2でやりたいですよね?

ということで、AWS FargateでECS Exec、session managerをどのように運用に使っていけるか紹介いたします。

目次

想定作業、目標

下記のような想定作業、目標となります。

想定作業:

  • RDSに接続し管理作業(pgdump、pgrestore、レコードの状態確認)
  • ECS Fagateが使ってるEFSのファイル配置、ファイル取得
  • トラブル時のECSの状態把握(設定ファイル、環境変数等)

目標:

  • 管理用のEC2を建てたくない
    • 筆者が関係しているアカウントは200アカウント以上あり、各アカウントに踏み台を立てるのも、集中管理踏み台でVPCピアリングを考えるのも非常にどちらも避けたい感覚です(というか今苦しんでます)
  • 権限管理をAWSのロールに寄せたい
    • ec2環境では作業時にwinパスワード、sshキーを払い出すような仕組みを実装しているのですが(チケット管理+ssm)、トラブルが一定頻度で発生しています。また、利用者としても踏み台への接続情報(接続経路、IP)、認証情報を意識するのは煩雑です。
  • 監査ログをAWSに寄せたい
  • ECS Execでメンテナンスコマンドの手打ちを辞めたい
  • ローカルから、ポートフォワード等でデバッグ用のセッションを張りたい

基本のECS Exec実行

基本のECS Exec実行です。 工夫としてはタスクIDの取得が煩雑なため、環境情報の取得を自動取得にしているだけです。本取得条件は、1クラスターに1ecsサービスとなっているので、ECSクラスター、サービスの状況に応じ、適宜クエリ条件は編集してください。

cl=$(aws ecs list-clusters | jq -r '.clusterArns[0]' )
prefix=`echo ${cl} | sed -E 's/.+cluster\///g' `
taskarn=$(aws ecs list-tasks --cluster ${cl} | jq -r '.taskArns[0]')
taskid=`echo ${taskarn} | sed -E 's/.+task\/.+\///g' `
CONTAINER_NAME="${prefix}-manager-container"

echo ${cl};     \
echo ${prefix} ; \
echo ${taskarn}; \
echo ${taskid};  \
echo ${CONTAINER_NAME};  \

aws ecs execute-command  \
 --region    ap-northeast-1 \
 --cluster   ${cl} \
 --task      ${taskarn} \
 --container ${CONTAINER_NAME}\
 --command "/bin/sh" \
 --interactive

あと、ECS Execは、cloud shellから実行すれば、アクセスキー、シークレットキーいらずで、 AWS consoleアクセス、フェデレーション管理に寄せることができます。

aws ssm start-session

参考記事: https://winebarrel.hatenablog.com/entry/2021/10/23/165720

次に、対話式シェル以外でもいろいろできないか探っていきます。 上記ブログで下のようなコメントがあり、私は下のところで止まっておりました。

ECS ExecがどうもマネージドなSSM Agentを使って動いているということはわかっていたが、実際どうなっているのかよくわからなかった。

・・・・

aws ssm start-sessionを実行できそうな雰囲気があるが、--targetに何を渡せばいいのかよくわからなかった。

が、続けて読んでいくと、ソースコードを読めばtargetの書式が書かれてるとのこと。

https://github.com/aws/aws-cli/blob/c0edee0a7427b6e7b654df0696015e96105497a3/awscli/customizations/ecs/executecommand.py#L61-L73

def build_ssm_request_paramaters(response, client):
    cluster_name = response['clusterArn'].split('/')[-1]
    task_id = response['taskArn'].split('/')[-1]
    container_name = response['containerName']
    # in order to get container run-time id
    # we need to make a call to describe-tasks
    container_runtime_id = \
        get_container_runtime_id(client, container_name,
                                 task_id, cluster_name)
    target = "ecs:{}_{}_{}".format(cluster_name, task_id,
                                   container_runtime_id)
    ssm_request_params = {"Target": target}
    return ssm_request_params

ソースコードを読めば確かに早かったですね。。。 ということで、aws ssm start-sessionの各ドキュメントを試します。

まずは基本のsession接続。

cl=$(aws ecs list-clusters | jq -r '.clusterArns[0]' )
prefix=`echo ${cl} | sed -E 's/.+cluster\///g' `
taskarn=$(aws ecs list-tasks --cluster ${cl} | jq -r '.taskArns[]')
taskid=`echo ${taskarn} | sed -E 's/.+task\/.+\///g' `
CONTAINER_NAME="${prefix}-manager-container"
CONTAINER_ID=$(aws ecs describe-tasks --cluster $cl --task $taskid | jq -r --arg CONTAINER_NAME $CONTAINER_NAME '.tasks[0].containers[] | select(.name == $CONTAINER_NAME).runtimeId')

echo ${cl};     \
echo ${prefix} ; \
echo ${taskarn}; \
echo ${taskid};  \
echo ${CONTAINER_NAME};  
echo ${CONTAINER_ID};  

aws ssm start-session --target ecs:${prefix}_${taskid}_${CONTAINER_ID}

パラメータとしてCONTAINER_IDの取得が増えておりますが、つながりました。まあ、これだけだとecs execと同じためなんのありがたさもありませんが。

なお、AWS Fargateでのstart-session利用はAWS CLIソースコードを読んで得た知見であり、AWSがサポートしている使い方ではありません。

start-session with AWS-StartPortForwardingSession

では、次に、AWS-StartPortForwardingSessionを試していきましょう。 もしできれば、接続トラブル時の切り分け作業、インターネットに公開するのに抵抗のある管理ポートへのアクセス等、いろいろ使い道があります。

#前述のおまじない

aws ssm start-session --target ecs:${prefix}_${taskid}_${CONTAINER_ID} \
--document-name AWS-StartPortForwardingSession --parameters '{"portNumber":["80"],"localPortNumber":["18080"]}'

ドキュメントとパラメータを追加し実行です。

ということで、つながりました。まあ、cloudshellからだと使い道がありませんが(バックグランド実行のしかたがわかりませんでした)、ローカルマシンから利用するには便利そうです。

start-session with AWS-StartPortForwardingSessionToRemoteHost

では、次に、AWS-StartPortForwardingSessionToRemoteHostを試します。 「AWS-StartPortForwardingSessionToRemoteHost」って何?って人は、クラスメソッド様のAWS System Managerセッションマネージャーがリモートホストのポートフォワードに対応しましたをご覧ください。 ざっくりいってしまうと、session managerの対象ホストから他のホストにポートフォワードできる優れものです。session managerに対応していないRDSへポートフォワードできたりします。

これができれば、ローカルのGUIDB管理ツールからRDSへ接続が可能となります(CLIだと厳しいというメンバが一定数おりそれ用の対応検討です)。

#前述のおまじない

aws ssm start-session \
--target ecs:${prefix}_${taskid}_${CONTAINER_ID}  \
--document-name AWS-StartPortForwardingSessionToRemoteHost     --parameters '{"host":["RDSエンドポイント],"portNumber":["3306"], "localPortNumber":["3306"]}'

で、実施結果ですが、、。

2022/07、2022/09で二回ほど試しておりますが、現行のAWS Fargateに入ってるエージェントバージョンが未対応とのこと。当面、ローカルからの直接RDS接続はお預けです。

番外:ECS FargateでSSMセッションマネージャーのリモートホストのポートフォワード環境を構築する

ECS FargateでSSMセッションマネージャーのリモートホストのポートフォワード環境を構築するにかかれているように、Fargate基盤のエージェントを利用するのではなく、ssmエージェント設定したコンテナをfargateにデプロイすればリモートホストへのポートフォワード環境の構築は可能です。

また、上記の方法以外にも、下記のような仕込みをすることで対象コンテナへのポートフォワードRDS接続はできそうです。

これでコンテナに[stone](https://www.gcd.org/sengoku/stone/Welcome.ja.html)でも入れておけば、Fargateのタスクを踏み台にしてデータベースなどへのアクセスができる。

参考記事: https://winebarrel.hatenablog.com/entry/2021/10/23/165720

ECS Exec with 運用スクリプト

前述までの内容で、ポートフォワード、対話式でいろいろな管理オペレーションができそうということは見えてきました。 が、やはり、定型作業は単純化していきたいので対話式以外でのメンテナンス方式を考えていきます。

[アップデート] 実行中のコンテナに乗り込んでコマンドを実行できる「ECS Exec」が公開されました で、上の記事を見てると下記のような気になる記述が。

awsの公式ドキュメントをみてると、/bin/shを呼ぶ例しか書いていないのですが、任意のコマンドで呼べるんですね。 ということで、運用スクリプトをコンテナ内に配置し呼び出してみます。

#前述のおまじない
#pgdump⇒s3アップロードをしている運用スクリプトを呼び出している例

aws ecs execute-command  \
 --region    ap-northeast-1 \
 --cluster   ${cl} \
 --task      ${taskarn} \
 --container ${CONTAINER_NAME} \
 --command "/bin/sh -c '/work/backup.sh backupComment'" \
 --interactive


#s3ダウンロード⇒pgrestoreをしている運用スクリプトを呼び出している例
aws ecs execute-command  \
 --region    ap-northeast-1 \
 --cluster   ${cl} \
 --task      ${taskarn} \
 --container ${CONTAINER_NAME} \
 --command "/bin/sh -c '/work/restore.sh my-dev_20220902-153423_backupComment.dump'    " \
 --interactive

こんな感じで、運用スクリプトをECS Execから呼びだすことができます(なお、重い運用JOBの場合は、専用のタスクを起動した方がよいと考えております)。

ということで、既存構成に多少の手を加えることにより、ECS Execからメンテナンスをする仕組みが実装できました。 ECS Execで呼び出せるように運用スクリプトを定義しておけば、今後下記のようなこともできそうです。

  • 今までどおりcloudshellからOpsが手動でECS Execにて運用スクリプトを起動する
  • ChatOpsでECS Exec経由で運用スクリプトを起動する
  • チケット管理ツールのワークフローからECS Exec経由で運用スクリプトを起動する(一部のssm run-commandはこの方式で実施しています)

まとめ

ということで、AWS Fargate利用時の運用タスクの実行方法についてご紹介してみました。

執筆:@miura.toshihikoShodoで執筆されました