DjangoアプリでALBのヘルスチェックに失敗する問題の対処法
2023-03-28

前提
web(Django)、nginx、db(mysql)の構成でECSにDocker ECS プラグインを使用してデプロイを行っている。
dbはロードバランス対象外とするため、ymlファイルからポートの記述を削除させている。
事象
Djangoアプリをデプロイしたら、ALBのヘルスチェックに失敗してしまう。
Health checks failed with these codes: [400]NLBのときは正常だったが、ALBでデプロイしたらこのような事象が起きた。
原因
Djangoのsettings.pyのALLOWED_HOSTSにドメインしか指定していなかったのが原因。
NLBでは正常でALBでは失敗した原因
NLBは、TCPプロトコルに基づいて動作し、ヘルスチェックは主にTCP接続を確立できるかどうかをテストする。これは、DjangoアプリケーションのALLOWED_HOSTS設定に関係なく、TCPレベルでの接続のみを検証するため。
一方、ALBはHTTP/HTTPSプロトコルに基づいて動作し、ヘルスチェックではHTTPリクエストを送信してアプリケーションからHTTPステータスコードを受け取る。この場合、DjangoアプリケーションはALLOWED_HOSTS設定に基づいて、リクエストのホストヘッダーを検証する。ALBのヘルスチェックでは、リクエストのホストヘッダーがALLOWED_HOSTSにリストされているホストと一致しない場合、Djangoアプリケーションは400 Bad Requestエラーを返す。これが、ALBでヘルスチェックが失敗し、NLBでは問題が発生しなかった原因。
対応
webの対応(Django)
Djangoアプリケーションが実行されているECSコンテナのプライベートIPアドレスを取得し、ALLOWED_HOSTSに追加する処理の追加
settings.py
import requests
try:
resp = requests.get('http://169.254.170.2/v2/metadata')
data = resp.json()
container_meta = data['Containers'][0]
EC2_PRIVATE_IP = container_meta['Networks'][0]['IPv4Addresses'][0]
ALLOWED_HOSTS.append(EC2_PRIVATE_IP)
except requests.exceptions.RequestException:
passnginxの対応
- default.confファイルに以下のコードを追記する
location /healthcheck {
return 200 'OK';
add_header Content-Type text/plain;
}- AWSコンソールでEC2→ロードバランシング→ターゲットグループを選択し、nginxのヘルスチェックのパスを/healthcheckに変更
これでwebとnginxにおいてヘルスチェックに成功する。
まとめ
ALBでは、ホストのローカル IP を使用して Django アプリケーションにリクエストを送信しているためALLOWED_HOSTSにドメインのみ指定しているとヘルスチェックに失敗する。
NLBとALBではヘルスチェックの実行方法が異なるため、ALBでヘルスチェックが失敗し、NLBでは問題が発生しないといった事象が起きた。
感想
プロトコルについて理解が甘いため時間がかかった。。そのへんの学習も頑張ろう。