🐍

Terraform に入門する

2024/12/10に公開

🌟 はじめに

本記事は terraform Advent Calendar 2024 の10日目 の記事になります。

普段私は Terraform を使って AWS や Google Cloud のリソースを構築していますが、今回は 「Terraform を初めて使う方でも楽しめる内容」 を心がけて記事を書こうと思います。

これまでに Terraform × クラウドプロバイダに関する記事を書いたことがありますが、Terraform の利用経験がある方向けの内容が多かったと感じていました。
そこで今回は、Terraform を全く触ったことがない方が Terraform を少しでも触れるようになることを目指し、記載させていただこうと思います。

もしすでに Terraform を使ったことがある方は、基本に立ち返るきっかけとして、また新しい気づきを得る場としてお楽しみいただけると幸いです。

説明が不十分である点がある際はご指摘いただければと思います。

📖 対象読者

本記事の対象読者は以下です。

  • Terraform を全く触ったことがない方
  • Terraform を使ったみたいと思っている方

✅ ゴールの確認

本記事では下記をゴールとします。

  • なぜ Terraform を始めとした IaC ツールが必要なのかを知る
  • 🔥 Terraform の構築が始められる
  • Terraform の基本コマンドや基本構文を知る

💡 クラウド開発における課題を知る

本章ではまず、何故 Terraform を使う必要があるのかを考えてみます。
クラウドサービスを利用してシステムを構築する企業が増える中で、手動でクラウドリソースを管理する方法には以下のような課題が存在します。

  • 人的ミスが発生しやすい
    手動操作ではヒューマンエラーが発生するリスクが高く、リソースの設定ミスや環境構築の手順漏れがシステム全体の信頼性を損なう原因になる。

  • 環境の再現が難しい
    手作業で構築された環境では、同じ構成を再現するのが難しく、新しい環境や障害時の復旧に時間がかかる。

  • 構築手順の属人化
    リソースの構築方法が特定の個人に依存するため、担当者の不在や退職により管理が困難になる可能性がある。

  • 大規模な環境構築に時間がかかる
    手動操作はスケールしにくく、大量のリソースを迅速にデプロイすることが難しい。

  • 構成管理が困難
    リソースが増えると、全体の構成を一貫して把握し、変更を適切に管理することが困難になる。

などなどです。
これらの課題を解決するために、IaC (Infrastructure as Code) を活用し、クラウドリソース管理することが一つの対策として考えられます。

🤔 Infrastructure as Code (IaC) とは何か?

この章では Terraform の大元の概念でもある IaC について考えてみます。

Infrastructure as Code (IaC) とは、インフラストラクチャ(サーバー、ネットワーク、ストレージなど)の構築や管理をコード化する手法です。従来の GUI や手動操作に依存せず、コードで記述された設定ファイルを使用してインフラを定義・管理するため、インフラ管理の効率化や信頼性の向上を実現します。
従来の手動操作では、人的ミスや構成の一貫性の欠如といった課題がありましたが、IaC を導入することで以下のような利点を得ることができます。これにより、クラウド環境の大規模構築や運用の効率化が可能になります。

IaC のメリット

  1. 安全で一貫性のある展開
  • コード化による信頼性
    バージョン管理可能な設定ファイルを用いることで、インフラ構成の一貫性を確保。手作業で発生しやすい人的ミスを防ぐ。

  • 再利用性と共有性
    設定ファイルは再利用可能で、チーム内で容易に共有できます。これにより、属人化を防ぎ、効率的な作業が可能となる。

  • 予測可能な管理
    宣言的な設定ファイルを使用するため、変更が実際にどのように適用されるかを事前に把握できる。

  1. 標準化されたワークフロー
  • 直感的な記述
    宣言的な言語を用いてリソースを定義するため、初心者でも分かりやすい形式で記述可能である。

  • チーム間の統一
    チーム全体で標準化されたプロセスを共有できるため、作業フローのバラツキを削減する。

  • 依存関係の自動管理
    IaC ツールがリソース間の依存関係を自動的に計算し、構築の順序を最適化する。

  1. インフラの追跡管理
  • 状態ファイルによる正確な把握
    IaC ツールは現在のインフラの状態を追跡し、コードとの差分を確認できる。

  • 安全な変更管理
    差分に基づいて変更を適用するため、既存のリソースに不要な影響を与えるリスクを軽減する。

  • 迅速な復旧と再現
    同じコードで環境を再現可能なため、障害発生時の復旧作業を効率化ができる。

IaC を使うことで「クラウド開発における課題」を解決できる可能性があること分かりました。
IaC の具体的なサービスとして Terrafrom があります。次に Terraform とは何かを見ていきましょう。

🤔 Terraform とは何か?

Terraform は、HashiCorp 社が開発した IaC ツールです。
人間が読みやすい宣言的な設定ファイルでインフラを定義し、そのライフサイクルを管理します。

https://www.terraform.io/

Terraform の主な特徴

ドキュメントを参考にすると、以下のような特徴が挙げられます。

  1. マルチプロバイダー対応

    • AWS、Azure、GCP など、1000 以上のプロバイダーに対応。
    • Kubernetes、Helm、GitHub、Splunk など多様なサービスをサポート。
    • カスタムプロバイダーの作成も可能である。
  2. 統一された管理方法

    • 異なるプロバイダーのリソースを同じ言語で管理。
    • モジュール化による設定の再利用。
    • プロバイダー間のリソースを組み合わせた構成が可能である。
  3. Terraform を使った効率的なデプロイメントフロー

    • 明確な 5 ステップのワークフロー。
      1. Scope:プロジェクトのインフラの特定
      2. Author:設定の作成
      3. Initialize:初期化・プラグインのインストール
      4. Plan:変更のプレビューを確認
      5. Apply:変更の適用
    • 状態ファイルによる確実な変更管理。

下記に添付している画像は Terraform のドキュメントにある図解になります。
この図に示されているように、AWS や Google Cloud、Azureといった主要なクラウドプロバイダーだけでなく、Kubernetes、GitHub、Heroku、さらには VMware など、非常に多岐にわたるサービスやプロバイダーをサポートしています。これにより、異なる環境にまたがるインフラストラクチャを統一された手法で管理することが可能です。
Terraformは企業の特定の要件や技術スタックに柔軟に適応し、インフラ管理の効率化を促進します。

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code

この章で紹介した特長を基盤として、次のセクションでは Terraform の具体的な活用方法や手順について詳しく見ていきます。

🔥 Terraform に入門する

Terraform の install

Terraform をインストールする方法はいくつかありますが、ドキュメントベースの一般的なインストール方法を説明します。

macOS の場合

Homebrew is a free and open-source package management system for macOS. Install the official Terraform formula from the terminal.

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

上記に記載がある通り macOS の場合はパッケージマネージャーの Homebrew を使って install する方法が紹介されています。
ターミナルを開いて以下の手順で実行します。

  1. HashiCorp の Homebrew リポジトリ(Tap)を追加します。
brew tap hashicorp/tap
  1. hashicorp/tap/terraform を使って Terraform をインストールします。
brew install hashicorp/tap/terraform
  1. Terraform を最新バージョンに更新するために、まず Homebrew を更新します。
brew update
  1. その後、以下のコマンドを実行して Terraform をアップグレードします。
brew upgrade hashicorp/tap/terraform

※ Windows の場合, Linux の場合 は下記を参考してください。

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

インストール後の確認

Terraform が install されたことを確認するために確認を行いましょう。

  1. バージョン確認:terraform version
terraform version

最新 version の v1.10.1 が表示されていれば大丈夫です。(別の version が既に install されていた場合はそちらの version で進めていただいて大丈夫です。)

> Terraform v1.10.1
> on darwin_arm64
  1. ヘルプの表示:terraform -help
terraform -help

下記のようにヘルプが表示されていたら大丈夫です。

> Usage: terraform [global options] <subcommand> [args]

> The available commands for execution are listed below.
> The primary workflow commands are given first, followed by
> less common or more advanced commands.

> Main commands:
>   init          Prepare your working directory for other commands
>   ...

ここまでで Terraform コマンドが実行できるようになりました。
次の章からは Terraform を使って実際にリソースを構築してみましょう。

🛠️ Terraform を使ってリソースを構築する

この章では Terraform の基本コマンドを、シンプルな Nginx コンテナの例を使って説明します。
この例は公式ドキュメントの「Install Terraform」を少し改良した例になります。

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

※ 次の章で基本構文を追って行くので、気になる方は適宜そちらを参照してください。

今回使う Smaple Repository は下記になりますので、適宜 clone して頂ければと思います。

https://github.com/oyasumipants/getting-started-with-terraform

構成は以下のようになっております。

.
├── README.md
├── environments
│   ├── xxx
│   │   └── nginx
│   │       ├── main.tf
│   │       ├── provider.tf
│   │       └── variable.tf
│   └── yyy
│       └── nginx
│           ├── main.tf
│           ├── provider.tf
│           └── variable.tf
└── modules
    └── nginx
        ├── local.tf
        ├── nginx.tf
        ├── provider.tf
        └── variable.tf

terraform init

terraform init は、Terraform プロジェクトの初期化を行うコマンドです。このコマンドは以下の処理を実行します:

  1. プロバイダーのダウンロードとインストール
  2. バックエンドの初期化
  3. モジュールのダウンロード

Initialize the project, which downloads a plugin called a provider that lets Terraform interact with Docker.

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli#quick-start-tutorial

今回の例では、environments/xxx/nginx/main.tfで以下のようなモジュール構成を使用しています。

module "nginx" {
  source = "../../../modules/nginx"

  docker_container_name = var.docker_container_name
  container_ports = var.container_ports

  providers = {
    docker = docker
  }
}

実際に初期化を実行してみましょう。

cd environments/xxx/nginx
terraform 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 (self-signed, key ID BD080C4571C6104C)
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html
Terraform 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 Terraform can guarantee to make the same selections by default when you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.

この初期化プロセスでは以下が行われています。

  1. 指定された作業ディレクトリ(この場合、environments/xxx/nginx)でTerraformの初期化が行われ、設定ファイルが適切に読み込まれます。
  2. 必要なプロバイダー(例: Docker プロバイダー)が自動的にダウンロードされます。
  3. .terraform ディレクトリが作成され、ダウンロードしたプロバイダーや参照されるモジュールが格納されます。
  4. プロバイダーやモジュールのバージョン情報を記録した .terraform.lock.hcl ファイルが生成され、将来的に同じバージョンを再現可能にします。

terraform plan

terraform plan は、現在の状態から目的の状態への変更内容を確認するコマンドです。実際のインフラには変更を加えず、どのような変更が行われるかをプレビューできます。

terraform plan

実行すると、以下のような出力が表示されます。

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.nginx.docker_container.nginx will be created
  + resource "docker_container" "nginx" {
      + attach                                      = false
      ...
    }

  # module.nginx.docker_image.nginx will be created
  + resource "docker_image" "nginx" {
      + id           = (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 Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

この出力から、作成されるリソースとその設定内容を確認できます。

terraform apply

terraform applyは、実際にインフラストラクチャに変更を適用するコマンドです。

terraform apply

実行すると、まずplanの結果が表示され、続行の確認が求められます:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.nginx.docker_container.nginx will be created
  + resource "docker_container" "nginx" {
      + attach                                      = false
      ...
    }

  # module.nginx.docker_image.nginx will be created
  + resource "docker_image" "nginx" {
      + id           = (known after apply)
      ...
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 

yesを入力すると、実際の変更が開始されます。成功すると以下のような出力が表示されます:

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.nginx.docker_image.nginx: Creating...
module.nginx.docker_image.nginx: Still creating... [10s elapsed]
module.nginx.docker_image.nginx: Creation complete after 11s [id=sha256:bdf62fd3a32f1209270ede068b6e08450dfe125c79b1a8ba8f5685090023bf7fnginx]
module.nginx.docker_container.nginx: Creating...
module.nginx.docker_container.nginx: Creation complete after 0s [id=48cd702e7bdf1d921c14aeafe7e38943bbe3989f5f4a10499402d343d2edd826]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

terraform destroy

terraform destroyは、Terraform で作成したすべてのリソースを削除するコマンドです。

terraform destroy

このコマンドもapply同様、実行前に確認が求められます:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # module.nginx.docker_container.nginx will be destroyed
  - resource "docker_container" "nginx" {
      - attach                                      = false -> null
      ...
    }

  # module.nginx.docker_image.nginx will be destroyed
  - resource "docker_image" "nginx" {
      - id           = "sha256:bdf62fd3a32f1209270ede068b6e08450dfe125c79b1a8ba8f5685090023bf7fnginx" -> null
      ...
    }

Plan: 0 to add, 0 to change, 2 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: 

yesを入力すると、リソースの削除が開始されます。成功すると以下のような出力が表示されます:

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

module.nginx.docker_container.nginx: Destroying... [id=48cd702e7bdf1d921c14aeafe7e38943bbe3989f5f4a10499402d343d2edd826]
module.nginx.docker_container.nginx: Destruction complete after 0s
module.nginx.docker_image.nginx: Destroying... [id=sha256:bdf62fd3a32f1209270ede068b6e08450dfe125c79b1a8ba8f5685090023bf7fnginx]
module.nginx.docker_image.nginx: Destruction complete after 0s

Destroy complete! Resources: 2 destroyed.

terraform fmt

terraform fmtは、Terraform の設定ファイルを標準的なフォーマットに整形するコマンドです。

terraform fmt

このコマンドは:

  • インデントの調整
  • 改行の統一
  • スペースの調整

などを自動的に行い、コードの可読性を向上させます。チーム開発時には、コードレビュー前にこのコマンドを実行することをお勧めします。

✏️ 前章で取り扱った構成から学ぶ

この章では、サンプル Repository で扱った構成を参照して Terraform の基本文法を確認します。

1. module とは何か?

モジュールは、再利用可能な Terraform コードのパッケージです。
modules/nginx が関数のように用意されており、それを各環境から使用する形になります。
environments/xxx/nginx/main.tf を参照してください。
今回の例では xxx という環境から nginx の module を使う形になっております。
environmentsmodules, xxx というフォルダの文字列は著者が自由に付けた名前であり、制約はありません。

module "nginx" {
  source = "../../../modules/nginx"

  environment = var.environment
  container_ports = var.container_ports

  providers = {
    docker = docker
  }
}

モジュールの特徴には下記のようなものがあります。

  • コードの再利用性の向上
  • 設定の抽象化
  • 環境間での一貫性の確保
  • 変更の影響範囲の制御

モジュールの構成要素は簡単に、以下のようになっております。

  • source: モジュールのソースパス
  • 入力変数: モジュールに渡す変数

source で指定されたソースに記載されているリソースが構築できる形になります。

2. Variables, Locals(変数)とは何か?

Terraform では、変数を使用して設定を再利用可能にし、環境ごとの違いを管理します。

Variables(変数)

変数は variable ブロックで定義します。
modules/nginx/variable.tf を参照してください。

variable "environment" {
  type = string
  description = "Environment name."
}

variable "container_ports" {
  type = object({
    internal = number
    external = number
  })
  description = "Ports mapping for the Docker container."
}
  • description: 変数の説明文
  • type: 変数の型(string、number、bool など)
  • default: デフォルト値(任意)

environments/xxx/variable.tf を参照してください。
ここで nginx の module で定義された変数に具体的な値を代入します。

module "nginx" {
  source = "../../../modules/nginx"

  environment = var.environment # environment 変数を代入
  container_ports = var.container_ports # container_ports 変数を代入

  providers = {
    docker = docker
  }
}

これらの変数を環境毎に変えることにより、具体的な成果物が環境毎に変わるようになります。

Locals(ローカル変数)

ローカル値は、複雑な式や計算結果を再利用可能な形で保存します。
modules/nginx/local.tf を参照してください。
下記のように locals を定義します。

locals {
  container_name = "${var.environment}-nginx"
}

modules/nginx/nginx.tf を参照してください。

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = local.container_name # ローカル変数を使う

  ports {
    internal = var.container_ports.internal
    external = var.container_ports.external
  }
}

このようにしてローカル変数を使うことが可能です。

3. データ型について

Terraform の主要なデータ型は以下の通りです。

  • 基本型

    • string: 文字列型
    • number: 数値型
    • bool: 真偽値型
  • 複合型

    • list: 順序付きリスト型
    • map: キーと値のペア型
    • object: 異なる型を持つ名前付き属性の集合型

例えば今回の environment, ** container_ports** 変数は string 型や object 型を使っております。

variable "environment" {
  default = "xxx"
}

variable "container_ports" {
  default = {
    internal = 80
    external = 8000
  }
}

4. terraform ブロックとは何か?

terraform ブロックは、Terraform 自体の設定を定義するブロックです。
modules/nginx/provider.tf を参照してください。

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0.1"
    }
  }
  ...
}

主な設定項目は以下になります。

  • required_providers: 必要なプロバイダーとバージョンの指定
  • required_version: 必要な Terraform のバージョン指定
  • backend: 状態ファイルの保存場所の設定

今回の設定では required_providers のみ設定されております。

5. provider ブロックとは何か?

プロバイダーブロックは、特定のインフラストラクチャプラットフォームとの連携方法を定義します。
modules/nginx/provider.tf を参照してください。

provider "docker" {
  host = "unix:///var/run/docker.sock"
}

主な役割は以下になります。

  • プロバイダー固有の設定定義
  • 認証情報の設定
  • 接続先エンドポイントの指定

6. resource ブロックとは何か?

リソースブロックは、実際のインフラストラクチャコンポーネントを定義します。

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = var.docker_container_name

  ports {
    internal = var.container_ports.internal
    external = var.container_ports.external
  }
}

今回の構成要素は以下になります。

  • リソースタイプ(例:docker_container
  • リソース名(例:nginx

resource ブロックにはそれぞれ固有のフィールドがあり、それぞれ自分で設定した値を埋めることで必要な形式のリソースを構築することが可能です。

例えば docker_container の resource block は以下になります。

https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container

7. resource の参照とは何か?

リソース間の依存関係を表現するために、他のリソースを参照する方法です
modules/nginx/nginx.tf を参照してください。

resource "docker_image" "nginx" {
  name         = "nginx"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id # docker_image リソースの参照
  name  = local.container_name

  ports {
    internal = var.container_ports.internal
    external = var.container_ports.external
  }
}

参照の書き方は以下のようになります。

  • <リソースタイプ>.<リソース名>.<属性>
  • 例:docker_image.nginx.image_id

🎄 最後に

お疲れ様でした!
少し長かったですが、これで Terraform を使ってリソースを構築していく準備ができました!
Terrafrom を使って様々なリソースを構築してみてください!
過去に GoogleCloud × Terraform の記事を書いていたり、12/21(土)にも AWS × Terraform の記事を出したりする予定ですので、そちらもご確認いただければと思います 🙌

https://zenn.dev/oyasumipants/articles/8f0ac1a3395520

説明が不十分な点、誤字脱字などございましたら、ご指摘いただければと思います。
それでは良い Terraform ライフを!

Discussion