2
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?

More than 1 year has passed since last update.

Docker × AWS × terraform = Infrastructure as Code

Posted at

Docker上にterraformの環境を構築し、クラウドサービス上(AWS)に自動でインフラを構築する。
Docker上にterraform環境を構築したのは、ホスト環境をキレイなままに保っておきたかったからです。この記事では、開発環境と基礎知識をまとめます。

開発環境は、下記で実施しました。

開発環境
  • Edition: Windows 11 Home, Version: 22H2, OSビルド: 22621.1702
  • WSL2
  • Docker Desktop for Windows: 4.20.1 (110738)
  • Docker Engine: 24.0.2
  • Docker Compose: v2.18.1
  • terraform 1.4.6
  • aws(provider) 5.4.0

次に、ディレクトリは下記の構成にしました。

ディレクトリ構成

Docker/home/user
└terraform

まずは、必要なソフトをインストールしていきます。

AWS CLIのインストール

下記の公式サイトにアクセス

console
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
apt install unzip #Linux環境にインストールされていない場合に実行
unzip awscliv2.zip
sudo ./aws/install

インストールが完了されているか、AWSのVersionを確認する。

console
aws --version
# aws-cli/2.12.1 Python/3.11.3 Linux/5.10.16.3-microsoft-standard-WSL2 exe/x86_64.ubuntu.20 prompt/off
IAMユーザーの作成

AWSマネジメントコンソールから、AWS CLI管理用とTerraform用にIAMユーザーを作成していきます。下記のFullAccess権限を持ったユーザーを作成します。

  • AWS CLI管理用:すべてのリソースに対するFullAccess権限
  • Terraform用:すべてのリソースに対するFullAccess権限

作成したユーザーをAWS CLIのプロファイルに登録していきます。
AWS CLIを使ってAWSを操作する為に必要な認証情報になります。

profile
#aws configure [--profile] user_name

aws configure #defualt
AWS Access Key ID [None]: *******
AWS Secret Access Key [None]: *******
Default region name [None]: ap-northeast-1
Default output format [None]: json

aws configure  --profile terraform
AWS Access Key ID [None]: *******
AWS Secret Access Key [None]: *******
Default region name [None]: ap-northeast-1
Default output format [None]: json
Terraformのインストール

次にTerraformをインストールしていきます。

  1. tfenvインストール

    • GitHubリポジトリクローン
    console
    cd ~
    git clone --depth=1 https://github.com/tfutils/tfenv.git ~/.tfenv
    
    • パスを通す
    console
    echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bash_profile &&
    e ~/.bash_profile> source ~/.bash_profile
    
  2. terraformインストール

    console
    tfenv list-remote
    tfenv install <VERSION>
    tfenv use <VERSION>
    
  3. 動作確認

    terraform version
    
git-secrets インストール

git-secretsとは、アクセスキーやシークレットキー等credentials情報をGithubへアップロードすることを防いでくれるツール。

  • git-secretsインストール
console
#リポジトリをクローン
git clone https://github.com/awslabs/git-secrets
#インストール
cd git-secrets
make install

#動作確認
git secrets

######
usage: git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [<files>...]
   or: git secrets --scan-history
   or: git secrets --install [-f|--force] [<target-directory>]
   or: git secrets --list [--global]
   or: git secrets --add [-a|--allowed] [-l|--literal] [--global] <pattern>
   or: git secrets --add-provider [--global] <command> [arguments...]
   or: git secrets --register-aws [--global]
   or: git secrets --aws-provider [<credentials-file>]
######
  • git-secrets初期化
console
git secrets --register-aws --global
git secrets --install ~/.git-templates/git-secrets -f
git config --global init.templatedir ~/.git-templates/git-secrets
  • 動作確認
console
git init
git add .
git commit -m "test"
EC2起動

ここまでの設定が出来たら、テストとして、EC2を起動してみます。
terrafomディレクトリ直下にmain.tfを作成します。

console
touch main.tf
main.tf
provider "aws" {
    profile = "terraform"
    region = "ap-northeast-1"
}

resource "aws_instance" "hello-world" {
    ami = "ami-0f9816f78187c68fb" #EC2のamiを確認して下さい。
    instance_type = "t2.micro"
}

terraformディレクトリ直下へ移動してから、下記のコマンドを実行します。

./trraform/
terraform init
terraform plan
terraform apply
#############
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 #yesを入力
#############

マネジメントコンソールからEC2を検索し、インスタンスでEC2が起動されていることを確認して下さい。

.tfstateファイル

インスタンスを起動させるとterraformディレクトリに.tfstateファイルが作成される。.tfstateファイルには、terraformを使って構築したクラウドの状態が記載されている。

EC2削除

今回はテストとして、EC2を起動したので、確認出来たら削除しておきます。
下記コマンドを実行し、削除しておく。

./trraform/
terraform destroy

Terraformの基本コマンド

terraform plan

現状のソースコードを元に実行計画を確認

terraform plan
terraform plan [-var <KEY>=<VALUE>] [-var-faile <VAR_FILE>]
# -var <KEY>=<VALUE> 変数の指定
# -var-faile <VAR_FILE> 変数ファイルの指定
terraform apply

ソースコードに従って変更を適用する

terraform apply
terraform apply [-auto-approve][-var <KEY>=<VALUE>] [-var-faile <VAR_FILE>]
# -auto-approve 実行計画の確認なしで適用(yes省略)
# -var <KEY>=<VALUE> 変数の指定
# -var-faile <VAR_FILE> 変数ファイルの指定
terraform destroy

Terraform管理のインフラ環境を削除する

terraform destroy
terraform destroy [-auto-approve]
# -auto-approve 実行計画の確認なしで適用(yes省略)
terraform fmt

コードのインデント修正コマンド

terraform fmt
terraform fmt
HCL2について

HashiCorpLanguage2:HCL2とは、ハシコープ社の独自言語。

  • jsonによく似ているが少し違う。
  • 簡単なプログラムが組める。
  • #でコメントが記載出来る
  • KEY・VALUEを"."ではなく"="で指定
  • ヒアドキュメントが利用可能
  • ブロックごとに記述する
  • ブロックには、接頭辞のブロックタイプ、次に続くラベルがある。
ブロックタイプ
block_type
#ブロックタイプの種類:説明
locals #外部から変更できないローカル変数
variable #外部から変更可能な変数
terraform #Terraformの設定
provider #プロバイダ
data #Terraform管理していないリソースの取り込み
resource #Terraform管理対象となるリソース
output #外部から参照できるようにする値
locals(変数)ブロックの設定
  • HCL2で利用可能な変数は下記の2種類
locals.tf
locals   #ローカル変数。
         #プライベートな変数で外部外部から変更できない。
variable #外部から変更可能な変数。
         #コマンドライン実行時にオプションやファイル指定で上書きできる
locals.tf
#localsブロックで定義して${local.変数名}で参照
locals {
    project = "tastylog"
    env = "dev"
}

resource <RESORCE_TYPE> <RESORCE_NAME> {
    tags = {
        Name = "${local.project}-${local.env}-vpc"
    }
}

output <OUTPUT_NAME>{
    ####
}
variableブロックの設定
variable.tf
#variableブロックで定義して${var.変数名}で参照
variable "project" { #変数名
    type = string #変数の型
    default = "tastylog" #変数のデフォルト値
}

resource <RESORCE_TYPE> <RESORCE_NAME> {
    tags = {
        Name = "${var.project}-dev-vpc"
    }
}

output <OUTPUT_NAME>{
    ####
}
データ型
data_type.tf
#データの型名:種類・説明
string #プリミティブ:Unicode文字列
number #プリミティブ:数値。整数と少数の両方を表現
bool #プリミティブ:true/falseの2値。
object({<NAME>=<TYPE>, ・・・}) #構造体:キーバリュー型データ
tuple([<TYPE>, ・・・]) #構造体:各列の型が決まっている配列。
list(<TYPE>) #コレクション:特定の型で構成される配列。
map(<TYPE>) #コレクション:キーが文字列の配列
set(<TYPE>) #コレクション:値の重複がない配列
  • string
string_type.tf
variable "message" {
    type = string 
    default = "Hello World"
}

variable "max_count" {
    type = number
    default = 10
}

variable "is_enable" {
    type = bool
    default = true
}
  • object
    キーバリュー形式で定義されるデータ型
object_type.tf
variable "obj_sample" {
    type = object({
        name = string
        age = number
    })
    default = {
        name = "tanaka"
        age = 28
    }
}
  • tuple
    配列のN番目にどういった型を使うかが決められたデータ型
tuple_type.tf
variable "tuple_sample" {
    type = tuple([
        string, number #基本は配列でN番目の型が固定される
    ])
    default = ["tanaka", 28]
}
  • list
    全て同じ型で指定される配列
list.tf
variable "list_sample" {
    type = list(string)
    default = ["tanaka", "sato"]
}

username = var.list_sample[0]
  • map
    キーが文字列、バリューが指定された型となる配列
map.tf
variable "map_sample" {
    type = map(string)
    default = {
        "High" = "m5.2xlarge"
        "Mid" = "m5.2large"
        "Low" = "t2.micro"
    }
}

instance = var.map_sample.High
  • set
    バリューの重複が排除される配列
set.tf
variable "set_sample" {
    type = set(string)
    default = [
        "tanaka",
        "sato",
        "tanaka",
        "sato"
    ]
}

[for itm in var.set_sample : itm]
terraformブロックの設定

terraformとプロバイダー(AWS,GCP,Azure)は別物。
それぞれバージョンが存在し、日々変化していく。

  • terradformのバージョン固定
terraform_version_fix
required_version #Terraformのバージョン指定。main.tfに記載
  • プロバイダーのバージョン固定
provider_version_fix
required_providers #providerのバージョン指定。main.tfに記載
providerブロックの設定
provider_block
profile #AWSへアクセスする為のプロファイル(クレデンシャル)
region #デフォルトリージョン
dataブロックの設定

dataブロックでは、管理対象外のリソース(「AWSが作成したリソース」「既存システムのリソース」「外だしの設定」)を参照させる。
取り込みたいリソースによって記述方法が異なる。

outputブロックの設定

作成済みのリソースを外部参照できるようにするブロック。

output.tf
resource "aws_instance" "hello-world" {
    ami = "ami-0ce107...."
    instance_type = "t2.micro"
}

output "ec2_instance_id" { #参照時に指定する名前
    value = aws_instance.hello-world.id #出力する値
}
リソース参照

HCL2でリソース参照する記述
"."(ドット)で連結して参照

resource.tf
<BLOCK_TYPE>.<LABEL_1>.<LABEL_2>

"."(ドット)連結で表現されるリソースパスを"アドレス"と呼ぶ。
resourceブロックの場合、省略可能。

resource.tf
<LABEL_1>.<LABEL_2> #LABELから始まる
組み込み関数

Hashicorp社HP → "Docs" → "Terraform Language" → "Functions"以下にリファレンスがある。

function.tf
#種類:説明
numeric #数値演算、絶対値、切り上げ、切り捨て、最大、最小等。
string #文字列操作、フォーマット、結合、抜き出し等。
collection #配列操作、結合、分割、ソート等。
encoding #エンコード、Base64、CSV、JSON、YAMLなどの変換。
filesystem #ファイル操作。ディレクトリ名取得、ファイル読み取り等。
date & time #日付操作、現在時刻取得、データのフォーマット指定など。
Hash & Crypt #ハッシュおよび暗号化、ホスト名取得、サブネット名取得など。
IP Network #CIDR表記の演算、ホスト名取得、サブネット名取得など。
Type Conversion #型変換、bool、string、numberなどへの変換。
組み込み関数の試し方

指定された設定で関数を実行します。

funtion.tf
terraform console
[-var <KEY>=<VALUE>] #変数の指定
[-var-file <VAR_FILE>] #変数ファイルの指定

複数リソースの作成
count

複数リソースを生成する方法として、count,for_eachの2つがある。countとfor_eachは同時に作成する事はできない。指定された数分のリソースを生成する。

count.tf
resource <RESORCE_TYPE> <RESORCE_NAME> {
    count = 4
    
    ami = ""
    instance_type = ""    
    
    tags = {
        Name = "${count.index}"
    }
}
for_each

指定されたmapまたはsetを展開しながら複数リソース生成する

for_each.tf
# mapの場合
resource "aws_vpc" "vpc" {
    cidr_block = "192.168.0.0/20"
}

resource "aws_subnet" "subnet" {
    for_each = { #キーバリュー形式で指定
        "192.168.1.0/24" = "ap-northeast-1a"
        "192.168.2.0/24" = "ap-northeast-1c"
        "192.168.3.0/24" = "ap-northeast-1d"
    }
    
    vpc_id = aws_vpc.vpc.id
    #each.keyまたheach.valueで値を利用
    cidr_block = each.key
    availability_zone = each.value
}

配列を指定したい場合、toset関数を利用してsetへ変換する

toset.tf
# for_eachの場合
resource "aws_iam_user" "user" {
    for_each = toset([ #toset()でsetへ変換。重複削除
        "tanaka",
        "sato"
    ])
    
    name = each.key #利用可能なのはeach.keyのみ
}

for文

リスト型(list, map, set)を異なるリスト型(list, map)へ変換する構文

for.tf
#list,set => list
[for s in var.list : upper(s)]

#list,set => map
{for s in var.list :s => upper(s)}

#map => list
[for k, v in var.map : upper(v)]

#map => map
{for k, v in var.list :k => upper(v)}
フィルター
filter.tf
[for s in var.list upper(s) if s != ""]
3項演算子

terraformにif文がないので、if文の代わりに使用する

operator.tf
[var.a != "" ? var.a : "default_value"]
モジュール

モジュールとは、リソース生成処理を1つの塊にして呼び出せるようにしたもの(プログラミングにおける関数のようなもの)。関数というくらいなので、「呼び出し方法」と「定義方法」の2つがある。

  • 定義方法
    "modules"などの下に別フォルダ作成して定義する
    ROOT
    ├ modules
    ├ └ MODULE NAME
    └ main.tf

モジュールのディレクトリ内にファイルを作成していく。
MODULE NAME
├ variables.tf #引数
├ main.tf #処理
├ output.tf #戻り値
└ README.md #説明

  • 呼び出し方法
    "modules"ブロックを利用して呼び出す
    ROOT
    ├ modules
    ├ └ nginx_server
    └ main.tf
module.tf
module "webserver" {
  source = "./modules/nginx_server"

  #モジュールが必要とする引数を列挙する
  instance_type = "t2.micro"

モジュールの出力(output)は"."(ドット)表現(アドレス表現)で参照
ROOT
├ modules
├ └ nginx_server
└ main.tf

module.tf
module "webserver" {
  source = "./modules/nginx_server"

  instance_type = "t2.micro"
}
output "instance_id" {
  value = module.webserver.id
}
2
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
2
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?