はじめに
本記事は、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 つ目の変数(例の場合、nginx
やapp_server
)は任意の名称です。
Variables ブロック
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
ファイル内に記述し、コマンドライン引数で記述したファイルを宣言することで変数を設定できます。
aws_region=us-west-2
terraform plan -var-file=terraform.tfvars
利用方法は、宣言されたモジュール内でvar
より取得できます。
provider "aws" {
# region = "us-west-2" # BEFORE
region = var.aws_region # AFTER
}
Output ブロック
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 に記載していきます。
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 {
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を管理することができます。このサービスでは他にも実行履歴や環境変数なども同時に管理することが可能です!
まずは無料アカウント登録から!
terraform ファイルへも修正します。terraform
ブロックの中に cloud
ブロックを追加します。
(⚠️ Not backend
ブロック!)
terraform {
cloud {
organization = "organization-name" # 先ほど作成したOrganization名
workspaces {
name = "learn-terraform-aws"
}
}
# ~以下略~
}
CLIより操作できるようHCP Terraformにログインします。
# HCP Terraformログインコマンド
terraform login
yes
を入力すると、自動的にToken発行するサイトへ遷移します。トークン発行し、ターミナルへ入力します。
無事にログインできました!
# (再)初期化コマンド
terraform init
yes
で現在作成されているtfstateファイルをアップロード、no
で新規tfstateファイル作成しアップロードします。
これでtfstateファイルをHCP Terraformにて管理することができました!!
最後に2重管理になるので、ローカルファイルは削除します。
# ローカルファイルは削除
rm terraform.tfstate
S3バケットで管理
任意のS3バケットを作成し、その中に管理します。必要に応じ同時編集ロック制御のためのDynamoDBを追加してください。
terraform ファイルへも修正します。terraform
ブロックの中に backend
ブロックを追加します。
(⚠️ Not cloud
ブロック!)
terraform {
backend "s3" {
bucket = "mybucket-terraform-state-tutorial" # バケット名
key = "terraform.tfstate" # ファイル名
region = "ap-northeast-2" # リージョン
}
}
再度初期化、実行してみると、、?
無事に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
を基準に他ファイルを呼び出していきます。
# 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管理やファイル管理まで考察することができました。今回は単純なインフラ構築のため少ない記述のみですが、今後より業務環境に沿ったインフラ構築へ挑むにあたり引き続き学んでいきたいと思います!