最初に断っておきますが、本記事を見てAWSの使い方が分かると言うことはほぼほぼありません。
あくまで個人的にAWSでWebサーバ環境を構築した際の作業内容や感想などの単なる雑記です。
AWSに関しては、サーバ(EC2)やファイル共有環境(S3)が用意された環境において同環境で動作するプログラムを書いたことはありましたが、個人的に一から環境構築をしたことはありませんでした。
今まで記事にしてきたように、一般的なサーバ環境構築は(特にVirtualboxを用いた仮想サーバ環境を含めれば)頻繁に行ってきましたが、AWSには独自の世界観があるようなので一度チャレンジしてみたいと思っていたところ、昨今ちょうどそのような機会がありましたので、その際の作業内容を記録しておきたいと思います。
やったこと
そもそもの目的はAWS上でLaravelベースのWebアプリ環境を構築することですが、せっかくなのでDBはRDSを使用し、アップロードされたファイル(画像等)はS3に置くようにしたいと思いました。
そのような環境を構築するために行った作業内容ですが、ざっくり列記すると以下のような内容になります。
- AWSアカウント作成
- VPC設定
- サブネット設定
- インターネットゲートウェイ設定
- ルートテーブル設定
- EC2インスタンス生成
- swap設定
- Webアクセス用にインバウンドルールの追加
- Virtualminインストール
- Laravel環境構築
- RDS用サブネット設定
- RDS用サブネットグループ設定
- RDS用セキュリティグループ設定
- RDS用パラメータグループ設定
- RDS用オプショングループ設定
- RDSインスタンス生成
- Laravel-RDS連携設定
- S3バケット生成
- S3で静的ウェブサイトホスティングの有効化
- Laravel-S3連携用パッケージ追加
- Laravel-S3連携用IAMロール設定
- Laravel-S3連携用IAMロールをEC2にアタッチ
- Laravel-S3連携
一般的なホスティング環境で1つのサーバにLAMP環境を構築し、アップロードされたファイル管理も同サーバ上で行うだけであれば、上記手順の三分の一以下の労力で済むような気がします。ただ、AWSが持つ信頼性や拡張性を享受するためには必要な先行投資なのでしょう。
AWSアカウント作成
兎にも角にも、まずはAWS自体が使えるようになる必要があります。
と言うことでアカウント登録する訳ですが、カード情報なども必要になるため業務で使用するアカウントに関しては会社で用意してもらいました。ただ、個人的にも作業全般を把握しておきたいと思いましたので、私用アカウントを独自に作成しています。
無料枠が色々と用意されているので派手なことをしなければ費用は発生しないはずですが、「請求情報とコスト管理ダッシュボード」に関してはこまめに確認するようにしています。
VPC設定
VPCはざっくりと言えばAWS内に当該アカウント専用に作られた仮想ネットワークと言うことになりますが、後述するサブネットとの二重構造が必須のようで、この辺の理由はまだはっきりと理解できてはいません。
設定内容も、VPC名、リージョン、CIDRくらいです。
サブネット設定
前述のVPCに紐付ける形でサブネットを設定します。
EC2やRDSはこのサブネット内に配置されるイメージですが、特にRDSに関してはAZが異なる2つのサブネットから構成される「サブネットグループ」なるものに紐づける必要があるようです。このようになっているのはバックアップを考慮してのことらしいですが、詳細は不明。
設定内容としても、サブネット名、AZ、CIDRで、AZを意識している点が特徴かと。
前述のような理由で、最終的にはEC2を配置するサブネット(インターネットからアクセス可)とRDS用サブネット(インターネットからアクセス不可)を2つ(サブネットグループ用)の計3つのサブネットを構築することになります。
インターネットゲートウェイ設定
構築しただけのVPCはインターネットからアクセス不可状態なので、インターネットからアクセスできるように「口」を作ります(あくまで「口」を作るだけで、後述するルートテーブル設定まで行わないとインターネットからアクセスできるようになりませんが)。
名前を決めた上で、特定のVPCに「アタッチ」することで、そのVPCにインターネットゲートウェイという「口」ができた形になります。
ルートテーブル設定
前述したインターネットゲートウェイ経由でインターネットから特定のサブネットへの通信を可能にします。
指定するものは、名称、VPC、サブネットおよび「ルート情報」で、「ルート情報」としては送信元に「0.0.0.0/0」(誰でも的な意味)、ターゲットに先に作成したインターネットゲートウェイを指定することで、「インターネット上の任意の相手から指定されたインターネットゲートウェイ経由で指定されたVPN上の指定されたサブネットへの通信を可能にする」という設定になるようです。
EC2インスタンス生成
やっとWebサーバ本体であるEC2のインスタンス生成に漕ぎ着けました。
ここでは設定することが色々ありますが、要点だけ列記すると以下になります。
- AMI選択(今回は「Ubuntu Server 20.04 LTS (HVM), SSD Volume Type」を選択)
- インスタンスタイプ選択(とりあえずは無料枠の「t2.micro」)
- 「ネットワーク」として配置先のVPCを、「サブネット」として配置先サブネットを指定
- ストレージサイズは30GB(無料枠の上限らしいので)
- タグの追加で「Name」として当該インスタンスの名称を指定(EC2に関しては明示的に名前を指定させる箇所が存在しない)
- セキュリティグループとしてSSH(22ポート)で「0.0.0.0/0」(誰でも)からのインバウンドを許容するよう設定
- インスタンス生成時にキーペアを生成するか聞かれるので、生成して、秘密鍵をダウンロードしておく(忘れるとsshでの接続ができない)
それ以外にも設定項目はありますが、とりあえずはデフォルトのまま(詳しく知りたい方はGoogle先生に)。
インスタンス生成に若干の時間を要しますが、生成が終了したらsshでアクセスできるところまで確認します。先にダウンロードした秘密鍵を指定し、当該インスタンスのパブリックIP(コンパネ上で確認可)と、ユーザ名はAMIで異なる模様ですがUbuntuの場合は「ubuntu」というユーザーがデフォルトで存在するので、それらを指定してアクセスします。
swap設定
AWSの思想っぽいですが、AMIを展開しただけの初期状態ではswapが設定されていません。
swapに依存するようでは処理性能的に問題がありますし、AWSではその辺柔軟に変更できるようになっているのだからswapに依存せずにメモリ容量を増やすべき…と言うことでしょうか。
ただ、刹那的(特に後述するVirtualminのインストール時など)に多くのメモリを必要とする場合があり、その程度はswapで対処したいところです。
と言うことでswapを設定します。
sudo mkdir /var/swap
sudo dd if=/dev/zero of=/var/swap/swap0 bs=1M count=2048
sudo chmod 600 /var/swap/swap0
sudo mkswap /var/swap/swap0
sudo swapon /var/swap/swap0
上記では2GBのswap領域を用意していますが、必要に応じて適当に変更してください。
また上記設定だけだとシステム再起動時にswapが無効になってしまうようなので、「/etc/fstab」に以下の設定を追加しておきます。
/var/swap/swap0 swap swap defaults 0 0
Webアクセス用にインバウンドルールの追加
EC2インスタンスの生成時にセキュリティグループのインバウンドルールにSSHでのアクセス許可を設定しました。
ただ、これだけだとWeb(HTTP、HTTPS)でのアクセスができません。
加えて後述するVirtualminではコンパネへのアクセスに10000ポートを使用しますので、この許可も含めて設定の追加が必要になります。
と言うことで、改めてECインスタンス生成時に作成したセキュリティグループのインバウンドルールにソース「0.0.0.0/0」(誰でも)からポート22(HTTP)、443(HTTPS)、10000(Virtualmin)へのアクセスを許容するように設定します。
本作業はEC2インスタンス生成時にまとめて行っても問題ありませんでしたが、EC2の用途がWebサーバに限定されるものではないことを考慮して別作業としてあります。
Virtualminインストール
諸々Webサーバ運用に必要なパッケージをインストールする必要がありますが、個別に行うのは面倒ですし、今後のメンテナンスのことなども考慮してVirtualminを使用することにします。
Virtualminのインストール及びVirtualminを使用した仮想ホスト環境の構築に関しては別記事でも触れてきましたので、ここでは細かく説明しませんが、特記事項として2点。
- Virtualminのコンパネへのアクセスアカウントとして当該OS上の何らかのアカウントが必要になりますが、デフォルト状態ではパスワードが分かっているアカウントがありませんので、既存アカウントにパスワードを設定するなどして、前述の目的で使用できるようにしておきます。
- 仮想ホスト環境構築の過程でDBのインストールに関して聞かれますが、今回はRDSを使用するため、いずれのDBのインストールも「なし」で。
Laravel環境構築
Laravel環境構築に関しても過去記事で触れてきましたので今回は細かく説明しませんが、EC2において新規にLaravel環境を構築することは考え難く、開発環境で構築したLaravel環境をgit等で展開すると言うことになるかと思います。
今回の操作でもgitによるclone、pullおよびcomposerでのinstall実施が主な作業になりますが、そのためにはcomposerをインストールしておく必要があります。この作業も過去記事にありますので、詳細は割愛。
Laravel環境としてのドキュメントルートとVirtualminで構築した仮想ホストのドキュメントルート設定は合っていないと思いますので、そこを合わせた上でWebサーバを再起動し、Laravel環境へのアクセスができることまで確認します。
RDS用サブネット設定
今までの作業でとりあえずEC2上でLaravelを使ったWebアプリ環境が使用可能になりましたが、DB環境は未構築なので、ごく単純な(DBに依存しない)動作確認ができるだけです。
と言うことで、ここからはDBサーバとしてRDSインスタンスを生成し、Laravelから同インスタンス上のDBを使用できるようにしていきます。
まずはRDSのネットワーク環境としてサブネットグループが必要になりますが、さらにその前段としてサブネットグループを構成するサブネットが必要になります。
先の「サブネット設定」でも触れましたが、サブネットグループを構成するサブネットはAZが異なる2つのサブネットであることが求められるようなので、そのようにサブネットを構築します。
RDS用サブネットグループ設定
上記でRDS用のサブネットが用意できたので、次にサブネットグループの設定をします。
名前やVPCを指定したりしますが、当然ながら先の2つのサブネットを指定することが重要になります。
RDS用セキュリティグループ設定
EC2でセキュリティグループの設定を行ったようにRDSとしても通信に関する許可を行う必要があります。
インバウンドルールとして、タイプには「MySQL/Aurora」(今回はMySQL/MariaDBを使用するため)を指定することで対象ポートが3306となります。
一方で、ソースにはアドレスではなくアクセス元のEC2が属しているセキュリティグループを指定します。このようにすることで、同じセキュリティグループに属するEC2からRDSへのアクセスをまとめて許容できるようになるようです。
RDS用パラメータグループ設定
この辺から用途が今一つはっきりしなくなってくるのですが、とりあえずパラメータグループなるものを作成します。
入力項目「パラメータグループ」で対象DB(バージョン含めて)を選択できるのですが、おそらくはそのDBに必要なデフォルトパラメータを引き継いだ独自のグループを作成すると言うことなのだと思います。
RDS用オプショングループ設定
パラメータグループとの違いが分かりませんが、オプショングループなるものも作成します。
ここでも「エンジン」としてDB(今回はMariaDB)、「メジャーエンジンバージョン」として同DBとしてのバージョン(今回は10.5)を指定します。
先のパラメータグループでは「mariadb 10.5」のようにエンジンとバージョンがセットになった選択肢だったのに対し、こちらはエンジンとバージョンを別々に選択させる辺り、なぜこのような違いがあるのかが謎です。
RDSインスタンス生成
ついにRDSインスタンス生成までたどり着きました。
ただ、ここでの設定項目がかなり多く、ネット情報頼りで意味不明の設定もままあります。
ざっくり以下に列記しておきます。
- 作成方法:「標準作成」
- エンジン:MariaDB
- バージョン:最新のもの(現状はMariaDB 10.5.9)
- テンプレート:無料利用枠
- DBインスタンス識別子:適当に命名
- マスターユーザー名:root(ここも好みで)
- パスワードの自動生成:選択
- DBインスタンスクラス:db.t2.micro
- ストレージタイプ:汎用SSD
- ストレージ割り当て:20(とりあえずデフォルト)
- ストレージの自動スケーリングを有効にする:選択
- マルチ AZ 配置:選択できない(無料利用枠だから?)
- VPC:先に生成したVPC
- サブネットグループ:先に生成したサブネットグループ(ここで関係してくる)
- パブリックアクセス:なし
- 既存のVPCセキュリティグループ:先に生成したセキュリティグループ(ここで関係してくる)
- アベイラビリティーゾーン:1a
- 最初のデータベース名:必要に応じて適当に設定
- DBパラメータグループ:先に生成したパラメータグループ(ここで関係してくる)
- オプショングループ:先に生成したオプショングループ(ここで関係してくる)
- 自動バックアップの有効化:選択
- バックアップ保持期間:7日間
- バックアップウィンドウ:選択ウィンドウ
- 開始時間:19:00UTC(日本時間の午前4時)
- 期間:0.5(デフォルト値、今一つ意味不明)
- スナップショットにタグをコピー:選択(意味不明)
- 拡張モニタリングの有効化:選択
- 詳細度:60秒(デフォルト)
- モニタリングロール:デフォルト
- ログのエクスポート:エラーログのみ選択(細かな設定をせずに採取できるのはエラーログのみっぽい)
で、作成を実行し、少々待ちますが、この時マスターユーザーのパスワードが自動生成されているので、それを忘れずに記録しておきます。
DBの生成が終わったら、mysqlコマンド等でアクセスできることを確認します。
Laravel-RDS連携設定
生成したRDSインスタンス上のDB(今回はMariaDB)にLaravelからアクセスしてみます。
まずは、.envの所定の箇所に必要な情報を反映します。
DB_CONNECTION=mysql
DB_HOST=RDSダッシュボードで確認できるエンドポイント
DB_PORT=3306
DB_DATABASE=生成したDB名
DB_USERNAME=root
DB_PASSWORD=自動生成されたパスワード
上記を設定した上でmigrationなどを実行して、正しく動作すればOKです。
S3バケット生成
RDSを使用したDB環境構築と使用ができでしまえばある程度の運用は実現できます。
ロードバランサによる複数EC2での負荷分散などを行っても、DBが独立していることで登録された情報の共有もそのまま実現できます。
しかしアップロードされたファイルを管理しようと思うと、このままでは問題です。
現在の状態ではアップロードされたファイルはEC2のディスク(EBS)に保持されることになりますので、登録時と参照時で別のEC2にアクセスした場合、登録されたはずのファイルにアクセスできないことになります。
よって、アップロードされたファイルもEC2とは独立して共有可能な環境に置いておきたくなります。
上記のような環境として最初に思いつくのはS3でしょう。
また、S3は静的(バックエンドでのスクリプトによる処理を必要としない)Webサーバとしても機能できるため、Nuxtで生成したフロントエンド機能をS3に載せ、Laravelのバックエンド機能をEC2で運用し、両者が連携する形でWebサイトを運用することもできます。
と言うことで、S3にアップロードファイル管理用とフロントエンド用の2つのバケットを生成します。
バケットの生成では名前を指定するくらいですが、フロントエンド用バケットに関してはインターネットへの公開を期待するものであるため、「パブリックアクセスをすべてブロック」と言う設定を解除します(細かなブロックが設定できるようになりますが、それらも全て選択されていない状態にします)。
S3で静的ウェブサイトホスティングの有効化
上記でフロントエンド用バケットに関しては公開できるような状態にしましたが、実はこれだけでは当該バケットが静的Webサーバとして機能できる状態にはなっていない模様で、同バケットの「プロパティ」にある「静的ウェブサイトホスティング」を編集し、「静的ウェブサイトホスティング」を有効にする必要があります。
Laravel-S3連携用パッケージ追加
一方でアップロードファイル管理用バケットに対してはLaravelからのファイルの登録・参照等ができるようにする必要があります。
この辺、以前別のフレームワークで実装した際には色々と操作を実装する必要があったのですが、Laravelでは標準のStorageファサードを使用して従来のローカルディスク上でのファイルアクセスと変わらないインタフェースでS3上のファイル操作ができるようになっています。
ただ、そのための環境構築はある程度必要で、ここではそれを行っていきます。
まずはS3連携に必要なパッケージとして以下の2つをcomposerで追加しておきます。
- composer require league/flysystem-aws-s3-v3 ~1.0
- composer require league/flysystem-cached-adapter ~1.0
前者だけでも良いのかもしれませんが、公式ページを見ると「パフォーマンスを上げるために必要」っぽいことが書かれているので、とりあえず後者もインストールしておきます。
Laravel-S3連携用IAMロール設定
EC2からS3にアクセスできるようにするためには、関連する内容を許可するIAMロールが必要になる模様で、それを作成します。
ロール作成において、「ユースケースの選択」でEC2を選択し、ポリシーの作成ではJSONで以下の内容を設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::*"
]
},
{
"Effect": "Allow",
"Action": "*",
"Resource": [
"arn:aws:s3:::(アップロードファイル管理用バケット)",
"arn:aws:s3:::(アップロードファイル管理用バケット)/*"
]
}
]
}
上記ポリシーに対して名前をつけて、ロール作成の流れに戻り、先ほど作成したポリシーを選択しつつロールに名前を付けて、ロール作成は終了です。
Laravel-S3連携用IAMロールをEC2にアタッチ
上記で生成したロールをLaravelを搭載したEC2にアタッチします。
対象EC2に対して「アクション」から「セキュリティ」を選択し、「IAMロールの変更」で先に生成したロールを選択します。
Laravel-S3連携
上記で環境面の準備が整いましたので、いよいよLaravelからS3にアクセスしてみます。
まずは、.env内でS3に関する設定をします。
AWS_ACCESS_KEY_ID=(設定不要)
AWS_SECRET_ACCESS_KEY=(設定不要)
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=(アップロードファイル管理用バケット名)
ロールの設定を行ったことで、「AWS_ACCESS_KEY_ID」「AWS_SECRET_ACCESS_KEY」の設定は不要になるようです。
また、config/filesystems.php内のdisks設定でもs3の設定を行います。
そもそも、上記ファイル内には最初からs3の設定が存在するのですが、個人的にはdiskの名称として直接「s3」が見えていることには違和感があります。もし、何らかの事情でファイル管理環境をs3から別の環境に変更した場合、s3と言う名称のまま別の環境に合わせて書き換えるのも変ですし、diskとしてs3を指定している箇所を探し出して書き換えると言う作業も面倒です。
よって、今回は以下のような設定を行ってみました。
'private' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
],
Storageファサードのdiskメソッドで指定する名称は「private」で、具体的な環境はS3になります。
なお、開発環境においては、主にphpunitを用いて動作確認を行いますが、その際にはsetUp等で「Storage::fake(‘private’);」と指定することでファイル管理環境が自動的に「storage/framework/testing/disks/private」に変更されます。よって、上記のような設定を行っていても、S3が使用できない環境でもファイルの読み書きを伴うロジックの検証が行えます。
上記設定を行った上で、Laravel内の処理からStorageファサードを使用してファイルの登録を行い、対象としたS3バケットに当該ファイルが登録されればOKです。
所感
今回は兎にも角にもAWS上でEC2+RDS+S3でLaravelのWebアプリ環境を構築することが目的でしたので、細かな点は気にせず、とにかくWeb経由でEC2上のLaravelのアクションが呼び出せ、その延長でRDS上のDBにアクセスでき、かつS3にもファイルが登録できるところまで確認しました。
ここには書きませんでしたが、sshでのアクセスがうまくできなかったり、RDSへのアクセスがうまくできなかったりと、色々な困難を乗り越える必要がありましたし、いまだに謎の設定も多数あります。
加えて言えば、ELBやCloudFrontでの負荷分散も考慮したいですし、DNSでの名前解決も行う必要があります。後者に関しては、例えばお名前ドットコムで取得したドメインを使用したい場合、Route 53なる機能の設定やお名前ドットコムとRoute 53の連携なども必要になってくるようです。
AWSマスターとは言わないまでも、AWS触れまスターくらいは目指したいものですが、なかなか道のりは遠そうです…