2023.12.12   2024.02.20

【AWS】ECS FargateでNginxとAPPを動かす

AWS,  Nginx    

AWS ECS FargateでNginxとアプリケーションを動かすための設定例についてメモ書きします。

今回はFrontend(Next.js)とNginxを同じタスク内で動作させる設定となります。

また、FargateではNetwork Modebridgeが利用できないため、awsvpc を利用した設定となります。

本記事ではECS タスク定義にはjsonファイルを利用します。

Network Mode解説:ネットワークモードの選択
Fargateではawsvpcの設定が必須:タスク定義パラメータ:ネットワークモード

背景

画面表示にNext.jsを利用しており、Next.jsからbackendのAPIを実行する際、部分的にNext.jsのプロキシ機能を利用していました。

Next.jsとbackendは長時間、通信を行う要件が追加で発生しましたのですが、Next.jsのAPI受付時間の上限は基本30秒であり、それを超えた場合通信エラーとなってしまいます。

上限を変更する方法もあったのですが、非推奨な方法であり、その設定を行うと何が起こっても知りませんよという警告が出てしまいました。

そこでNext.jsのプロキシ機能の代用としてNginxの利用を検討することとなりました。

実装

DockerfileはECRにpushしたものを利用します。
※ECRのリポジトリ作成やPush方法については説明を割愛

Dockerfile

FROM --platform=linux/amd64 nginx:1.25.3-alpine-slim

ADD ./nginx/dev/default.conf /etc/nginx/conf.d/default.conf

FROM --platform=linux/amd64でどのCPUアーキテクチャでビルトするか明示的に指定しています。
(amd64はx86_64, x86と同じ意味)

利用イメージをnginx:latestではなく、nginx:1.25.3-alpine-slimにしているのは2023年12月12日時点で重要度の高い脆弱性がnginx:latestの方に出ていたからです。(ECRにイメージをpushすると確認できます)

ADDでローカル環境の./nginx/devにあるdefault.confでNginx内の/etc/nginx/conf.d/default.confを上書きしています。

default.conf

Dockerfile内で上書き配置しているdefault.confの内容です。

# TTLが5秒を超えるとAmazonDNSサーバーに名前解決しにいく設定。
# resolverの設定を利用するには下記で設定しているようにset <変数名> <ドメイン>で対象ドメインを変数にしなければならない
# 169.254.169.253はAWS内部からのみ接続可
resolver 169.254.169.253 valid=5s;

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

    # 最大転送ファイルサイズ設定
    client_max_body_size 200M;

    # タイムアウト時間の設定(秒)
    proxy_read_timeout 60;
    proxy_connect_timeout 60;
    proxy_send_timeout 60;

    # ヘッダー情報設定
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    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 hoge.api.jp

    location /v1/login {
        # resolverとsetを利用時、リクエストパラメータがある場合は${is_args}${args}を設定に入れる必要がある
        proxy_pass proxy_pass https://${backend_server}/v1/login${is_args}${args};
    }
    location /v1/reset-password {
        proxy_pass https://${backend_server}/v1/reset-password${is_args}${args};
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

listen 5000;はECS(Nginx)の前にあるALB(アプリケーションロードバランサー)が受け付けた通信を5000番ポートに送信するように設定しているので5000としています。

location /の設定をproxy_pass http://localhost:3000;としているのは、FargateのNetwork Mode awsvpcはタスク単位でプライベートIPアドレスが設定されているためです。

今回はFrontend(Next.js)とNginxを同じタスク内で動作させる設定となるため、Frontend(Next.js)とNginxの通信はlocalhostで行えることになります。
マニュアル:ネットワークモードの選択:awsvpc

よって、NginxからNext.jsに通信する際はhttp://localhost:3000(Next.jsのデフォルトポートは3000)となります。

このあたりの設定に誤りがあるとECSのexit code:1がになってNginxが停止してしまいます。

# ECSログ
2023-12-11T16:07:00.477+09:00   TASK    StoppedCode:TaskFailedToStart
2023-12-11T16:07:33.202+09:00   CONTAINER:frontend  LastStatus:STOPPED HealthStatus:UNKNOWN (exit code: 143)
2023-12-11T16:07:33.202+09:00   CONTAINER:nginx LastStatus:STOPPED HealthStatus:UNKNOWN (exit code: 1)
2023-12-11T16:07:33.202+09:00   TASK    LastStatus:DEPROVISIONING

ログの確認方法:https://rurukblog.com/post/essential-container-in-task-exited/

また特定のpassでの通信を受け取った際は、Next.jsではなく別タスクで動作しているbackendコンテナ(https://hoge.api.jp)に転送する設定をlocation /v1/loginlocation /v1/reset-passwordで行っています。

AWSのALBのIPアドレスは定期的に変更されるため、resolverset等の設定を行い、名前解決したIPアドレスをキャッシュするのではなく、都度IPを確認するようにする必要があります。
※Nginxは一度名前解決したIPは何もしないとずっと同じ値でキャッシュされます。

詳細についてはこちらの記事に記載しています。
【AWS】フロントのECSにNginxを導入後、時間経過でログイン不可になる

ecs-task-def.json

ECSのタスク定義については次のようになります。
portMappingsでNext.js、Nginxそれぞれでのポート番号を利用するの設定しています。

{
  "deploymentConfiguration": {
    "maximumPercent": 200,
    "minimumHealthyPercent": 100
  },
  "deploymentController": {
    "type": "CODE_DEPLOY"
  },
  "desiredCount": 1,
  "enableECSManagedTags": false,
  "enableExecuteCommand": false,
  "healthCheckGracePeriodSeconds": 0,
  "launchType": "FARGATE",
  "loadBalancers": [
    {
      "containerName": "nginx",
      "containerPort": 5000,
      "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:targetgroup/xxxxxxx-blue/xxxxxxxxxxxxxxx"
    }
  ],
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "assignPublicIp": "DISABLED",
      "securityGroups": ["sg-xxxxxxxxxxxxxxxxxx"],
      "subnets": [
        "subnet-xxxxxxxxxxxxxxxxx", 
        "subnet-xxxxxxxxxxxxxxxxx
      ]
    }
  },
  "placementConstraints": [],
  "placementStrategy": [],
  "platformFamily": "Linux",
  "platformVersion": "1.4.0",
  "schedulingStrategy": "REPLICA",
  "serviceRegistries": [],
  "tags": [
    {
      "key": "NAME",
      "value": "hoge"
    }
  ]
},
{
  "containerDefinitions": [
    {
      "cpu": 0,
      "essential": true,
      "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/dev/frontend-nextjs:latest",
      "linuxParameters": {
        "initProcessEnabled": true
      },
      "name": "frontend",
      "portMappings": [
        {
          "containerPort": 3000,
          "hostPort": 3000,
          "protocol": "tcp"
        }
      ]
    },
    {
      "cpu": 256,
      "environment": [],
      "essential": true,
      "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/dev/nginx:latest",
      "memoryReservation": 512,
      "linuxParameters": {
        "initProcessEnabled": true
      },
      "name": "nginx",
      "portMappings": [
        {
          "containerPort": 5000,
          "hostPort": 5000,
          "protocol": "tcp"
        }
      ]
    }
  ],
  "cpu": "512",
  "executionRoleArn": "arn:aws:iam::xxxxxxxxxxxxxx:role/ecs-task-execution-role",
  "family": "lottie-dev-admin-front-frontworker",
  "memory": "2048",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  },
  "tags": [
    {
      "key": "NAME",
      "value": "hoge"
    },
  ],
  "taskRoleArn": "arn:aws:iam::xxxxxxxxxxxxx:role/ecs-task-role"
}
コメント
現在コメントはありません。
コメントする
コメント入力

名前 (※ 必須)

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

送信