コンテンツにスキップ

Terraform - 基本

概要

インフラ構築や設定をコード(テンプレートファイル)を使って自動化するためのツール、いわゆるIaCです
多くのプロバイダ(クラウド / ツール)に対応しており、インフラ構成を宣言的に定義 1 できることが特徴です

ファイル構成

Terraformでは、基本的にxxx.tfという拡張子:tfで定義された情報に従って各種リソースを構築します
単一ファイルにすべてを定義することも可能ですが、分離することが一般的です

以下、ファイル構成の基本を記述します。

  • テンプレートファイル(.tf)に構成情報、プロバイダ情報2、変数等を記載していく
  • tfファイルは、HCLというHashiCorp製の独自言語
    • 実体は設定ファイル記述用DSLでJSONとも互換性あり
  • 以下はサンプルのシンプルなディレクトリ構成
    • 実運用時は、環境(Prod / Dev 等)後にディレクトリを分ける等の考慮が必要3

      directory
       |- providers.tf          :プロバイダ設定
       |- main.tf               :リソース定義(ファイル名は任意)
       |- variables.tf          :変数設定
       |- terraform.tfstate     :リソースの現状を示すファイル(自動作成)
      

providers.tf

実行するTerraformのバージョンを指定4したり、プロバイダを指定する

  • 複数のプロバイダに対応しているため、どのプロバイダで構築するかを定義する
    以下は、AWSをプロバイダとしてproviders.tfを記述したものとなる

    terraform {
        backend "s3" {
            bucket                  = "aws-terraform"
            key                     = "path/terraform.tfstate"
            region                  = "ap-northeast-1"
            shared_credentials_file = "$HOME/.aws/credentials"
            profile                 = "xxxxx"
        }
        required_version        = ">=0.12"
    }
    
    provider aws {
        shared_credentials_file =  "$HOME/.aws/credentials"
        profile                 =  "xxxxx"
        region                  =  "ap-northeast-1"
    }
    

認証情報もproviders.tfに直接記述可能が、セキュリティリスクを考慮し環境変数で定義するが多い

  1. 単一アカウントでの管理手法

    • TF_VAR_<変数名>というかたちで環境変数で定義する
    • terraformコマンド実行時にこの環境変数が読み込まれ、変数に値が代入される

      $ export TF_VAR_access_key="AKIAXXXXXXXXXXXXXXXXXX"
      $ export TF_VAR_secret_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      
  2. 複数アカウント環境の管理手法

    • shared_credentials_fileを利用可能
    • profileを利用してアカウントをスイッチ可能

後述のterraform.tfstateの出力先をbackendで指定できる

  • このファイルはリソースの状態管理に使われる重要なファイルのため、ローカルは非推奨
  • コンフリクト時の影響を考慮し、git等は非推奨
  • ProviderがAWSであれば、S3/DynamoDBに配置するのが一般的

main.tf

Terraformにて、構築するリソース情報を定義する

  • resource "<リースの種類>" "<リソース名>" {} といった構文で指定
  • <リソース名>は任意の名前を設定可能であり、用途を識別できるようにする
  • ブロック中には設定項目名 = 設定値の形式で記述する

定義済みの他リソースの情報を参照することも可能

  • <リソースの種類>.<リソース名>.<属性名>で指定する
  • 依存関係はTerraform側で制御されているので、原則、考慮不要
  • 明示的に指定したい場合は、depends_onで指定する

    resource "aws_security_group" "allow_tls" {
        name        = "allow_tls"
        description = "Allow TLS inbound traffic"
        vpc_id      = aws_vpc.main.id
    
        ingress {
            description = "TLS from VPC"
            from_port   = 443
            to_port     = 443
            protocol    = "tcp"
            cidr_blocks = [aws_vpc.main.cidr_block]
        }
    
        egress {
            from_port   = 0
            to_port     = 0
            protocol    = "-1"
            cidr_blocks = ["0.0.0.0/0"]
        }
    }
    
    resource "aws_security_group_rule" "add-rule" {
        type                     = "ingress"
        from_port                = 0
        to_port                  = 0
        protocol                 = "-1"
        source_security_group_id = aws_security_group.allow_tls.id
    
        security_group_id = aws_security_group.allow_tls.id
    
        depends_on = aws_security_group.allow_tls
    }
    
    →作成したセキュリティグループ自身のIDをInboundルールに追加する例です

variables.tf

変数情報をmain.tf等から分離するためのテンプレートファイル

  • main.tf内で記述可能だが、可読性を考慮し別ファイルで管理することが多い

変数はvariable <変数名> {}といった構文で定義

variable "env" {
    type        = string
    description = "環境種別"
    default     = "dev"
}

  • type
    変数の型を定義する(String/List/Map など)
  • default
    変数のデフォルト値
    コマンド実行時や別ファイルを利用して上書き可能
  • description
    対象変数の説明を示す(コメントのように利用する)

terraform.tfstate

Terraformが管理しているリソースの状態を表すファイルとなる

  • デフォルトではローカルにtfstateファイルが生成される
  • ProviderがAWSであれば、s3などのリモートバックエンドで管理することが多い5

作成タイミングはterraform applyを実行すると生成される

  • 次回以降、terraform planを実行時に前回実行時のリソース情報と差分が表示される
  • 実機との差分確認は行わないという設計方針6となっている

リソースを分割≒コマンド実行単位を分割することで、更新頻度の高低でtfstateを分割できる

  • tfstateの実体は以下のようなjsonファイル(リソース:ECSのtfstate)

    {
    "version": 4,
    "terraform_version": "0.12.14",
    "serial": 1,
    "lineage": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "outputs": {},
    "resources": [
        {
        "module": "module.ecs",
        "mode": "managed",
        "type": "aws_ecs_service",
        "name": "xxx-api",
        "provider": "provider.aws",
        "instances": [
            {
            "schema_version": 0,
            "attributes": {
                "cluster": "arn:aws:ecs:ap-northeast-1:xxxxxxx:cluster/dev",
                "desired_count": 1,
                "id": "arn:aws:ecs:ap-northeast-1:xxxxxxx:service/dev-xxx-api",
                "launch_type": "FARGATE",
                "name": "dev-xxx-api",
                "task_definition": "dev-xxx-api:1",
                ...
            },
            "dependencies": [
                "module.ecs_service.xxx-api.aws_ecs_task_definition.task"
            ]
            }
        ]
        },
        ...
    ]
    }
    

コマンド

Terraformで構築を行う際の基本的なコマンドについて、説明します

$ terraform init  # BackendとプロバイダのInitialize
$ terraform plan  # 実行内容の確認、チェック
$ terraform apply # 実行

terraform init

  • ワークスペースを初期化するコマンド
  • Terraform を実行するためには、まずterraform init でワークスペースの初期化が必須
  • .tf(テンプレート)ファイル内の plugin(aws provider など)のダウンロード処理などが走る

terraform plan

  • Terraform による実行計画を参照するコマンド
  • .tf(テンプレート)ファイルに情報を元に、どのようなリソースが 作成/修正/削除 されるかを参照可能

terraform apply

  • .tf(テンプレート)ファイルに記載された情報を元にリソースを作成するコマンド
  • リソースが作成後、 terraform.tfstate に、作成されたリソースに関連する情報が保存される
  • 2度目以降の実行後には、1世代前のものが terraform.tfstate.backup に保存される
  • Terraform において、この状態を管理する terraform.state ファイルが非常に重要となるため、手動更新等は行わないこと

  1. 「t2.micro インスタンスを1つ」と書くだけで、作成手順を意識しなくてもその通りに出来上がるということ。手続き的に構築する必要がない 

  2. Terraformとして、対応しているクラウドサービスやツール群のこと 

  3. ベストプラクティスは人によってまちまち?正解が分からない... 

  4. バージョンによっては破壊的な変更も多いようなので、定義しておいたほうが無難 

  5. ローカルだと意図しない変更や削除される可能性があるため 

  6. https://www.terraform.io/docs/state/purpose.html