🎄

Terraform Workspaceを2年以上運用しての振り返り

2021/12/13に公開

この記事はTerraform Advent Calendar 2021 13日目の記事です。

はじめに

この記事ではTerraformのWorkspaceを2年運用してみた結果どうなったか、という話をしていきます。「Workspaceが良いぞ」という記事ではなく、実際に使ってきて感じたことをまとめております。

Terraform Workspaceについて

Terraform Workspaceとは何か、という話はTerraformを書いたことがあれば一度は目に触れたことがあるでしょう。

Workspace機能について一言で説明すると、Workspaceを切り替えることで一つのコードで複数の環境を作ることができるという機能のことです(語弊があったらすみません)。一見すると、開発環境、QA環境、本番環境を切り替えられて便利な機能だなと思いきや、Terraformの公式ドキュメントを読むと、

In particular, organizations commonly want to create a strong separation between multiple deployments of the same infrastructure serving different development stages (e.g. staging vs. production) or different internal teams. In this case, the backend used for each deployment often belongs to that deployment, with different credentials and access controls. Named workspaces are not a suitable isolation mechanism for this scenario.

ということが書かれており、Workspaceは環境ごとで分離するために提供されていないのです。
とはいえ上手く使えばTerraformのコードを使い回すことができ便利ではあります。

2年運用してみて嬉しかったこと😄

開発,QA環境でterraform applyすることで本番環境前にapplyできるか事前に確認できる

これはWorkspaceの本来の使い方に近いものなのでまさに恩恵を受けている部分になります。
terraform planコマンドはとても強力な機能です。しかし運用しているとplanは実行できてもapplyはできないケースが時々存在します。
過去にplanも通っているしapplyも大丈夫だろうとPull Requestをmain branchへmergeした後、問題が発覚するということがありました。本番apply前に確認できるのは安心感がありとてもありがたいです。
もちろんこの話はWorkspaceではなくてもできることですが、Terraformの開発環境のコードと本番環境のコードを分けた場合この2つが必ずしも一致していないケースが往々にして存在してきます。それは開発と本番環境の差分が存在していたり、Terraformに慣れていないメンバーのヒューマンエラーであったり、diffを取り損ねていたり理由は様々です。Workspace運用はコードのapplyテストを強制できるという面でも良かったです。

${terraform.workspace}が便利

Terraform構文内で${terraform.workspace}を入れることで、Workspaceの文字列を利用できます(current-workspace-interpolation)。
例えばS3のバケット名だけ変えて環境ごとに作成したい場合、バケット名に${terraform.workspace}を入れて、Workspaceを切り替えてapplyすればsystemA-development-log,systemA-staging-log,systemA-production-logといった形で各環境のバケットが作成できます。

resource "aws_s3_bucket" "systemA_log_bucket" {
  bucket = "systemA-${terraform.workspace}-log"
  acl    = "private"
}

これによりvariable.tfファイルが全く要らないというケースができたり、社内の命名ルールに則って各環境の文字列を入れることができたのは良かったです。

コードの修正量が少なく済む

Workspaceを使った場合変更が1箇所で済む場合が多く、ここも嬉しかったところです。
環境ごとにディレクトリを作っているAnsibleを運用した経験があるのですが、同じような変更を開発,QA,商用環境へ反映させたいときがよく発生しました。3箇所も同じ変更を書き直し、diffを取り問題が無いか確認するというのはとても神経がいる作業で、慣れていてもミスが起きました。
Workspace運用ではこの修正作業での修正量が少なくなるのでありがたかったです。

2年運用してみて嬉しくなかったこと😥

countを利用することでapplyの事前確認ができなくなる

色々な記事でも取り上げられているのですが、Workspaceを使う際に本番環境だけリソースを設定したい場合はcountというものを使って差分を吸収します。countで環境ごとに分けるというのが悪いわけではないのですが、場合によってはcountを使ったために起きる弊害みたいなものが出てきました。
その最たる例として挙げられるのがapplyの事前確認ができないというケースです。これは嬉しかったことで挙げていた「applyが事前に確認できる」という利点が失われることになります。
例えばAWS Route53を運用しているとSaaS製品のドメインの認証のためにCNAMEやTXT, DKIMレコード等を登録してほしいということが発生します。そうすると開発環境のRoute53には設定入れなくて良いけど、本番環境には設定を入れたいという状況が起きてしまいます。

resource "aws_route53_record" "sample" {
  count   = terraform.workspace == "production" ? 1 : 0
  zone_id = "sample_zone"
  name    = "xxx.sample.jp"
  type    = "TXT"
  ttl     = "300"
  records = [
    "xxxxxxxxxxxxxxxxxxxxx"
  ]
}

理想は開発と本番環境を同じような環境を作れると良いのですが、現実はそうもいかないもので予算や利用しているSaaSの関係(1会社に1アカウントしか払い出さないとか)で必ずしも理想通りにはいきません...orz
これを解消するためにcountを利用するのですが、この時本番環境へのapplyが最初のapplyになるため、テストができない状況になります。

countとfor_eachを同じresourceに使えない

countを利用するとそのresource内でfor_eachが使えなくなります。 実行しようとすると以下のようなErrorが表示されます。

The "count" and "for_each" meta-arguments are mutually-exclusive, only one
should be used to be explicit about the number of resources to be created.

for_eachを使いたいけど使えないケースが発生してしまうところは残念です。

countを使うケースが想像以上に多かった

運用を続けていると当初考えていた以上にcountを使うケースが増えてきました。
Workspaceを使っていこうと決めた時、各環境で差分があったものの数は多くならないだろうと考えてました。しかし、1箇所でも環境の差分があるとそれが他のリソースに波及するということが起きました。
例えば、経費とかの関係でQA環境と本番環境で同じDBに接続している場合です。DBだけでなくそのネットワークやセキュリティグループも本番環境だけ必要でQA環境は要らないとします。
ここでcountを使って本番環境用ネットワークだけを作成するのですが、本番環境のサブネットをQA環境EKSと本番環境EKSで参照しようとするとさらにcountが必要になり参照が複雑になりました。
これはTerraformのディレクトリの分けも相まって起きたことですが、複雑に作ってしまったAWSリソースをTerraformで記述しようとすると複雑になるというのを痛感させられました。

まとめ

Workspaceを使ってきてみて嬉しいことがある一方で嬉しくないこともありました。
Terraformの設計も大事ですが、それで管理するAWSアカウントやアーキテクトの設計も大事だと考えさせられました。
今後はModuleとWorkspaceを上手に使い分ける方向も導入していきたいです。
この記事がTerraformを使うどなたかの参考になれば幸いです。

GitHubで編集を提案

Discussion