LoginSignup
5
4

More than 3 years have passed since last update.

LP公開のためのインフラをTerraform + AWSでささっと?構築する(ソース付き)

Posted at

はじめに

独立起業している知人(建築系なので、以下👷‍♂️)から、自社で直接仕事の依頼を受けるためのランディングページを作りたい...🔥という相談を受けました

MVPの考え方からいけば、こういうのを使ったり、作るにしてもherokuなどを使ったりでもよかったのかもしれませんが、
業務で触れていなかったRoute53やCloudfrontなどのサービスを触る格好のチャンスに思えたので、AWSのアカウントを新規で作って1から構築することとしました

もっとも自分で作ると言っても、コンソールから手でポチポチやって後々に🤗ってなるのは嫌だったので、
以前勧めてもらって気になっていたTerraformを勉強して、LP公開+ちょっとした処理をするためのAPI...という一式を作成しようと決めました
(職場のインフラはあまりIaCを意識したものになっていないので、業務に還元できないかな...という思いも少しあります🤔)

ささっととか虫のいいことを言っています🐞が、
作ってる最中は己のDNSやhttpsなどに対する基礎知識の足りなさから、もうだいぶ苦しみました⚰
ので、そこら辺の備忘録も兼ねて、紹介していきたいと思います

注意:理由は後述しますが、環境構築には状況により数日かかります...ちっともささっとじゃない!

どんなものを作るか

👷‍♂️と対面やGoogleハングアウトで会議を重ねて、大体以下のような話が出てきました

  • LP制作は外注しようね
    • 👷‍♂️がLPのテキストや構成を考えることに意欲的で、割とちゃんとした仕様を作ってくれて大いに助かる
    • デザイン・コーディングまでやって頂けるフリーランスの方にランサーズにて依頼
  • インフラと問い合わせを受ける仕組みをぼくが作るね
    • 問い合わせの内容は、👷‍♂️との会話用に作ったSlackで受けとることに
    • 副次的に出てきた要件
      • APIトークンのハードコードは嫌だよね
      • 問い合わせフォームあるしhttps必須だね
  • もちろん独自ドメイン取りたいよね
    • Route 53で見てみたら.jpドメインが受け入れ難い金額💸だったため、レジストラはお名前.com、ネームサーバはRoute 53を利用
    • 当初ここら辺の概念を全く分かってなくて時間が溶けました。こちらこちらの記事がオススメです
  • SSL証明書の更新は自動化したいよね
    • 個人的にタイムリーな話だったため
    • ここも何も分かってないことに気づきこちらなど読みました

どのように作るか

ということで、以下のようなインフラを構築🏗することにしました!

構成図

簡単にですが一通り説明します
本番運用するものはLPや問い合わせ機能はインフラとは別で管理したいですが、本記事では簡便のため全部まとめてTerraformで作成します

名称 役割
お名前.com ドメインを取得・管理。Terraform管理外
Route 53 ネームサーバ(Hosted Zone)いっこ作る
Certificate Manager(⭐️) SSL証明書の生成・管理。更新自動化のためDNS検証を利用
Secrets Manager(🔐) Slack APIのトークンを管理
CloudFront LPを公開。特定のパス配下をAPIへ回す。地域制限を使って日本(と米国)に絞る
API Gateway 問い合わせAPIのエンドポイント。リージョンAPIとして構築
S3 LPの置き場所と、CloudFrontのアクセスログ保管先
Lambda 問い合わせをSlackに投稿
Slack チャンネルに問い合わせが上がってくる。Terraform管理外

書いてて気づいたんですが、これそのままSPAの公開なんかにも応用できそうな構成ですね😎

作ったもの

GitHubを見てください:octocat:
だけだとあんまりなので、 順を追って説明していきます

準備

Terraformの前にいくつか必要なものがあります
具体的な作り方は記事の本旨から外れるため、各自おググりください🙄

お名前.comでドメインを取得

他のレジストラでもいいものと思います
Route 53を使う場合は、ネームサーバへの登録含め自動化できるんでしょうか?
(.jpが高いからとか言ってた割に.comなのはサンプルだからです)

ドメイン名

Slackで投稿用Botを作成

Slack APIはchat.postMessageを使います
所定の権限を付与したbotを作成した上で、OAuth Access Tokenをメモっておきましょう

Access Token

また、投稿先のチャンネルIDも必要になりますので、https://xxxxx.slack.com/messages/CGxxxxxxxCGxxxxxxxの部分も確認しておいてください

AWSアカウントとユーザ

PowerUserAccessがついたユーザのAWS_ACCESS_KEY_IDおよびAWS_SECRET_ACCESS_KEYが必要です
あと、作っただけでたくさんお金のかかるものは定義していないつもりですが、万が一に備えて請求ダッシュボードの設定やアラームは設定して頂いておいた方がよいでしょう
請求については自己責任でお願いします🙏

いざ構築

準備ができたら、実際にTerraformにて環境を構築していきます!

の前に、以下をインストールしておいてください
具体的な方法、およびWindowsの方は🙄

  • direnv
  • tfenv
    • terraformのインストールもお願いします
    • 本稿ではv0.12.3を使用しています
ターミナル
$ direnv version
2.20.1

$ tfenv -v
tfenv 1.0.1

$ terraform -v
Terraform v0.12.3

それではやっていきましょう
なにはともあれ、まずはリポジトリを取得します

ターミナル
$ git clone https://github.com/yktakaha4/lp-infra-by-terraform.git

$ cd lp-infra-by-terraform
$ ls -l

次に、各種設定ファイルを作っていきます
.sampleとついているやつがサンプルなので、コピーの上値を埋めてください
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYdomain_nameは準備の項で用意した値、
resource_prefixはS3のバケット名など各種リソースに設定されるので、人と被らなさそうなものを任意で設定してください

なお、terraform.tf.sampleは、tfstateファイルをS3で管理する場合必要になります
お試しでやるようでしたら特に不要です

ターミナル
$ cp -p terraform.tfvars.sample terraform.tfvars
$ vim terraform.tfvars

$ cp -p .envrc.sample .envrc
$ vim .envrc
$ direnv allow

# 複数人で共有して開発する場合のみ設定
$ cp -p terraform.tf.sample terraform.tf
$ vim terraform.tf

ここまで険しい道のり⛰でしたが、いよいよterraformコマンドを使っていきます

Terraformは、カレントディレクトリ直下にあるtfファイルを参照して各種リソースを作成します
planで作成されるリソースの確認、applyで適用です。簡単ですね!

今回はドメインの取得をお名前.comで行なっていますので、
Route 53でホストゾーンを作成した後に、ネームサーバのアドレスをお名前.comの管理コンソールに設定する必要があります
こちらは手作業で行う必要があるため、まずホストゾーンのみ作成するコマンドを実行します

-target=xxxxで、特定のリソースのみ作成することができます
今回は以下のリソースのみ作成します

route53.tf
resource "aws_route53_zone" "domain_name" {
  name = "${var.domain_name}"
}

それでは、いってみましょう!

ターミナル
# 環境構築
$ terraform init

# お名前.comにネームサーバーの情報を登録する必要があるため、まずホストゾーンのみ作成する
$ terraform plan -target=aws_route53_zone.domain_name
$ terraform apply -target=aws_route53_zone.domain_name

Outputs:

name_servers = [
  "ns-xxxx.awsdns-xx.org",
  "ns-xxxx.awsdns-xx.co.uk",
  "ns-xxxx.awsdns-xx.com",
  "ns-xxxx.awsdns-xx.net",
]

Outputsとして、上記のようにネームサーバーのドメインが表示されたら無事成功です
AWSのコンソールからRoute 53 -> ホストゾーンと辿っていくとドメインができてるはずですので、見てみてください

ホストゾーン

いい感じですね!

ここで、Terraformを一度離れて、ネームサーバーをお名前.comへ登録します
こちらに具体的な手順が書いてありますので、参考にしつつやってみてください

ドメインNavi

できましたでしょうか?

お名前.comのコンソールからの登録自体は割とすぐに終わるのですが、
設定したネームサーバが浸透(という言い方でいいんでしょうか?)するには、特に再作成時は最大で72時間程度かかるそうです😕
ネームサーバの変更が完了していないと以降のコマンドを打ってもタイムアウトしますので、ここはグッとこらえて時が経つのを待ちます...

digコマンドを使うと、ドメイン名に対応するネームサーバを確認することができるので、時間を開けて見てみてください

ターミナル
# ネームサーバの確認用コマンド
$ dig example-domain.com ns

;; ANSWER SECTION:
xxxxxxxxx.com.          172756  IN      NS      ns-xxxx.awsdns-xx.net.
xxxxxxxxx.com.          172756  IN      NS      ns-xxxx.awsdns-xx.org.
xxxxxxxxx.com.          172756  IN      NS      ns-xxxx.awsdns-xx.co.uk.
xxxxxxxxx.com.          172756  IN      NS      ns-xxxx.awsdns-xx.com.

# ↑で返却される内容が、 name_servers で表示されたネームサーバに問い合わせた時と同じだったら準備完了
$ dig example-domain.com @ns-xxxx.awsdns-xx.org ns

いずれにせよすぐには終わらないので、もういくつか先に済ませておいた方がいい内容をやっておきます

Slack APIから取得したトークンとチャンネルIDをSecrets Managerに登録します

ターミナル
$ terraform plan -target=aws_secretsmanager_secret_version.slack_api
$ terraform apply -target=aws_secretsmanager_secret_version.slack_api

applyが完了したらAWSコンソールを開いて、それぞれのシークレットの値を編集してください
本来、Terraformで作ったリソースに手で変更を加えると差分検知されるような仕組みになっているのですが、
このシークレットの値については差分検知の確認対象外にしています

Secrets Manager 2019-07-08 22-47-30(1).png

そうこうしているうちにDNSの浸透が済んだら、あとは一気に作成できます✌️

-targetの指定を外すと全てのリソースを作成できるので、一思いにやってしまいましょう
CloudFrontなど作成に時間のかかるリソースも色々あるので、1時間ほどは見ていただいた方がいいようです

ターミナル
# 残りのリソースを適用
$ terraform plan
$ terraform apply

...無事に正常終了しましたでしょうか?

もしもうまくいったようであれば、しばし待ったのちご自身のhttps://example-domain.com/にアクセスすると、以下のようなページが表示されるはずです
(なお、htmlはPaper Kit 2を切り貼りしてでっち上げたものです)

LP

おもむろにボタンも押してみましょう...!

Slack

これにてガンガン仕事を受けられますね!!!🍾🎂🎉

トラブルシューティング(構築)

私が引っかかったものを晒しておきます
以下以外のものが出てきた場合は、コメントで教えて頂けたら補記したいと思います
(こうすれば解決できた...というところまでやっていただけるとありがたいです)

その1
# CloudFrontでエラーの場合は、AWSコンソールを確認し、ステータスが Deployed になるまで待つ
Error: error waiting until CloudFront Distribution (xxxxx) is deployed: timeout while waiting for state to become 'Deployed' (last state: 'InProgress', timeout: 1h10m0s)

# Deployed になった後、 untaintの実行
$ terraform untaint aws_cloudfront_distribution.domain_name
Resource instance aws_cloudfront_distribution.domain_name has been successfully untainted.

# 残りのリソースを適用
$ terraform apply
その2
# ACMで発行できる証明書の数は1年間で上限があり、作成・削除を繰り返す等して最大値を越えるとエラーとなってしまう
Error: Error requesting certificate: LimitExceededException: Error: you have reached your limit of 10 certificates in the last year.

# サポートに投げると上限緩和してもらえる
# https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-limits.html#limit-certs-yearly

削除方法

作るのに引き換え削除は楽チンです。terraform destroyで削除できます

ただし何も指定しないと本当に全部消えます👋
planをよく確認したり、target=xxxxxで部分的に消していくなど、慎重に作業した方がよいでしょう

コンソール
$ terraform plan --destory
$ terraform destroy

トラブルシューティング(削除)

作るときと同様、引っかかったものを書いておきます

その1
# 以下エラーの場合は、AWSのコンソールなどから対象のバケットの中身を空にする
Error: error deleting S3 Bucket (xxxxx): BucketNotEmpty: The bucket you tried to delete is not empty

所感

環境構築を手作業で行なっていると、いつも「これで確認すべき項目は十分だろうか...?」とか「設定値正しく設定できてるかな...?」とか、
よしんば動いたとしても「本当にあれでよかったんだろうか...🤢」という気持ちが付きまとっていた(しかも時間が経つと全部忘れる)のですが、
インフラをコードに落とし込めると、アプリケーションエンジニアにとってもグッと距離感が近づいたような気がしました
特に、リソース間の依存関係を理解するという意味では、GUIよりもコードの方が優れているように感じました

また、個人開発だと、寝る前のちょっとした時間で開発したり、前回の作業から間が空いてしまったり...ということもよくあると思いますが、
どこまで何を作ったか、tfファイルやtfstateファイルにしっかり残るので、安心してやったことを忘れることができるというのが地味にありがたかったです😋

今回の記事では、作成したtfファイルについてはほとんど触れませんでしたが、
各ファイルの設定値とAWSコンソールの状態を比較したり、内容を追加・削除してみたりと、ぜひ各自で色々試して頂くことをお勧めします!
(よくないところがあったら、プルリクやコメントで指摘頂ければ幸いです)

自分が書いたtfファイルで次々にインフラが組み上がっていく様を見ていると、
それこそ創造主になったような気持ち🌏になれますよ!

参考資料

オススメ情報のリンクを貼っておきます📚

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4