Vue.js で作ったSPAを静的サイトとしてAWS S3(+ CloudFront)からホスティングすれば、ランニングコストを低く抑えられるのでオススメです。アクセス数にもよりますが、個人のウェブサイトなどであれば100円程度/月で済んだりします。
今回はそんな Vue.js の AWS S3 へのデプロイ手順と、CloudFront を使った独自ドメイン + HTTPS 対応方法、さらに CircleCI を使った CI/CD の導入までザッと整理します。
最終的な本番環境イメージ
最終的な本番環境の構成イメージは次のとおりです。
Vue.js アプリの準備 (前提)
前提として、ここでは Vue.js アプリのデプロイ手順を扱います。
Vue.js の基礎知識については、過去の記事をご覧ください。
1. AWS S3 へのデプロイ手順
まずは単純な AWS S3 へのデプロイ手順です。
S3の準備
本番サーバとなるS3のセットアップです。
バケット新規作成
S3 のコンソールからバケットを新規作成します。
「バケット」はドメインと同じようなネーミングにしておくと分かりやすいです。「ブロックパブリックアクセス」は静的サイトとして公開するので全てオフにします。
静的サイトとして使う
バケット作成後、プロパティタブから「静的ウェブサイトホスティング」を選択して、静的サイトとして使えるようにします。
設定項目は次のとおりです。
- インデックスドキュメント(必須):
index.html
- エラードキュメント(必須):
error.html
バケットポリシーの追加
アクセス権限タブから「バケットポリシー」を選択し、次のようにJSONコードでポリシーを追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}
]
}
S3へのデプロイ
これで最低限の本番環境は整ったので、開発コンソールで本番用コードをビルドします。
npm run build
生成される dist
ディレクトリを先ほど作った S3 にアップします。
そしてブラウザから次のURLを叩けば、 Vue.js の静的サイトが表示されます。
<bucket-name>.s3-website-<AWS-region>.amazonaws.com/dist
例えば、バケット名が「mybucket」、リージョンが「東京(ap-northeast-1)」の場合は、S3のURLは次のようになります。
mybucket.s3-website-ap-northeast-1.amazonaws.com/dist
これでデプロイ自体は完了です。
2. CloudFront での独自ドメイン + HTTPS化手順
次にデプロイしたS3の静的サイトを、独自ドメイン + HTTPS でアクセスできるようにします。
これは Route53 + AWS Certificate Manager + CloudFront で実現できます。
Route53 独自ドメイン登録
ケース1: Route53 で新規取得
コンソールの登録済みドメインページの「ドメインの登録」から独自ドメインを購入します。
ケース2: Route53 のサブドメインを使う
Route53 に登録済のドメインのサブドメインを使う場合は、必要な追加設定はありません。
ケース3: 他社ドメインを Route 53 に登録
他社で取得したドメインを使う場合、「Route53」と「他社ネームサーバ」のそれぞれで設定が必要です。
Route 53 での設定
コンソールのホストゾーンページの「ホストゾーンの作成」から他社取得ドメインを登録します。
タイプ「NS」と「SOA」がデフォルトで自動生成されます。「NS」は他社ネームサーバに入力する情報です(次のような4つの値)。
ns-2xx.awsdns-26.com.
ns-1xxx.awsdns-36.org.
ns-1xxx.awsdns-33.co.uk.
ns-8xx.awsdns-40.net.
他社ネームサーバでの設定
Route53 で自動生成された NS レコードの値を、他社ネームサーバ1〜4として登録します。
AWS Certificate Manager で証明書登録
AWS Certificate Manager のコンソールにて、Route53 に登録したドメインの証明書を取得します。
(注意)CloudFront で HTTPS 化をする場合、必ず「US EAST」で登録しないといけません。東京リージョンを選ばないように注意しましょう。
*.独自ドメイン
と設定すれば、サブドメイン分もまとめて登録できます。
証明書の発行が完了すると、DNS 設定が書かれたCSVファイルをダウンロードできます。
Route53 に CNAME 追加
ダウンロードした DNS 設定の内容にしたがって、Route53 に CNAME を追加します。
ドメインのホストゾーン画面から「レコード設定の作成」を選択し、次のように設定します。
- 名前: DNS 設定の
Record Name
(例_3560a31fe01xxxxxxxxxx
) - タイプ:
CNAME
- 値: DNS 設定の
Record Value
(例_xxx.acm-validations.aws.
)
(僕の環境下では、追加した CNAME はすぐに反映されました。)
CloudFront の設定
ここまで来て、やっと CloudFront の設定ができます。
CloudFront のコンソールからディストリビューションを追加します。「Web」を選択して、次のように設定していきます。
Origin Settings
- Origin Domain Name: 本番 S3 バケットを選択
- 以外、デフォルト
Default Cache Behavior Settings
- Viewer Protocol Policy: Redirect HTTP to HTTPS
- 以外、デフォルト
Distribution Settings
- Alternate Domain Names (CNAMEs): 本番で利用するドメイン
- SSL Certificat: Custom SSL Certificate(ACM で作った証明書を選択)
- Default Root Object:
dist/index.html
- 以外、デフォルト
以上の内容でディストリビューションを作成します。
作成後、CloudFront ディストリビューションの「Domain Name」をコピーします。
Route53 に CloudFront のエンドポイントを追加
Route53 ホストゾーンの「レコードセットの追加」から、先ほどコピーした「Domain Name」をAレコードとして追加します。
- 名前: 独自ドメイン(もしくはサブドメイン)
- タイプ: A
- 値: CloudFront ディストリビューションの「Domain Name」
ブラウザから独自ドメインを叩いて、S3の静的サイトがHTTPSで表示できればOKです。
403, 404エラーに対応
Failed to load resource: the server responded with a status of 403
vue-router
などで URL が動的に変わる場合、何も設定をしていないとブラウザのコンソール上でこういったエラーが出てしまいます。
例えば、コンタクトページ /contact
を Vue.js SPA 内で作った場合、S3上に dist/contact.html
という実際のファイルがないので、このようなエラーが出ると考えられます。
解決方法
CloudFront でエラーページの設定すれば、このエラーを解決できます。
CloudFronnt ディストリビューションの「Error Pages」タブからカスタムエラーレスポンスを作成します。
- HTTP Error Code: 403, 404(2回に分けて作成)
- Customize Error Response: Yes
- Response Page Path:
/
- HTTP Response Code: 200 OK
3. CircleCI での CI/CD 導入手順
コードを更新するたびに手作業でテストして、ビルドして、デプロイするのは大変なので、CircleCI で一連の作業を全て自動化します。
事前準備: アカウント連携
アカウントを持っていない場合は、CircleCI のホームページからサインアップして、自身の GitHub アカウントと連携させます。
IAM ポリシーの追加
CircleCI に付与する S3 のファイル更新権限を準備します。
IAM のコンソール から「ポリシーの作成」を選択し、次の JSON でポリシー内容を規定します。(<bucketname>
は各自のバケット名)
{
"Version": "2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":[
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource":"arn:aws:s3:::<bucketname>"
},
{
"Effect":"Allow",
"Action":[
"s3:PutObject",
"s3:PutObjectAcl",
"s3:DeleteObject"
],
"Resource":"arn:aws:s3:::<bucketname>/*"
}
]
}
ポリシー作成後に表示される「アクセスキーID」と「シークレットアクセスキー」をコピーしておきます。
CircleCI の権限設定
CircleCI プロジェクトの設定画面「AWS Permissions」で、先ほど生成した「アクセスキーID」と「シークレットアクセスキー」を登録します。
CircleCI の CI/CD 設定
プロジェクトのルートパスに .circleci/config.yml
を作成し、CircleCI の CI/CD 設定を書きます。
AWS S3 に自動デプロイする設定は次のとおりです。
(注意)YAMLファイルではインデントが重要です
version: 2.1
orbs:
aws-s3: circleci/aws-s3@1.0.11
executors:
default:
docker:
- image: circleci/node:10.17.0
- image: circleci/python:2.7
commands:
npm_install:
steps:
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
- run: npm install
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
paths:
- node_modules
jobs:
build:
executor: default
working_directory: ~/repo
steps:
- checkout
- npm_install
- run: npm run test
deploy:
executor: default
working_directory: ~/repo
steps:
- checkout
- npm_install
- run:
name: build
command: API_ID=$API_ID API_TOKEN=$API_TOKEN npm run build
- aws-s3/sync:
from: dist
to: s3://mybucket/dist
overwrite: true
workflows:
version: 2
continuous-deploy:
jobs:
- build
- deploy:
requires:
- build
filters:
branches:
only: master
全般
orbs
は、デプロイに必要なコマンドなどの設定がまとめられたパッケージで、ここでは circleci/aws-s3@1.0.11
を使っています。
コマンドの実行環境には docker
の circleci/node:10.17.0
と circleci/python:2.7
を使っています。
jobs
で build
プロセス(CI)と deploy
プロセス(CD)を定義し、workflows
で処理の順番を指定しています。
commands
では、各プロセスで実行するコマンドの実行内容を定義しています。
CI (継続的インデグレーション)
build
プロセスの部分です。npm_install
して npm run test
するだけの単純な設定です。
これで GitHub に push されたコードに対して自動テストが実行されます。(マージ前のブランチ画面でテストが成功したか確認できます。)
CD (継続的デリバリー)
deploy
プロセスの部分です。
まず、npm_install
し、npm run build
で本番用コード dist
を生成します。
そのビルドしたコードを aws-s3/sync
で S3 にアップロードします。
workflows
で filters: branches: only: master
を指定しているので、master ブランチへのマージのみをトリガーとして、この deploy
プロセスが走ります。
環境変数の設定
.circleci/config.yml
で使う環境変数は、CircleCI の管理画面(プロジェクトページ > Environment Variables)で定義します。
.env ファイルの取り扱い
Vue.js アプリで使う環境変数は、.env
ファイル内に VUE_APP_
の接頭辞で定義します(公式ドキュメントはこちら)。
この .env
ファイルには API キーなどの機密情報が含まれるので、通常は .gitignore
で git の管理対象から外します。
一方、CircleCI では GitHub から本番用コードをビルドするので、.env
がないと問題が発生してしまいます。
これを回避するために、npm run build
実行時に CircleCI の管理画面で定義した環境変数を渡してあげます。
例えば、API_ID
と API_TOKEN
という環境変数を定義した場合は、次のようになります。
command: API_ID=$API_ID API_TOKEN=$API_TOKEN npm run build
なお、渡された環境変数を Vue.js 内で使うには、webpack.config.js
に webpack.DefinePlugin
を追加しないといけません。(これについての詳細は、今後別記事にまとめます。)
※ もっと効率的な環境変数の渡し方があれば教えていただきたいです!
デプロイ動作確認
コードを変更して GitHub にプッシュし、master ブランチにマージさせます。
そのマージをトリガーに処理が走れば、CircleCI の管理画面にステータスが表示されます。
deploy
プロセスまで完了したら、変更が本番環境に反映されます。
本番ファイル更新がすぐに反映されない?
S3 の本番ファイルを更新しても、CloudFront のエッジサーバ上にキャッシュが残っていると、その更新は即座に反映されません。
CloudFront の Invalidation を実行すれば、このキャッシュをリセットできます。より詳しい内容は以下の記事でまとめています。