こんにちは!
ウェブアプリケーションの開発でデファクトスタンダードな VueJS / NuxtJS ですが、SSR 時のデプロイ方法についてはみなさん悩まれることが多いのではないでしょうか?
SPA であれば AWS Cloud Front と S3 のコンビで十分に運用可能ですが、要件に SSR が入ってくると Nodejs のランタイムが必要になりますね。
そんな時に大活躍するのが ECS, Fargate です!
実は、Lambda と API Gateway のコンビの方がもっと手軽にSSRを構築可能です。しかし、Lambda にはレスポンスボディサイズの制約、デプロイ可能な ZIP サイズの制約などがあり、開発が進むにつれて窮屈になってきます…。
そもそも Lambda はマイクロサービスの思想で付き合った方が健全だと感じます(制約も多いですし)ので、SSR、つまりフロントエンド全体のレンダリングの責任を持たせるのはアーキテクチャーとしてイマイチな気がします。
例えば、わたしたちのコーポーレートサイトは、現在 VueJS / NuxtJS で構築されていますが、以前までは手軽に AWS Lambda で SSR させていましたが、制作が拡大させていくにつれて Lambda の制約事項に色々と引っ掛かり、 2019 年よりインフラを ECS, Fargate へ完全に移管しています。
例えば、Laravel でバックエンドの API を構築している場合、Laravel を実行する Nginx、PHP 環境が必要です。
それらを EC2 インスタンスに展開し、ELB と繋いで…ということをしていると、デプロイプロセスが複雑になったり、EC2 自体にVulsを実行するなどしてセキュリティのアップデートを定期的に実行しないといけません。
これは、Nuxtjs の SSR で同様のことが言えます、継続的にソフトウェアのバージョンが形骸化しないようにアップデート、パッチを当てるなどが必要です。
Aws にマネージ メントを任せられる部分は、どんどん任せていき、開発効率向上に努めていくことが重要です。Fargate を用いることで、EC2 インスタンスの管理から解放されるでしょう。
実際に開発を行った場合の流れを解説します。
まず、ローカルで開発時する際に Docker コンテナーを介してNPMコマンドを実行するか、介さないかを選択する必要があります。
私の推奨は、NuxtJS や NextJS の SSR 程度であれば、ローカル開発では Docker を介さないで開発することを推奨します。
複数人での開発時に Docker わからない人は苦しむし、NVM で NodeJS のバージョンを指定すれば大体不具合発生しません。( SSR で OS 依存した処理がなければ)NodeJS のバージョンだけ最低限合わせて開発するようにしましょう。
こちらのページに則って環境を構築します。
$ yarn create nuxt-app sampleproject
> ...
$ cd sampleproject
$ npm run dev
HOST=0.0.0.0とPORTを指定します。
...
"scripts": {
"dev": "HOST=0.0.0.0 PORT=3000 nuxt",
"build": "nuxt build",
"start": "HOST=0.0.0.0 PORT=3000 nuxt start",
},
...
NodeJS のイメージは公式を指定しましょう。タグでバージョン指定が可能です。
FROM node:10.20.1-jessie
RUN mkdir -p /var/www/sampleproject
WORKDIR /var/www/sampleproject
COPY ./ /var/www/sampleproject
RUN npm run build
EXPOSE 3000
ENTRYPOINT ["npm", "run", "start"]
事前に Docker Desktop をインストールしておきましょう。以降で使用する Docker コマンドが一緒にインストールされます。
$ docker build -t sampleproject .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sampleproject latest XXXXXX 52 seconds ago 838MB
$ docker run -d -p 3000:3000 sampleproject
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4085a439c0bd sampleproject "npm run start" 4 seconds ago Up 3 seconds 0.0.0.0:3000->3000/tcp hungry_williams
access to : https://blog.ragate.co.jp:3000
事前に ECR へリポジトリを作成しておいて下さい。
$ npm run build
$ $(aws ecr get-login --no-include-email --region ${AWS_DEFAULT_REGION})
$ docker build -t sampleproject:latest .
$ docker tag sampleproject:latest:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/sampleproject:latest
$ docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/sampleproject:latest
これで AWS デプロイ前の下準備が終わりました!
ECR へ Docker イメージがちゃんとプッシュされているか、ローカルで起動できているか、確認してくださいね。
ALB を構築したら、基本的に ECS の設定をザクザクしていくだけです。タスク定義にて Docker 定義を色々と書きますが、ここがかなり難所です。
Docker に深く精通いていないと細かいチューニングは難しいので、細かい設定は抜きにしてざっくりと構築します。
AWS のマネージメントコンソールより、ALB を構築します。( VPC などの細かい設定は割愛します )
ECS の画面より、タスクの定義の作成を行います。
起動タイプは Fargate を使用します。
基本的に初期設定で進みますが、「コンテナーの追加」だけ自分で設定を行います。先ほど ECR へ用意したイメージを指定する必要があります。
また、ここの CPU とメモリーサイズのが、使用料金に関わってきますので、学習時は必ず最低スペックなどにしてください。( 最高スペックにしてアクセスしまくると軽く月額 10 万円を超えます )
あとはどの VPC へアタッチするかなどの設定ですので、割愛します。お手元の VPC に合わせて環境構築を行ってください。
私たちの経験上、クラスターはサービス単位( 1 ドメイン単位)で構築するのが良いでしょう。なぜなら、ALB のターゲットグループへの ECS よしなにインスタンスをアタッチしますが、その際にパスパターンでアタッチするからです。詳しく知りたい方は、構築後にターゲットグループを見てみてください。
Fargate 指定します、クラスターの役割は、「 Docker コンテナーを実行するインスタンスの管理」となりますので、ここでもし EC2 を指定すれば、 Docker コンテナーを EC2 でをマネージします。Fargate にすることで、Lambda のような感覚で EC2 インスタンスは隠蔽され、裏側でよろしくやってくれます。素敵ですね。ただ、私の所感では、あくまでも OS レベルのセキュアは AWS がやってくれますが、ネットワークのレベルでのセキュアは開発者が担保しないといけません。( Private Subnet、Public Subnet のアクセスポリシー厳格にするなど)Lambda との大きな違いは、インスタンスの属するネットワークのセキュアは開発者に依存すると言えます。( Lambda でも VPC にアサインできるような機能ありますがあれはあくまでもアクセスができるようになる話と解釈しています)
以降は VPC の設定などになりますので、割愛します。お手元の環境に合わせて指定してください。
ではいよいよサービスを作成します、起動したクラスターの画面に移動してください。
サービスの作成画面ですが、ここに Lambda との決定的な違いがあります。
それは、「タスク数」という項目で常時起動可能なタスク数を指定可能ということです。例えば負荷の高い API サーバー、レスポンス速度を常に求められるタスクは、タスク数を調整することをお勧めします。
話を戻すと、Nuxt の SSR に関してはタスク数 1 で十分です。というのも、Cloud Front を手間に入れてキャッシュさせるケースが多いためです。( Cloud Front でリクエストが止まるので常時起動させる意味がない )
タスク数の考え方としては、私たちの経験上の下記のパターンがあります。
以降は、VPC、ロードバランサーに接続するなどを行うので割愛します。
ポイントとしては、ECS は勝手にターゲットグループを作成し、指定した ALB に勝手にアタッチするということです。運用の際にピーク時などに勝手に台数を増やしてアタッチするなどを Fargate がよしなにやってくれます。( Auth Scaling )
デプロイ後に動作確認をする際に、ターゲットグループのインスタンスが healthy にならない場合は、ネットワークの設定などを見直してみましょう。
例えば、 ALB が Private ネットワークにいるなどの場合にターゲットグループのインスタンス判定は un healthy となり、Fargate がインスタンスに問題が起こったと認識し無限にデタッチ→アタッチを続けます。
弊社で採用事例の多い Fargate、ECS ですが、まだまだ奥深いです。特にタスクの定義周りはかなりチューニングを細かくできるので、ぜひ触ってみてください。
また、デプロイ自動化は Code Pipeline に任せると Github と連携してかなり楽できますよ、オススメです。
ECS, Fargate の構築・運用はお気軽にご相談ください。
スモールスタート開発支援、サーバーレス・NoSQLのことなら
ラーゲイトまでご相談ください
低コスト、サーバーレスの
モダナイズ開発をご検討なら
下請け対応可能
Sler企業様からの依頼も歓迎