けけずんセルフハッキング

エンジニアっぽい雰囲気を醸しだしているかのようなブログです!

SPA な Web アプリを継続的デリバリーする環境を作る (3) 〜 インフラ( AWS )編

概要

SPA な Web アプリを継続的デリバリーする環境を作るシリーズ第三回、インフラ編。 序章はこちら。

kkznch.hatenablog.com

全4部構成でお届けする。

今回は長いぞ。

ハンズオンするぞ

AWS の画面からポチポチ操作してインフラを構築しよう。 そもそも AWS に登録してないという人は登録作業からやってくれ。 AWS 使いたくないという人は画面をそっと閉じよう。

今回は図中の AWS Cloud の枠内の構築をしていく。

f:id:kkznch:20200222125448p:plain

AWS でポチポチする際に何かしら項目への入力が求められたりするが、本記事で記述がないものに関してはデフォルト値を使用してくれ。 なお設定ミスって多額の料金を請求されても責任はとらんので、気をつけてね。

本当は環境の構築に CloudFormation または Terraform を使おうと思っていたんだけど、そこまでキャッチアップしきれていないのですまんな。 誰か代わりに書いて説明も含めて公開してくれ。

バックエンドのインフラ作るよ

バックエンドで動作するWeb サーバ、 APP サーバの構築をしていく。 一見サーバが二つあるかのように書いているが、その実は一つのサーバ内で Web サーバと APP サーバが動いている。 分けてもいいけど今は分ける必要ないので一つのサーバ内で動作させるよ。

VPC でネットワークの構築

心を無にして AWS のコンソールから VPC の画面を開き、各項目を以下の内容でポチポチ作っていこう。

  • VPC の作成
    • 名前タグ: app-vpc
    • IPv4 CIDR ブロック:10.0.0.0/16
  • サブネット の作成
    • 名前タグ:app-subnet
    • VPC:app-vpc
    • IPv4 CIDR ブロック:10.0.128.0/24
  • インターネットゲートウェイ の作成
    • 名前タグ:app-igw
    • 作成後は app-igw を選択し、「アクション」ボタンから app-vpc にアタッチする
  • ルートテーブル の作成
    • 名前タグ:app-route-table
    • VPC:app-vpc
    • 「ルート」タブから編集を開き、以下の項目を入力してルートを設定する
      • 送信先:0.0.0.0/0
      • ターゲット:app-igw
    • 「サブネットの関連付け」タブから編集を開き、app-subnet を関連付ける
  • Elastic IP の取得
    • IPv4アドレスプール:Amazonプール
    • ここで取得した IP アドレスを覚えておく(以下、この IP アドレスを ELASTIC_IP として記述する)

EC2 でサーバの構築

心を無にして AWS のコンソールから EC2 の画面を開き、各項目を以下の内容でポチポチ作っていこう。

  • EC2 の作成
    • AMI:Amazon Linux 2 AMI (HVM), SSD Volume Type
    • インスタンスタイプ:t2.micro
    • インスタンスの詳細の設定
      • ネットワーク:app-vpc
      • サブネット:app-subnet
    • タグの追加
      • キー:NAME
      • 値:app-ec2
    • セキュリティグループ
      • セキュリティグループ名:app-sg-ec2
      • 説明:app-sg-ec2
      • ルール
        • タイプに SSH, HTTP を設定し、ソースに 0.0.0.0/0 を設定する
    • キーペア
      • 「新しいキーペアの作成」を選択する
      • キーペア名:app-keypair-ec2
      • キーペアを 絶対にダウンロードしてね
  • Elastic IP で EC2 に IP を紐付け

ここまでの手順で外部から EC2 にアクセスすることができるようになる。

EC2 に SSH で接続する

ダウンロードしたキーペア app-keypair-ec2.pem をそのまま使って SSH 接続しようとすると鍵のパーミッションおかしいよって怒られるので、app-keypair-ec2.pem の権限を変更する必要がある。 便宜上、app-keypair-ec2.pem はホームディレクトリに置いてあるとする。

$ chmod 600 ~/app-keypair-ec2.pem

権限を変更したら ssh コマンドにオプションでキーペアを指定して実行する。 ELASTIC_IP は先程 AWS の Elastic IP で取得、EC2 に割り当てした IP アドレスだよ。

$ ssh ec2-user@ELASTIC_IP -i ~/app-keypair-ec2.pem

以降はローカルと EC2 どちらでコマンドを実行したかを区別するために、プロンプトの頭が $ ならローカルでの実行、 ec2$ なら EC2 での実行ということにする。

EC2 で Nginx と Laravel の実行環境の構築

SSH 接続ができたら EC2 内部でポチポチ作業していく。

yum の初期設定とアップデート

始めに yum が文字化けするので以下のように各ファイルを直して ~/.bashrc を読み込み直す。 /etc/locale.conf は編集する際に sudo コマンドで管理者権限を付与しないと保存できないはずなので、sudo vi /etc/locale.conf みたいな感じで vi を起動して編集しよう。

/etc/locale.conf

- LANG=en_US.UTF-8
+ LANG=ja_JP.UTF-8
+ LC_CTYPE=ja_JP.UTF-8

~/.bashrc は管理者権限がなくても大丈夫なのでそのまま編集しよう。

~/.bashrc

+ # 末尾に追加する
+ source /etc/locale.conf

上記二つのファイルを編集した後、以下のコマンドを実行して設定を反映させよう。

ec2$ source ~/.bashrc

次に yum をアップデートする。 yum でインストールやアップデートをする際は管理者権限がないと実行できないので、sudo を付けて実行しよう。

ec2$ sudo yum update -y

nginx のインストールと設定

yum のアップデート完了後は nginx をインストールして設定をする。 Amazon Linux 2 は amazon-linux-extras というコマンドを提供しているため、今回はそのコマンドを叩いて nginx のインストールを行う。

ec2$ sudo amazon-linux-extras install nginx1.12

インストール後は nginx の設定ファイルを以下のように編集しよう。

/etc/nginx/nginx.conf

- server {
-     listen       80 default_server;
-     listen       [::]:80 default_server;
-     server_name  _;
-     root         /usr/share/nginx/html;
-
-     # Load configuration files for the default server block.
-     include /etc/nginx/default.d/*.conf;
- 
-     location / {
-     }
- 
-     error_page 404 /404.html;
-         location = /40x.html {
-     }
- 
-     error_page 500 502 503 504 /50x.html;
-         location = /50x.html {
-     }
- }
+ server {
+     server_name localhost;
+     listen 80;
+     root /app/public;
+     index index.php index.html;
+ 
+     error_log  /var/log/nginx/error.log;
+     access_log /var/log/nginx/access.log;
+ 
+     location / {
+         try_files $uri $uri/ /index.php$is_args$args;
+     }
+
+     location ~ \.php$ {
+         fastcgi_split_path_info ^(.+\.php)(/.+)$;
+         fastcgi_pass unix:/var/run/php-fpm/www.sock;
+         fastcgi_index index.php;
+         include fastcgi_params;
+         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+         fastcgi_param PATH_INFO $fastcgi_path_info;
+     }
+ }

nginx から php-fpm へリクエストを流す的なことを書いている。 そもそも nginx と php-fpm がなんなんだよって人はこちらを参考にしてくれ。

qiita.com

php, php-fpm のインストールと設定

続いて php7.3 、Laravel に必要な php の拡張を EC2 にそれぞれインストールしていく。

ec2$ sudo amazon-linux-extras install php7.3
ec2$ sudo yum install -y php-bcmath php-mbstring php-xml

インストール後は php-fpm の設定ファイルを以下のように編集する。

/etc/php-fpm.d/www.conf

- user = apache
- user = apache
+ user = nginx
+ group = nginx    
- ;listen.owner = nobody
- ;listen.group = nobody
+ listen.owner = nginx
+ listen.group = nginx

git と composer のインストール

GitLab からリポジトリを取ってくるために git をインストールする。 yum でインストールするだけ。

ec2$ sudo yum install -y git

それと PHP のパッケージを管理するために composer をインストールする。 こいつは yum ではインストールできないので、公式の手順に沿ってインストールする。

ec2$ curl -sS https://getcomposer.org/installer | php
ec2$ sudo mv composer.phar /usr/local/bin/composer

ここまででバックエンドのインフラは準備できた。 後はバックエンド用リポジトリを手動でデプロイし、 Web サーバと APP サーバを起動してあげればよい。

バックエンド用リポジトリの手動デプロイ

EC2 から GitLab のリポジトリに公開鍵認証方式でアクセスするために、 EC2 上で鍵を作成して GitLab のリポジトリに公開鍵を設置する。 公開鍵認証がわからない人はここを見てくれ。

knowledge.sakura.ad.jp

鍵生成コマンドはこれ。

ec2$ ssh-keygen -t rsa -b 4096
# 入力を求められても全部エンターでオッケー

作った鍵を cat で出力してコピーし、GitLab のバックエンド用リポジトリから「Settings」 → 「Repositories」を開いて「Deploy Keys」の項目に公開鍵を貼り付ける。

ec2$ cat ~/.ssh/id_rsa.pub

git からバックエンド用リポジトリを clone し、/app にフォルダを移動する。

ec2$ cd ~/
ec2$ git clone git@gitlab.com:自分のユーザ名/app-backend.git
ec2$ sudo mv app-backend /app

/app に移動して env ファイルをコピーし、composer コマンドを実行する。 これと合わせてストレージのリンクや Laravel の中で使われる鍵の生成なども行う。

ec2$ cd /app
ec2$ cp .env.example .env
ec2$ composer install
ec2$ chmod -R 777 storage
ec2$ php artisan storage:link
ec2$ php artisan key:generate

ここまでで手動デプロイが完了したことになる。

nginx と php-fpm の起動、自動起動の設定

以下のコマンドで nginx と php-fpm を起動する。

ec2$ sudo systemctl start nginx
ec2$ sudo systemctl start php-fpm

ついでに nginx と php-fpm の自動起動を設定する。 これで EC2 が再起動した際に自動で nginx, php-fpm が立ち上がるようになる。

ec2$ sudo systemctl enable nginx
ec2$ sudo systemctl enable php-fpm

API の実行確認

ここまで進めると上手くいっていれば API が実行できるようになっているはず。 以下のコマンドを実行してくれ。 ELASTIC_IP は先程 AWS の Elastic IP で取得、EC2 に割り当てした IP アドレスよ。

ec2$ curl http://ELASTIC_IP/api

{"body":"Hello, World!","datetime":"2020-02-20T09:00:00.000000Z"}

上記のように値が返ってきていれば大丈夫。 返ってこない人は最初から一個ずつ確認して頑張ってくれ。

フロントエンドのインフラ作るよ

インフラっていうほど大それたものでもないが、フロントエンドのデプロイ先となる S3 バケットの作成をする。 S3 バケットにビルドした成果物を置き、公開設定と Web サイトのホスティング設定をするだけでユーザーが外部からアクセスすると SPA な画面を返すようになる。

S3 でバケットの作成

AWS で S3 の画面から S3 バケットを作成する。 なおバケット名はユニークである必要があるため、他と衝突しないバケット名を考えよう。

本記事では上記の名前で説明を進めていくが、おそらく既に使用されているので実際に使用することはできないはず。 実際に作成する際はバケット名の末尾に現在の日付時刻を入れたり、自分のあだ名をローマ字にしたものを間に挟むなりして工夫してくれ。

S3 で Web サイトのホスティング設定

外部から S3 にアクセスした際に、指定した Web ページを返すように設定していく。 作成した S3 バケットを選択し、以下の手順で設定をしていく。

  • 「プロパティ」タブ → 「Static website hosting」を選択する
  • 「このバケットを使用してウェブサイトをホストする」を選択する
  • 「インデックスドキュメント」に index.html と入力して保存する

これで Web サイトのホスティングの設定は以上、簡単である。

ブロックパブリックアクセスの設定

この機能がいまいち理解できていない。 おそらく公開設定などをミスって意図していないものが公開されないようにするための機能だと思うが、どうなんだろうか。 気になる人は以下を参照してくれ。

dev.classmethod.jp

ではでは、作成した S3 バケットを選択して以下の手順で設定をしていこう。

  • 「アクセス権限」タブ → 「ブロックパブリックアクセス」を選択する
  • 全てのチェックボックスからチェックを外して保存する

お分かりだろうか。 細かい設定が面倒くさいので、全てを公開した。 大切なファイルとかが人に見られても責任は一切とりませんので、お気をつけて。

バケットポリシーの設定

バケット内にあるオブジェクトにアクセスできるかできないかなど細かい設定をバケットポリシーで設定する。 作成した S3 バケットを選択し、以下の手順で設定をしていく。

  • 「アクセス権限」タブ → 「バケットポリシー」を選択し、以下を入力して保存する
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::app-bucket/*"
        }
    ]
}

"Resource": "arn:aws:s3:::app-bucket/*" この部分は自分が作成したバケットの名前を入れてあげること。 ちなみにこちらも全てのオブジェクトを公開するよう設定している。

フロントエンド用リポジトリの手動デプロイ

S3 の準備ができたらフロントエンド用リポジトリを手動デプロイしてみる。 その際に S3 にデプロイするための権限と、デプロイするためのコマンドが必要になるのでその準備から行う。

S3 へアクセスするユーザの作成と設定

AWS の IAM というサービスを使って S3 へアクセスするユーザの作成と設定をする。 IAM のページを開き、以下の内容でユーザを作ってくれ。

  • ユーザの作成
    • ユーザ名:app-s3-user
    • 「プログラムによるアクセス」にチェックを入れる
  • ポリシーのアタッチ
    • 「既存のポリシーを直接アタッチ」
    • 「AmazonS3FullAccess」にチェックを入れる
  • アクセス情報を覚えておく(それぞれの環境で異なるよ)
    • アクセスキーID:access_key_id
    • シークレットアクセスキー:secret_access_key

AWS CLI のインストールと設定

バックエンドとは異なり、フロントエンドはこの手順を踏まなくても自動デプロイまで行う環境を作ればデプロイされるようになるのだが、現段階で作った環境が適切に動作するか確認するために手動でデプロイして動作確認しておく。 手動デプロイはローカルから行うので注意。

S3 へ手動デプロイする際には AWS CLI を用いてデプロイを行う。 mac だと brew を使って AWS CLI をインストールできる。 brew 知らんし入ってないという人はこれ見て頑張ってくれ。

brew.sh

では AWS CLI をインストールしよう。

$ brew install acscli

これだけで入る。 なお mac 以外でインストールする場合は手順が異なると思うので、調べて頑張ってくれ。

AWS CLI をインストールした後は初期設定を行う。 以下のコマンドを実行し、ぽちぽち入力していく。 AWS Access Key IDAWS Secret Access Key には先程取得したアクセスキー ID とシークレットアクセスキーを入力する。

$ aws configure

AWS Access Key ID [None]: access_key_id
AWS Secret Access Key [None]: secret_access_key
Default region name [None]: ap-northeast-1
Default output format [None]: json

これでコマンドの準備はできた。

S3 への手動デプロイするぞ

ローカルでフロントエンド用リポジトリの場所に移動し、ビルドを実行するんだけどその前に本番環境用の env ファイルを書き換える必要ありけり。 API を叩く向き先になる部分、こいつに AWS の Elastic IP で取得した IP アドレスを入れておく。 ELASTIC_IP の部分には取得した IP アドレスをそのまま書いてね。

.env.production

NODE_ENV=production
- VUE_APP_API_BASE_URL=http://あとでEC2のIPアドレス入れるよ
+ VUE_APP_API_BASE_URL=http://ELASTIC_IP

これでビルドする。

$ npm run build

ビルドが実行されるとリポジトリ内に dist というフォルダが作成されているはず。 以下のコマンドを実行して dist を S3 に設置する。

$ aws s3 sync --delete dist/ s3://app-bucket/

これでフロントエンドの手動でのデプロイが完了。

サイトにアクセスして確認

ここまで設定を済ませるとアプリ自体は一通り完成したことになる。 それでは実際にサイトにアクセスして SPA な画面を表示し、API を実行して取得した値が表示されているか確認しよう。

サイトの URL は S3 バケットの Web サイトのホスティング欄に記述されているので、以下の手順で URL を探してページを開こう。

  • 「プロパティ」タブを選択する → 「Static website hosting」を選択する
  • 「エンドポイント」に記述された URL をクリックして開く

これで以下のように表示されれば成功、おつかれさま。

f:id:kkznch:20200223171823p:plain