IDCF テックブログ

IDCF テックブログ

クラウド・データセンターを提供するIDCフロンティアの公式テックブログ

TerraformでIDCFクラウドに仮想マシンを作成する方法

こんにちは、事業推進本部SE部の山手です。

今回は「Terraform」を利用してIDCFクラウド内に仮想マシンを作成し、同時にその仮想マシンにSSHでアクセスできる環境を構築する方法を紹介します。

目次

Terraformとは

Terraformとは、HashiCorp社が提供しているIaC(Infrastructure as Code)ツールです。

IaCツールとは簡単にいうと、インフラ環境をコードとして管理するツールです。
そのため、コマンドを実行するだけで誰もが同じインフラ環境を構築することができるようになります。

www.terraform.io

UIで操作して仮想マシンを作成することは簡単ですが、複数作成する場合は手順漏れや人為的ミスが起こりがちです。 そのようなミスの発生を、Terraformを使って防ぎましょう。

実施内容

◆仮想マシンの作成
◇グローバルIPアドレスの取得
◇ポートフォワーディング設定
◇ファイアウォールの設定
(◇は、作成した仮想マシンにSSHでアクセスするために、仮想ルーターに対して設定を行っています。)

作業手順

それでは、実際に作成してみましょう。
※今回はRocky Linux9.2で作業しています。

1.Terraformのインストール

# インストール
[ayamate@terraform]$ sudo yum -y install terraform
# 確認
[ayamate@terraform]$ terraform version

2.作業用ディレクトリの作成

Terraformは、カレントディレクトリ内の構成ファイル(拡張子は「.tf」)をもとにインフラ環境を操作します。
そのため、作業用のディレクトリを作成してその中で操作する必要があります。ディレクトリの作成に命名規則はありません。
※今回はディレクトリ名「terraform」で作成しました。

#作業用ディレクトリの作成
[ayamate@terraform]$ mkdir terraform
#作業用ディレクトリに移動
[ayamate@terraform]$ cd terraform

3.Terraform構成ファイル(以下、構成ファイル)の作成

作業用のディレクトリの中に構成ファイルを作成する必要があります。構成ファイルの作成に命名規則はありませんが、拡張子は「.tf」である必要があります。 また、構成ファイルを複数に分けることも可能です。
※今回はファイル名「main.tf」にまとめて作成しました。

#作業用ディレクトリ配下に構成ファイルを作成
[ayamate@terraform]$ vi main.tf
今回作成した構成ファイルの全体像
#変数の宣言
variable myip { default = "***,***,***,***" }
variable cloudstack_api_url { default = "WWWWWWWWWWWWWWWWWWWWWWWW" }
variable cloudstack_api_key { default = "XXXXXXXXXXXXXXXXXXXXXXXX" }
variable cloudstack_secret_key { default = "YYYYYYYYYYYYYYYYYYYYY" }
variable network_id { default = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" }

#プロバイダーの指定
terraform {
  required_providers {
    cloudstack = {
      source = "cloudstack/cloudstack"
      version = "0.4.0"
    }
  }
}

#IDCFクラウドAPIキーの参照先
provider "cloudstack" {
  api_url = "${var.cloudstack_api_url}"
  api_key = "${var.cloudstack_api_key}"
  secret_key = "${var.cloudstack_secret_key}"
}

#作成するVMの詳細設定
resource "cloudstack_instance" "vm" {
  name = "XXXXXXXXXXXXXXXXX"
  service_offering= "light.S1"
  network_id = "${var.network_id}"
  template = "CentOS 7.9 64-bit"
  zone = "volt"
  keypair = "YYYYYYYYYYYYYYYYYYYY"
}

#取得するグローバルIPアドレスの設定
resource "cloudstack_ipaddress" "public_ipaddress" {
  network_id = "${var.network_id}"
  zone = "volt"
}

#フォワーディング設定
resource "cloudstack_port_forward" "pf_ssh" {
  ip_address_id = "${cloudstack_ipaddress.public_ipaddress.id}"
  forward {
    protocol = "tcp"
    private_port = 22
    public_port = 22
    virtual_machine_id = "${cloudstack_instance.vm.id}"
  }
}

#ファイアウォール設定
resource "cloudstack_firewall" "myip" {
  ip_address_id = "${cloudstack_ipaddress.public_ipaddress.id}"

  rule {
    cidr_list = ["${var.myip}/32"]
    protocol = "tcp"
    ports = ["22"]
  }
}

#実行
output "public_ipaddress" {
  value = "${cloudstack_ipaddress.public_ipaddress.ip_address}"
}

この構成ファイルを実行してIDCFクラウド上に仮想マシンを作成することができます。

今回作成した構成ファイルの詳細説明

【変数宣言】

variable myip { default = "***,***,***,***" }
variable cloudstack_api_url { default = "WWWWWWWWWWWWWWWWWWWWWWWW" }
variable cloudstack_api_key { default = "XXXXXXXXXXXXXXXXXXXXXXXX" }
variable cloudstack_secret_key { default = "YYYYYYYYYYYYYYYYYYYYY" }
variable network_id { default = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" }

・myip
 ⇒クラウド環境への通信を許可するIPアドレス

・cloudstack_api_key_url
 ⇒IDCFクラウドAPIのURL
 (IDCFクラウドコンソールの「アカウント設定」の「API」の「リージョンとエンドポイント」の「エンドポイント」の値)
 https://console.idcfcloud.com/user/apikey
 ※リージョンごとにエンドポイントが違うのでご注意ください

・cloudstack_api_key
 ⇒IDCFクラウドAPIのAPI Key
 (同じ画面の「User Key」の「API Key」の値)

・cloudstack_secret_key
 ⇒IDCFクラウドAPIのSecret Key
 (同じ画面の「Secret Key」の値)

・network_id
 ⇒仮想マシン作成先のネットワークID
 (「コンピュート」の「ネットワーク」の「作成先ネットワーク名」の「ネットワークID」の値)
 https://console.jp-east-3.idcfcloud.com/compute/network/
 ※上記URLは東日本リージョン3の画面なので、他のリージョンの場合は画面上で対象リージョンをお選びください

【プロバイダーの指定】

#プロバイダーの指定
terraform {
  required_providers {
    cloudstack = {
      source = "cloudstack/cloudstack"
      version = "0.4.0"
    }
  }
}

#IDCFクラウドAPIキーの参照先
provider "cloudstack" {
  api_url = "${var.cloudstack_api_url}"
  api_key = "${var.cloudstack_api_key}"
  secret_key = "${var.cloudstack_secret_key}"
}

・provider
 ⇒IDCFクラウドでは、クラウド基盤のソフトウェアとしてCloudStackを利用しているため Cloud Stack のTerraformプロバイダーを利用します。

【仮想マシン設定】

#作成するVMの詳細設定
resource "cloudstack_instance" "vm" {
  name = "XXXXXXXXXXXXXXX"
  service_offering= "light.S1"
  network_id = "${var.network_id}"
  template = "CentOS 7.9 64-bit"
  zone = "volt"
  keypair = "YYYYYYYYYYYYYYYYY"
}

・name
 ⇒作成するVMの名前

・service_offering
 ⇒仮想マシンタイプの指定

・network_id
 ⇒仮想マシン作成先のネットワークID
 ※マシン作成のためにネットワークIDは必須です。

・template
 ⇒利用するテンプレート

・zone
 ⇒作成先ゾーンの指定

・keypair
 ⇒公開鍵認証に利用する公開鍵の名前
 ※事前にSSH Keyの取得が必要です。

【グローバルIPアドレス設定】

#取得するグローバルIPアドレスの設定
resource "cloudstack_ipaddress" "public_ipaddress" {
  network_id = "${var.network_id}"
  zone = "volt"
}

・zone
 ⇒作成先ゾーンの指定

【ポートフォワーディング設定】
※仮想ルーターに対して、設定を行っています。

#フォワーディング設定
resource "cloudstack_port_forward" "pf_ssh" {
  ip_address_id = "${cloudstack_ipaddress.public_ipaddress.id}"
  forward {
    protocol = "tcp"
    private_port = 22
    public_port = 22
    virtual_machine_id = "${cloudstack_instance.vm.id}"
  }
}

・private_port
 ⇒宛先プライベートポートの指定

・public_port
 ⇒受信元パブリックポートの指定

【ファイアウォール設定】
※仮想ルーターに対して、設定を行っています。

#ファイアウォール設定
resource "cloudstack_firewall" "myip" {
  ip_address_id = "${cloudstack_ipaddress.public_ipaddress.id}"

  rule {
    cidr_list = ["${var.myip}/32"]
    protocol = "tcp"
    ports = ["22"]
  }
}

・cidr_list
 ⇒アクセス元のIPアドレス

・ports
 ⇒特定IPアドレスからのアクセスを許可するポート番号

【取得したグローバルIPアドレスの表示】

#IPアドレスの表示
output "public_ipaddress" {
  value = "${cloudstack_ipaddress.public_ipaddress.ip_address}"
}

4.コードの実行

構成ファイルを作成したら、3つのTerraformコマンドを実行します。

①構成ファイルを含む作業ディレクトリを初期化する(必須)
# 初期化作業
[ayamate@terraform]$ terraform init

Initializing the backend...

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

(Terraform has been successfully initialized!と表示され初期化が完了したことがわかります。)

②構成ファイルを実行すると何が作成されるのか事前に確認する
# 事前の確認
[ayamate@terraform]$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudstack_firewall.myip will be created
  + resource "cloudstack_firewall" "myip" {
      + id            = (known after apply)
      + ip_address_id = (known after apply)
      + managed       = false
      + parallelism   = 2

      + rule {
          + cidr_list = [
              + "***.***.***.***/32",
            ]
          + icmp_code = (known after apply)
          + icmp_type = (known after apply)
          + ports     = [
              + "22",
            ]
          + protocol  = "tcp"
          + uuids     = (known after apply)
        }
    }

  # cloudstack_instance.vm will be created
  + resource "cloudstack_instance" "vm" {
      + display_name     = (known after apply)
      + expunge          = false
      + group            = (known after apply)
      + id               = (known after apply)
      + ip_address       = (known after apply)
      + keypair          = "XXXXXXXXXX"
      + name             = "terraform-test"
      + network_id       = "YYYYYYYYYY"
      + project          = (known after apply)
      + root_disk_size   = (known after apply)
      + service_offering = "light.S1"
      + start_vm         = true
      + tags             = (known after apply)
      + template         = "CentOS 7.9 64-bit"
      + zone             = "volt"
    }

  # cloudstack_ipaddress.public_ipaddress will be created
  + resource "cloudstack_ipaddress" "public_ipaddress" {
      + id            = (known after apply)
      + ip_address    = (known after apply)
      + is_portable   = false
      + is_source_nat = (known after apply)
      + network_id    = "XXXXXXXXXX"
      + project       = (known after apply)
      + tags          = (known after apply)
      + zone          = "volt"
    }

  # cloudstack_port_forward.pf_ssh will be created
  + resource "cloudstack_port_forward" "pf_ssh" {
      + id            = (known after apply)
      + ip_address_id = (known after apply)
      + managed       = false

      + forward {
          + private_port       = 22
          + protocol           = "tcp"
          + public_port        = 22
          + uuid               = (known after apply)
          + virtual_machine_id = (known after apply)
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + public_ipaddress = (known after apply)

qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

(Plan: 4 to add, 0 to change, 0 to destroy.と表示され、四つの項目(上から、ファイアウォール設定、仮想マシン作成、パブリックIPアドレス取得、ポートフォワーディング設定)が実行されることを事前に確認できます。)

③事前確認した内容を実行する
# 実行
[ayamate@terraform]$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudstack_firewall.myip will be created
  + resource "cloudstack_firewall" "myip" {
      + id            = (known after apply)
      + ip_address_id = (known after apply)
      + managed       = false
      + parallelism   = 2

      + rule {
          + cidr_list = [
              + "***.***.***.***/32",
            ]
          + icmp_code = (known after apply)
          + icmp_type = (known after apply)
          + ports     = [
              + "22",
            ]
          + protocol  = "tcp"
          + uuids     = (known after apply)
        }
    }

  # cloudstack_instance.vm will be created
  + resource "cloudstack_instance" "vm" {
      + display_name     = (known after apply)
      + expunge          = false
      + group            = (known after apply)
      + id               = (known after apply)
      + ip_address       = (known after apply)
      + keypair          = "XXXXXXXXXX"
      + name             = "terraform-test"
      + network_id       = "YYYYYYYYYY"
      + project          = (known after apply)
      + root_disk_size   = (known after apply)
      + service_offering = "light.S1"
      + start_vm         = true
      + tags             = (known after apply)
      + template         = "CentOS 7.9 64-bit"
      + zone             = "volt"
    }

  # cloudstack_ipaddress.public_ipaddress will be created
  + resource "cloudstack_ipaddress" "public_ipaddress" {
      + id            = (known after apply)
      + ip_address    = (known after apply)
      + is_portable   = false
      + is_source_nat = (known after apply)
      + network_id    = "XXXXXXXXXX"
      + project       = (known after apply)
      + tags          = (known after apply)
      + zone          = "volt"
    }

  # cloudstack_port_forward.pf_ssh will be created
  + resource "cloudstack_port_forward" "pf_ssh" {
      + id            = (known after apply)
      + ip_address_id = (known after apply)
      + managed       = false

      + forward {
          + private_port       = 22
          + protocol           = "tcp"
          + public_port        = 22
          + uuid               = (known after apply)
          + virtual_machine_id = (known after apply)
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + public_ipaddress = (known after 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

cloudstack_ipaddress.public_ipaddress: Creating...
cloudstack_instance.vm: Creating...
cloudstack_ipaddress.public_ipaddress: Creation complete after 1s [id=b10ec419-ca98-4f13-9e30-d7f6dce7328d]
cloudstack_firewall.myip: Creating...
cloudstack_instance.vm: Still creating... [10s elapsed]
cloudstack_firewall.myip: Still creating... [10s elapsed]
cloudstack_instance.vm: Still creating... [20s elapsed]
cloudstack_firewall.myip: Still creating... [20s elapsed]
cloudstack_firewall.myip: Creation complete after 23s [id=b10ec419-ca98-4f13-9e30-d7f6dce7328d]
cloudstack_instance.vm: Still creating... [30s elapsed]
cloudstack_instance.vm: Still creating... [40s elapsed]
cloudstack_instance.vm: Still creating... [50s elapsed]
cloudstack_instance.vm: Creation complete after 58s [id=83c120ab-b6ae-4f3b-9f21-bfe363517f13]
cloudstack_port_forward.pf_ssh: Creating...
cloudstack_port_forward.pf_ssh: Creation complete after 8s [id=b10ec419-ca98-4f13-9e30-d7f6dce7328d]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

public_ipaddress = "***.***.***.***"

(Do you want to perform these actions?と表示され、実際に作成するか尋ねられます。
yesと回答すると、Apply complete! Resources:4 added, 0 changed, 0 destroyedと表示され作成が完了したことを確認できます。)

終わりに

AWSの環境をTerraformで構築する情報は多いですが、cloudstackのTerraformプロバイダーを利用した仮想マシンの作成については情報が少なく、プロバイダーを指定するコードの作成に時間がかかりました。
(AWSはhashicorpの公式プロバイダーのため、プロバイダーの指定の仕方が異なります。)
繰り返し利用できるコードですので、皆さまもぜひIaCツールで仮想マシンを作成してみてください!

おまけ

#プロバイダー指定に失敗した際のエラー文例
Error: Failed to query available provider packages

Error: Invalid provider local name

というようなエラー文が出た際はプロバイダーの指定方法を見直してみてください。

▼参考にさせていただいた資料
■2016年時点で同様の検証を行った記事
https://qiita.com/akito1986/items/df84e1015d46f4be7b48 qiita.com

■プロバイダー(CloudStack)のバージョンの確認先
https://registry.terraform.io/providers/cloudstack/cloudstack/latest registry.terraform.io

Copyright © IDC Frontier Inc.