Elastic BeanstalkのMultiContainer環境にRails+Nuxtをデプロイする
Elastic BeanstalkでMultiContainer環境を構築し、RailsとNuxt.jsをデプロイします。
今回の環境
- ElasticBeanstalk(Multi-container Docker)
- Rails 5.2 (ruby:2.6)
- Nuxt.js 2.11 (node:12-alpine)
- PostgreSQL (RDS)
目次
初期設定
AWSのアカウント登録
アカウント登録については以下の記事を参考にしてください。
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/iam-servicerole.html
ElasticBeanstalkの環境を作成する
AWSマネジメントコンソールからElasticBeanstalkを検索し開く。
最初に新しいアプリケーションを作成する必要があります。 「アプリケーション名」はそれぞれのElasticBeanstalk環境をグルーピングするためのグループです。 特にこだわりがないならアプリケーション名だけ適当に入力して「作成」します。
次に右上のアクションから「環境の作成」をクリックする。
ウェブサーバー環境を選択したまま「選択」をクリック。
アプリケーションの情報を決めて設定していきます。 プラットフォームをMulti-container Dockerに設定します。
DBやインスタンスなど、追加の設定をしたい場合「より多くのオプションの設定」をクリックします。 この中で重要な点をいくつか紹介します。
ソフトウェア
では環境変数を設定できます。容量
ではEC2のインスタンスタイプを変更できます。データベース
ではその名の通りどのDBを使用するか設定できます。- 設定のプリセットをカスタムにすると
ロードバランサー
を設定できます。HTTPSの設定やポートの設定もこちらです。(環境作成時に設定しないとその後、EBから設定できないので気をつけましょう)
環境変数系の用意
ソフトウェア
をクリックして環境変数を設定しましょう。
デプロイ環境では以下の環境変数を個別に指定する必要があります。
NODE_ENV
=production
RAILS_ENV
=production
RAILS_MASTER_KEY
= (これはbackend/config/master.keyの値)
データベースを設定する
今回のプロジェクトではデータベースをPostgreSQLに設定します。
データーベース
の項目をクリックして、データベースの変更とユーザー名・パスワードの設定をしましょう。
ロードバランサー の設定
まずApplication Load Balancer
に必要なポートを列挙します。
それぞれにHTTP、HTTPSのどちらにするのか設定し、HTTPSにする場合はACM(AWS Certificate Manager)で発行した証明書を設定する必要があります。(既存の証明書をインポートすることもできます)
以下の写真では全てHTTPになっていますが、私の環境の場合80番以外HTTPSに設定します。
環境を作成する
設定が終わったら「環境の作成」ボタンをクリックして確定しましょう。
すると環境の作成を始め、初期設定のままならサンプルアプリケーションがデプロイされることになります。 環境の作成には5分~10分くらいかかるので気長に待ちましょう。
アプリケーションを確認する
構築が完了するとアプリケーションを管理する画面に遷移します。 画面上部にあるURLを開くとサンプルアプリケーションに接続できます。
このような画面が見えるかと思います。
本番アプリケーションをデプロイする
ここからはサンプルアプリケーションではなく実際のアプリケーションをデプロイしていきます。 私がデプロイ時に困ったことや気をつけた方がいいなと思った細かい事象はこの章の最後に一覧化しておきますので参考にしてください。
まず今回ElasticBeanstalkの設定をするに当たって必要なファイルは以下の通りです。 順番に設定していきましょう。
- Dockerrun.aws,json
- .ebextentions/
- elb-listener.config
- elb-secure.config
- .elasticbeanstalk/
- config.yml
Dockerrun.aws.json
全体の説明は複数コンテナの Docker 設定を参照してください。
docker-compose.ymlとDockerrun.aws.jsonはある程度互換性があります。
docker-compose.yml | Dockerrun.aws.json |
---|---|
services以下のサービス名 | name |
image | image |
hostname | hostname |
該当なし | memory |
ports | portMappings |
volumes | volumes+mountPoints |
depends_on | links |
environment | environment |
command | command |
まず3行目のvolumes
でホスト側のどのソースをマウントしたいのか設定します。
name
は任意の識別子なので自分でわかるものを指定してください。
host
にはどのパスをマウントするのか設定します。
そしてここで設定したname
をcontainerDefinitions
にあるmountPoints
で使用します。sourceVolume
に対して先ほど設定した任意の識別子を記述し、containerPath
にマウント先のコンテナ内のパスを設定します。
次にenvironment
についてです。
containerDefinitions
配列内のオブジェクトがそれぞれ1つのコンテナになっています。あるコンテナに書いたenvironment
はそのコンテナ内でしか展開されないようになっていますので気をつけてください。
そしてこのDockerrun.aws.jsonは基本的にGit管理下に含めるため、重要なキーなどを絶対に含めないように気をつけてください。秘密にしなければいけない環境変数はAWSコンソールから環境プロパティに設定しましょう。
最後にmemory
に関してです。
これに関しては具体的にどうと言えるわけではありませんが、t2.microのメモリ1GiBを超えないように気を付けることだけは徹底しましょう。
合計で1GiBよりも小さく設定している理由としては、限界まで設定すると逆にパフォーマンスが悪くなる可能性、そしてインスタンスにsshをした時にコマンドを走らせる猶予を考慮してのことです。
感覚的な話ですがmemory
を256に設定した時はメモリが足りずに動かなくなってしまっていた状況が見られたので低すぎるのは厳禁です。
.ebextentions/elb-listener.configと.ebextentions/elb-secure.config
APIコンテナに接続するために8080番の設定を別途します。 他には基本HTTPS通信にする予定なので443ポートの設定も書いておきましょう。
SSLCertificateArns
はACMで取得した証明書のIDに書き換えておきましょう。
重要な値なので.ebextentions/elb-secure.config
は.gitignoreでGit管理下から外しておきましょう。
- https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/command-options-general.html#command-options-general-environmentprocess-process
- https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/environments-cfg-alb.html#environments-cfg-alb-namespaces
.elasticbeanstalk/config.yml
このファイルはコマンドを使用して生成します。
以下の記事を見ながらawsebcli
をインストールしてください。
インストール後、デプロイしたいプロジェクトのルートディレクトリでeb init
を実行して対話的に設定してください。
コマンドの実行が完了するとファイルが生成されていますので完了です。
デプロイする
Dockerrun.aws.jsonのあるプロジェクトルートからeb deploy
コマンドを実行しましょう。
git archive
コマンドが実行されZIP化されたソースコードがS3にアップロードされます。その後S3からECSを通してDockerコンテナの立ち上げなどが行われます。
Dockerコンテナの起動にはDockerrun.aws.jsonが参照されて、設定通りに起動されます。
このコマンドには1分程度かかり、その後反映まで少し時間がかかります。 デプロイが完了したら、AWSコンソールでヘルスチェックがグリーンになっていることも確認できます。
(補足)デプロイ時に気を付けること一覧
順不同なのでざっと見て必要なところだけ確認してください。
私も詰まった部分が多すぎて全てを詳細に書ききれません。 内容についてコメントで聞いていただければ、答えられる部分に関しては答えたいと思っています。
EC2やDocker
- Multi-container Dockerの場合、デプロイ時にDockerfileからコンテナを作成することができません。構築済みのコンテナをDockerHubやECRにアップロードしておく必要があります。
Multicontainer Docker configuration - Container definition format - 初期のEC2インスタンスはt2.microでメモリが1GiBに設定されています。これから作成する
Dockerrun.aws.json
にメモリ設定する時上限を超えないように気をつけてください。
インスタンスタイプ - Amazon EC2 | AWS - デプロイ時Dockerコンテナ起動後にその中に入って処理をしたい場合は別途スクリプトを書く必要があります。
【AWS】Elastic Beanstalkのデプロイ時に docker exec する - Qiita - ElasticBeanstalkのソフトウェア->環境プロパティから設定した値は全てのコンテナ内で共有されます。
- t2.microインスタンスでは
nuxt build
やnode-sass
などの処理が重く耐えきれない場合があります。インスタンスタイプを上げるか、ビルドはインスタンス外でやってしまうことをお勧めします。
Nuxt
- node:alpine環境でインストールするパッケージに
node-gyp
が含まれている場合、別途python
,make
,g++
をインストールする必要があります。 - node_modulesのディレクトリをマウントしている場合、npm installしたのにパッケージが消えてしまう場合があるので気を付ける必要があります。(空のnode_modulesで上書きされてしまうため)
node_modules/ and Docker volume mount 問題と対策 - castaneaiのブログ - NuxtでSSRをする場合、
API_URL
とAPI_URL_BROWSER
の違いを正確に理解して設定する必要があります。
DockerとNuxtのSSRでConnectionRefusedが発生する問題の原因を解説する - NAKKA-Kの技術ブログ Cannot find module
が出るからといってnpx
を使うと一時的なインストールでしかないのでその後で詰まります。
npxでnodeモジュールを実行する - Qiita- nuxt2.9未満で
eslint-module
を使用している場合、nuxt.config.jsのbuildModules
にeslint-module
を設定することができません。 GitHub - nuxt-community/eslint-module: ESLint module for Nuxt.js - nuxt.config.jsの中で
process.env
を使用している場合、nuxt build
する時点で環境変数が設定されている必要があるのでどのタイミングで環境変数が設定され、どのタイミングでビルドが走るのか常に意識しておく必要があります。ローカルでビルドする場合はdocker build
コマンドに--build-arg
オプションをつけて変数を設定してください。
HTTPS
- フロントエンド(ブラウザからAPIにアクセスする場合)とバックエンドで片方のHTTPS化を忘れると混合コンテンツのエラーが発生するので気を付ける必要があります。
混合コンテンツの防止 | Web | Google Developers - OAuthを使用する場合、HTTPSにする必要があります。
Rails
- RailsでActionCableを使用している場合、production環境のデフォルトがRedisを使うようになっているので動かなくなります。Redisを設定するか、開発環境と同じ設定にする必要があります。
Action Cableでリアルタイムチャットアプリの作成方法 (Rails 5.1.4にて)(その1) herokuで動かす! - Qiita
まとめ
今回のデプロイでは全ての工程が長く、かつその他の問題も複雑に絡んできたせいで多くの障害がありました。 その苦難を少しでも解決するためにこの記事を書きました。
このデプロイで得た教訓としては、結局地道に見当・検証することが重要でした。 そして実際の細かな動きをトレースして全体の構成を想像すれば大体解決しました。
既に記憶から薄れ始めている部分もあるので、聞かれないと思い出さない箇所もあって全てを詳細に書ききれていません。 そのため追加で詳しく聞きたい箇所などあればコメントください。 少しでも解決の一助になればと思います。
引用
- npxでnodeモジュールを実行する - Qiita
- Multicontainer Docker configuration - Container definition format
- インスタンスタイプ - Amazon EC2 | AWS
- node_modules/ and Docker volume mount 問題と対策 - castaneaiのブログ
- DockerとNuxtのSSRでConnectionRefusedが発生する問題の原因を解説する - NAKKA-Kの技術ブログ
- 混合コンテンツの防止 | Web | Google Developers
- Action Cableでリアルタイムチャットアプリの作成方法 (Rails 5.1.4にて)(その1) herokuで動かす! - Qiita
- npxでnodeモジュールを実行する - Qiita
- 【AWS】Elastic Beanstalkのデプロイ時に docker exec する - Qiita
- https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/command-options-general.html#command-options-general-environmentprocess-process
- https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/environments-cfg-alb.html#environments-cfg-alb-namespaces ebコマンド(awsebcli)のインストール - Qiita