ECSのRunTask時にAGENT接続起因のエラーを手動で発生させる方法

2021.08.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。

ECSでは、タスクを実行するときにAPIなどを利用して「RunTask」イベントを実行することで、タスクを実行することができます。この際に、通常は問題なくタスクの実行ができると思いますが、場合によっては「丁度タスクを実行したタイミングで、ECS Agentの接続が切断されている」という事象が発生し、これによりタスクの実行がエラーになるケースがあります。

その際には、APIのレスポンスとして下記に記載のAGENTという理由コードでエラーが返却されて「RunTask」イベントは失敗します。

下記のドキュメントにも注釈として記載されているとおり、ECS Agentが1時間に数回切断して再接続することは通常の操作の一環として予期されることなので、「RunTask」の実行処理においては、これを考慮した実装が必要となってきます。(もちろん、システムによっては「リカバリ可能なので対応しない」という実装方針もあると思います)

Amazon ECS コンテナエージェントは、通常の操作の一環として、1 時間に数回切断して再接続するため、エージェントの接続イベントが予期されます。これらのイベントは、コンテナエージェントまたはコンテナインスタンスに問題があることを示すものではありません。

さて、これを考慮してリトライするような実装をした場合に当然テストをしたくなるのですが、ECS Agentが再接続を実施するタイミングは不明なので、なかなかテストが難しいです。

今回は、この「ECS Agentの接続が切断された場合に発生するAGENT起因のエラー」を、無理やり手動で発生させる方法を書いておきたいと思います。

注意点

この方法は特に推奨される方法などではありませんので、もし試す場合には「開発環境」や「テスト環境」などの破壊しても修復可能な環境でお試しください。また、もっと良い方法があるかもしれません。

前提

今回の方法は、Fargateではなく、ECSインスタンス上(EC2)でタスクを実行する環境で試しています。

ECS Agentの接続先を特定する

まずはECS Agentの接続先を特定します。ECSインスタンス上にログインし、下記のコマンドで接続先を探します。

$ cat /var/log/ecs/ecs-agent.log | grep "Establishing a Websocket connection"

ecs-X-N.[region].amazonaws.comというホストが2つ確認できるはずです。

今回のケースでは、ecs-a-4.ap-northeast-1.amazonaws.comecs-t-4.ap-northeast-1.amazonaws.comに接続していることがログから読み取れたので、今度は更にIPアドレスを特定します。

hostコマンドで取得できますが、インスタンスの環境にhostコマンドがない場合にはpingでも構いません。

$ host ecs-a-4.ap-northeast-1.amazonaws.com
ecs-a-4.ap-northeast-1.amazonaws.com has address 52.119.223.69
$ host ecs-t-4.ap-northeast-1.amazonaws.com
ecs-t-4.ap-northeast-1.amazonaws.com has address 52.119.221.107

ここで取得できるIPを後ほど利用するので、メモしておきます。なお、このIPはドメインごとに3つあるはずなので時間をずらしつつ調べていきます。最終的には以下のように合計6つのIPが特定できるはずです。

ここで特定できなかった場合には、後述のNACLの設定のあとに、また調べてみてください。

# ecs-a-4
52.119.223.69/32
52.119.219.115/32
52.119.220.39/32

# ecs-t-4
52.119.221.107/32
52.119.223.82/32
52.119.219.118/32

NACLのアウトバウンド設定

IPが特定できたら今度はNACLのアウトバウンドルール設定を実施します。

ECSインスタンスが起動しているサブネットのNACLを開いて、アウトバウンドルールの編集をします。

例として、80番台にecs-a-4を、90番台にecs-t-4のIPを「拒否」で設定していきます。タイプは「すべてのトラフィック」でOKです。

キャプチャはそれぞれ1アドレスだけですが、最終的にすべてのIPを拒否登録します。

ECS Agentの接続エラー確認

NACLの設定ができたら、ECS Agentのログを見てみましょう。下記のようなコマンドでECS Agentのログを確認します。

$ less -S +F /var/log/ecs/ecs-agent.log

以下のようなログが出力されていれば、接続エラーになっていることが確認できます。

Error connecting to TCS: websocket client: unable to dial ecs-a-4.ap-northeast-1.amazonaws.com

このとき、前述のIPアドレスがすべて特定できていないケースでは、あたらしく見つかったIPアドレスへの接続が成功するので、確実にエラーになるまで手順を繰り返します。

RunTaskのエラー確認

最後に、RunTaskが想定どおりエラーになるか確認しましょう。

AWS CLIやboto3などでRunTaskイベントを呼び出した後に、CloudTrailでイベントを確認します。CloudTrailの「イベント履歴」を開いて、フィルタの「イベント名」にRunTaskを設定し、呼び出したイベントの結果を確認しましょう。

CloudTrail イベントレコード

{
    "eventVersion": "1.08",
(snip...)
    "responseElements": {
        "tasks": [],
        "failures": [
            {
                "arn": "arn:aws:ecs:ap-northeast-1:999999999999:container-instance/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "reason": "AGENT"
            }
        ]
    },
(snip...)
    "eventCategory": "Management"
}

このようにreasonAGENTのエラーが発生していることが確認できるかと思います。

なお、一時的に通信を元にもどしたいときは、NACLで「すべて許可」になっているルールの「ルール番号」を100から10などの若い数字に変更すれば、そのルールが優先されるので通信は元に戻ります。

おまけ

最初は「hostsの設定じゃダメなの?」と思って試してみたのですが、hostsでは既存の通信セッションに対して影響を及ぼさなかったので、NACLで即時に通信影響がでるようにしています。

まとめ

以上、ECS Agentのエラーを手動で発生させる方法でした。

相当ニッチな情報ですが、同じようなテストをしたい方の参考になると良いなと思って書いてみました。

どなたかのお役に立てば幸いです。それでは!