ECS タスクで SIGTERM 受信後にタスクスケールイン保護を有効にできるか確認してみた
結論
以下のエラーにより有効化できませんでした。
"responseElements": {
"protectedTasks": [],
"failures": [
{
"arn": "arn:aws:ecs:ap-northeast-1:012345678901:task/16d027f0eff642119b22dd6551e6dba8",
"reason": "TASK_STOPPING_OR_STOPPED"
}
]
},
検証内容
Dockerfile とアプリケーションファイルは以下の通りです。
FROM python:3.9-slim
RUN pip install boto3
COPY app.py /app/app.py
WORKDIR /app
CMD ["python", "app.py"]
import signal
import sys
import time
import datetime
import os
import boto3
# ECSクライアントを初期化
ecs_client = boto3.client('ecs', region_name='ap-northeast-1')
CLUSTER_NAME = "task-protection-test-cluster"
def get_task_arn():
"""メタデータエンドポイントからタスクARNを取得"""
try:
import urllib.request
import json
metadata_uri = os.environ.get('ECS_CONTAINER_METADATA_URI_V4')
if not metadata_uri:
return None
task_metadata_url = f"{metadata_uri}/task"
with urllib.request.urlopen(task_metadata_url) as response:
task_metadata = json.loads(response.read().decode())
return task_metadata['TaskARN']
except Exception as e:
print(f"Failed to get task ARN: {e}")
return None
def enable_task_protection():
"""TaskProtectionを有効にする"""
try:
task_arn = get_task_arn()
if not task_arn:
return False
print(f"Attempting to enable TaskProtection for task: {task_arn}")
response = ecs_client.update_task_protection(
cluster=CLUSTER_NAME,
tasks=[task_arn],
protectionEnabled=True
)
print(f"TaskProtection enabled: {response}")
return True
except Exception as e:
print(f"Failed to enable TaskProtection: {e}")
return False
def signal_handler(signum, frame):
print(f"[{datetime.datetime.now()}] Received signal: {signum}")
if signum == signal.SIGTERM:
print("SIGTERM received! Attempting to enable TaskProtection...")
success = enable_task_protection()
if success:
print("TaskProtection enabled! Continuing to run...")
return
else:
print("Failed to enable TaskProtection. Shutting down...")
sys.exit(0)
# シグナルハンドラーを登録
signal.signal(signal.SIGTERM, signal_handler)
print(f"[{datetime.datetime.now()}] Application started. PID: {os.getpid()}")
# 無限ループ
counter = 0
while True:
counter += 1
print(f"[{datetime.datetime.now()}] Running... counter: {counter}")
time.sleep(10)root@6a5e8e5c7f52
上記アプリケーションをビルドして ECR にイメージをプッシュし、以下の手順を実行しました。
- ECR のイメージを使用するタスク定義を作成
- タスク定義からタスクスケールイン保護が無効な ECS サービスをデプロイ
- ECS サービスの Auto Scaling の設定で最小タスク、最大タスクを 0 に設定
アプリケーションでは SIGTERM を受信後に UpdateTaskProtection API をコールする処理になっています。
SIGTERM は 3 の手順で発生させています。
ECS のアプリケーションを正常にシャットダウンする方法 | Amazon Web Services ブログ
タスクが停止すると、ECS はそのタスク内の各コンテナに停止シグナルを送信します。
3 の手順実行後、タスクが終了していることを確認しました。
CloudTrail には同タスクから UpdateTaskProtection API が実行された記録がありましたが、冒頭に記載した failures のレスポンスが記録されていました。
注意点として、タスクスケールイン保護に失敗した場合は API 自体が失敗するのではなく failures に失敗理由が含まれる点です。
ただし、例外の場合には API 自体のエラーとして記録されます。
Amazon ECS タスクスケールイン保護のエンドポイント - Amazon Elastic Container Service
障害が発生すると、次の情報が返されます。
...
例外が発生すると、次の情報が返されます。
上記仕様はアプリケーション側の実装にも影響する可能性があります。
例: UpdateTaskProtection API の例外処理のみ実装している場合
上述の通り UpdateTaskProtection API でのスケールイン保護が有効にならなかった場合にはレスポンスに failures が含まれるものの、API 自体は成功扱いになります。
そのため、failures が含まれていても例外処理には遷移しないため、明示的にレスポンスに failures が含まれるかどうかを確認する実装も必要だと思われます。
try
UpdateTaskProtection API の呼び出し
UpdateTaskProtection API のレスポンスを確認
if UpdateTaskProtection API のレスポンスに failures が含まれてるか
failures の場合の処理
catch
UpdateTaskProtection API 自体が失敗した際の例外処理
まとめ
今回は ECS タスクで SIGTERM 受信後にタスクスケールイン保護を有効にできるか確認してみました。
どなたかの参考になれば幸いです。
Discussion