TerraformでGCP環境を構築してみる

はじめまして。GMOアドマーケティングでインフラを担当しているa.sです。
今回はTerraformを用いて、GCP上に検証環境を構築する機会がありましたので、構築に伴う一連の作業をご紹介します。


構築内容


既存のオンプレミス環境から接続可能なKVSサーバ環境を構築します。
目的として、オンプレミス環境上に構築しているKVSサーバを物理的にスケールする作業のコストを
クラウド化することで下げたいというものです。

以下のような構成になります。


  • KVSサーバはGCPで提供されているCentOS7のイメージを使用して構築します。
  • オンプレミス環境とGCP環境の通信はVyOSとGCP上のVPN Gatewayで暗号化して行います。
  • KVSへの接続は内部ロードバランサを介して各KVSサーバにバランシングする設計としています。
  • Terraformのtfstateファイルの管理はGCS(Google Cloud Storage)上で行います。
  • Terraformで作成したインスタンスのミドルウェア等の管理は踏み台サーバ(bastion Server)上からansibleで行います(本ブログでは触れません)。

残念ながら、VPN環境は現時点では構築できていないため、その他のGCPリソースをTerraform化したもののご紹介となります。


Terraformを使用する事前準備


予め用意しておくもの

  • TerraformとCloud SDKをインストール可能な環境
    弊社ではCentOS7の環境で実施します。
  • GCPのプロジェクト
    GCPの環境を構築するためにGCP上でプロジェクトを作成しておきます。
  • 作成したGCPのプロジェクトでAPIを有効化
    こちらを参考に、作成したGCPプロジェクトで「Google Compute Engine API」を有効化しておきます。

Google Cloud Console上からTerraform用のサービスアカウントを作成する

TerraformとCloud SDKのAPI認証用にGoogle Cloud Console上からサービスアカウントを作成します。

1.Google Cloud Consoleログイン後、画面左側から「IAM & admin」を選択し、「Service accounts」を選択します。

2.遷移した画面上部にある「CREATE SERVICE ACCOUNT」を選択します。

3.「Service account name」に適当な名前を入力し、「Role」部で必要な権限を選択します。
今回はGCE(Google Compute Engine)及びGCSを操作するために、
「Compute Admin (beta)」及び「Storage Admin」を付与します。


鍵が必要になるので、json形式の鍵を選択して、「CREATE」を選択します。
この鍵は再ダウンロードが出来ないので、大切に保管します。
※鍵の再設定は可能なので、万が一鍵を紛失した際は再設定を行います。

何かと使えるのでGCP用のCloud SDKのインストールと認証
※本ブログではGoogle Cloud Storageのbucket作成にしか使いません

Terraform実行サーバでCloud SDKのインストール及び、認証作業を実施します。

公式ドキュメントの「yum(Red Hat と CentOS)」部を参考にしています。

1.リポジトリを登録します

2.yumでCloud SDKをインストールします

3.Cloud SDK及びTerraformを操作するユーザにスイッチし(任意)、作成したサービスアカウントのjsonを設置します

4.Cloud SDKで作成したサービスアカウント及びGCPのプロジェクトを認証します

5.認証した情報を確認します。

tfstateファイルをGoogle Cloud Storage上で管理するため、保存するためのバケットを作成する

Terraformはデフォルトだと「terraform.tfstate」という名前のファイルで管理対象のインフラの状態を保存しています。
Terraformはこのファイルに基づいて、インフラの構成の変更などを検知し、必要に応じてリソースの作成や削除、変更などを行っています。
チームでTerraformを運用する場合、各々でtfstateファイルの状態が異なると矛盾が生じてしまうため、今回の場合はGCS上にtfstateファイルを保持することで情報の一貫性を保ちます。

Cloud SDKインストール時に併せてインストールされるgsutilコマンドを用いてGCS上にtfstateファイルを保存しておくためのバケットを作成します。

gsutilコマンドの各オプションの意味は以下の通りです。

  • mb
    バケットを作成します
  • -p 
    作成するバケットの対象プロジェクト名を指定します
  • -c
    作成するストレージクラスを指定します
  • -l
    バケットロケーションを指定します
  • gs://example_tf-state-prod/
    「gs://バケット名/」として作成するバケットを指定します

Terraformのインストール

Terraformのインストールを行います。
今回はTerraform実行ユーザの「~/terraform」以下にインストールします。

インストール後、以下コマンドでバージョンが確認できればインストールに成功しています。


Terraformのコードを書く


Terraformの実行環境が整ったところで、ここからは実際にTerraformのコードを書いていきます。
Terraform構成の構文はHashiCorp Configuration Language、HCLという言語で記述します。
「*.tf」という拡張子を持つファイルにコードを書くことで、Terraformがそれをテンプレートとして自動的に認識します。

Terraform用に適当なディレクトリを作成して、その中に設定ファイルを作成していきます。
今回作成する「*.tf」ファイルの構成は以下の通りです。今回は分かりやすくするために各機能ごとにファイルを分けました。

以下からは個別に設定ファイルと、設定内容の詳細を記載します。

provider.tf

接続するプロバイダー(今回はGCP)を設定します。

  • provider
    接続先を設定します。今回はgoogleとなります
  • credentials
    認証情報を設定します。作成したサービスアカウントから発行されたjsonのkeyのパスを指定します
  • project
    対象のプロジェクトを設定します
  • region
    対象のリージョンを設定します

backend.tf

tfstateファイルをGCS上で管理するための設定です。

  • backend
    対象のバックエンドサービス(gcs)を指定します
  • bucket
    作成したバケットを指定します
  • path
    対象のtfstateファイルのパスを指定します
  • credentials
    認証情報を指定します。作成したサービスアカウントから発行されたjsonのkeyのパスを指定します

variables.tf

各設定ファイル内で使用する変数を設定します。
変数は “${var.変数名}” の形で使用します。

  • variable “region”
    「asia-northeast1」を変数”region”に登録します
  • variable “region_zone”
    「asia-northeast1-a」を変数”region_zone”に登録します
  • variable “bastion_ssh_keys”
    “bastion_ssh_keys”という変数に「exampleadmin」ユーザと、そのユーザ用の公開鍵(ssh-rsa 〜)を登録しています。
    ここで登録したユーザはsudo権限が与えられた状態でサーバに設定され、併せて公開鍵を登録しているので秘密鍵ログインが可能になります
  • variable “kvs_ssh_keys”
    「variable “bastion_ssh_keys”」と同様です

network.tf

カスタムVPCネットワークを設定します。

  • resource “google_compute_network” “example”
    ネットワークリソースを作成します
    name
    ネットワークの一意の名前を設定します。ここでは「example」としています
  • resource “google_compute_subnetwork” “subnet1”
    サブネットワークリソースを作成します
    ・name
    サブネットワークの一意の名前を設定します。ここでは「subnet1」としています
    ・ip_cidr_range
    作成するネットワークレンジを設定します。ここでは「192.168.10.0/24」を設定しています
    ・network
    このサブネットワークが属する親ネットワークを設定します。ここでは直前で作成した「example」ネットワークを設定しています
    ※「${google_compute_network.example.name}」とは、「google_compute_network」というリソースの中の「example」というリソースの中の「name」に当たるものという意味で、ここでは
    「resource “google_compute_network” “example”」内で設定されている「name = “example”」を指定しているということになります。
    ・description
    このサブネットワークの説明となります
    ・region
    このサブネットワークが属するリージョンを設定します

bastion.tf

GCEでbastion Serverを構築するための設定です。

  • resource “google_compute_address” “bastion01”
    bastion Serverに割り当てる静的グローバルIPアドレスリソースを設定します
    name
    一意の「bastion01」というリソース名を設定します
    region
    静的グローバルIPアドレスリソースを作成するリージョンを設定します。
  •  resource “google_compute_instance” “bastion01”
    作成するGCEインスタンスリソースの情報を設定します
    name
    一意の「bastion01」というリソース名を設定します
    machine_type
    作成するインスタンスのマシンタイプを設定します
    zone
    インスタンスを作成するゾーンを設定します
    tags
    インスタンスに付与するタグのリストを設定します
    boot_disk
    インスタンス作成に使用するOSイメージを設定します
    metadata_startup_script
    サーバ起動時に流すコマンドを設定できます
    ここではSSHのポートを10022で使用できるようにし、GCPのCentOS7のイメージはデフォルトUTCなためJSTに変更しています
    network_interface
    インスタンスに接続するネットワークを設定します
    ここではnetwork.tfで作成したネットワークからの静的プライベートIPを割り振り、
    「resource “google_compute_address” “bastion01” 」で作成した静的グローバルIPを割り振ります。
    metadata
    ここではSSH用の公開鍵を登録しています。個別にインスタンスに公開鍵を登録するため「block-project-ssh-keys」を有効化します。「sshKeys」には変数で登録した公開鍵を指定します

kvs.tf

GCEで3台のKVS Serverを構築するための設定です。3台とも同スペックで構築するためリソース名に変数を使って一つの設定で3台分のインスタンスを作成することも出来るのですが、このように1台1台個別に設定しないと後述の「instancegroupes.tf」で設定するインスタンスグループに登録できなかったためこのような記述となりました
静的グローバルIPアドレスリソースの設定が無いことを除いて、こちらの説明は「bastion.tf」と重複するため割愛します

firewall.tf

ファイアウォールルールを設定します。項目が多いためそれぞれピックアップして説明します。
「xxx.xxx.xxx.xxx/xx」となっている箇所は弊社固定IPになります。

  • resource “google_compute_firewall” “allow-icmp”
    「example」ネットワーク内の「bastion」タグが付いたリソースに対してソースIPからicmpを許可しています
  • resource “google_compute_firewall” “allow-tcp-10022”
    「example」ネットワーク内の「bastion」タグが付いたリソースに対してソースIPから10022番ポートを許可しています
  • resource “google_compute_firewall” “allow-tcp-8091”
    「example」ネットワーク内の「kvs」タグが付いたリソースに対してソースIPから8091番ポートを許可しています
  • resource “google_compute_firewall” “allow-internal”
    「example」ネットワーク内の「server」タグが付いたリソースに対してソースIPから全ての通信を許可しています
  • resource “google_compute_firewall” “allow-internal-lb”
  • resource “google_compute_firewall” “allow-health-check”
    これらの設定は内部ロードバランサ及びヘルスチェックのアクセスを、「example」ネットワーク内の「kvs」タグが付いたリソースに対して許可しています。内部ロードバランサからのアクセスを許可する設定は公式ドキュメントを参考にしています

instancegroupes.tf

内部ロードバランサから各インスタンスにバランシングさせるためには、対象のインスタンスをインスタンスグループとして登録する必要があります。今回は非マネージドインスタンスグループとして作成します。インスタンスグループに関して詳しくは公式ドキュメントをご参照下さい。

  • resource “google_compute_instance_group” “kvs-group”
    インスタンスグループを作成します。
    ・name
    インスタンスグループの名前を設定します
    ・description
    作成するインスタンスグループの説明です
    ・zone
    インスタンスグループを作成するゾーンを指定します
    ・instances
    インスタンスグループに登録するインスタンスを指定します
    今回はkvs.tfで作成するインスタンスを指定しますが、他のケースで使用する場合にも基本的には以下の形で指定の形で問題ないと思います
    ${google_compute_instance.インスタンス名.self_link}
    ※self_linkとは、作成されたリソースのURIを示します。基本的にTerraform側でGCP側から動的に取得してくれます。

internallb.tf

内部ロードバランサの設定を行います。ここでは実際の内部ロードバランサの設定とともに、ヘルスチェックと転送ルールも併せて設定しています。

  • resource “google_compute_health_check” “kvs-health-check”
    内部ロードバランサが使用するヘルスチェックリソースを設定します
    ・name
    ヘルスチェックリソースの一意の名前を設定します
    check_interval_sec
    ヘルスチェックの間隔を設定します
    timeout_sec
    接続失敗を判断するまでの秒数を設定します
    tcp_health_check
    ヘルスチェック接続先のポートを設定します
  • resource “google_compute_region_backend_service” “kvs-int-lb”
    内部ロードバランサを設定します
    name
    バックエンドサービスの名前を設定します
    protocol
    使用するプロトコルを設定します。ここではTCPを指定しています
    health_checks
    使用するヘルスチェックリソースを設定します。ここでは先程設定した「kvs-health-check」を指定しています
    timeout_sec
    接続要求が失敗したと判断するまで待機する時間を設定します
    session_affinity
    使用するアフィニティ機能を設定します。詳細はTerraformの公式ドキュメントと、GCPの公式ドキュメントを参照して下さい
    region
    バックエンドが存在するリージョンを設定します。ここでは「variables.tf」で設定した変数を利用しています
    backend
    作成する内部ロードバランサを利用するインスタンスグループを設定します
  • resource “google_compute_forwarding_rule” “kvs-int-lb-forwarding-rule”
    内部ロードバランサが利用する転送ルールを設定します
    name
    転送リソースの一意の名前を設定します
    load_balancing_scheme
    使用するロードバランシングのタイプを設定します。今回は内部ロードバランサなので「INTERNAL」を指定しています
    ports
    内部ロードバランサに使用するポートを設定します。このポート宛のパケットが、この転送ルールで指定したバックエンドサービス(kvs-int-lb)に転送されます
    region
    転送ルールを使用するリソースが存在するリージョンを設定します
    network
    負荷分散対象のネットワークを設定します。ここでは「network.tf」で作成した「example」ネットワークを指定します
    subnetwork
    負荷分散対象のサブネットワークを設定します。ここでは「network.tf」で作成した「subnet1」ネットワークを指定します
    backend_service
    転送ルールに一致したパケットの転送先のバックエンドサービスを設定します。ここでは先程作成した「kvs-int-lb」を指定します
    ip_address
    パケットを最初に受信する静的IPを設定します。この場合だとクライアント側からは「192.168.10.100:11210」宛にKVS用のパケットを送信することになります。

ここまででTerraformのコードの準備が出来ました。


Terraformの実行


コードの準備ができたら、実際にTerrafomを実行してGCP上にリソースを構築します。

コードの検証
先ずは 「terraform validate」コマンドを使用して作成したコードの構文チェックを行います。

実行した結果、何も表示されなければ構文に問題はありません。
エラーが出た場合はその内容に沿ってコードの修正を行う必要があります。

実行計画
構文に問題がなければ、次に「terraform plan」コマンドを実行してGCP上のリソースにどのような変更を加えるか、実行計画を確認します。このコマンドを実施しても実際のGCPリソースに変更は加えられません。

実行した内容を確認し、実際にコードの内容通りに変更が加えられるかどうかなどをチェックします。

適用
実行計画に問題がなければ、「terraform apply」コマンドを使用して実際にコードをGCPリソースに適用します。

実行した内容を確認し、エラーなどが出ていなければGCP上にリソースが作成されているはずです。
後は実際にGoogle Cloud Console上や、「terraform show」コマンドなどでリソースの情報を確認してみましょう。

 


Terraformで作成したリソースの削除


Terraformで作成したリソースを全て削除する場合は以下の通り実行します

リソースの削除計画の確認
先ず「terraform plan -destroy」コマンドを使用してどのリソースを削除するのかを確認しておきます。このコマンドを実施しても実際のGCPリソースに変更は加えられません。

実行した内容を確認し、削除対象に問題がないことを確認します。

リソースの削除
terraform destroy」コマンドを使用して実際に削除処理を行います。実行後確認プロンプトが出てくるはずなので、「yes」を入力して実行を進めます。

実行した内容を確認し、エラーが出ていないことと、対象リソースが削除されていることを確認します。
また、Google Cloud Console上でも同様にリソースが削除されていることを確認できると思います。

まとめ

今回はGCP上のリソースをTerraformで作成する場合の一連の作業と、各設定ファイルの詳細、リソースを削除するまでをご紹介しました。
Terraformはまだ検証のみの使用であるため、ご紹介できた機能はごく一部だと思いますが、同じようにGCPリソースをTerraformで管理を始めたような方に少しでもご参考になれば幸いです。