Terraform & Azure デプロイ設計4原則

Posted on Mar 9, 2016

注: 2018/1/8にサンプルを更新しました。更新エントリはこちら

情報がありそうでない

以前のエントリで書いたとおり、TerraformでAzureへデプロイする方式をClassicからResource Managerへ移行しているところです。

今後も継続して試行錯誤するとは思うのですが、ふらふらしないように原則を作りました。この手の情報はありそうでないので、参考になればと思いこのエントリを書いています。

なお、考え方は他のクラウドやデプロイツールでも応用できるかと。

4原則

  1. セキュリティファースト
  2. 手順書をなくそう
  3. 分割境界にこだわりすぎない
  4. 早すぎる最適化は悪

なお、サンプルのTerraformファイル群を、Githubに置いておきました。

今後ガラガラポンする可能性は大いにありますが、現時点ではこんな構造です。

.
├── .gitignore
├── main.tf
├── availability_set
│   ├── avset_web.tf
│   ├── avset_db.tf
│   └── variables.tf
├── network
│   ├── sg_backend.tf
│   ├── sg_frontend.tf
│   ├── variables.tf
│   └── vnets.tf
├── storage
│   ├── storage_backend.tf
│   ├── storage_frontend.tf
│   └── variables.tf
└── terraform.tfvars

Availability Setに対するVMのデプロイはTerraformの外でやっています。まだTerraformのAzure RM Providerにない、ということもありますが、VMの増減はアドホックだったり、別ツールを使いたいケースが多いので。

1. セキュリティファースト

セキュリティはデザイン時に考慮すべき時代です。機密情報が漏れないように、また、身内がうっかりリソースを壊して泣かないようにしましょう。

  • 認証情報は変数指定し、設定ファイルから読み込む

    • サブスクリプションIDやOAuth Client ID/Secretなどを、リソースを作るtfファイルに書かない
    • terraform.tfvarsなどにまとめて書く
  • 認証情報や現物情報が入ったファイルはバージョン管理ツールから除外する

    • Gitなら.gitignoreに指定する
    • .tfstateなど現物情報(Azure上のIDなど)が入る結果ファイルも除外
      • チームで使う場合はファイルではなく、Consulなどのリモートバックエンドを使うと思いますが、念のため
  • RBACで必要最小限の権限を付与する

    • Terraformの外の話ですが、サービスプリンシパルを作る時には意識しましょう
    • 身内がリソースをうっかり壊したら、それは管理者の責任です
  • ネットワークセキュリティグループはサブネットに指定しておく

    • 個々のVMの管理者に任せず、サブネットで絞っておきましょう
      • VMはアドホックに作られるケースが多く、ルーズになりがちです
    • サンプルではフロントエンドとバックエンドサブネットそれぞれにセキュリティグループを指定しています
      • フロントの受信はPort 80、443、22を許可 (できれば22はソースIP指定)
      • バックの受信はフロントサブネットからのみ許可 (Internetからの通信を deny all)

2. 手順書をなくそう

どうせなら手順書を無くす心意気でやりましょう。Infrastructure as Codeのメリットのひとつです。コードで手順を語りましょう。わかりやすさ重視です。

ドキュメントを否定する訳ではなく、コード化に至った背景、ポリシーや使い方、前提条件はドキュメント化し、あとはコードで語る。という世界観です。

3. 分割境界にこだわりすぎない

TerraformのModuleをはじめ、最近のデプロイツールはリソースや処理単位をグルーピングできます。ここがアーキテクトの腕の見せ所です。安易に「ベストプラクティス教えろや」という人は残念ながら残念です。大事なことなので2回言いました。

グルーピング、分割する目的は、

  • main.tfの肥大化を防止し、コードの見通しを良くする
  • 再利用しやすくする
  • 責任範囲を明確化し、オーナー意識を醸成する
  • 権限とコードを一致させる

などが挙げられます。規模が小さく関わる人が少ないうちは無理して分割する必要はないですが、大きくなってくるとメリットがあります。

以下が分割単位、境界ポリシーの例です。

  • リソースタイプで分割する

    • サンプルはその例
      • ネットワーク、ストレージ、VM Availability Setで分割
    • 直観的
    • デプロイに関わる人数が少ない間はこれがおすすめ
  • 組織単位で分割する

    • コンウェイの法則
    • リソースタイプ = 組織 という場合もある
      • ネットワーク管理者が別グループ、など
  • 地理的に分割する

    • リージョンやロケーションで分割
    • リソースタイプと組み合わせる手もある
      • “Network_JapanEast"など
  • 静的なリソースと動的なリソースを分ける

    • 変化の頻度で分ける
      • ネットワークが頻繁に変わることはまれ
      • VMは増減が激しい
    • 動的なリソースは対象から外す、別手段とする手も

スカッとしませんが、ひとつのポリシーにこだわらず、複数組み合わせてもいいと思います。そんな世界に僕らは生きています。

4. 早すぎる最適化は悪

最適化できる人 = その道のエキスパート です。使いはじめたばかりの段階では、最適化とか無理。また、システムの外部環境や制約がはじめから決まっていることは、まれです。

なので、はじめから「最強の構成」を目指さないほうがいいでしょう。特に分割方針。きっとすぐに変えたくなります。

ひとつのmain.tfで動かしながら、まずTerraformやAzureの仕様や挙動を理解しましょう。そして、慣れてきて、システムの外部環境や制約が見えてきた時点で分割方針を決めてもいいのではないか、と思います。

そして、

  • リファクタリングできるなら、する
  • リファクタリングできなくても、理解の上で維持し機会を待つ、または、次の機会に活かす
  • はじめに作った人へマサカリを投げない

完璧を求めずにいきましょう。

でも、しつこいですが、セキュリティだけは、はじめから意識してくださいね。Security by design。