2023.12.26   2024.01.22

【AWS】フロントのECSにNginxを導入後、時間経過でログイン不可になる

Nginx,  AWS    

AWSのECSでフロントにNext.jsとNginx(リバースプロキシ)を構築し、プライベートALB経由でバックエンドのAPIと通信するようなシステムを作成しました。

初めのうちはNginxとプライベートALBが上手く通信でき、ログイン処理が行えていたのですが、数日経ってログインができないという報告が上がりました。

ログを調べてみると、NginxとプライベートALBの通信間で502エラーで出ていました。

default.conf の設定は次のようなイメージです。

# /etc/nginx/conf.d/default.conf

server {
    # NGINXの受付ポート
    listen       4000;
    server_name  nginx;

    # 通常の通信はhttp://localhost:3000(Next.js)に飛ばす
    location / {
  # localhost は 127.0.0.1 (IPv4), ::1 (IPv6) どちらともに名前解決されるので、明示的に127.0.0.1を設定する
        proxy_pass http://127.0.0.1:3000;
    }

    # passに/user/loginが含まれる場合バックエンドのAPIと通信を行う
    location /user/login {
        proxy_pass "https://api.dev.jp/user/login";
    }
}

原因

Nginxはstartまたはreload時にドメインの名前解決を行い、そのIPをTTLに関係なく次のrestart、 reload時まで保持する挙動となっているそうです。(参考)
つまり、一度NginxをECSにデプロイすると最初に名前解決されたIPアドレスが保持され続けます。

次に、AWSのALBはトラフィック量に応じて自動的にスケールされALBが自動的にスケールアップするのに伴い、ロードバランサーのIPアドレスは動的に変わるという仕様があります。(マニュアルAWS-Black-Belt(8ページ))

このことから、プライベートALBが自動でスケールアップされると、NginxにキャッシュされたプライベートALBのIPアドレスが現在の値と異なってしまうため、502エラーが発生していた訳です。

やってみたこと(失敗)

Nginxのdefault.confにresolverの設定を追加します。

# TTLが5秒を超えるとAmazonDNSサーバーに名前解決しにいく設定
resolver 169.254.169.253 valid=3s;

server {
 ・・・・・・・・・・・・
}

こうすることで、3秒間プライベートALBと通信できなかったら、AmazonDNSサーバーに再度名前解決を行い、最新のIPアドレスを取得してくれるようになる想定でした。

AmazonDNSサーバーのIPアドレスについては下記マニュアルを参照。
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-dns.html

結論(解決策)

上記の設定だけではALBのアドレスが変更されても、Nginx側のIPが変更されなかった。

どうやらresolverを設定しただけでは名前解決してくれないらしく、名前解決するにはsetパラメータを利用してドメインを変数化しないといけないとのこと。

加えて、リクエストパラメータ(https://api.dev.jp/user/login?name='taro'の?name='taro'部分)がプロキシ対象のリクエストのパスに含まれる場合、set`パラメータを使用するとリクエストパラメータが消えてしまう仕様もあるので、修正内容は次のようになった。

# resolverで指定するDNSのIPは、VPCのネットワークに+2した値
resolver 169.254.169.253 valid=3s;

# /etc/nginx/conf.d/default.conf

server {
    # NGINXの受付ポート
    listen       4000;
    server_name  nginx;

    # 通常の通信はhttp://localhost:3000(Next.js)に飛ばす
    location / {
  # localhost は 127.0.0.1 (IPv4), ::1 (IPv6) どちらともに名前解決されるので、明示的に127.0.0.1を設定する
        proxy_pass http://127.0.0.1:3000;
    }

 # ドメインを変数backend_serverに入れる
 set $backend_server api.dev.jp # 追加

    # passに/user/loginが含まれる場合バックエンドのAPIと通信を行う
    location /user/login {
        # ${backend_server}で接続先URLを渡す。さらに${is_args}${args}でリクエストパラメータを渡す;
        proxy_pass https://${backend_server}/user/login${is_args}${args}; # 修正
    }
}

変数$is_argsはリクエスト行に引数がある場合は?となり、引数なしの時は空文字列になります。
変数$argsはurlに書かれたgetパラメータが保存される変数です。

この修正内容でECSにデプロイしたところ問題なく動作するようになりました。

※ resolver 169.254.169.253 は AWS内部からのみアクセス可能なようです。

参考https://www.ponkotsu-log.com/entry/2017/07/02/002353

コメント
現在コメントはありません。
コメントする
コメント入力

名前 (※ 必須)

メールアドレス (※ 必須 画面には表示されません)

送信