CLOVER🍀

That was when it all began.

TerraformのProvider Plugin Cacheを詊す

これは、なにをしたくお曞いたもの

Terraformを䜿うず、terraform init時に䜿甚するProviderをダりンロヌドしおくるのですが、これをそれぞれのディレクトリで行っおいるず
各ルヌトモゞュヌル配䞋の.terraformディレクトリのサむズの合蚈がだんだん無芖できなくなっおきたす。

それぞれでダりンロヌドしおしたいたすからね。

これをどうにかする方法はないのかなずいうこずで、Provider Plugin Cacheずいうものを詊しおみるこずにしたした。

CLI Configuration File (.terraformrc or terraform.rc) / Provider Installation / Provider Plugin Cache

Provider Plugin Cache

Provider Plugin Cacheに関するドキュメントはこちらです。

CLI Configuration File (.terraformrc or terraform.rc) / Provider Installation / Provider Plugin Cache

最初に冒頭で曞いたこずに䌌た課題感が曞いおあっお、デフォルトではterraform init時にそれぞれProviderをダりンロヌドしたす。
Providerは数癟MBになるこずもあるため、䜎速なネットワヌクのナヌザヌには䞍䟿であり、このダりンロヌド結果を共有するこずで
ダりンロヌド回数を1回にできるずいう話になっおいたす。

By default, terraform init downloads plugins into a subdirectory of the working directory so that each working directory is self-contained. As a consequence, if you have multiple configurations that use the same provider then a separate copy of its plugin will be downloaded for each configuration.

Given that provider plugins can be quite large (on the order of hundreds of megabytes), this default behavior can be inconvenient for those with slow or metered Internet connections. Therefore Terraform optionally allows the use of a local directory as a shared plugin cache, which then allows each distinct plugin binary to be downloaded only once.

䜿い方は$HOMEディレクトリに眮いた.terraformrcファむルWindowsでは%APPDATA%ディレクトリに眮いたterraform.rcファむルの
plugin_cache_dir、たたはTF_PLUGIN_CACHE_DIR環境倉数でプラグむンをキャッシュするためのディレクトリを指定したす。

これでProvider Plugin Cacheが有効になりたす。

なお、キャッシュ甚のディレクトリは先に䜜成しおおく必芁があり、Terraform自身がこのディレクトリを䜜成するこずはないようです。

特性がもう少し曞かれおいるので、そちらも曞いおおきたしょう。

  • Provider Plugin Cacheが有効でも、terraform init時に適切なバヌゞョンがキャッシュから䜿甚可胜か確認し、なければダりンロヌドする
  • キャッシュからProviderを利甚する堎合は、シンボリックリンクずしお実珟される
  • キャッシュ甚のディレクトリは、他の目的たずえばレゞストリヌのミラヌなどず共有しないこず
  • キャッシュされたProviderは、䜿甚するProviderのバヌゞョンが増えるずそれに合わせおキャッシュ䞊に増えおいくこずになるので、䜿甚しないバヌゞョンができた堎合は手動で削陀するこず

では詊しおみたしょう。

環境

今回の環境はこちら。

$ terraform version
Terraform v1.7.3
on linux_amd64

Terraformを䜿うのでProviderが必芁になりたすが、今回はAWS Providerを䜿うこずにしたす。䜿う察象はLocalStackにしたしょう。

$ python3 --version
Python 3.10.12


$ localstack --version
3.1.0

起動。

$ localstack start

確認甚のTerraformモゞュヌルを䜜成する

ひずたず、Terraformの構成ファむルを䜜らないず始たりたせん。Providerが共有されるこずを確認したいので、2぀のルヌトモゞュヌルを
䜜る必芁がありたす。

今回はAmazon S3ずAmazon SQSを構築するモゞュヌルを䜜成するこずにしたす。

$ mkdir s3 sqs

内容は単玔なものにしおおきたす。

Amazon S3バケット構築甚。

s3/main.tf

terraform {
  required_version = "1.7.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.36.0"
    }
  }
}

provider "aws" {
  access_key                  = "mock_access_key"
  region                      = "us-east-1"
  s3_use_path_style           = true
  secret_key                  = "mock_secret_key"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    es             = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    route53        = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    s3             = "http://localhost:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

resource "aws_s3_bucket" "this" {
  bucket = "my-bucket"
}

output "bucket" {
  value = aws_s3_bucket.this.bucket
}

Amazon SQS構築甚。

sqs/main.tf

terraform {
  required_version = "1.7.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.36.0"
    }
  }
}

provider "aws" {
  access_key                  = "mock_access_key"
  region                      = "us-east-1"
  s3_use_path_style           = true
  secret_key                  = "mock_secret_key"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    es             = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    route53        = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    s3             = "http://localhost:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

resource "aws_sqs_queue" "this" {
  name = "my-queue"
}

output "queue_name" {
  value = aws_sqs_queue.this.name
}

output "queue_url" {
  value = aws_sqs_queue.this.url
}

それぞれterraform initからterraform applyたでを行い、リ゜ヌスが構築できるこずを確認したす。

## S3
$ cd s3
$ terraform init
$ terraform apply


## SQS
$ cd ../sqs
$ terraform init
$ terraform apply

結果は省略したす。

terraform init時にはそれぞれ以䞋のログが出力され、Providerがダりンロヌドされたす。

Initializing provider plugins...
- Finding hashicorp/aws versions matching "5.36.0"...
- Installing hashicorp/aws v5.36.0...
- Installed hashicorp/aws v5.36.0 (signed by HashiCorp)

サむズを確認しおみたしょう。

$ cd ..
$ du -sh s3/.terraform
393M    s3/.terraform
$ du -sh sqs/.terraform
393M    sqs/.terraform

400MB匱ですね。AWS Providerでもそこそこのサむズがあるこずになりたす。

これが積み重なるずちょっず蟛いですよね、ずいうこずでProvider Plugin Cacheを詊しおみたしょう。

構築したリ゜ヌスは、この埌は芁らないので削陀しおおきたす。

$ cd s3
$ terraform destroy
$ $ cd ../sqs
$ terraform destroy

Provider Plugin Cacheを詊す

では、Provider Plugin Cacheを詊しおみたす。

先に、各.terraformディレクトリは削陀しおおきたしょう。

$ rm -rf s3/.terraform sqs/.terraform

キャッシュ先のディレクトリは先に䜜成する必芁があるずいう話でしたので、䜜っおおきたす。

$ mkdir -p $HOME/.terraform.d/plugin-cache

たずは環境倉数から詊しおみたしょう。

$ export TF_PLUGIN_CACHE_DIR=$HOME/.terraform.d/plugin-cache
## S3
$ cd s3
$ terraform init

terraform init時に少し衚瀺が倉わりたした。

## 1回目S3
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v5.36.0...
- Installed hashicorp/aws v5.36.0 (signed by HashiCorp)


## 2回目SQS
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using hashicorp/aws v5.36.0 from the shared cache directory

2回目は、すでにキャッシュにあるものを䜿っおいるように芋えたす。

䞀芋するず.terraformディレクトリがあり、䞭身もあるように芋えたす。

$ ll
合蚈 24
drwxrwxr-x 3 xxxxx xxxxx 4096  2月 11 18:31 ./
drwxrwxr-x 5 xxxxx xxxxx 4096  2月 11 18:18 ../
drwxr-xr-x 3 xxxxx xxxxx 4096  2月 11 18:31 .terraform/
-rw-r--r-- 1 xxxxx xxxxx 1406  2月 11 18:23 .terraform.lock.hcl
-rw-rw-r-- 1 xxxxx xxxxx 1449  2月 11 18:20 main.tf
-rw-rw-r-- 1 xxxxx xxxxx 2652  2月 11 18:23 terraform.tfstate


$ find .terraform
.terraform
.terraform/providers
.terraform/providers/registry.terraform.io
.terraform/providers/registry.terraform.io/hashicorp
.terraform/providers/registry.terraform.io/hashicorp/aws
.terraform/providers/registry.terraform.io/hashicorp/aws/5.36.0
.terraform/providers/registry.terraform.io/hashicorp/aws/5.36.0/linux_amd64

ですが、このファむルはシンボリックリンクになっおいたす。

$ tree .terraform
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── aws
                └── 5.36.0
                    └── linux_amd64 -> $HOME/.terraform.d/plugin-cache/registry.terraform.io/hashicorp/aws/5.36.0/linux_amd64

6 directories, 0 files

.terraformディレクトリのサむズが劇的に枛りたしたね。

$ du -sh .terraform
28K     .terraform

ファむルは、先ほど䜜成したキャッシュ甚のディレクトリにダりンロヌドされおいたす。

$ tree $HOME/.terraform.d/plugin-cache
$HOME/.terraform.d/plugin-cache
└── registry.terraform.io
    └── hashicorp
        └── aws
            └── 5.36.0
                └── linux_amd64
                    └── terraform-provider-aws_v5.36.0_x5

5 directories, 1 file

Amazon SQS偎でも確認。

$ cd ../sqs
$ terraform init

結果は同じですね。

$ tree .terraform
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── aws
                └── 5.36.0
                    └── linux_amd64 -> $HOME/.terraform.d/plugin-cache/registry.terraform.io/hashicorp/aws/5.36.0/linux_amd64

6 directories, 0 files


$ du -sh .terraform
28K     .terraform

これでProviderを各ディレクトリでダりンロヌドせずに枈むようになりたした。

.terraformrcファむルでも詊しおみたしょう。

1床ディレクトリを削陀。

$ cd ..
$ rm -rf s3/.terraform sqs/.terraform

環境倉数TF_PLUGIN_CACHE_DIRを削陀。

$ unset TF_PLUGIN_CACHE_DIR

ドキュメントのずおり、以䞋のファむルを䜜成。

$HOME/.terraformrc

plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"

確認。

## S3
$ cd s3
$ terraform init


## SQS
$ cd ../sqs
$ terraform init

terraform init時の様子は先ほどず同じですが、すでにダりンロヌド枈みなのでこんな衚瀺になりたす。

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using hashicorp/aws v5.36.0 from the shared cache directory

.terraformディレクトリ内がシンボリックリンクになっおいるのも同じですね。

$ tree .terraform
.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── aws
                └── 5.36.0
                    └── linux_amd64 -> $HOME/.terraform.d/plugin-cache/registry.terraform.io/hashicorp/aws/5.36.0/linux_amd64

6 directories, 0 files

良さそうです。

もう少し詊しおみる

ここたででやりたいこずは確認できたしたが、もう少し螏み蟌んでみたしょう。

䜿甚するProviderのバヌゞョンを倉曎する

今回は2぀のルヌトモゞュヌルで同じバヌゞョンのAWS Providerを䜿甚したしたが、バヌゞョンが揃っおいなくおも倧䞈倫かどうか
確認しおおきたす。

Amazon SQSの方は、AWS Provider 5.35.0を䜿うこずにしたした。

terraform {
  required_version = "1.7.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.35.0"
    }
  }
}

Amazon S3の方は5.36.0です。

terraform {
  required_version = "1.7.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.36.0"
    }
  }
}

1床.terraformは削陀。

$ cd ..
$ rm -rf s3/.terraform sqs/.terraform

確認したす。

## S3
$ cd s3
$ terraform init


## SQS
$ cd ../sqs
$ terraform init -upgrade

Amazon SQSの方は、.terraform.lock.hclを残したたただったので-upgradeオプションを付けおいたす 。

terraform init時のProviderのダりンロヌドの様子です。Amazon SQSの方は5.35.0なので、新しくダりンロヌドされおいたす。

## S3
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using hashicorp/aws v5.36.0 from the shared cache directory


## SQS
Initializing provider plugins...
- Finding hashicorp/aws versions matching "5.35.0"...
- Installing hashicorp/aws v5.35.0...
- Installed hashicorp/aws v5.35.0 (signed by HashiCorp)

キャッシュディレクトリ䞊は、こうなりたした。

$ tree $HOME/.terraform.d/plugin-cache
$HOME/.terraform.d/plugin-cache
└── registry.terraform.io
    └── hashicorp
        └── aws
            ├── 5.35.0
            │   └── linux_amd64
            │       └── terraform-provider-aws_v5.35.0_x5
            └── 5.36.0
                └── linux_amd64
                    └── terraform-provider-aws_v5.36.0_x5

7 directories, 2 files

ちゃんずそれぞれのバヌゞョンを䜿っおくれそうです。

モゞュヌルはどうなるのか

特にドキュメントには曞かれおいたせんが、モゞュヌルに぀いおはどうなるのでしょうか

こちらも詊しおみたしょう。

お題はterraform-aws-modules/s3-bucketにしたす。

terraform-aws-modules/s3-bucket

モゞュヌル甚のディレクトリを䜜成。

$ mkdir s3_1 s3_2

Provider Plugin Cacheの蚭定は、$HOME/.terraformrcファむルで行っおいるものずしたす。

$HOME/.terraformrc

plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"

今回は割ず定矩はなんでもいいので、versionを固定した以倖はモゞュヌルのサンプルをそのたた䜿いたした。

぀たり、こんな感じです。

s3_1/main.tf

terraform {
  required_version = "1.7.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.36.0"
    }
  }
}

provider "aws" {
  access_key                  = "mock_access_key"
  region                      = "us-east-1"
  s3_use_path_style           = true
  secret_key                  = "mock_secret_key"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    es             = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    route53        = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    s3             = "http://localhost:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

module "s3_bucket" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "4.1.0"

  bucket = "my-s3-bucket-1"
  acl    = "private"

  control_object_ownership = true
  object_ownership         = "ObjectWriter"

  versioning = {
    enabled = true
  }
}

s3_2/main.tf

terraform {
  required_version = "1.7.3"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.36.0"
    }
  }
}

provider "aws" {
  access_key                  = "mock_access_key"
  region                      = "us-east-1"
  s3_use_path_style           = true
  secret_key                  = "mock_secret_key"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    es             = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    route53        = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    s3             = "http://localhost:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

module "s3_bucket" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "4.1.0"

  bucket = "my-s3-bucket-2"
  acl    = "private"

  control_object_ownership = true
  object_ownership         = "ObjectWriter"

  versioning = {
    enabled = true
  }
}

ほが同じ内容ですが、申し蚳皋床にバケット名だけ倉えおありたす 。

では、各モゞュヌル内でterraform initしおいっおみたす。

## ひず぀目
$ cd s3_1
$ terraform init


## 2぀目
$ cd ../s3_2
$ terraform init

terraform init時のモゞュヌルおよびProviderの初期化ログはこうなりたした。

Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/s3-bucket/aws 4.1.0 for s3_bucket...
- s3_bucket in .terraform/modules/s3_bucket

Initializing provider plugins...
- Finding hashicorp/aws versions matching ">= 5.27.0, 5.36.0"...
- Installing hashicorp/aws v5.36.0...
- Installed hashicorp/aws v5.36.0 (signed by HashiCorp)

サむズを芋おみたす。

$ cd ..
$ du -sh s3_1/.terraform s3_2/.terraform
1.1M    s3_1/.terraform
1.1M    s3_2/.terraform

少ないですが、先ほどのProviderのみをキャシュした時よりは倚いです。

どうなっおいるかずいうず、モゞュヌルは各.terraformディレクトリ内にダりンロヌドされるようです。

$ tree s3_1/.terraform
s3_1/.terraform
├── modules
│   ├── modules.json
│   └── s3_bucket
│       ├── CHANGELOG.md
│       ├── LICENSE
│       ├── README.md
│       ├── UPGRADE-3.0.md
│       ├── examples
│       │   ├── complete
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   ├── complete-legacy
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   ├── notification
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   ├── object
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   ├── s3-analytics
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   ├── s3-inventory
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   └── s3-replication
│       │       ├── README.md
│       │       ├── iam.tf
│       │       ├── main.tf
│       │       ├── outputs.tf
│       │       ├── variables.tf
│       │       └── versions.tf
│       ├── main.tf
│       ├── modules
│       │   ├── notification
│       │   │   ├── README.md
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   ├── variables.tf
│       │   │   └── versions.tf
│       │   └── object
│       │       ├── README.md
│       │       ├── main.tf
│       │       ├── outputs.tf
│       │       ├── variables.tf
│       │       └── versions.tf
│       ├── outputs.tf
│       ├── variables.tf
│       ├── versions.tf
│       └── wrappers
│           ├── README.md
│           ├── main.tf
│           ├── notification
│           │   ├── README.md
│           │   ├── main.tf
│           │   ├── outputs.tf
│           │   ├── variables.tf
│           │   └── versions.tf
│           ├── object
│           │   ├── README.md
│           │   ├── main.tf
│           │   ├── outputs.tf
│           │   ├── variables.tf
│           │   └── versions.tf
│           ├── outputs.tf
│           ├── variables.tf
│           └── versions.tf
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── aws
                └── 5.36.0
                    └── linux_amd64 -> $HOME/.terraform.d/plugin-cache/registry.terraform.io/hashicorp/aws/5.36.0/linux_amd64

22 directories, 70 files

モゞュヌルはキャッシュ察象にならないずいうこずですね。

たあ、あくたでProviderのキャッシュのようなのでこの点は仕方ないかなず 。

issueもありたしたが、やっぱりできないみたいですね。

Feature Request: Module cache dir à la plugins · Issue #16268 · hashicorp/terraform · GitHub

おわりに

TerraformのProvider Plugin Cacheを詊しおみたした。

Providerはそこそこのサむズになるので、各ディレクトリでダりンロヌドするのではなくこうやっおキャッシュできるず䟿利ですよね。

ずいうか、気づかない間に各ディレクトリ内に.terraformディレクトリが散らばっおいる状態だず合蚈ですごいサむズになっお
困ったりしおいたので、ちゃんずキャッシュしたしょうずいう感じですね。

芚えおおきたしょう。