Help us understand the problem. What is going on with this article?

Github, CircleCI, S3, CloudFront, Middlemanを活用して、https対応のサーバレスで自動deployなWEBサイト構築

この記事は リクルートライフスタイル Advent Calendar 2016 の15日目の記事です。
@MomokoAsai です。社内ではFirebaseの推進を行っております。
それではさっそく本題に。

実現すること

「Github, CircleCI, S3, CloudFront, Middlemanを活用して、https対応のサーバレスで自動deployなWEBサイト構築」です。

コーポレイトサイトなどの動的要素がないサイトは、簡単実装&低コスト運用が出来るような構成にしてしまいましょう。

そして、https対応をしていると、SEO的な優遇があり、将来的には必須になりそうです

https化も行ってしまいましょう。

全体構成

全体構成図.png

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 処理を実行するよう設定します。

circle.yml
...

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を使います。
1. 検証環境用のAWSのIAMからユーザーを作成
2. 作成ユーザー(or所属グループ)にAmazonS3FullAccessのポリシーを追加
3. Access Key / Secret Key をユーザーに発行

❒ アプリケーションの設定
gem middleman-s3_syncを使ってS3にuploadが出来るように処理を記載します。
Deployコマンドで指定した「bundle exec middleman s3_sync -e [環境名]」の環境名で出し分け設定が出来ます。
CircleCIの環境変数に登録した値は、RubyのコードからENV['HOGE']で取得することが可能です。(設定方法後述)

config.rb
...

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作成(後述)してから追加します。
20161214061822.png

CircleCIの設定詳細については公式ドキュメントを参照ください

3. AWS

3-1. S3

❒ Bucket作成
静的Webサイトホスティングを行うBucketを作成します。
作成が完了したら、CircleCIの環境変数に設定することをお忘れなく。

❒ 静的ウェブサイトホスティング機能設定
S3で静的ウェブサイトホスティング機能を有効にします。(本番環境、検証環境)
この時、「インデックスドキュメント」を設定します。「エラードキュメント」は”あると尚良し”という感じです。

20161214062225.png

S3へのAccess制限はCloudFront(後述)の設定で行います。
S3の設定はここまで。

3-2. CloudFront

S3へのアクセスはCloudFront経由のみ許可するよう設定していきます。
CloudFront経由からのアクセスにすることで
・ CDNエッジサーバーにキャッシュされることで高速化
・ S3のみより料金も安くなりやすい(S3へのリクエスト回数が減るため)
という利点があります。

特に、動画や音楽などの大容量配信を行う場合はCloudFront経由だと「ボリューミーディスカウント」が効くことがあり、お得です。

❒ Origin Settings
オリジンの設定を行います。

  1. 静的サイトの配信のため、配信方法は 「Web」を選択
  2. Origin Domain Name にS3バケットの静的ウェブサイトホスティング endpointを指定
    → CloudFrontがS3をオリジンサーバーとして認識し、S3を見に行くようになります。
  3. Origin Access IdentityCreate a New Identity にし、アクセス制御の設定を新規作成
  4. Grant Read Permissions on BucketYes, Update Bucket Policyに設定
    → この設定でCloudFrontにS3バケット内のオブジェクト読み取り許可が付与されます。

❒ Default Cache Behavior Settings
キャッシュ設定、配信プロトコルの設定を行います。

  1. 配信プロトコルを指定
    ★ 「Redirect HTTP to HTTPS」を推奨しますが、サイトに合わせて設定してください。

  2. 許可するHTTP methodを決める
    ★ お問い合わせフォーム等がある場合はPOSTの許可を忘れずに行いましょう。

配信プロトコルについては以下を参考ください

あとは基本デフォルト通りでOKです。キャッシュ時間を変更したい場合はTTLをいじってみて下さい。

❒ Distribution Settings

  1. Alternate Domain Name に、ACMで作成したサーバ証明書に一致するFQDNを設定
  2. SSL CertificateRequest or Import a Certificate with ACMを選択し、SSL証明書の発行を行う(ACMでのSSL証明書発行方法は後述)。発行が完了したら、Custom SSL Certificate にてACMで作成したSSL証明書を選択
  3. Supported HTTP Versions は、HTTP/2を選択
  4. Default Root Object にはS3Webサイトホスティングで設定した「インデックスドキュメント」と同じファイル名を指定
  5. 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」をクリック。
20161214042037.png

承認が完了したら、発行の完了です。
20161214042122.png

AWS Management Consoleへアクセスして、発行を確認しましょう。
20161214042234.png

無事、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>

S3のアクセス制限の設定も確認してみましょう
20161214055943.png

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をメモして下さい。

20161214064143.png

この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でレコード追加します。

20161214151637.png

これでRoute53の設定が出来ました。

4. 画面確認

Access!!!

$ curl https://example.com

Success!!!

$ curl https://example.com
<!DOCTYPE html><html><head ....

FIN

構築・運用工数を減らせる部分はどんどん減らしていきましょう。
そしてクリエイティブな作業に工数かけていきましょう。

MAY THE FORCE BE WITH YOU

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away