はじめに
いままで IaC は Ansible しかやってなかった人が Terraform って何? って状態からスタートして調べてやったことを記載する。
Terraform を動かす環境は、端末とサーバで対応できるように、Mac と CentOS 両方で実施した。
実際に Terraform で GCP 上に VM 1台を構築して挙動を確認した。
記載構成
下記の構成で記載している
- Terraform とは?
- 個人的に Terraform とは?を調べた内容をまとめたもの
- 実施構成
- 今回実施する構成 (GCP, stateファイル保存場所など)
- Terraform インストール
- 端末(MacOS)、サーバ(CentOS) の2種類のインストール方法を記載
- GCP 向け環境設定
- GCP 向けの設定、ステートファイル保存場所の作成
- GCP での VM インスタンス構築試験
- VM インスタンスを1台構築、変更、削除、ライフサイクルを実際に試した内容
Terraform とは?
概要
- Infrastructure as Code (IaC) Tool
- インフラストラクチャ定義ツール
- 宣言的 (Declarative) 記載モデル
- 記述言語は HCL (HashiCorp Language. 独自言語)
- リソースをコード(.tf)で定義してを自動化
- ステータスはステートファイル(.tfstate)を Terraform が生成・維持して構成管理
- 開発は HashiCorp 社で、OSS として公開
- Enterprise 版がある
- Hashicorp による商用サポート
- 機能の追加
- GUI, Workspace などチーム機能, CI/CD 機能, などなど
- Sentinel (有償版のみ) があり、Policy as Code 機能の提供
- Terraform Cloud という SaaS 版もある (5名までは無料)
- 様々なインフラに対応している
Terraform の特徴・構成
hashicorp japan の GitHub でリンク公開しているスライドがわかりやすかったです
https://docs.google.com/presentation/d/1Ovdee0FIrJ_h66B5DToQNYKWJ9XRbudS0RCk4d_x1Eg/edit?usp=sharing
Ansible との違い
Ansible は命令実行モデルとなり、サーバでのOSより上の構成管理を得意としているらしい(参考)
分類としては、サーバー定義ツール [分類参照: Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan] にあたるらしく、Terraform と領域を分ける
得意なレイヤを分けて使われているようです
[参照: Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan]
- インフラストラクチャ定義ツール: Terraform
- クラウドなどインフラ全体
- 高い水準でインフラストラクチャをプロビジョニング、構成/設定する
- サーバー定義ツール: Ansible
- サーバー自体の細部を扱う (OSより上の構成管理)
- ソフトウェアパッケージ、ユーザーアカウントなど、さまざまなタイプの構成/設定が含まれる
コンテナ環境で例えると
イメージとしてはコンテナ環境だと、ざっくり下記のような感じなのかなと個人的に思っています。
(右側はオートヒーリング等はなかったり(クラウド等インフラ側機能)、イメージ作成はPackerがあったり違いは色々あります)
- Kubernetes ≒ Terraform
- 宣言モデル. 全体の定義をする
- Dockerfile ≒ Ansible
- 命令実行モデル. 単体の構成管理をする
その他,インフラストラクチャ定義ツール
各クラウドベンダが下記のプロビジョニングツールを出している
クラウドベンダ | プロビジョニングツール |
---|---|
AWS | AWS CloudFormation |
GCP | Cloud Deployment Manager |
Azure | Azure Resource Manager |
上記と Terraform どちらを使うかが選択肢になりそうです。
Terraform で対応してない機能を使いたい場合は、各クラウドベンダ提供のツール、でなければ Terraform が良さげです。
下記画像の通り、情報量の多さとしては、terraform が多そうです。(2021.02.13時点)
※ Resource Manager は別の意図の検索も含まれているトレンド(Azure つけると下がりすぎたのでこれで比較した)
ざっくり結論 (個人的な感想含む)
- クラウドプロビジョニングには Terraform (インフラストラクチャ定義ツール)が便利そう
- サーバ単体のミドルウェア以上は Ansible などで棲み分けで利用が良さそう
- Terraform 競合は各クラウドベンダが出しているツールで、特別な事情がなければ Terraform で良さそう
実施構成
今回は下記のような構成で実施している
- Terraform インストール環境
- 下記2種類
- Local-PC(Mac)
- Remote-Server (CentOS)
- 今回、CI-Server での構築はしない (実際 CI/CD 管理では、CIサーバで GitOps などを実施されるようです)
- 下記2種類
- .tfファイル
- 外部リポジトリで管理する予定(記載範囲外)
- ステートファイル(tfstate)
- GCS (GoogleCloud Storage) に専用バケットを構築して保存・参照先にする
- provider
- 各プロビジョニング先の provider が必要
- 今回は hashicorp/google を使用する (init時に自動でインストールされる)
Terraform インストール
terraform と必要になるツールのインストールを実施する
実施環境
MacBook Air (M1, 2020) : 手元端末で terraform を実施する想定
CentOS 8 : サーバ利用想定
Mac でのインストール
Terraform を実行するためのインストール内容を記載します
Homebrew, Google Cloud SDK がインストール済みなら、「2. tfenv インストール」のみで対応可能です
- Homebrew インストール
- tfenv インストール
- Google Cloud SDK インストール
1. Homebrew インストール
Homebrew のページのインストールコマンドにしたがってインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
※パスワード要求が一度されます
Install 完了後下記画面が出るので sh の profile に登録する
echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> /Users/[username]/.zprofile
eval $(/opt/homebrew/bin/brew shellenv)
最後にバージョン確認しておく
% brew --version
Homebrew 3.0.0
Homebrew/homebrew-core (git revision fdab0; last commit 2021-02-11)
2. tfenv install
複数の terraform を管理してくれるtfenvがあるのでインストールする
brew install tfenv
現在 tfenv での terraform サポートバージョン確認する
tfenv list-remote
下記のように表示される (2021.02.11時点)
% tfenv list-remote
0.15.0-alpha20210210
0.15.0-alpha20210127
0.15.0-alpha20210107
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc1
0.14.0-beta2
0.14.0-beta1
0.14.0-alpha20201007
0.14.0-alpha20200923
0.14.0-alpha20200910
0.13.6
...
上記で alpha,beta,rc を外して安定板をインストールするので今回は0.14.6で実施する
tfenv install 0.14.6
下記のような画面が出るので、案内に従い use でインストールしたバージョンを指定する
tfenv use 0.14.6
terraform が指定したバージョンがインストールされ利用できることが確認できる
% terraform version
Terraform v0.14.6
VScode 対応
HashiCorp が terraform 用の Extension を出しているので利用可能
3. Google Cloud SDK インストール
今回、対象を Google Cloud にするので Google Cloud SDK をインストールする
インストール方法は公式サイトを参照し実行する
https://cloud.google.com/sdk/docs/downloads-interactive?hl=ja
zsh で実施したかったため、実際には下記でイントールした
cd ~
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-324.0.0-darwin-x86_64.tar.gz
tar zxvf google-cloud-sdk-324.0.0-darwin-x86_64.tar.gz
rm google-cloud-sdk-324.0.0-darwin-x86_64.tar.gz
./google-cloud-sdk/install.sh --rc-path ~/.zprofile -q
sed -i -e 's/bash.inc/zsh.inc/g' ~/.zprofile
source ~/.zprofile
初期設定を実施する
gcloud init
CentOS でのインストール
Terraform を実行するためのインストール内容を記載します
Ansible でのインストール Role 例を記載します
- Terraform インストール
- Google Cloud SDK インストール
- (2021.12.18追記) tfenv インストール
1. Terraform インストール (CentOS)
terraform 公式サイトを参照してインストールする
https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/gcp-get-started#install-terraform
ansible role 例は下記の通り。(CentOS8で実証済み)
---
- name: install dnf repository centos8
shell: "dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo"
become: true
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "8"
- name: install terraform centos8
dnf:
name: terraform
state: latest
enablerepo: "hashicorp"
become: true
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "8"
terraform がインストールできたことを確認する (Versionは2021.02.12時点)
% terraform --version
Terraform v0.14.6
2. Google Cloud SDK インストール
---
- name: install yum repository centos8
yum_repository:
name: google-cloud-sdk
description: "Google Cloud SDK repo"
baseurl: https://packages.cloud.google.com/yum/repos/cloud-sdk-el8-x86_64
enabled: no
gpgcheck: yes
repo_gpgcheck: yes
gpgkey:
- https://packages.cloud.google.com/yum/doc/yum-key.gpg
- https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
become: true
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "8"
- name: install google cloud sdk centos8
dnf:
name: google-cloud-sdk
state: latest
enablerepo: "google-cloud-sdk"
become: true
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "8"
gcloud がインストールできたことを確認する (Versionは2021.02.12時点)
% gcloud --version
Google Cloud SDK 327.0.0
alpha 2021.02.05
beta 2021.02.05
bq 2.0.64
core 2021.02.05
gsutil 4.58
3. (2021.12.18追記) tfenv インストール
下記、tfenv install ansible 例 (ansible ユーザのディレクトリ配下にインストールする)
- name: tfenv / clone tfenv for user
git:
repo: 'https://github.com/tfutils/tfenv.git'
dest: "{{ ansible_user_dir }}/.tfenv"
version: v2.2.2
- name: tfenv / bash_profile setting for user
lineinfile:
dest: "{{ ansible_user_dir }}/.bash_profile"
line: 'export PATH=$HOME/.tfenv/bin:$PATH'
backup: yes
when:
- '"bash" in ansible_env.SHELL'
- name: tfenv / zprofile setting for user
lineinfile:
dest: "{{ ansible_user_dir }}/.zprofile"
line: 'export PATH=$HOME/.tfenv/bin:$PATH'
backup: yes
when:
- '"zsh" in ansible_env.SHELL'
GCP 向け環境設定
terraform で GCP を操作するための設定や準備をする
実行環境
下記に載せている GCP 環境
https://qiita.com/suzuyui/items/947867f52897417ee31b
プロジェクトおよび VPC は作成済み
今回は、サービスプロジェクトへ GCE で 1台 VM を構築する
※今回実施内容的には、subenetの記載方法とroleの追加以外は特に単独プロジェクトと差異はない
実施項目
下記の順番で実施する
- terraform 向け Service Account 作成
- tfstate ファイル保存 GCS バケット作成・設定
- terraform init
1. terraform 向け Service Account 作成
terraform が GCP の操作をするためのサービスアカウントを作成して、terraformへ設定する
作成内容は Terraform 側のチュートリアル内容に則す (Setting up GCP Webコンソール利用想定での記載になっている)
1.1. 実施するプロジェクトを選択 / Select the project you created in the previous step.
gcloud で VM インスタンス構築するプロジェクトとその IAM 権限をもつユーザアカウントを設定する
gcloud config set account [PROJECT_USER_ACCOUNT]
gcloud config set project [PROJECT_ID]
※ログインしてない場合は、gcloud auth login
を最初に実施する
1.2. サービスアカウント新規作成 / Under "Service account", select "New service account". Give it any name you like.
Terraform 向けのサービスアカウントを作成する
gcloud iam service-accounts create terraform --display-name "Terraform"
1.3. Editor ロールの付与 / For the Role, choose "Project -> Editor".
作成したサービスアカウントにEditor
ロールを付与する (これは参照先のままで、環境ごとに適切なロールを付与すること)
gcloud projects add-iam-policy-binding [PROJECT_ID] \
--member serviceAccount:terraform@[PROJECT_ID].iam.gserviceaccount.com \
--role roles/editor
1.4. Json キーの作成とダウンロード / Leave the "Key Type" as JSON. Click "Create" to create the key and save the key file to your system.
作成したサービスアカウントのキーを生成する (参照)
gcloud iam service-accounts keys create terraform_serviceacoount_credential.json \
--iam-account terraform@[PROJECT_ID].iam.gserviceaccount.com
実施したフォルダに、terraform_serviceacoount_credential.json
ってファイルが生成されます
(共有 VPC を利用している場合) 共有 VPC 側でも Subnet 利用ロールを付与
gcloud config set account [HOST_PROJECT_USER_ACCOUNT]
gcloud config set project [HOST_PROJECT_ID]
gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
--member serviceAccount:terraform@[PROJECT_ID].iam.gserviceaccount.com \
--role roles/compute.networkUser
1.5. main.tf の作成
GCP の認証情報 (作成したサービスアカウントのキー)と、操作するプロジェクトを指定する
provider "google" {
credentials = file("terraform_serviceacoount_credential.json")
project = "[PROJECT_ID]"
}
以上で、terraform 実際対象のプロジェクトの指定と、認証の設定まで完了
2. tfstate ファイル保存 GCS バケット作成・設定
ステートファイル(tfstate)を共有利用できるように、GCSに保存用のバケットを作成して、terrafromへ設定する
https://www.terraform.io/docs/language/settings/backends/gcs.html
上記 URL にhighly recommendedとして、enable Object Versioning on the GCS bucket
とあるので、
versioning と lifecycle の設定を実施する
パラメータは、無料枠に収まるようにストレージクラスをSTANDARD
、リージョンをus-west1
にした。(月5Gまで対象.参照)
- [PROJECT-ID] : 対象プロジェクトのID
- [BUCKET_NAME] : 保存するバケット名。GCP全体でユニークな必要がある。
gsutil mb -p [PROJECT-ID] -c STANDARD -l us-west1 -b on gs://[BUCKET_NAME]/
gsutil versioning set on gs://[BUCKET_NAME]/
gsutil lifecycle set terraform-state-bucket_lifecycle.json gs://[BUCKET_NAME]/
{"rule": [{"action": {"type": "Delete"}, "condition": {"numNewerVersions": 5}}]}
上記作成したバケットをバックエンドとして指定する。
terraform {
backend "gcs" {
bucket = "[BUCKET_NAME]"
prefix = "terraform/state"
}
}
以上で、trraform のバックエンド設定完了
3. terraform init
Terraform の初期化を行う
必要なプラグインのイントールなども実施する
terraform init
実行結果は下記の通り
GCPに関するProvider Plugin がインストールされてたことが確認できる
以上で、terraform から GCP を操作する設定完了
GCP での VM インスタンス構築試験
実際に GCP で VMインスタンスを 1台構築して、動作を確認する
実施項目
下記の順番で実施する
- VMインスタンス定義
- terraform plan
- terraform apply
- terraform destroy
- lifecycle
1. VMインスタンス定義
VM 一台を構築するリソース定義ファイルを作成する
(参考)
とりあえず今回の試験は、ファイルを分けずに main.tf に追記して実施している
resource "google_compute_instance" "vm_instance" {
name = "terraform-create-sv01"
machine_type = "f1-micro"
zone = "us-west1-b"
boot_disk {
initialize_params {
image = "centos-cloud/centos-7"
}
}
network_interface {
subnetwork = "projects/[HostProjectName]/regions/us-west1/subnetworks/private-subnet01"
access_config {
}
}
}
記載方法の詳細は Terraform 公式サイト参照
- 記載内容
- name: VMインスタンス名
- machine_type: マシンタイプ
- zone: VM作成ゾーン
- boot_disk/initialize_params/image: イメージ名
- https://cloud.google.com/compute/docs/images#gcloud
-
gcloud compute images list
で一覧取得可能で、該当するイメージ名を指定する
- network_interface/subnetwork: VMで設定するサブネット
- ここでは共有VPCサブネットを指定するためprojectsから指定している
- 共有 VPC で利用許可された
privete-subnet01
というサブネットを利用して作成する例となっている
2. terraform plan
実際にどのような変更をするかを事前に確認できる
plan では変更は行われない (dry run)
terraform plan
実行結果は下記の通り
VM が +
で追加されることが見える
terraform plan で Error が出る場合
下記のようなエラーが出る場合はgcloud auth application-default login
を実施する
Error: Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block. No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'. Original error: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
gcloud auth application-default login
3. terraform apply
変更の適用を実施する
実際に VM が作成される
terraform apply
実行結果が下記となる
Enter a value: と出るのでyes
と入力する
terraform apply 実施中のステートファイルロックの様子
terraform apply
中にバックエンドにしているバケットを見ると、ロックファイルが作成されていることがわかる
% gsutil ls gs://[BUCKET_NAME]/terraform/state/
gs://[BUCKET_NAME]/terraform/state/default.tflock
gs://[BUCKET_NAME]/terraform/state/default.tfstate
% gsutil ls gs://[BUCKET_NAME]/terraform/state/
gs://[BUCKET_NAME]/terraform/state/default.tfstate
変更
リソースを変更して動作を確認する (参考
下記のように先程のファイルにtags = ["web", "dev"]
行を追記する
resource "google_compute_instance" "vm_instance" {
name = "terraform-create-sv01"
machine_type = "f1-micro"
zone = "us-west1-b"
+ tags = ["web", "dev"]
#...
変更したらterrafrom apply
をして動作を確認する
terraform apply
出力例が下記の通り。
追加した tags のところが、+
となっていることを確認できる。
確認したらEnter a value: でyes
を入力して適用を実施する
4. terraform destroy
作成したリソースの削除を実施する
実際に VM が削除される
terraform destroy
apply時同様、Enter a value: と出るのでyes
と入力する
以上で、削除も完了となる
5. lifecycle
5.1 デフォルト動作
再構築が必要な動作の確認として、VMインスタンスのリネームを実施する
VM 名をリネーム (.tfファイルのname:を変更) すると、現状の VM が削除され新しい名前の VM が追加される
(参考)
前段階で削除しているので、
terraform apply
を実施して VM インスタンスが構築された状況にしてから下記を実施する。
resource "google_compute_instance" "vm_instance" {
- name = "terraform-create-sv01"
+ name = "terraform-create-sv02"
#...
上記変更後にterraform apply
した結果が下記であり、
-/+ destroy and then create replacement とある通り、削除後に作成して置き換える動作になる
下記yes
入力後に、terraform-create-sv01がDestroyされ、
その後に、terraform-create-sv02 がCreateが実施される様子が見える
上記の動作を変えたい場合は、lifecycleの記載を実施する
lifecycle は下記の 3種類があり、それぞれ試してみる
- lifecycle
- create_before_destroy
- prevent_destroy
- ignore_changes
5.2 create_before_destroy
新しい交換用のオブジェクトを作成後に、古いオブジェクトを削除します
一時的に2重で存在することになるため、衝突や重複などでの問題がないかリソース理解が必要になります
※参照(https://www.terraform.io/docs/language/meta-arguments/lifecycle.html#create_before_destroy)
本件での記載例は下記の通り
再度リネームして、lifecycle
を追記してます (一度google_compute_instance
箇所全体を再掲)
resource "google_compute_instance" "vm_instance" {
name = "terraform-create-sv01"
machine_type = "f1-micro"
zone = "us-west1-b"
tags = ["web", "dev"]
boot_disk {
initialize_params {
image = "centos-cloud/centos-7"
}
}
network_interface {
subnetwork = "projects/[HostProjectName]/regions/us-west1/subnetworks/private-subnet01"
access_config {
}
}
lifecycle {
create_before_destroy = true
}
}
上記を apply した結果が下記となり、
+/- create replacement and then destroy とデフォルト時と出力が変わっていることが確認できる
実行動作も、下記の通り、**Creating...から始まり、完了後にDestroyng...**が始まることが見える
5.3 prevent_destroy
このメタ引数をtrueに設定すると、引数が構成に存在する限り、
Terraform はリソースに関連付けられたインフラストラクチャオブジェクトを破壊する計画をエラーで拒否する。
これはデータベースインスタンスなどに有効。ただし、resource ブロック自体が構成から削除された場合は、リソース破棄を防ぐことはできない。
(参照)
下記の通り、name
とlifecycle
の箇所を変更して挙動を確認します。
resource "google_compute_instance" "vm_instance" {
- name = "terraform-create-sv01"
+ name = "terraform-create-sv02"
#...
lifecycle {
- create_before_destroy = true
+ prevent_destroy = true
}
}
plan または、 apply を実施すると下記のような出力となり、
Error: Instance cannot be destroyedとなる
5.4 ignore_changes
属性名のリストを指定して、指定した属性への変更は無視するようにする。
将来変更される予定があるデータについて影響を与えないようにしておくことができる。
リストの代わりに、special キーワードall
を使用して、すべての属性を無視するようにすることもできる。
(参照)
下記記載例。
tags
で試す。
実施前はtags = ["web", "dev"]
のみ箇所を、
"ignoretest"を追加してtags = ["web", "dev", "ignoretest"]
にしている
lifecycle
で ignore_changes =
にtags
を追加して、tagsの変化を無視するようにしている。
resource "google_compute_instance" "vm_instance" {
name = "terraform-create-sv01"
machine_type = "f1-micro"
zone = "us-west1-b"
tags = ["web", "dev", "ignoretest"]
boot_disk {
initialize_params {
image = "centos-cloud/centos-7"
}
}
network_interface {
subnetwork = "projects/[HostProjectName]/regions/us-west1/subnetworks/private-subnet01"
access_config {
}
}
lifecycle {
ignore_changes = [
# Ignore changes to tags, e.g. because a management agent
# updates these based on some ruleset managed elsewhere.
tags,
]
}
}
下記が上記変更後に、terraform apply
を実施した例。
**No changes. Infrastructure is up-to-date.**となり、tagsが変更されているが無視される。
以上で、VMインスタンスの構築から削除、再構築や削除防止などのライフサイクル管理などを試験し動作確認できた。
さいごに
Terraform を調べて環境構築、VMインスタンス構築・管理まで実施してみた
既存から Terraform ファイルを作成する GCP が作成している Terraformer なるものがあるらしく、今後試してみたい
また、組織・プロジェクト、VPC などの基礎的な箇所にお IaC 化や、GKE 構築なども実施する予定
参考資料
GitHub: hashicorp-japan/terraform-workshop-jp
https://github.com/hashicorp-japan/terraform-workshop-jp
https://docs.google.com/presentation/d/1Ovdee0FIrJ_h66B5DToQNYKWJ9XRbudS0RCk4d_x1Eg/edit?usp=sharing
Hashicorp Learn / Get Started - Google Cloud
https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-build#setting-up-gcp
Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan (2017) 著:Kief Morris 監訳:宮下 剛輔 訳:長尾 高弘
https://www.oreilly.co.jp/books/9784873117966/
Infrastructure as Codeを実現するTerraformとAnsible~それぞれの得意領域と使い分け~
https://www.lac.co.jp/lacwatch/service/20201216_002380.html
Google Cloud Platformで学ぶTerraform 〜基礎編〜 著:カエルと空
https://techbookfest.org/product/6331235183886336
Terraform Language - The lifecycle Meta-Argument
https://www.terraform.io/docs/language/meta-arguments/lifecycle.html