はじめに
ここ半年は官公庁系の案件にて、CloudFormation(以降Cfn)を用いた基盤構築業務に従事しています。
前職でTerraformは使用していたので、IaCに触れるのは初めてではないのですが、Cfnに触れたのは今回の案件が初めてです。
この記事ではCfnに触れてみて感じたことを書いていきますので、これから触る方の参考になれば幸いです。
あくまで筆者の個人的な感想であり、人によって感じ方が違う点や、筆者の知識が不足しているが故の感想も含まれている点だけご了承くださいm(_ _)m
Cfnの良かった点
学習コストが低い
Cfnのテンプレートはjson or yml形式で記載するため、Terraformと違いとても理解しやすいです。
AWSを触る方や、インフラエンジニアの方なら馴染みのある形式のため、
AWSのサービスの概要さえつかめてしまえば、すぐにでも書き始められるな~と感じました。
Cfnに限った話ではないですが、コードの書き方自体よりもサービス自体の理解のほうが遥かに大事です。
公式ドキュメントが充実している!!
個人的にはこれが結構大きいです。
AWS公式のサービスだけあり、ドキュメントが充実しています。
設定値の根拠等を聞かれる場合、ブログの記事よりも公式ドキュメントの内容のほうが遥かに信頼性上がりますからね、、、
AWSサポートに問い合わせが可能
Terraformでもできるのかもしれないですが、CfnはAWSのサービスだけあり、サポートに問い合わせた際の回答が的確でした。
特に複数リージョン感でのスタック参照や、コンソールではできるのでCfnではできない実装等もあり、そういった際にサポートから回答をもらえるのは開発を進めていく上でとても頼りになります。
Cfnの個人的に良くなかった点
できることが少ない
あくまでTerraformとの比較にはなってしまいますが、Cfnはシンプルで学習コストが低い代わりに、できることは結構少ないです。
個人的にきついと感じた点としては、設定値を一部変えて同様のリソースを複数作成したい場合、Terraformの場合は for_earchを使えばコードの記述量を抑えて繰り返し処理みたいなことができます。
resource "aws_subnet" "public" {
for_each = local.public_subnets
vpc_id = aws_vpc.this.id
availability_zone = each.value.availability_zone
cidr_block = each.value.cider_block
map_public_ip_on_launch = each.value.map_public_ip_on_launch
tags = each.value.tags
}
resource "aws_subnet" "private" {
for_each = local.private_subnets
vpc_id = aws_vpc.this.id
availability_zone = each.value.availability_zone
cidr_block = each.value.cider_block
map_public_ip_on_launch = each.value.map_public_ip_on_launch
tags = each.value.tags
}
locals {
public_subnets = {
public_subnet_1a = {
availability_zone = "ap-northeast-1a"
cider_block = cidrsubnet(var.vpc_cidr, 8, 0)
map_public_ip_on_launch = true
tags = {
Name = "${var.prefix}-public-1a"
}
}
public_subnet_1c = {
availability_zone = "ap-northeast-1c"
cider_block = cidrsubnet(var.vpc_cidr, 8, 1)
map_public_ip_on_launch = true
tags = {
Name = "${var.prefix}-public-1c"
}
}
}
}
locals {
public_subnets = {
public_subnet_1a = {
availability_zone = "ap-northeast-1a"
cider_block = cidrsubnet(var.vpc_cidr, 8, 0)
map_public_ip_on_launch = true
tags = {
Name = "${var.prefix}-public-1a"
}
}
public_subnet_1c = {
availability_zone = "ap-northeast-1c"
cider_block = cidrsubnet(var.vpc_cidr, 8, 1)
map_public_ip_on_launch = true
tags = {
Name = "${var.prefix}-public-1c"
}
}
}
private_subnets = {
private_subnet_1a = {
availability_zone = "ap-northeast-1a"
cider_block = cidrsubnet(var.vpc_cidr, 8, 2)
map_public_ip_on_launch = false
tags = {
Name = "${var.prefix}-private-1a"
}
}
private_subnet_1c = {
availability_zone = "ap-northeast-1c"
cider_block = cidrsubnet(var.vpc_cidr, 8, 3)
map_public_ip_on_launch = false
tags = {
Name = "${var.prefix}-private-1c"
}
}
}
}
Cfnで上記を実装しようとすると、まずLambda関数を作成し、Cfnテンプレートの中で作成したLambdaをカスタムマクロとして呼び出して処理を実行させる必要があります。
当然Lambdaを作成する手間もありますし、運用保守の手間もあり、結果的には今回のプロジェクトでは採用されませんでした。
複数Stack間での参照がかなりややこしい
CfnにはStackという概念があり、基本的には1つのymlファイルにつき、1Stackです。
しかし一つのymlファイルに作成するすべてのリソースを作成することは可読性や、運用保守の観点、チーム開発の観点から現実的ではないため、サービス単位や、システム単位でymlファイルを分けて開発を進めていきます。
そうすると、自ずとStack数も増えていきます。
CfnにはStack間で値を受け渡しする機能があるのですが、この依存関係があることで、Stack内のリソース削除や値の変更ができないといった事象が起こります。
簡単な例で解説します。
下記ではNetwork系のリソースを作成しているStackと、EC2を作成しているStackがあり、EC2のStackにてNetworkStackにあるSecurityGroupを参照しています。
この状態ならEC2を削除、又は再作成処理が走るような変更は実施できます。
新たにEC2をターゲットにしているALBのStackがあります。
この状態では、EC2はALBのターゲットとして参照されているため、EC2の削除や再作成が行えません。
もちろんこれらはTerraformであっても、依存関係で更新が失敗することはあります。
しかしCfnの場合は依存関係を解消するためにコードを修正するだけではなく、ALBのターゲットからEC2を外して、その後にEC2の削除を行うなどの、Stack更新の順序も関わってきます。
Terraformの場合は、Stackという概念ではなく、terraform applyのタイミングでディレクトリ内の全ての.tfファイルが読み込まれるため、依存関係をいい感じに処理してくれるので負荷がなかった印象です。
ここはプロジェクトによってStack管理の方針が違っていたり、さらにCI/CDでより効率的な方法があるので一概には言えませんが、少なくとも筆者の場合はTerraformから離れてみて便利さを痛感しています。。w
日本語でコメントが書けない
正確に言うと、日本語でコメントは書けますが、テンプレートはS3に置く必要があります。
ローカルに置いたテンプレートに日本語でコメント書くとデプロイでエラーが出ます。
ここは正直S3に置いたテンプレートをデプロイ時に読み込めばいいだけの話なので不便とかではないのですが、、、
最後に
あとから見返すと、Cloudformationのデメリット部分が強調されるような内容になってしまいましたが、個人的には学習コストが低く、ドキュメントが充実していることから、長期的な運用までを見据えるととてもいいツールに感じました。
Terraformの場合、できる人が少ない=属人化しやすいので、構築時は便利だけど、運用保守の段階でなれた人がいなくなって困るみたいなケースは少ないかなと思いました。
ここまで個人的な感想ではありましたが、誰かの参考になれば幸いですm(_ _)m

