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

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

できたもの

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

f:id:mazgi:20180215023829p:plain https://sakemeshi.love/

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

なお次回ハッカソンはGoです!

denatechstudio.connpass.com

構成

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

インフラ構築編

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しておく。

f:id:mazgi:20180215033043p:plain

tfstate 格納用 S3 Bucket

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

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

f:id:mazgi:20180215033214p:plain

詳しくは以下参照。

Backend Type: s3 - Terraform by HashiCorp

Infra as Terraform

インフラをコードで書く

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

github.com

主な内容は以下の通り。

  • 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

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

f:id:mazgi:20180215043508p:plain

Terraform 実行

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

$ source prepare.sh
$ bin/terraform apply

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

f:id:mazgi:20180215035427p:plain

細かいことを書くと今回のドメイン自体は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 . したまま。

f:id:mazgi:20180215045406p:plain

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

f:id:mazgi:20180215045503p:plain

おわりに

ということで(ほぼ)手でぽちぽちせずに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が分かれるのは綺麗だけども。。

gist.github.com

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