S3 + CloudFrontをTerraformで設定してCircleCIで更新する

「Terraform で S3+CloudFront+SSL/TLS 証明書 w/ ACM を設定して Hugo で作った static な Web サイトを CircleCI で自動 deploy する」やつができた。

できたもの

普通のいかにもHugoで作った Web サイトができた。
もう 2018 年なので手オペなどせず Infrastructure as Code で構築かつ CI でコンテンツ deploy です。
中身はまだない。
きっと酒とメシについての何かが書かれるのでしょう。

これはそもそも先日開催したハッカソンでやろうとして途中までしか進められなかったので、その補習も兼ねてます。
なお次回ハッカソンは Go です!

DeNA Techathon: 著者と学ぶ「Go ならわかるシステムプログラミング」黙々会 - connpass

構成

図を描く気力がなかったのでテキストで。

インフラ構築編

Terraformで以下を行なっている。

  1. Route 53 にドメインのゾーン情報を登録する
  2. コンテンツ更新用の IAM グループと IAM ユーザーを払い出す
  3. コンテンツ格納用の S3 バケットを作る
  4. ACM で SSL/TLS 証明書を発行する
  5. CloudFront distribution を設定する(ACM の証明書使いたいので)
  6. CloudFront distribution を Route 53 に登録する

コンテンツ更新編

GitHub への push をトリガーにCircleCIで以下を行なっている。

  1. Hugo で static な Web コンテンツ生成する
  2. 生成した Web コンテンツを S3 に同期する
  3. CloudFront のキャッシュをクリアする

GitHub

GitHubで以下を管理している。

  • sakemeshi.terraform
    • Terraform リポジトリ
    • 中身は Route 53 にドメインのゾーンを登録して module を呼び出しているくらい
    • AWS の credential 等は sakemeshi.terraform-secret リポジトリに置いて submodule として参照している
  • terraform-aws-static-website
    • 自分用 Terraform module
    • S3+CloudFront で static な Web サイトを作るもろもろが詰まっている
  • (private) sakemeshi.terraform-secret
    • AWS の credential などが入っている
    • credential store 導入とかはまた今度
  • (private) sakemeshi.content
    • Hugo による Web サイトの中身
    • このリポジトリの master に push すると CircleCI が動いて S3 に deploy される

つくりかた

コンセプトとしては WebUI を手でぽちぽちしたくないので Terraform で Infra as Code します!

AWS Web コンソールぽちぽち

最初からコンセプトに反するようだが 2018 年時点では手でポチポチすることもまだ必要なのです。(あるいは CLI)
作るものは 2 つ。

Terraform 実行用 IAM User

terraform-admin という名前で AdministratorAccess を attache した IAM ユーザーを作る。
credential も払い出して、今回の場合は sakemeshi.terraform-secret リポジトリに push しておく。

tfstate 格納用 S3 Bucket

Terraform は設定したクラウド環境の状態を tfstate というファイルで管理するのでそれを格納する S3 バケットを作る。

バケット名は ${AWSアカウント名}-terraform としてバージョニングを有効にする。
先ほど作った IAM ユーザー terraform-admin からアクセスできれば良い。

詳しくは以下参照。

Backend Type: s3 - Terraform by HashiCorp

Infra as Terraform

インフラをコードで書く

Terraform で構築するもろもろは専用の GitHub リポジトリを作って terraform.tf に書く。
先述の通りここで公開している。

mazgi/sakemeshi.terraform

主な内容は以下の通り。

  • prepare.sh
    • terraform バイナリのダウンロードと展開
    • 必要な環境変数の export
    • なお命名は職場の文化に由来する
  • terraform.tf
    • Terraform で行いたいもろもろ
    • ただしほとんどは module に追い出しているので、実際の内容は Route 53 のゾーン設定と module の読み出し程度
  • terraform.tfvars
    • 後述の private な credential リポジトリ内への symlink
    • AWS の ACCESS_KEY や SECRET_KEY などが書かれている

terraform.tf で参照している module は公開しているが後述のエラーやリージョンが固定などの残課題がある。

Terraform Module Registry | mazgi/static-website/aws

先述の通り Terraform を実行する IAM ユーザーの credential 等は別の private リポジトリを作って配置している。
公開できないが中身はこの程度。

Terraform 実行

ようやく Terraform を実行できる環境が整ったので実行!
なお prepare.sh は環境変数を export するので必ず source する。

$ source prepare.sh
$ bin/terraform apply

実行すると初回はもろもろのリソースが作られ、1 件のエラーが発生する。
2 回目以降はこのように 1 件のエラーが発生する。。
後述する。

細かいことを書くと今回のドメイン自体は Route 53 で管理していないため、別途ネームサーバーを Route 53 に向けておく等の手順が必要です。

CircleCI で自動 build && deploy

Terraform で空の Web サイトができたので中身を作っていく。

GitHub リポジトリを用意しておもむろに hugo new site . する。
適当なテーマを submodule として追加する。
Quick Start通り。

Hugo は今後がんばることにして今回は CircleCI 2.0 をがんばる。
結論を書くとこういう .circleci/config.yml を書いた。
ハマった。

そのほかは本当に hugo new site . したまま。

何はともあれこれで CircleCI での Web サイトのビルドと S3 への同期が行えるようになった!めでたい!

おわりに

ということで(ほぼ)手でぽちぽちせずに Infrastructure as Code で static な Web サイトが作れたし更新の仕組みもできた!やったね!

ポイント

以下あまり書けてないので機会があったら書く。

Terraform

  • 最近の Terraform は terraform init が必要
    • その作業ディレクトリでの初回実行や module のバージョン上げた際など
  • 最近の Terraform は terraform apply したあと本当に実行するか聞いてくる
    • そのため terraform apply[ENTER] してコーヒーを買いにいくと何十分後に戻っても Do you want to perform these actions? というメッセージが出迎えてくれる(もちろんあなたのクラウド環境には何も変化はない)
    • terraform apply -auto-approve というわるいオプションがある
  • tfstate 格納用 S3 バケット(S3 backend)を使うためには環境変数 or ~/.aws 内に credential が必要
    • 私は prepare.sh の中で export している(ので必ず source する)
  • ACM の検証が従来のメールだけではなく DNS でもできるようになっていて Terraform でも対応していた

CircleCI 2.0

  • Docker 便利だけども
    • 使う Docker イメージにもよるが当然 curlgit も入ってなかったりする
    • 自分用 Docker イメージ作りたくなる
    • 開発環境が Docker Hub に公開できる Docker イメージで完結している場合は便利そう
  • checkoutgit clone --recursive 相当の機能がない(?)

(特に)ハマりどころ

terraform apply 時の aws_acm_certificate_validation エラー

terraform apply する際に毎回このエラーが発生する。

Error: Error applying plan:

1 error(s) occurred:

- module.static-website.aws_acm_certificate_validation.website: 1 error(s) occurred:

- aws_acm_certificate_validation.website: Certificate needs [VALIDATON_RECORD.YOURDOMAIN VALIDATON_RECORD.YOURDOMAIN] to be set but only [VALIDATON_RECORD.YOURDOMAIN] was passed to validation_record_fqdns
  

これ YOURDOMAIN*.YOURDOMAIN でバリデーションレコードが同じで、Route 53 上は 1 レコードしかないものを Terraform のAWS providerが 2 レコード返ってくることを期待しているように見えるんだけどどうしたものか。

CircleCI 2.0 で attach_workspace するために ca-certificates パッケージが必要

前フェイズで永続化したワークスペースを後フェイズで attach_workspace して取り出すために ca-certificates パッケージ必要なのはハマった。
こちらの記事に助けられた。

CircleCI で x509 という証明書エラーに遭遇したときの対処 - Qiita

それはそうと再掲するが .circleci/config.yml がこんな長さになった。つらい。
workflow として build と deploy が分かれるのは綺麗だけども。。

ともかくこういうサイトをいくつか作りたいニーズがあったのでやっていきます。