※この記事は3-shake Advent Calendar 2023の21日目の記事です
OpenTofuとは?
OpenTofuはTerraformからforkされた、OSSのインフラ構成管理ツールです。
Linux Foundation傘下のプロジェクトとして、MPL2ライセンスで正式リリースに向け開発が進められています。
※OpenTofuが生まれた背景に関してはこの記事では触れません。
※1/10に正式リリースされる予定みたいです。
公式サイト: https://opentofu.org
リポジトリ: https://github.com/opentofu/opentofu
使ってみる
今回は、terraformのチュートリアルにあるDockerでNginxを動かす例にOpenTofuを使用してみます。
インストール(MacOS)
$ brew install opentofu
$ tofu -v
OpenTofu v1.6.0-rc1
on darwin_arm64
1. main.tfの作成
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "3.0.2"
}
}
}
provider "docker" {}
resource "docker_image" "nginx" {
name = "nginx:latest"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.image_id
name = "sample"
ports {
internal = 80
external = 8000
}
}
2. tofu init
terraform init
同様、.terraform.lock.hcl
と.terraform
が作成される。
$ tofu init
Initializing the backend...
Initializing provider plugins...
- Finding kreuzwerker/docker versions matching "~> 3.0.1"...
- Installing kreuzwerker/docker v3.0.2...
- Installed kreuzwerker/docker v3.0.2. Signature validation was skipped due to the registry not containing GPG keys for this provider
OpenTofu has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that OpenTofu can guarantee to make the same selections by default when
you run "tofu init" in the future.
OpenTofu has been successfully initialized!
You may now begin working with OpenTofu. Try running "tofu plan" to see
any changes that are required for your infrastructure. All OpenTofu commands
should now work.
If you ever set or change modules or backend configuration for OpenTofu,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
3. 差分確認
terraform同様、tofu plan
で差分確認ができます。
$ tofu plan
OpenTofu used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
OpenTofu will perform the following actions:
# docker_container.nginx will be created
+ resource "docker_container" "nginx" {
+ attach = false
+ bridge = (known after apply)
+ command = (known after apply)
+ container_logs = (known after apply)
+ container_read_refresh_timeout_milliseconds = 15000
+ entrypoint = (known after apply)
+ env = (known after apply)
+ exit_code = (known after apply)
+ hostname = (known after apply)
+ id = (known after apply)
+ image = (known after apply)
+ init = (known after apply)
+ ipc_mode = (known after apply)
+ log_driver = (known after apply)
+ logs = false
+ must_run = true
+ name = "sample"
+ network_data = (known after apply)
+ read_only = false
+ remove_volumes = true
+ restart = "no"
+ rm = false
+ runtime = (known after apply)
+ security_opts = (known after apply)
+ shm_size = (known after apply)
+ start = true
+ stdin_open = false
+ stop_signal = (known after apply)
+ stop_timeout = (known after apply)
+ tty = false
+ wait = false
+ wait_timeout = 60
+ ports {
+ external = 8000
+ internal = 80
+ ip = "0.0.0.0"
+ protocol = "tcp"
}
}
# docker_image.nginx will be created
+ resource "docker_image" "nginx" {
+ id = (known after apply)
+ image_id = (known after apply)
+ keep_locally = false
+ name = "nginx:latest"
+ repo_digest = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so OpenTofu can't guarantee to take exactly
these actions if you run "tofu apply" now.
4. リソース適用
terraform同様、tofu apply
で適用ができます。
$ tofu apply
OpenTofu used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
OpenTofu will perform the following actions:
# docker_container.nginx will be created
+ resource "docker_container" "nginx" {
+ attach = false
+ bridge = (known after apply)
+ command = (known after apply)
+ container_logs = (known after apply)
+ container_read_refresh_timeout_milliseconds = 15000
+ entrypoint = (known after apply)
+ env = (known after apply)
+ exit_code = (known after apply)
+ hostname = (known after apply)
+ id = (known after apply)
+ image = (known after apply)
+ init = (known after apply)
+ ipc_mode = (known after apply)
+ log_driver = (known after apply)
+ logs = false
+ must_run = true
+ name = "sample"
+ network_data = (known after apply)
+ read_only = false
+ remove_volumes = true
+ restart = "no"
+ rm = false
+ runtime = (known after apply)
+ security_opts = (known after apply)
+ shm_size = (known after apply)
+ start = true
+ stdin_open = false
+ stop_signal = (known after apply)
+ stop_timeout = (known after apply)
+ tty = false
+ wait = false
+ wait_timeout = 60
+ ports {
+ external = 8000
+ internal = 80
+ ip = "0.0.0.0"
+ protocol = "tcp"
}
}
# docker_image.nginx will be created
+ resource "docker_image" "nginx" {
+ id = (known after apply)
+ image_id = (known after apply)
+ keep_locally = false
+ name = "nginx:latest"
+ repo_digest = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
OpenTofu will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
docker_image.nginx: Creating...
docker_image.nginx: Still creating... [10s elapsed]
docker_image.nginx: Creation complete after 16s [id=sha256:8aea65d81da202cf886d7766c7f2691bb9e363c6b5d9b1f5d9ddaaa4bc1e90c2nginx:latest]
docker_container.nginx: Creating...
docker_container.nginx: Creation complete after 1s [id=7d8166b585c6307a8c2ad3df5b422720a414bf29d12353348ce8bbd06e85677a]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
これで、nginxコンテナが作成されたことが確認できました。
5. リソース削除
これもterraform同様、tofu destroy
で可能です。
$ tofu destroy
docker_image.nginx: Refreshing state... [id=sha256:8aea65d81da202cf886d7766c7f2691bb9e363c6b5d9b1f5d9ddaaa4bc1e90c2nginx:latest]
docker_container.nginx: Refreshing state... [id=7d8166b585c6307a8c2ad3df5b422720a414bf29d12353348ce8bbd06e85677a]
OpenTofu used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
- destroy
OpenTofu will perform the following actions:
# docker_container.nginx will be destroyed
- resource "docker_container" "nginx" {
- attach = false -> null
- command = [
- "nginx",
- "-g",
- "daemon off;",
] -> null
- container_read_refresh_timeout_milliseconds = 15000 -> null
- cpu_shares = 0 -> null
- dns = [] -> null
- dns_opts = [] -> null
- dns_search = [] -> null
- entrypoint = [
- "/docker-entrypoint.sh",
] -> null
- env = [] -> null
- group_add = [] -> null
- hostname = "" -> null
- id = "7d8166b585c6307a8c2ad3df5b422720a414bf29d12353348ce8bbd06e85677a" -> null
- image = "sha256:8aea65d81da202cf886d7766c7f2691bb9e363c6b5d9b1f5d9ddaaa4bc1e90c2" -> null
- init = false -> null
- ipc_mode = "private" -> null
- log_driver = "json-file" -> null
- log_opts = {} -> null
- logs = false -> null
- max_retry_count = 0 -> null
- memory = 0 -> null
- memory_swap = 0 -> null
- must_run = true -> null
- name = "sample" -> null
- network_data = [
- {
- gateway = "172.17.0.1"
- global_ipv6_address = ""
- global_ipv6_prefix_length = 0
- ip_address = "172.17.0.2"
- ip_prefix_length = 16
- ipv6_gateway = ""
- mac_address = ""
- network_name = "bridge"
},
] -> null
- network_mode = "default" -> null
- privileged = false -> null
- publish_all_ports = false -> null
- read_only = false -> null
- remove_volumes = true -> null
- restart = "no" -> null
- rm = false -> null
- runtime = "runc" -> null
- security_opts = [] -> null
- shm_size = 64 -> null
- start = true -> null
- stdin_open = false -> null
- stop_signal = "SIGQUIT" -> null
- stop_timeout = 0 -> null
- storage_opts = {} -> null
- sysctls = {} -> null
- tmpfs = {} -> null
- tty = false -> null
- wait = false -> null
- wait_timeout = 60 -> null
- ports {
- external = 8000 -> null
- internal = 80 -> null
- ip = "0.0.0.0" -> null
- protocol = "tcp" -> null
}
}
# docker_image.nginx will be destroyed
- resource "docker_image" "nginx" {
- id = "sha256:8aea65d81da202cf886d7766c7f2691bb9e363c6b5d9b1f5d9ddaaa4bc1e90c2nginx:latest" -> null
- image_id = "sha256:8aea65d81da202cf886d7766c7f2691bb9e363c6b5d9b1f5d9ddaaa4bc1e90c2" -> null
- keep_locally = false -> null
- name = "nginx:latest" -> null
- repo_digest = "nginx@sha256:bd30b8d47b230de52431cc71c5cce149b8d5d4c87c204902acf2504435d4b4c9" -> null
}
Plan: 0 to add, 0 to change, 2 to destroy.
Do you really want to destroy all resources?
OpenTofu will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
docker_container.nginx: Destroying... [id=7d8166b585c6307a8c2ad3df5b422720a414bf29d12353348ce8bbd06e85677a]
docker_container.nginx: Destruction complete after 1s
docker_image.nginx: Destroying... [id=sha256:8aea65d81da202cf886d7766c7f2691bb9e363c6b5d9b1f5d9ddaaa4bc1e90c2nginx:latest]
docker_image.nginx: Destruction complete after 0s
Destroy complete! Resources: 2 destroyed.
Terraformからの移行
OpenTofuでは既存のTerraformのtfファイルやtfstateがそのまま使えます。
手順としては、stateのバックアップを取りつつ、OpenTofuでのinitをし、差分が出ないか確認するだけのようです。
今のところ、移行は簡単ですが、開発が進むにつれ、新機能の追加、Terraform ProviderやRegistryとの互換性が維持できなくなる可能性は十分ありそうです。
おわりに
TerraformのforkであるOpenTofuについて紹介しました。
Terraformとの使用感の差はなく、移行も簡単にできそうです。
今後のIaCの選択肢の一つにOpenTofuが入ってくるのかどうなのか、注目したいと思います。