言いたいこと
- IaCの運用がビジネスのスピードに合わなくなってきている
- IaCが辛い原因は考え方じゃなくてツールにあると思う
- Terraform のmoduleの再利用性がないのは DI ができないから
- DIのできるインフラ構築ツールがでてくれば、インフラ構築の大変さが劇的に変わる(はず)
発端: IaC って辛いよね
発端となったのはこちらのスライドです。
Infrastructure as Codeに疲れたので、僕たちが本来やりたかったことを整理する
簡単に説明すると、「IaCっていい加減つらいよね。なんでもかんでもCode化すればいいってもんじゃないよね。」という話です。
辛みについてはすごく共感できましたが、ただ、ここに語られてることって、 IaCの辛さじゃなくて、terraform の辛さだと思うんです。
ツールが現状の運用のニーズに合わなくなってきただけだと思うんです。
Terraform がなぜ辛いのか?
Terraform が辛い原因を一言でいうと、 module のポータビリティーの低さだと私は考えています。
ここからは具体例で説明します。
例: ELB + SecurityGroup + Route53 record からなるモジュール
ELB, ACM, Security Group などの public 層のリソースをまとめたモジュールを作りたいとします。
ELBは以下のような定義であるとします:
resource "aws_elb" "internal" {
name = "${var.app_name}-${var.env}-internal"
security_groups = ["${aws_security_group.internal-elb.id}"]
subnets = [
"${lookup(var.subnet_ids, "private-1a")}",
"${lookup(var.subnet_ids, "private-1c")}",
]
internal = true
listener {
instance_port = "${var.listener_port["instance"]}"
instance_protocol = "http"
lb_port = "${var.listener_port["lb"]}"
lb_protocol = "https"
ssl_certificate_id = "${data.aws_acm_certificate.wildcard.arn}"
}
health_check {
healthy_threshold = "${var.health_check["healthy_threshold"]}"
unhealthy_threshold = "${var.health_check["unhealthy_threshold"]}"
timeout = "${var.health_check["timeout"]}"
target = "${var.health_check["target"]}"
interval = "${var.health_check["interval"]}"
}
cross_zone_load_balancing = true
idle_timeout = 60
connection_draining = true
connection_draining_timeout = 300
tags {
"Name" = "${var.app_name}-${var.env}-internal"
}
lifecycle {
ignore_changes = ["access_logs", "idle_timeout"]
}
}
このとき、 ELB を internet faced にするか inner にするかくらいなら、 boolean 変数で注入すれば外からコントロールできますよね。
internal = "${var.is_internal}"
ところが、ELBのリスナーをサービスAとサービスBで独自のものを作りたい場合があります。
運用に即した例としては、 HTTP から HTTPS にリダイレクトする ELB を作りたいとします。
ところが、現状のterraform はこれを外から注入する手段はありません。
AsIs の解決策
AsIs の解決策としては、以下の2つです(module 使わないは論外として):
- ELB の定義をこの moudle から外に出す
- ELBのリスナーだけ異なる似たような module を2つ作る
解決策1: ELBの定義を module の外に出す
この方法だと、そもそもALBの定義が module の外に出てしまうのだから、この module には重要なことが書かれなくなるわけです。これは module を使わない、といっているのと似たような感じになります。
解決策2: ELBの定義だけ差し替えた2つの module を作る
以下のような2つのモジュールを作ります。
- module1:
public-elb
- module2:
public-elb-with-redirect
module2 のELBの定義は以下のようになります(module1との差分のみ表示):
// ほかはmodule1と同じ
...
listener {
instance_port = "${var.listener_port_ssl["instance"]}"
instance_protocol = "http"
lb_port = "${var.listener_port_ssl["lb"]}"
lb_protocol = "https"
ssl_certificate_id = "${data.aws_acm_certificate.wildcard.arn}"
}
...
この方法だと、 似てるけど listner の部分だけ違う module が複数できるわけです。これでは他の部分のコードが二重管理になります。さらに別の部分も変更したいとなると、似てるmoduleがどんどん増えていきます。これは管理がとても煩雑です。
本来やりたいこと = DI (Dependency Injection)
これに対して、listener 定義の部分だけ外部から注入できれば、module は1つですみます。これによって、汎用的な用途で使える public レイヤの module ができるわけです。これはいろんなところに使い回せます。
// こんな言語はありませんが、あくまでイメージです。。
// public_elb module
const public_elb = new Elb({ listener: new Listener({ redirect: false }) });
// public_elb_with_redirect module
const public_elb_with_redirect = new Elb({ listener: new Listener({ redirect: true }) });
参考: Dependency Injection (依存性の注入)
terraform は module に DI を導入するか?
たぶん No だと思います。
terraform でこれをやろうとすると、根本的にアーキテクチャの変更を強いられると思うんです。だから多分terraform に DI は来ないと思う。
一方で、 pulumi や aws-cdk みたいなツールは汎用プログラミング言語でインフラ定義をするので、DIが比較的導入しやすいんじゃないかと思います。だからこれらのツールはこれから有望な選択肢になるんじゃないかと思うんです。
(もちろん、ツール側がDIを導入してくれなかったら意味はないんですが。。)
DIができるツールが出てくればインフラ構築の辛さが劇的に変わる
DIができるツールが出てくれば、moduleに相当するもののポータビリティも上がるので、githubとかでいろいろな開発者がmoduleを公開していくようになると思います。
そうなると、オープンソースの資産が充実してきて、同じ要件をアリもので代用できるようになっていくと思います。つまり、インフラ定義が楽になると思うんです。
終わりに
つまるところ、だからみなさん一緒に aws-cdk や pulumi を勉強しませんか?という話ですw
参考
この一連のツイート
辛みについてはすごく共感できましたが、IaCが辛いんじゃなくて、terraformが現状の要件に合わなくなってきただけだと思う。aws-cdk とかpulumi みたいなterraform alternativeなツールも出てきていて、辛いところは解決する方向に向かっていくと思う。 https://t.co/dUTUpvCUie
— billthelizard (@billthelizardx) 2019年2月24日
terraform module 辛いって方、意見をお願いします。next terraform なツールの重要な要件だと思うんですよ。#jawsdays #jawsdays2019 https://t.co/LDsZt5bq4u
— billthelizard (@billthelizardx) 2019年2月24日