Joifup Blog

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

2023-03-28

ECSエラーDjangoAWSDocker

前提

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:
    pass

nginxの対応

  1. default.confファイルに以下のコードを追記する
location /healthcheck {
        return 200 'OK';
        add_header Content-Type text/plain;
    }
  1. AWSコンソールでEC2→ロードバランシング→ターゲットグループを選択し、nginxのヘルスチェックのパスを/healthcheckに変更

これでwebとnginxにおいてヘルスチェックに成功する。

まとめ

ALBでは、ホストのローカル IP を使用して Django アプリケーションにリクエストを送信しているためALLOWED_HOSTSにドメインのみ指定しているとヘルスチェックに失敗する。

NLBとALBではヘルスチェックの実行方法が異なるため、ALBでヘルスチェックが失敗し、NLBでは問題が発生しないといった事象が起きた。

感想

プロトコルについて理解が甘いため時間がかかった。。そのへんの学習も頑張ろう。