ECS Execのロギングに関して

2022.06.02

ECS Exec を有効にする方法は各所で書かれてるので、この記事では軽く触れる程度にして、実運用に際して気になるロギングに関する情報が薄かったので調査・整理してみました。
例示しているのはFargateですが、内容自体はEC2onECSでも共通かと思います。

ECS Execは簡単便利、だけど、、

従来、Fargateのタスクにログインするには、以下のような手順を採る必要がありました。

  1. ssmエージェントをコンテナにインストール
  2. ssmでアクティベーションコードを作成する
  3. タスク起動時に、アクティベーションコードと紐付けてSSMに登録
  4. CLIから aws ssm start-session でログイン、またはマネコンから

アクティベーションコードは有効期限がある(デフォルト1日)ため、基本的には必要になった都度発行する必要がありました。

これが ECS Exec だと以下のステップだけで出来るようになりました。

  1. SessionManager関連の権限をTaskRoleに追加する
  2. サービスの設定(あるいはRunTaskの際の設定)でECS Execを有効にする
  3. CLIから aws ecs execute-commandでログイン

これだけです。何より(有効期限とかはありませんので)一度やるだけでOKというのがとても楽です。検証環境で頻繁にログイン&デバッグしたい、という用には非常にマッチしそうですね。
が逆に 何時でもログイン可能 という事はリスクでもあります。用が済んだらECS ExecをOffにするといった運用も良いかも知れません。

いずれにせよ、ロギングは必須だよね、という事で今記事のテーマであるロギング機能に関して見てきたいと思います。

ECS Exec を有効にする

最初に言っておくと、Logはデフォルトで有効になります。
cdkでECS Execを有効にしてみます。

new ecs.FargateService(scope, id, { // cdk v2の場合はクラス名が違います
    :
    enableExecuteCommand: true,
})

ミニマム設定だとこれだけです。
デフォルトのLog出力先は、タスク定義で設定されているLogGroupです。
cdkだと、紐付いているタスクロールにも権限を自動で追加してくれます。

Log関連権限の対象リソースが になってしまうのはバグでしょうかね?

早速タスクを起動してログインしてみましょう。

$ aws ecs execute-command --cluster {your-cluster-name} --task {your-target-task-id} --container {your-target-container-name} --interactive --command "/bin/sh"

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


Starting session with SessionId: ecs-execute-command-xxxxxxxxxxxxxxxxxxx
sh-4.2#

成功すれば、sessionIdが表示され、対話型シェルにアクセスできます。
失敗する場合は恐らく環境マターです。チェックスクリプト があるので、ご自身の環境でチェックして対応して下さい。
なおログインできるのは、サービスで設定有効にした後に起動したタスクだけです。

ログイン出来たなら、シェルで適当にコマンド打ってからログアウトすると、Logstreamが出力されます。

LogStream名は sessionId になるみたいですね。
試した範囲では、ログアウトするまでLogは出力されませんでした。

Logが出力されない?

コマンドログを Amazon S3 または CloudWatch Logs に正しくアップロードするには、コンテナイメージで script と cat をインストールする必要があるため、ご注意ください。

公式ガイドより

素のAmazonLinux2には script が入ってませんのでご注意下さい。。
util-linux パッケージを入れればOKですが、その他にも色々入るのでちょっとモヤりどころです。

出力先LogGroupを指定する

Logには、ユーザが入力した全ての入力とそれに対する出力が記録されますので、どんな文言が記録されるか予測不能です。LogFilterに引っかかってアラームが誤発砲したりといった可能性を考えると、通常のアプリログとは別のLogGroupにしておくのが良さそうです。

new ecs.Cluster(scope, id, {  
    clusterName: name,  
    :
    executeCommandConfiguration: {
        logging: ecs.ExecuteCommandLogging.OVERRIDE,  
        logConfiguration: {  
          cloudWatchLogGroup: someLogGroup  
        }
    },  
})

デフォルト出力設定をOVERRIDEします。他にもS3に出力したり、暗号化するといった設定も出来ますので、必要に応じて設定して下さい。

なお上記の設定があると、上述のタスクロールへ自動付加される権限の対象リソースも ではなく適切なものになります。

Logと実行者の紐付け

LogはSessionIdとのみ紐付けられており、実行者が誰かといった事は分かりません。
Session情報はSystemsManagerのSessionManagerで参照できます。が、従来のアクティベーションコード方式だと、SessionのOwnerが実行者情報だったのですが、ECS Execの場合はそうではなくなっています。

OwnerはAssumedRoleとなっており、実行者はここからは分かりません。

なので CloudTrail でイベント情報を見る必要があります。イベント名を ExecuteCommand でFilterして、Sessionの開始時間を参考にそれらしきイベントを探します。

イベントの詳細情報を開いて、当該SessionIdが含まれていれば当たりです。同じ詳細情報内のUserIdentity情報から実行者が特定できました。

// 関係部分のみ抜粋
{
    "userIdentity": {
        "sessionContext": {
            "sessionIssuer": {
                "arn": ".......",
                "userName": "......."

    "responseElements": {
        "session": {
            "sessionId": "ecs-execute-command-xxxxxxxxxxx",

おわり

以上、ECS Execの際のロギング機能に関して、一通り見てみました。

ECS Exec は手軽にタスクにログイン出来て非常に便利なのですが、実環境で運用する場合は、 何時でも誰でもログイン可能 では問題があるので(今回は触れてませんが)ユーザの権限管理などは検討する必要があります。
またそもそも、ログインしたところでツールがインストールされてないと何も出来ない訳ですが、それを想定して最初からFatなイメージを用意するのは、ベストプラクティス(=イメージに含めるのは必要最低限にする)に逆行するアクションなので悩ましいトコロです。 Distrolessだとシェルすら入っていないですし。。

と、色々考える事はあるのですが、困った時には間違い無く非常に便利な機能なので、色々総合して適切な運用をしたいですね。