この記事は リクルートライフスタイル Advent Calendar 2016 の15日目の記事です。
@MomokoAsai です。社内ではFirebaseの推進を行っております。
それではさっそく本題に。
実現すること
**「Github, CircleCI, S3, CloudFront, Middlemanを活用して、https対応のサーバレスで自動deployなWEBサイト構築」**です。
コーポレイトサイトなどの動的要素がないサイトは、簡単実装&低コスト運用が出来るような構成にしてしまいましょう。
そして、https対応をしていると、SEO的な優遇があり、将来的には必須になりそうです。
- Google はChromium Blog 「Moving Towards a More Secure Web」で全 HTTP サイト(非SSL/TLS サイト)に対して警告を表示させることを公言。
- Googleウェブマスター向け公式ブログにて、HTTPSをSEOで優遇することを言及。
https化も行ってしまいましょう。
全体構成
Githubにpushすると、それをフックにCircleCIがBuild & Deployを行います。
Deploy先はS3で、push対象ブランチによって、検証環境・本番環境のどちらかにDeployが行われます。
ACMでSSL(TLS)証明書を発行・自動更新を行い、CloudFrontを通してS3のサイトにアクセスします。
それぞれの役割
サービス | 役割 |
---|---|
モ | 私。生きること |
Github | ソースコード管理 |
CircleCI | Build & Deploy |
AWS S3 | Webホスティング |
AWS CloudFront | キャッシュ & HTTP/2配信 |
AWS ACM | SSL証明書 |
AWS Route53 | ドメイン管理 |
AWSサービスについて補足
-
ACM (AWS Certificate Manage)
- 2016年5月に東京リージョンがリリース
- SSL(TLS)証明書を無料で発行
- さらに、証明書の更新を自動で行ってくるため、メンテナンスフリー
-
CloudFront
- 2016年9月に東京リージョンでhttp/2プロトコルを使った配信が可能になりました。
アプリケーション構成
- Ruby 2.3.3
- static site generatorsのMiddlemanを使用
- gem middleman で静的サイトをジェネレート
- gem middleman-s3_sync を使ってS3へuploadも簡単に出来るよう設定
$ ruby -version
ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin16]
$ bundle exec gem list middleman
*** LOCAL GEMS ***
middleman (4.1.11)
middleman-cli (4.1.11)
middleman-core (4.1.11)
middleman-s3_sync (4.0.3)
Middlemanですが、弊社Tech Blogの作成にも使われております。
手順
1. Github
今回用意するブランチは以下です。
ブランチ名 | 用途 |
---|---|
master | 本番にReleaseされるブランチ |
staging | 検証環境にReleaseされるブランチ |
feature/xxx | 開発用ブランチ |
基本はGithub flowで開発しているのですが、
今回は独自に 「staging」 というブランチを用意し、検証環境用のブランチも作成する方針にしています。
2. CircleCI
2-1. Build & Deploy設定
指定されたブランチへのgit push
をトリガーに、Build & Deploy 処理を実行するよう設定します。
...
deployment:
production:
branch: master
commands:
- bundle exec middleman build
- bundle exec middleman s3_sync -e production
staging:
branch: staging
commands:
- bundle exec middleman build
- bundle exec middleman s3_sync -e staging
2-2. AWSへのAccess設定
次に、CircleCIがAWS S3にアクセスするための Access Key / Secret Key 設定を行います。
「検証環境」と「本番環境」それぞれ作成します。
最終的にはCloudFront経由でのAccessになるため、バケット名をドメインに合わせる必要はありません。
❒ AWS側設定
Access Key / Secret Keyの発行を行います。AWS IAM
を使います。
- 検証環境用のAWSのIAMからユーザーを作成
- 作成ユーザー(or所属グループ)に
AmazonS3FullAccess
のポリシーを追加 - Access Key / Secret Key をユーザーに発行
❒ アプリケーションの設定
gem middleman-s3_sync
を使ってS3にuploadが出来るように処理を記載します。
Deployコマンドで指定した「bundle exec middleman s3_sync -e [環境名]」の環境名で出し分け設定が出来ます。
CircleCIの環境変数に登録した値は、RubyのコードからENV['HOGE']
で取得することが可能です。(設定方法後述)
...
configure :staging do
activate :s3_sync do |s3_sync|
s3_sync.acl = 'private'
s3_sync.bucket = ENV['AWS_S3_BUCKET_NAME_STAGING']
s3_sync.region = ENV['AWS_S3_REGION_STAGING']
s3_sync.aws_access_key_id = ENV['AWS_S3_ACCESS_KEY_STAGING']
s3_sync.aws_secret_access_key = ENV['AWS_S3_SECRET_KEY_STAGING']
end
end
configure :production do
activate :s3_sync do |s3_sync|
s3_sync.acl = 'private'
s3_sync.bucket = ENV['AWS_S3_BUCKET_NAME_PRODUCTION']
s3_sync.region = ENV['AWS_S3_REGION_PRODUCTION']
s3_sync.aws_access_key_id = ENV['AWS_S3_ACCESS_KEY_PRODUCTION']
s3_sync.aws_secret_access_key = ENV['AWS_S3_SECRET_KEY_PRODUCTION']
end
end
❒ CircleCI側設定
AWSで発行した Access Key / Secret Key を。CircleCIの環境変数に登録します。
S3のBucket名とRegionは、S3のBucket作成(後述)してから追加します。
CircleCIの設定詳細については公式ドキュメントを参照ください
3. AWS
3-1. S3
❒ Bucket作成
静的Webサイトホスティングを行うBucketを作成します。
作成が完了したら、CircleCIの環境変数に設定することをお忘れなく。
❒ 静的ウェブサイトホスティング機能設定
S3で静的ウェブサイトホスティング機能を有効にします。(本番環境、検証環境)
この時、「インデックスドキュメント」を設定します。「エラードキュメント」は”あると尚良し”という感じです。
S3へのAccess制限はCloudFront(後述)の設定で行います。
S3の設定はここまで。
3-2. CloudFront
S3へのアクセスはCloudFront経由のみ許可するよう設定していきます。
CloudFront経由からのアクセスにすることで
・ CDNエッジサーバーにキャッシュされることで高速化
・ S3のみより料金も安くなりやすい(S3へのリクエスト回数が減るため)
という利点があります。
特に、動画や音楽などの大容量配信を行う場合はCloudFront経由だと「ボリューミーディスカウント」が効くことがあり、お得です。
❒ Origin Settings
オリジンの設定を行います。
- 静的サイトの配信のため、配信方法は 「Web」を選択
-
Origin Domain Name にS3バケットの静的ウェブサイトホスティング endpointを指定
→ CloudFrontがS3をオリジンサーバーとして認識し、S3を見に行くようになります。 - Origin Access Identity を Create a New Identity にし、アクセス制御の設定を新規作成
-
Grant Read Permissions on Bucket を Yes, Update Bucket Policyに設定
→ この設定でCloudFrontにS3バケット内のオブジェクト読み取り許可が付与されます。
❒ Default Cache Behavior Settings
キャッシュ設定、配信プロトコルの設定を行います。
-
配信プロトコルを指定
★ 「Redirect HTTP to HTTPS」を推奨しますが、サイトに合わせて設定してください。 -
許可するHTTP methodを決める
★ お問い合わせフォーム等がある場合はPOSTの許可を忘れずに行いましょう。
配信プロトコルについては以下を参考ください
あとは基本デフォルト通りでOKです。キャッシュ時間を変更したい場合はTTLをいじってみて下さい。
❒ Distribution Settings
- Alternate Domain Name に、ACMで作成したサーバ証明書に一致するFQDNを設定
- SSL Certificate の Request or Import a Certificate with ACMを選択し、SSL証明書の発行を行う(ACMでのSSL証明書発行方法は後述)。発行が完了したら、Custom SSL Certificate にてACMで作成したSSL証明書を選択
-
Supported HTTP Versions は、
HTTP/2
を選択 - Default Root Object にはS3Webサイトホスティングで設定した「インデックスドキュメント」と同じファイル名を指定
- Create Distribution で設定完了
CloudFrontは設定反映に少し時間がかかるため、愛する夫のために料理をしつつ、反映を待ちましょう。
3-3. ACM (AWS Certificate Manager)
❒ SSL証明書の発行
SSL証明書を発行します。
AWS Consoleのガイダンスに従って証明書発行作業を行います。
申請するドメインにACMからドメイン認証メールが届くので、メール受信設定を事前にしておいて下さい。
※ いずれかの1つで認証できればOKです。
例) example.com のドメインで証明書発行する場合の認証メール宛先
・ Whoisでドメイン管理者情報が公開されている連絡先メールアドレス
・ administrator@example.com
・ hostmaster@example.com
・ postmaster@example.com
・ webmaster@example.com
・ admin@example.com
メール届いたら「To approve this request, go to Amazon Certificate Approvals at (リンク)」のリンク部分を押下します。
承認画面に飛ぶので「I Approve」をクリック。
AWS Management Consoleへアクセスして、発行を確認しましょう。
無事、SSL(TLS)証明書が発行されました。
3-4. 疎通確認
ここで一度、疎通確認をしてみましょう。
CloudFrontのDistributionsをみると、反映状況が確認できます。
Status部分がDeployed 状態になったら、以下を試して疎通確認をしましょう。
❒ s3に直接アクセス: 【正】アクセス不可であること
$ curl http://example.com.s3-website-ap-northeast-1.amazonaws.com
<html>
<head><title>403 Forbidden</title></head>
<body>
<h1>403 Forbidden</h1>
<ul>
<li>Code: AccessDenied</li>
<li>Message: Access Denied</li>
<li>RequestId: xxxxxxxx </li>
<li>HostId: xxxxxxx </li>
</ul>
<hr/>
</body>
</html>
CloudFrontからのアクセスのみが許可になっています。
❒ CloudFront経由でアクセス: 【正】アクセス可能であること
$ curl http://xxxxx.cloudfront.net/index.html
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>CloudFront</center>
</body>
</html>
$ curl -L http://xxxxx.cloudfront.net/index.html
<!DOCTYPE html><html><head ....
$ curl https://xxxxx.cloudfront.net/index.html
<!DOCTYPE html><html><head ....
httpでのアクセスもちゃんとhttpsにリダイレクトされていますね。
3-5. Route53
❒ DNS設定
ドメインの公開をするため、DNSの設定を行います。ドメインに対してレコードを登録していきましょう。
【✕】 S3のendpointに紐付け
【◯】 CloudFront経由でS3紐付け
になるため、CloudFront側のDomain Name
をメモして下さい。
このDomain NameをDNSのレコードに追加します。
CNAMEで使いしたいところですが、問題が発生します。
DNSのレコードに登録するときの注意点
CNAMEレコードには特性があり、CNAMEレコードに登録したドメインと同じドメイン/サブドメインを他レコードに登録できません(RFC規約)
例) ↓ができない
example.com IN CNAME www.example.com.
example.com IN CNAME test.example.com.
例) ↓もできない
example.com IN CNAME www.example.com.
example.com IN NS ns4.p16.xxx.net.
例) ↓もできない
example.com IN CNAME www.example.com.
example.com IN A 216.239.xxx.xxx
例) ↓はOK
www.example.com IN CNAME example.com.
example.com IN A 216.239.xxx.xxx
今回でいうと、こうしたいところ
example.com IN CNAME [CloudFront Domain Name] ← ダメ
example.com IN A 98.138.xxx.xxx
example.com IN A 206.190.xxx.xxx
example.com IN A 203.xxx.xxx.xxx
ですが、RFC規約によりこの登録は出来ません。
そこでAmazon Route53は「Alias」という独自機能で ↑の状態を実現出来るようにしました。
DNSサーバーの外部からは A レコードとして見えるようになります。
example.com Alias [CloudFront Domain Name]
→ 外部からは 「example.com IN A [CloudFrontのIP]」で見えている
example.com IN A 98.138.xxx.xxx
example.com IN A 206.190.xxx.xxx
...
このAlias、CloudFrontのIPアドレスが変更されると同時に A レコードが指すIPアドレスも自動的に変更される形になります。
ということで、CloudFrontのDomain Nameを、公開するホスト名のAliasesでレコード追加します。
これでRoute53の設定が出来ました。
4. 画面確認
Access!!!
$ curl https://example.com
Success!!!
$ curl https://example.com
<!DOCTYPE html><html><head ....
FIN
構築・運用工数を減らせる部分はどんどん減らしていきましょう。
そしてクリエイティブな作業に工数かけていきましょう。
MAY THE FORCE BE WITH YOU