[小ネタ] Terragruntを使っていたらディスクが埋まってた

## Terragrunt を使っていたら、ディスクがいつの間にか埋まっていた！

こんにちは [Terragrunt](https://terragrunt.gruntwork.io/) 使いです！

最近 Macから「ディスク容量足りない！」とお叱りを受けることがあり、原因を調査していたのですが、その調査中に**異常にサイズが肥大しているterragruntのプロジェクトを発見しました。**

```shell-session
$ du -h -d 1
...
とあるTerragruntプロジェクトのディレクトリ 30GB (!?)
...
```

**おまえかー！！**

そしてそのプロジェクトの中を調査してみると、どうやらプロジェクトが肥大していた原因は `terragrunt apply` のたびに作成される `.terragrunt-cache` ディレクトリのようでした。

`.terragrunt-cache` ディレクトリとはなんでしょう？ [公式ドキュメント](https://terragrunt.gruntwork.io/docs/features/caching/)によると、一部のキャッシュ（削除可能なデータ）を保存しておくためのもののようです。

> **Terragrunt creates a `.terragrunt-cache` folder in the current working directory** as its scratch directory. It downloads **your remote Terraform configurations** into this folder, runs your Terraform commands in this folder, and **any modules and providers those commands download also get stored in this folder.** You can safely delete this folder any time and Terragrunt will recreate it as necessary.

実際、Terragruntを実行すると以下が `.terragrunt-cache` ディレクトリに作成されます。

- `aws-provider` などのTerraformプラグイン類 （Goバイナリ）
    - `provider-aws`だと **350MB前後ぐらい**
    - **今回のディスク容量不足はこれが原因**
- Terragrunt内で `source = "..."` で参照しているディレクトリ(リポジトリ)
    - プロジェクトにもよるが大きくても数MBなので大したサイズではない
- state ファイル
    - 他と比較できるほど大きなサイズになっていることはないと思われる


一般的にTerragruntプロジェクトはモジュールおよび状態（ステート）の管理単位を細かくする傾向があるので、 `環境 ✕ モジュール` 分の上記が確保されてしまいます。

件のTerragruntプロジェクトをざっと見ると一部ディレクトリで `環境 5 * モジュール 10` 程度はあったので、少なくとも `5 * 10 * 350MB = 17GB` ぐらいは重複したGoバイナリでディスクが埋められることになります。


## 肥大化した `.terragrunt-cache` への対処方法

### Terraformプラグイン類 （Goバイナリ） のキャッシュ位置をシステムで統一する

Terragrunt/Terraform 関係なく、そもそもバカデカいサイズのTerraformプラグインのバイナリをそれぞれのTerraform実行ディレクトリに置く必要がありません。

以下の設定を `~/.terraformrc` に書き、一回ダウンロードしたプラグインバイナリが再利用されるようにしましょう。

```text:~/.terraformrc
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
```

:::note
参考: `.terraformrc` のリファレンス
https://developer.hashicorp.com/terraform/cli/config/config-file
:::

ただ、これを実行した後、 `terragrunt run-all` などで複数のモジュールを一気に実行するとよくわからないエラーが出て実行に失敗することがありました。Terragrunt上での同時実行に弱いのかもしれません。

しかし現実的に言って、Terragrunt/Terraformを扱う上では設定は必須だと思います。そうじゃないとディスクがもったいなさすぎる。


### `.terragrunt-cache` ディレクトリを削除する

肥大化したキャッシュディレクトリが既にある場合は削除しましょう。

[公式では以下の `find` コマンド / `TERRAGRUNT_DOWNLOAD` 環境変数による方法が紹介されています。](https://terragrunt.gruntwork.io/docs/features/caching/)

```sh
# ディレクトリの確認
$ find . -type d -name ".terragrunt-cache"
# 削除
$ find . -type d -name ".terragrunt-cache" -prune -exec rm -rf {} \;

# あるいは TERRAGRUNT_DOWNLOAD でキャッシュの場所をあらかじめ固定しておいてたまにキャッシュを削除する
$ export TERRAGRUNT_DOWNLOAD=/Users/user/.my-terragrunt-cache-gonna-be-cleared-for-one-day
```

## その他

### Terragrunt がモジュールをコピーする範囲を減らす

あまりディスク利用量の削減には貢献しませんが、Terragruntがモジュールディレクトリをコピーする際のコストも毎回となるとそこそこ大きなものです。

たとえば、以下のように `source = "..."` を記述している場合、本来必要な `modA` だけではなく、その親の `../../../modules` 自体のディレクトリが　`.terragrunt-cache` にコピーされます。

```terraform
terraform {
  source = "../../../modules//modA"
}
```

次のように `source = "..."` に記述してある親ディレクトリを害の無い範囲で絞り込みます。無駄なコピーがなくなるので、実行時のパフォーマンスも向上します。

```terraform
terraform {
  # 修正後 - ../../../modules/modA をコピーする起点として / を参照する
  source = "../../../modules/modA///"

  # 修正前 - ../../../modules をコピーする起点として modA を参照する
  # source = "../../../modules//modA"
}
```

:::note warn
ただし、この方法は source がリモートのgitリポジトリ等を参照している場合などは使えません。逆に、ローカルのディレクトリを参照している場合はほぼペナルティ無く実行できるのでオススメです。
:::

## まとめ

Terragrunt を使っていて何故かマシンのディスクが埋まってきていると通知を受ける場合は、一度Terragruntプロジェクトのデータサイズを確認したほうがいいでしょう。