Terraform ちょっとだけ中身 入門
モチベーション
Nature RemoのTerraform Providerを作ったことで、terraformへの理解が深まった。
やってみるとあまり難しくないterraformだが、通常のプログラミング言語と異なる文法、独特な用語も多く、とっつきづらさはある。
この記事は、ソフトウェアエンジニアがterraformを触ってみよう、と思った際の最初の踏み台になればいいと思っている。
注意
以下は、terraformのイメージがないエンジニア向けの記述であり、厳密な話はしない。インフラ系のプロから見れば雑もいいところだと思われる。プロの方々には、この記事はあくまでも入門の入門の入門としての記述だと思っていただき、読み飛ばすか笑い飛ばすかしていただきたい。
terraformが必要な理由(IaCは何を達成したい?)
terraformが普及した背景にはIaC(Infrastructure as Code)という思想がある。
これは大雑把には、インフラのようなハードウェアもソフトウェアのように管理しよう、という思想である。では、ハードウェアをソフトウェアのように管理するとはどういうことなのか、またそうすると何が嬉しいのか。
ハードウェアをソフトウェアのように管理するということ
ハードウェアの存在や設定をコードで管理しよう、というのが大枠の理解である。コードに載せると次のような利点が出てくる。
- 適切な権限さえ与えられていれば、そのコードを流用することで同じインフラを立ち上げることができる
- 手動設定によるうっかりミスなどがなくなる。同じものは同じように作れる。
- コードそれ自体がドキュメントとして機能する。
- (Git等と組み合わせれば)ソフトウェアのようにバージョン管理や差分の把握がやりやすくなる。既存のソフトウェアエンジニアリングのノウハウを流用できる。
そこでterraformの出番
大前提として、AWSやGCPのようなクラウドサービスを使うとする。次の問題は、どういうコードでハードウェアを管理するか?という問題が出てくる。
例えば、設定をコードに落とし込むというだけなら、クラウドサービスのAPIを叩いてインフラを立ち上げるシェルスクリプトでもよい。
色々なIaCツールや手法がある中で、terraformが選ばれるのは、インフラに修正を加える前後の差分が確認できたり、どのサービスに対しても同じようなフォーマットで記述できる点にあると思われる。例えばGCPでterraformの書き方を把握してしまえば、AWSもサービスの中身は調べる必要があるものの文法は分かる。
terraformの主要概念
terraform providerの作り方のチュートリアルで使われているコードを例にとって説明する。
Provider
ものすごく雑に表現すると、APIクライアント。何のAPIかというと、AWSであったり、GCPであったり、Azureであったりする。
例えば、AWSのTerraform Providerが存在したら、それはAWSのAPIを操るAPIクライアントである、くらいの捉え方で概ね問題ない。
チュートリアルのコードを見てみよう。
Providerというのは、要はterraform風にAPIクライアントをラップした概念である。terraform風にすると何が嬉しいかというと、リソースの変更(後述)を把握できるようになる。diffコマンドをインフラに対して打てるようなイメージだ。
Resource
ものすごく雑に表現すると、API clientで操作できるサービスのこと。
リソースを定義したり、値を変更したりすると、providerの裏にあるAPIクライアントがAPI経由でサービスに変更を加える。
Resourceは少し複雑で、CRUDが定義できていないといけない。
具体的には、Resourceのモデルは次のintefaceを満たす必要がある。
実際に、リソースモデルはそうなるようにAPIを操作するメソッドを定義している。
例えば、チュートリアルのコードではAPIクライアント以下のような操作をさせて、これらのインターフェースを実装している。
Data Source
ものすごく雑に表現すると、APIクライアント経由で読み取りできるデータのこと。リソースとほぼ同じような情報しか取れないこともあるし、独自の情報が取れることがある。
チュートリアルのコードを見てみよう。READ的なエンドポイントを叩いているであろうことが分かる。
Terraform ProviderとAPI クライアントの違い
上でAPIクライアントとだいたい同じような概念、と説明したproviderは、どこがAPIクライアントと違うのだろうか。
基本的にAPIクライアントはサービスの状態に関する情報を持たない(ステートレスである、とも表現できる)。一方、terraform provider(とresource)はサービスの状態に関する情報を持つ。
つまり、大枠で、ステートフルなAPIクライアント=terraformくらいに捉えておいて問題はないと個人的には考えている。状態を持つからこそ、設定の追加・更新・削除による差分を把握できるようになる。
ではステート(状態)はどこに保存されるの?
ローカルで実行するのであれば、terraforom init
を実行したときにdefault.tfstate
というファイルが生成されるはずだ。開いてみるとjsonのような形式で記述がされている。この中にサービスの状態が保存されている。
backendにAWSのS3やGCPのGCSを指定した場合には、指定したバケットの中にdefault.tfstate
が保存されるのが見える。
terraformの限界
繰り返すが、結局terraformはAPIクライアントのラッパーのような存在なので、terraformでできることはAPIクライアントにできることの範囲内にしかない。つまり公開されているAPIでできること以上の操作をterraformに期待することはできない。
APIの公開がされたリソースはterraform化の道が開ける(公開→即リソース公開ではない点に注意。APIからリソースを作るのにも時間が必要)が、そうでない場合は絶望的だと思う。気長に待とう。
terraformと相性がいいもの・悪いもの
状態を管理するのがterraformなので、静的なインフラの管理とは相性がよい。
一方で、AWS Fargate, Cloud RunのコンテナなどはCI/CDからのデプロイがメインだったり、インフラの変更というよりはアプリケーションの変更なので、terraformで管理するのはしんどい。インスタンスのvCPU数やメモリは静的なのでterraformで管理しやすいが、コンテナの中身の更新をするのには向いていない。
こういったアプリケーションはterraformではなく、GitHub ActionsなどのCI/CDといったデプロイツールなどで更新した方がよい。
幸いにもterraformにはignore_changesという設定があり、例えばCloud Runであれば、コンテナの変化をterraform上で無視できるので、インフラの静的な側面の管理に集中できる。
resource "google_cloud_run_v2_service" "default" {
name = "cloudrun-service"
location = "us-central1"
ingress = "INGRESS_TRAFFIC_ALL"
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
+ life_cycle = {
+ ignore_changes = [
+ template.0.containers.0.image
+ ]
+ }
}
まとめ
- terraform ≒ ステートフルなAPIクライアント
- 主要な概念は3種類
- Provider: APIクライアント
- Resource: APIクライアントで操作できるサービス
- Data Source: APIクライアントで取得できるデータ
- terraformはインフラを明示的に管理する(≒IaC)ためのツールである
- terraformは静的なインフラを管理し、アプリケーションの管理はデプロイツールに任せる
Discussion