1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraformを入門したお話

Posted at

はじめに

本記事は、Infrastructure as Code(IaC)ツールである Terraform の基本構造を学び、Docker コンテナから AWS EC2 インスタンスの作成までを行います。また state管理や tfファイル管理なども踏まえながら実務に近い形を学んでいきます。ぜひ〜!

Terraform とは?

HashiCorp 社が提供する Infrastructure as Code(IaC)を実現するツールです。インフラの構成をコードで宣言的に記述することで、インフラ構成を自動的かつ一貫性を持って管理できます。

また Terraform の大きな特徴は、AWS、Microsoft Azure、Google Cloud Platform など、複数のクラウドプロバイダーやサービスに対応したマルチプラットフォーム対応である点です。マルチクラウド環境においても統一して作成することが可能です。

作業環境

今回 2 つの環境を terraform で構築していきます。それぞれで事前に必要な環境をご確認ください。

[Docker を用いた NGINX サーバー構築]

  • Rancher Desktop を使用した Docker CLI 環境
  • 事前にコンテナが起動済み(docker psコマンドで確認可能)

[AWS EC2 インスタンス構築]

  • AWS アカウントおよび AWS CLI
  • 作成する AWS リソースへのアクセス権限を持つ IAM ユーザーと適切な認証情報

参考資料

本記事のハンズオンは以下チュートリアルに沿って進めていきます。

インストール

初めに Terraform をインストールします。今回はbrewを用いたインストール方法をご紹介します。

# Homebrewに新しいレポジトリを作成
brew tap hashicorp/tap

# インストール
brew install hashicorp/tap/terraform

# 動作確認
terraform -help

Terraform 基本構文

ブロックごとに構成を説明していきましょう。

Terraform ブロック

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0.1" # dockerのバージョン
    }
  }
  required_version = ">= 1.2.0" # terraformのバージョン
}

ここでは terraform の全体に関わる設定を記述します。
具体的には terraform のバージョンや利用するプロバイダー(モジュール化しているサービス・機能)のバージョンなどを記述することができます。

Provider ブロック

# docker利用
provider "docker" {}

# AWS利用
provider "aws" {
  region  = "us-west-2"
}

ここではプロバイダーに関する設定を記述します。terraform ブロックのrequired_providersに含まれているプロバイダーを記述します。
利用するプロバイダーごとに必要な設定内容は異なりますが、リージョンやエンドポイントなど様々です。

Resource ブロック

# Dockerイメージを作成する
resource "docker_image" "nginx" {
  name         = "nginx"  # Dockerイメージ名
  keep_locally = false # destroy時Dockerイメージも削除する
}

# EC2インスタンスを作成する
resource "aws_instance" "app_server" {
  ami           = "ami-0a463f27534bdf246" #AmazonマシンイメージのID
  instance_type = "t2.micro" # インスタンスタイプ
}

ここでは具体的に構成するインフラ内容を記述します。
プロバイダーに紐づく Resource を記載します。2 つ目の変数(例の場合、nginxapp_server)は任意の名称です。

Variables ブロック

variables.tf
variable "aws_region" { # 変数名
  description = "AWS region" # 変数の概要
  type        = string # 型
  default     = "us-west-2" # デフォルト値
}

ここでは変数値を記述します。具体的な変数の値の受け渡しはいくつか方法があります。

①実行時に宣言
コマンド実行時、コンソール上にて対話形式で変数を設定できます。

$ terraform plan
var.aws_region
  Enter a value: us-west-2  # 手動で入力

②コマンドライン引数
コマンド実行時、-var引数にて変数を設定できます。

terraform plan -var 'aws_region=us-west-2'

③環境変数
TF_VAR_に続く変数を定義することで、変数を設定できます。

$ export TF_VAR_aws_region=us-west-2
$ terraform plan

④tfvarsファイル
terraform.tfvarsファイル内に記述し、コマンドライン引数で記述したファイルを宣言することで変数を設定できます。

terraform.tfvars
aws_region=us-west-2
terraform plan -var-file=terraform.tfvars

利用方法は、宣言されたモジュール内でvarより取得できます。

main.tf
 provider "aws" {
  # region  = "us-west-2" # BEFORE
  region  = var.aws_region # AFTER
 }

Output ブロック

output.tf
output "instance_id" { # 変数名
  description = "ID of the EC2 instance" # 変数の概要
  value       = aws_instance.app_server.id # 
}

ここでは作成したリソース情報において出力させる情報を記述します。
すでに作成されている環境に対し情報を取得できます。(作成環境がないと出力できない)

# アウトプットコマンド
$ terraform output
instance_id = "i-0241e32ad00df9b28"

実装例 ① Docker+NGINX サーバー構築

Docker を用いた NGINX サーバーを Terraform で作成してみます。
terraform のコードは main.tf に記載していきます。

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

provider "docker" {
  host = "unix:///Users/hoge/.rd/docker.sock" # ユーザー環境に準ずる
}

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

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "tutorial"

  ports {
    internal = 80
    external = 8000
  }
}

以下のようなエラーが発生することがあります

Error: Error pinging Docker server: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
│
│   with provider["registry.terraform.io/kreuzwerker/docker"],
│   on main.tf line 21, in provider "docker":
│   21: provider "docker" {}

Docker daemon というのはコンテナの起動管理をするサーバーを指し、そちらに接続できないよ、というエラーです。
今回 Docker 環境を RancherDesktop にて作成しているためデフォルト値では実行できず、明示的にエンドポイントを指定します。

$ docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT                              ERROR
default             Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
rancher-desktop *   Rancher Desktop moby context              unix:///Users/hoge/.rd/docker.sock
(base)

参照:Terraform の Docker Provider サンプルを M1 Mac の Rancher Desktop で動かす

terraform コマンド

# 初期化コマンド
terraform init

# 実行コマンド
terraform apply

以下のように作成内容が表示されます。問題なければyesを入力します。

https://localhost:8000 へアクセスしサーバーが動いていることを確認できれば OK!

指定した名前のコンテナが作成できていることも確認できます。

最後は作成した環境を削除まで行います!

# 削除内容確認コマンド
terraform plan -destroy
# 削除実施コマンド
terraform destroy

その他便利コマンド

起動以外に便利!なコマンドをご紹介。

# 現在の状態と設定ファイルの差分チェック
terraform plan

# フォーマッター
terraform fmt

# 構文チェック
terraform validate

実装例 ② AWSEC2 インスタンス構築

続いて、AWS の EC2 インスタンス起動を terraform で作成してみます。

複数のユーザー情報を保存しており、AWS 認証情報をプロファイルに保存している場合、Terraform 実行時に指定することが可能です。指定がない場合はdefaultが利用されます。

export AWS_PROFILE={ 呼び出したいprofile-name }

AWS 公式:名前を指定されたプロファイルを使用する

MFA 認証のあるユーザーの場合は一時セッション情報を利用しアクセス可能です。
設定方法についてこちら →MFA 認証ユーザーでの AWSCLI アクセス設定

他にも AWS 認証情報を Terraform 記述ファイルへ直に記載したり、宣言方法は様々です。
⚠️ どの方法でも決して情報漏洩にならないように!お気をつけください!!

参考:Terraform でクレデンシャルを読み込む方法あれこれ

terraform ファイル

main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "app_server" {
  ami           = "ami-0a463f27534bdf246" #AmazonマシンイメージのID(ここではAmazon Linux)
  instance_type = "t2.micro"
}

terraform コマンド

# 初期化コマンド
terraform init

# 実行コマンド
terraform apply

以下のように作成内容が表示されます。問題なければyesを入力します。

AWS ダッシュボードより中身を確認してみると、、?

見事に EC2 インスタンスが起動できていることが確認できました!

state 管理

terraform 実行すると自動的にローカル上にterraform.tfstateが作成されます。これは「今のリソース」の情報を Terraform 側で管理するファイルとなります。

しかしこのterraform.tfstateは Git 管理推奨ではないため、管理する方法があります

今回は2種類の保存方法を行います。

HCP Terraformで管理

HCP Terraform(旧 Terraform Cloud)を用いてtfstateを管理することができます。このサービスでは他にも実行履歴や環境変数なども同時に管理することが可能です!

まずは無料アカウント登録から!

ログイン完了したら、Organizationを作成します。
image.png

terraform ファイルへも修正します。terraform ブロックの中に cloud ブロックを追加します。
(⚠️ Not backendブロック!)

main.tf
terraform {
  cloud {
    organization = "organization-name" # 先ほど作成したOrganization名
    workspaces {
      name = "learn-terraform-aws"
    }
  }
  # ~以下略~
}

CLIより操作できるようHCP Terraformにログインします。

# HCP Terraformログインコマンド
terraform login

yesを入力すると、自動的にToken発行するサイトへ遷移します。トークン発行し、ターミナルへ入力します。

image.png

無事にログインできました!

image.png

# (再)初期化コマンド
terraform init

image.png

yesで現在作成されているtfstateファイルをアップロード、noで新規tfstateファイル作成しアップロードします。

image.png

これでtfstateファイルをHCP Terraformにて管理することができました!!
最後に2重管理になるので、ローカルファイルは削除します。

# ローカルファイルは削除
rm terraform.tfstate

S3バケットで管理

任意のS3バケットを作成し、その中に管理します。必要に応じ同時編集ロック制御のためのDynamoDBを追加してください。

はじめにS3バケットを作成します。
image.png

terraform ファイルへも修正します。terraform ブロックの中に backend ブロックを追加します。
(⚠️ Not cloudブロック!)

backend.tf
terraform {
  backend "s3" {
    bucket = "mybucket-terraform-state-tutorial" # バケット名
    key    = "terraform.tfstate" # ファイル名
    region = "ap-northeast-2" # リージョン
  }
}

再度初期化、実行してみると、、?

image.png

無事にS3バケットにアップロードされていました!

これでtfstateファイルをS3バケットにて管理することができました!!
最後に2重管理になるので、ローカルファイルは削除します。

# ローカルファイルは削除
rm terraform.tfstate

参考:

tf ファイル管理

現在のファイル構成はこのようになっています。

  • main.tf - Terraformの主要設定ファイル
  • variables.tf - 変数定義ファイル
  • output.tf - 実行後に出力する値を定義ファイル
  • backend.tf - tfstate管理先の定義ファイル
  • terraform.tfstate - Terraformが管理する現在のインフラ状態を保存するファイル
  • terraform.tfstate.backup - 前回の状態のバックアップファイル

現状の場合、main.tfに全てのリソース記述を記載しています。より複雑で様々なリソースでの構築を行う場合、そして開発/本番環境など複数環境に対し構築を行う場合、どのようなファイル構成がよいか、考えてみました。

最終的なファイル構成

IaC/
├── env/                      # 環境別の設定
│   ├── dev/                  # 開発環境の設定
│   │   ├── main.tf           # メインのTerraform設定
│   │   ├── variables.tf      # 変数定義
│   │   ├── vars.tfvars       # 変数の値
│   │   ├── backend.tf        # バックエンド設定
│   │   └── .terraform/       # Terraformの状態管理
│   └── prd/                  # 本番環境の設定
│       ├── main.tf           # メインのTerraform設定
│       ├── variables.tf      # 変数定義
│       ├── vars.tfvars       # 変数の値
│       ├── backend.tf        # バックエンド設定
│       └── .terraform/       # Terraformの状態管理
├── shared/                   # 共有リソース
│   └── provider.tf           # プロバイダー設定(AWS等)
└── modules/                  # 再利用可能なモジュール
   ├── lambda/                # Lambda用のモジュール
   │   ├── main.tf            # Lambda関数のメイン設定
   │   └── variables.tf       # モジュールの変数定義
   └── fargate/               # AWS Fargate用のモジュール
       └── main.tf            # Fargateのメイン設定

参考:

各ファイルの構成について紹介します。

envファイル

作成したい環境ごとにフォルダを分け、このmain.tfを基準に他ファイルを呼び出していきます。

main.tf
# Terraform の設定
module "shared_provider" {
  source = "../../shared" # 
}

# Lambda 関数の作成
module "module_lambda" {
  source = "../../modules/lambda"
  env    = var.env
  name   = module.hoge.name
}

リソース作成に必要な情報をvar変数で受け渡したり、他リソースのOutput値を受け渡したりすることができます。

sharedファイル

環境ごとに差分が出ない、Terraform ブロックや、Provider ブロックなどを記述します。
Symbolic Linkを行うことで、ファイルをコピペすることなく、一元管理も可能です。

$ cd IaC/env/dev 
$ ln -s ../../shared/provider.tf

modulesファイル

リソースごとモジュール化し、envファイルのmain.tfより呼び出されます。
もし変数を利用する場合はmodules配下でもvariables.tf定義が必要になります。

DRY原則について

Terraform の管理方法を調べていると、「DRY(Don't Repeat Yourself)」という言葉がよく出てきます。
これは、ソフトウェア開発原則の1つで

すべての知識はシステム内において、単一、かつ明確な、そして信頼できる表現になっていなければならない。

ということ。ただ単にコードの重複をしない、というよりも再利用可能な処理を抽象化し、情報を共通化することが重要(らしい)

もっと詳しく知りたい方はこちら⇩
DRY原則の適用範囲について

おまけ

環境ごとのterraformファイル管理方法として、terraform workspaceで行う方法もありました。
今回においては環境切り替えを自身でやる必要があったのと、環境差分が大きいと不向きであることから採用しませんでした。

実施環境にもよると思いますのでこちらも参考ください。

まとめ

今回は初めてterraformでのIaCを体験し、state管理やファイル管理まで考察することができました。今回は単純なインフラ構築のため少ない記述のみですが、今後より業務環境に沿ったインフラ構築へ挑むにあたり引き続き学んでいきたいと思います!

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?