BIGLOBEの「はたらく人」と「トガッた技術」

Astroで構造化されたTerraformを管理してみる

Terraformコードの構造化が進み、変更するたびになんどもterraform applyを叩くはめになったことはありませんか。 実行する順番を間違えてトラブルになったことはありませんか。 そんな悩みを抱えているときに見つけたAstroというツールを紹介します。

f:id:biglobe-editor2:20210316160239j:plain

自己紹介

基盤本部基盤系システム部に所属している、梶田(かじた)といいます。 現在、基幹システムのクラウド移行プロジェクトに従事しています。 BIGLOBEの基幹システムは10年以上稼働し続けており、独自技術がふんだんに使われているので移行プロジェクトは一筋縄ではいきません。 クラウドに移行する際、将来のさらなる自動化や、動いているシステムとリンクするドキュメントを残す目的でインフラのコード化もすすめています。 自分の関わるシステムではインフラのコード化にTerraformを活用しています。

対象の読者

今回の内容は、Terraformを使ってひととおりシステム構築をすすめてきた経験を持っている人が対象です。

Terraformコードの構造化と直面した課題

Terraformを使うメリットの1つにソースコードを構造化しやすい点が上げられます。 実際に弊社でTerraformを使ってコード化に取り組んでいくと、以下のような段階を踏んで構造化がすすんでいきました。

  1. サンプルコードをコピペするなど、1つのファイルに全部のリソースを定義する(モノリス)
  2. 後から読む人が理解しやすいように、モジュール(Web、Database、Networkなどの意味のある塊)毎にファイルを分ける
  3. モジュール毎にディレクトリを分け、モジュールを個別に構築できるようにする

Terraformコードの構造化が進んでいくと、2つの問題が発生しました。

  1. 複数モジュールにまたがって修正した際、1つづつapplyを実行するのは面倒で、実行し忘れることもある
  2. モジュール間に依存関係があり、順番を間違えるとエラーになる

とくにCI/CDツールで自動化したときいろいろと悩むことになりました。 そのような中、Terraformの情報をいろいろ調べているときに見つけたのが、Astroです。

Astroとは

Astroは、Uberが開発した複数ディレクトリに分割されたTerraformファイルをまとめて実行するユーティリティです。 現在、GitHub上でApache License 2.0ライセンスとして公開されています。

Astroは、実行に関する設定をYAMLフォーマットのファイルで記載します。 この設定ファイルには、ディレクトリで分割されたTerraformファイルの依存関係を記述することができ、依存関係を考慮した順序でTerraformを実行します。

つまり構造化されたTerraformを定義することで、apply漏れや実行順序の間違いを防止できるユーティリティです。

今回は、Astroの基本的な使い方と使っていてはまったことを紹介しようと思います。

Astroを使ってみる

Astroをインストールする

本記事の執筆時点の段階で、Astroのインストール方法は2つあります。

1つはGitHubから、自分の環境にあったファイルをダウンロードする方法です。 ダウンロードしたファイルを解答すると、astrotvlという2つのコマンドが含まれています。 この2つのファイルをPATHの通ったディレクトリにコピーすればインストールは完了です。

もう1つの方法は、goコマンドを利用する方法です。 具体的には、go 1.12以上をインストールしてgo getコマンドを実行します。 詳細は、astroのReadmeを参照してください。

Astroは実行時に必要なバージョンのTerraformをダウンロードするので、Terraform単体をダウンロードする必要はありません。

astro.yamlファイルを用意する

Astroは、astro.yamlという名のYAMLファイルにTerraformを実行するときの設定を記載します。 次にこんな構成のTerraformファイルを想定して、astro.yamlのフォーマットを説明します。

root/
 ├ network/ ... VPCのリソースを定義したTerraformファイル群
 │  ├ provider.tf
 │  └ vpc.tf
 │
 ├ lb/ ... ALBのリソースを定義したTerraformファイル群
 │  ├ provider.tf
 │  ├ security_group.tf
 │  └ alb.tf
 │
 ├ web/ ... Webサーバのリソースを定義したTerraformファイル群
 │  ├ provider.tf
 │  ├ security_group.tf
 │  └ ec2.tf
 │
 └ database/ ... Databaseのリソースを定義をしたTerraformファイル群
    ├ provider.tf
    ├ security_group.tf
    └ rds.tf

1. モジュールを追加する

rootディレクトリ直下にastro.yamlという名前のファイルを作ります。 astro.yamlの内容は、こんな感じになります。

---
terraform:
  version: 0.13.5

modules:
  - name: network
    path: network

  - name: lb
    path: lb

  - name: web
    path: web

  - name: database
    path: database

最初の"terraform/version"で実行時に利用するTerraformのバージョンを指定します。 astroは初回実行時に指定されたバージョンのTerraformをダウンロードしてくれます。

modules以下で、実行するTerraformファイルを格納しているディレクトリを指定します。 "modules/name"でディレクトリにユニークなモジュール名をつけます。モジュール名は、あとで依存関係を指定するときに利用します。 "modules/path"でディレクトリのパスを指定します。

そんな感じに、まとめて実行したいTerraformの格納ディレクトリを記載します。

2. モジュール間の依存を追加する

ひととおりモジュールの記載が終わったら、モジュールの依存関係を追加します。 依存関係は、"deps"で指定します。

depsを追加したastro.yamlの内容は下記のようになります。 depsキー配下に、"module: <モジュール名>"をリストで追加します。

---
terraform:
  version: 0.13.5

modules:
  - name: network
    path: network

  - name: lb
    path: lb
    deps:
      - module: network

  - name: web
    path: web
    deps:
      - module: network
      - module: lb
      - module: database

  - name: database
    path: database
    deps:
      - module: network

今回は、lbモジュールとdatabaseモジュールはnetworkモジュールに、webモジュールは他の3つ(lb、web、database)のモジュールに依存しています。 このように記載してからAstroを実行すると、以下の順序でTerraformが実行されます。

  1. network
  2. lb, database
  3. web

その他

ここで紹介しきれませんでしたが、astro.yamlには他に下記のような設定も記載できます。 詳細は、astroのReadmeを参照してください。

  • Terraformの実行前に実行するコマンド。事前にロールを切り替えたりする際に利用します
  • Remote Backendの設定
  • variableにわたすパラメーター。astro実行時に置き換えることもできます

実行計画を確認してみる

まずは、実行計画を確認します。Terraformのplanコマンドにあたります。 AstroもTerraformと同じく、"plan"コマンドで実行計画が確認できます。 "--config"オプションで指定しなければ、astro.yamlをカレントディレクトリの中で探します。 "-v"オプションを指定すると少し詳細な情報が表示されます。

astro plan -v --config astro.yaml

実行すると、このような実行結果が得られます。 もし、変更箇所があった場合、変更箇所の情報が合わせて表示されます。

[database] Initializing...
[network] Initializing...
[web] Initializing...
[lb] Initializing...
[network] Planning...
[lb] Planning...
[database] Planning...
[web] Planning...
network: OK No changes (0s)
database: OK No changes (0s)
lb: OK No changes (0s)
web: OK No changes (0s)
Done

特定のモジュールに対してだけコマンドを適用したい場合、”--modules”でモジュール名を指定します。 下記は、lbとwebモジュールのみを実行する例です。

astro plan --config astro.yaml --modules lb,web

実際に構築してみる

astro.yamlで定義したモジュールに対してapplyを実行します。 注意事項として、AstroにはTerraformと異なり実行前の最終確認はありません。 applyを実行すると直ちに構築が開始されるので必ず先にplanを実行するクセをつけましょう。

astro apply -v --config astro.yaml

applyコマンドも”--modules”オプションを使って個別にモジュールを指定して実行できます。

astro apply --config astro.yaml --modules lb,web

環境を破壊(destroy)する

Astroには、構築に関するコマンドはplanとapplyしか用意されていません。 それ以外のコマンドを実行したいときは直接Terraformを利用します。

Astroを使うときのTips

applyしたら即実行

使い方の説明でも述べましたが、Astroはapplyを実行したときの最終確認は一切行わず直ちに構築を開始します。 必ず、planで変更内容を確認するようにしましょう。

.astroディレクトリのcacheは定期的に消しましょう

Astroはplanapplyを実行すると、astro.yamlファイルのあるディレクトリに.astroという名前のディレクトリを作ります。 そして、astro.yamlファイルで定義された全モジュールのTerraformファイルをディレクトリ毎コピーします。 さらにこのコピーをモジュールの数だけ作るので、実行後は思いもがけないほどファイルが作られてしまいます。

使っている開発エディターによっては、ファイルスキャンが走って遅くなることがあるのでまめに削除して掃除しましょう。 また、誤ってGitのレポジトリに登録してしまわないように、.astroディレクトリを.gitignoreに含めたほうがよいでしょう。

実行時のログ

Astroは裏でTerraformを実行しています。Astroを使っていると、エラーの調査などでTerraformの出力を確認したくなることがあります。 Terraformの実行結果は、".astro/<ランダム文字列>/<モジュール名>/logs"ディレクトリ配下にログとして保管されています。

Astroを実行するたびにランダム文字列のディレクトリが増えるので、繰り返し実行していると自分の探している実行時ログの場所がわからなくなります。 そういうこともあるので、.astroディレクトリはまめに掃除しましょう。

Terraformのローカルモジュールを使うとモジュールパスが変わってしまう

システムが複雑になり、Terraformのモジュールを活用することがあるかもしれません。 Astroは、ローカルパス参照のモジュールと相性がよくありません。

実行すると.astroディレクトリにTerraformファイル一式をコピーすると説明しましたが、その影響で実行時ディレクトリが変わってしまうためモジュールのパスも変わってしまいます。

とりあえず動かすだけならAstroに合わせてパスをかえてしまえばよいのですが、Terraformと併用すると都度モジュールのパスを書き換えることになるのであまり良い手ではなさそうです。ここは、別のソース(GitHubとか)で指定するようにするのが良いかもしれません。

2020年1月を最後に開発が止まっている

もしかするともっと良いツールが存在するのかもしれません。 でもソース自体が公開されているので、Go言語の勉強がてら色々気になるところに手を加えながら使うといいのかもしれないですね。

最後に

というわけで、Astroというツールを足早に紹介しました。 機能を絞りつつ痒いところに手が届くツールだと思います。

※ GitHub は、GitHub Inc.の商標または登録商標です。

※ Terraformは、HashiCorp,Inc.の米国およびその他の国における商標または登録商標です。

※ Uberは、Uber Technologies, Inc.の商標または登録商標です。