はじめに
独立起業している知人(建築系なので、以下👷♂️)から、自社で直接仕事の依頼を受けるためのランディングページを作りたい...🔥という相談を受けました
MVPの考え方からいけば、こういうのを使ったり、作るにしてもherokuなどを使ったりでもよかったのかもしれませんが、
業務で触れていなかったRoute53やCloudfrontなどのサービスを触る格好のチャンスに思えたので、AWSのアカウントを新規で作って1から構築することとしました
もっとも自分で作ると言っても、コンソールから手でポチポチやって後々に🤗ってなるのは嫌だったので、
以前勧めてもらって気になっていたTerraformを勉強して、LP公開+ちょっとした処理をするためのAPI...という一式を作成しようと決めました
(職場のインフラはあまりIaCを意識したものになっていないので、業務に還元できないかな...という思いも少しあります🤔)
ささっととか虫のいいことを言っています🐞が、
作ってる最中は己のDNSやhttpsなどに対する基礎知識の足りなさから、もうだいぶ苦しみました⚰
ので、そこら辺の備忘録も兼ねて、紹介していきたいと思います
注意:理由は後述しますが、環境構築には状況により数日かかります...ちっともささっとじゃない!
どんなものを作るか
👷♂️と対面やGoogleハングアウトで会議を重ねて、大体以下のような話が出てきました
- LP制作は外注しようね
- 👷♂️がLPのテキストや構成を考えることに意欲的で、割とちゃんとした仕様を作ってくれて大いに助かる
- デザイン・コーディングまでやって頂けるフリーランスの方にランサーズにて依頼
- インフラと問い合わせを受ける仕組みをぼくが作るね
- 問い合わせの内容は、👷♂️との会話用に作ったSlackで受けとることに
- 副次的に出てきた要件
- APIトークンのハードコードは嫌だよね
- 問い合わせフォームあるしhttps必須だね
- もちろん独自ドメイン取りたいよね
- 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を見てください
だけだとあんまりなので、 順を追って説明していきます
準備
Terraformの前にいくつか必要なものがあります
具体的な作り方は記事の本旨から外れるため、各自おググりください🙄
お名前.comでドメインを取得
他のレジストラでもいいものと思います
Route 53を使う場合は、ネームサーバへの登録含め自動化できるんでしょうか?
(.jpが高いからとか言ってた割に.comなのはサンプルだからです)
Slackで投稿用Botを作成
Slack APIはchat.postMessageを使います
所定の権限を付与したbotを作成した上で、OAuth Access Tokenをメモっておきましょう
また、投稿先のチャンネルIDも必要になりますので、https://xxxxx.slack.com/messages/CGxxxxxxx
のCGxxxxxxx
の部分も確認しておいてください
AWSアカウントとユーザ
PowerUserAccess
がついたユーザのAWS_ACCESS_KEY_ID
およびAWS_SECRET_ACCESS_KEY
が必要です
あと、作っただけでたくさんお金のかかるものは定義していないつもりですが、万が一に備えて請求ダッシュボードの設定やアラームは設定して頂いておいた方がよいでしょう
請求については自己責任でお願いします🙏
いざ構築
準備ができたら、実際にTerraformにて環境を構築していきます!
の前に、以下をインストールしておいてください
具体的な方法、およびWindowsの方は🙄
$ 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_ID
、AWS_SECRET_ACCESS_KEY
、domain_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
で、特定のリソースのみ作成することができます
今回は以下のリソースのみ作成します
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へ登録します
こちらに具体的な手順が書いてありますので、参考にしつつやってみてください
できましたでしょうか?
お名前.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で作ったリソースに手で変更を加えると差分検知されるような仕組みになっているのですが、
このシークレットの値については差分検知の確認対象外にしています
そうこうしているうちにDNSの浸透が済んだら、あとは一気に作成できます✌️
-target
の指定を外すと全てのリソースを作成できるので、一思いにやってしまいましょう
CloudFrontなど作成に時間のかかるリソースも色々あるので、1時間ほどは見ていただいた方がいいようです
# 残りのリソースを適用
$ terraform plan
$ terraform apply
...無事に正常終了しましたでしょうか?
もしもうまくいったようであれば、しばし待ったのちご自身のhttps://example-domain.com/
にアクセスすると、以下のようなページが表示されるはずです
(なお、htmlはPaper Kit 2を切り貼りしてでっち上げたものです)
おもむろにボタンも押してみましょう...!
これにてガンガン仕事を受けられますね!!!🍾🎂🎉
トラブルシューティング(構築)
私が引っかかったものを晒しておきます
以下以外のものが出てきた場合は、コメントで教えて頂けたら補記したいと思います
(こうすれば解決できた...というところまでやっていただけるとありがたいです)
# 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
# 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
トラブルシューティング(削除)
作るときと同様、引っかかったものを書いておきます
# 以下エラーの場合は、AWSのコンソールなどから対象のバケットの中身を空にする
Error: error deleting S3 Bucket (xxxxx): BucketNotEmpty: The bucket you tried to delete is not empty
所感
環境構築を手作業で行なっていると、いつも「これで確認すべき項目は十分だろうか...?」とか「設定値正しく設定できてるかな...?」とか、
よしんば動いたとしても「本当にあれでよかったんだろうか...🤢」という気持ちが付きまとっていた(しかも時間が経つと全部忘れる)のですが、
インフラをコードに落とし込めると、アプリケーションエンジニアにとってもグッと距離感が近づいたような気がしました
特に、リソース間の依存関係を理解するという意味では、GUIよりもコードの方が優れているように感じました
また、個人開発だと、寝る前のちょっとした時間で開発したり、前回の作業から間が空いてしまったり...ということもよくあると思いますが、
どこまで何を作ったか、tfファイルやtfstateファイルにしっかり残るので、安心してやったことを忘れることができるというのが地味にありがたかったです😋
今回の記事では、作成したtfファイルについてはほとんど触れませんでしたが、
各ファイルの設定値とAWSコンソールの状態を比較したり、内容を追加・削除してみたりと、ぜひ各自で色々試して頂くことをお勧めします!
(よくないところがあったら、プルリクやコメントで指摘頂ければ幸いです)
自分が書いたtfファイルで次々にインフラが組み上がっていく様を見ていると、
それこそ創造主になったような気持ち🌏になれますよ!
参考資料
オススメ情報のリンクを貼っておきます📚
-
VSCodeでTerraformを書くときの設定
- 私はVSCodeで開発していますが、Intellijなんかもいい感じに使えるらしいです
-
【ダウンロード版】Pragmatic Terraform on AWS
- ともあれこちらを読むことをお勧めします
- 今回使わなかったサービス含め、様々なリソースの作り方について具体的に書いてあります
-
Terraformの公式ドキュメント
- 設定値など分からないことがあったら、
resource "aws_xxxxx"
などのリソース名で検索して↑のサイトを見れば基本全部書いてあります
- 設定値など分からないことがあったら、
-
Terraform職人入門: 日々の運用で学んだ知見を淡々とまとめる
- tips集です。記法や考え方が頭に馴染んできたら読むのをお勧めします