LoginSignup
0
1

More than 5 years have passed since last update.

TerraformのModuleを利用して複数VMを簡単に作成する(CloudStack)

Posted at

やったこと

Terrafformを利用して複数VMの作成。

背景

以前の記事に書かせてもらっているが、検証用のインフラ構築の際にブラウザやら単純なClientツールで設定するのが面倒。
Terraformなら簡単に構築できそうなので今検証中という感じ。

環境

  • OSX: 10.11.1
  • Go: 1.7.3

前提条件

  • IDCFクラウドの以下の情報を取得していること
    • APIKey
    • SecretKey
  • Goのビルド環境があること

手順

Terraformのビルド

以前の記事に記載の方法でforkしたterraformをビルドする。

Terraformの設定

Terraformでは共通化可能な設定をモジュールという単位で保持することができる。
今回は、VMの設定とポートフォワーディングの設定をモジュール化して、通常の設定ファイルから参照するようにした。

以下設定ファイルが置かれているディクレトリから見たときのディクレクトリ構造。

$ tree .
.
├── cloudstack.tf // 設定ファイル
├── terraform.log // log file
├── terraform.tfstatef
├── terraform.tfstate.backup
├── terraform.tfvars
└── vm_instances
    └── terraform_test // モジュールディレクトリ
        └── test.tf

2 directories, 6 files
$

Moduleの作成

以下のように通常の設定ファイルの形式で設定する。

vm_instances/terraform_test/test.tf
variable counter {}
variable public_ipaddress_id {}

resource "cloudstack_instance" "vm" {
  name = "${format("terraform-test-0%s", var.counter)}"
  service_offering = "light.S1"
  network_id = "26a035ad-3802-4622-8657-b0df6e33cb0a"
  template = "CentOS 7.2 64-bit"
  zone = "weber"
  keypair = "aueno"
  group = "terraform-test"
  expunge = true
}

resource "cloudstack_port_forward" "pf_ssh" {
  ip_address_id = "${var.public_ipaddress_id}"
  forward {
    protocol = "tcp"
    private_port = 22
    public_port = "${10022 + var.counter - 1}"
    virtual_machine_id = "${cloudstack_instance.vm.id}"
  }
}

変数定義されているものは、呼び出す際に渡される変数である。
counter はVMの名前に利用しており、public_ipaddress_id はポートフォワーディング設定するときに指定するものである。

設定ファイルの作成

今回は3台のサーバーを構築してみる。以下のような設定となる。
Moduleに渡すべき引数を設定していることがわかると思う。
またここでは先日リリースしたGPUが使えるIDCFクラウド東日本リージョン2に対して設定を行っている。

cloudstack.tf
variable api_key {}
variable secret_key {}
variable zone {}
variable network_id {}
variable myips {
  type = "list"
}

provider "cloudstack" {
  api_url = "https://compute.jp-east-2.idcfcloud.com/client/api"
  api_key = "${var.api_key}"
  secret_key = "${var.secret_key}"
}

module "vm_instance_1" {
  source = "./vm_instances/terraform_test"
  counter = 1
  public_ipaddress_id = "${cloudstack_ipaddress.public_ipaddress.id}"
}

module "vm_instance_2" {
  source = "./vm_instances/terraform_test"
  counter = 2
  public_ipaddress_id = "${cloudstack_ipaddress.public_ipaddress.id}"
}

module "vm_instance_3" {
  source = "./vm_instances/terraform_test"
  counter = 3
  public_ipaddress_id = "${cloudstack_ipaddress.public_ipaddress.id}"
}

resource "cloudstack_ipaddress" "public_ipaddress" {
  network_id = "${var.network_id}"
  zone = "${var.zone}"
}


resource "cloudstack_firewall" "myips" {
  ip_address_id = "${cloudstack_ipaddress.public_ipaddress.id}"

  rule {
    cidr_list = "${var.myips}"
    protocol = "tcp"
    ports = ["1-65535"]
  }
}

output "public_ipaddress" {
  value = "${cloudstack_ipaddress.public_ipaddress.ip_address}"
}

変数ファイルの作成

設定ファイルに変数の定義もすることは出来るが、少し汚くなるかなと思い今回は変数ファイルを作成した。
変数ファイルの内容は以下である。

terraform.tfvars
network_id = "26a035ad-3802-4622-8657-b0df6e33cb0a"
zone = "weber"
myips = [
  "sss.sss.sss.ss1/32",
  "sss.sss.sss.ss2/32",
]

myipsに接続を許可させたいcidrを指定する。

大元の設定ファイルの同一ディレクトリに terraform.tfvars というファイルで作成すればオプション指定せずに読み込んでくれます。

VM作成

Moduleのインポート

ローカル環境のモジュールの場合もあらかじめ、terraform get でモジュールをインポートする必要がある。

$ terraform get
Get: file:///Users/aueno/workspace/test/vm_instances/terraform_test
Get: file:///Users/aueno/workspace/test/vm_instances/terraform_test
Get: file:///Users/aueno/workspace/test/vm_instances/terraform_test
$ 

事前の状態

VM一覧

before_vm.png

IPアドレス

before_ipaddress.png

Apply実行

さてここまでで、CloudStackを弄る準備ができました。
まずはplanで何をするのか確認しておきます。
APIKEY, SECRET_KEYはご自身のIDCFクラウドのAPIKey, SecretKeyを指定してください。

$ TF_VAR_api_key=$APIKEY TF_VAR_secret_key=$SECRET_KEY terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.


The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ cloudstack_firewall.myips
    ip_address_id:                        "<computed>"
    managed:                              "false"
    parallelism:                          "2"
    rule.#:                               "1"
    rule.2149869111.cidr_list.#:          "1"
    rule.2149869111.cidr_list.1098773020: "zzz.zzz.zzz.zz/32"
    rule.2149869111.icmp_code:            "<computed>"
    rule.2149869111.icmp_type:            "<computed>"
    rule.2149869111.ports.#:              "1"
    rule.2149869111.ports.1101440173:     "1-65535"
    rule.2149869111.protocol:             "tcp"
    rule.2149869111.uuids.%:              "<computed>"

+ cloudstack_ipaddress.public_ipaddress
    ip_address: "<computed>"
    network_id: "26a035ad-3802-4622-8657-b0df6e33cb0a"
    project:    "<computed>"
    zone:       "weber"

+ module.vm_instance_1.cloudstack_instance.vm
    display_name:     "<computed>"
    expunge:          "true"
    group:            "terraform-test"
    ip_address:       "<computed>"
    keypair:          "aueno"
    name:             "terraform-test-01"
    network_id:       "26a035ad-3802-4622-8657-b0df6e33cb0a"
    project:          "<computed>"
    service_offering: "light.S1"
    template:         "CentOS 7.2 64-bit"
    zone:             "weber"

+ module.vm_instance_1.cloudstack_port_forward.pf_ssh
    forward.#:                              "1"
    forward.~3923475130.private_port:       "22"
    forward.~3923475130.protocol:           "tcp"
    forward.~3923475130.public_port:        "10022"
    forward.~3923475130.uuid:               "<computed>"
    forward.~3923475130.virtual_machine_id: "<computed>"
    ip_address_id:                          "<computed>"
    managed:                                "false"

+ module.vm_instance_2.cloudstack_instance.vm
    display_name:     "<computed>"
    expunge:          "true"
    group:            "terraform-test"
    ip_address:       "<computed>"
    keypair:          "aueno"
    name:             "terraform-test-02"
    network_id:       "26a035ad-3802-4622-8657-b0df6e33cb0a"
    project:          "<computed>"
    service_offering: "light.S1"
    template:         "CentOS 7.2 64-bit"
    zone:             "weber"

+ module.vm_instance_2.cloudstack_port_forward.pf_ssh
    forward.#:                              "1"
    forward.~1844077387.private_port:       "22"
    forward.~1844077387.protocol:           "tcp"
    forward.~1844077387.public_port:        "10023"
    forward.~1844077387.uuid:               "<computed>"
    forward.~1844077387.virtual_machine_id: "<computed>"
    ip_address_id:                          "<computed>"
    managed:                                "false"

+ module.vm_instance_3.cloudstack_instance.vm
    display_name:     "<computed>"
    expunge:          "true"
    group:            "terraform-test"
    ip_address:       "<computed>"
    keypair:          "aueno"
    name:             "terraform-test-03"
    network_id:       "26a035ad-3802-4622-8657-b0df6e33cb0a"
    project:          "<computed>"
    service_offering: "light.S1"
    template:         "CentOS 7.2 64-bit"
    zone:             "weber"

+ module.vm_instance_3.cloudstack_port_forward.pf_ssh
    forward.#:                              "1"
    forward.~1201611294.private_port:       "22"
    forward.~1201611294.protocol:           "tcp"
    forward.~1201611294.public_port:        "10024"
    forward.~1201611294.uuid:               "<computed>"
    forward.~1201611294.virtual_machine_id: "<computed>"
    ip_address_id:                          "<computed>"
    managed:                                "false"


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

8つのリソースが追加されることがわかると思います。
以下でApply実行。

  $ TF_VAR_api_key=$APIKEY TF_VAR_secret_key=$SECRET_KEY terraform apply
cloudstack_ipaddress.public_ipaddress: Creating...
  ip_address: "" => "<computed>"
  network_id: "" => "26a035ad-3802-4622-8657-b0df6e33cb0a"
  project:    "" => "<computed>"
  zone:       "" => "weber"
module.vm_instance_2.cloudstack_instance.vm: Creating...
  display_name:     "" => "<computed>"
  expunge:          "" => "true"
  group:            "" => "terraform-test"
  ip_address:       "" => "<computed>"
  keypair:          "" => "aueno"
  name:             "" => "terraform-test-02"
  network_id:       "" => "26a035ad-3802-4622-8657-b0df6e33cb0a"
  project:          "" => "<computed>"
  service_offering: "" => "light.S1"
  template:         "" => "CentOS 7.2 64-bit"
  zone:             "" => "weber"
module.vm_instance_1.cloudstack_instance.vm: Creating...
  display_name:     "" => "<computed>"
  expunge:          "" => "true"
  group:            "" => "terraform-test"
  ip_address:       "" => "<computed>"
  keypair:          "" => "aueno"
  name:             "" => "terraform-test-01"
  network_id:       "" => "26a035ad-3802-4622-8657-b0df6e33cb0a"
  project:          "" => "<computed>"
  service_offering: "" => "light.S1"
  template:         "" => "CentOS 7.2 64-bit"
  zone:             "" => "weber"
module.vm_instance_3.cloudstack_instance.vm: Creating...
  display_name:     "" => "<computed>"
  expunge:          "" => "true"
  group:            "" => "terraform-test"
  ip_address:       "" => "<computed>"
  keypair:          "" => "aueno"
  name:             "" => "terraform-test-03"
  network_id:       "" => "26a035ad-3802-4622-8657-b0df6e33cb0a"
  project:          "" => "<computed>"
  service_offering: "" => "light.S1"
  template:         "" => "CentOS 7.2 64-bit"
  zone:             "" => "weber"
cloudstack_ipaddress.public_ipaddress: Creation complete
cloudstack_firewall.myips: Creating...
  ip_address_id:                        "" => "9692c7c9-f3ca-445a-b375-3a1ba488d886"
  managed:                              "" => "false"
  parallelism:                          "" => "2"
  rule.#:                               "" => "1"
  rule.2149869111.cidr_list.#:          "" => "1"
  rule.2149869111.cidr_list.1098773020: "" => "sss.sss.sss.sss/32"
  rule.2149869111.icmp_code:            "" => "<computed>"
  rule.2149869111.icmp_type:            "" => "<computed>"
  rule.2149869111.ports.#:              "" => "1"
  rule.2149869111.ports.1101440173:     "" => "1-65535"
  rule.2149869111.protocol:             "" => "tcp"
  rule.2149869111.uuids.%:              "" => "<computed>"
cloudstack_firewall.myips: Creation complete
module.vm_instance_1.cloudstack_instance.vm: Still creating... (10s elapsed)
module.vm_instance_2.cloudstack_instance.vm: Still creating... (10s elapsed)
module.vm_instance_3.cloudstack_instance.vm: Still creating... (10s elapsed)
module.vm_instance_2.cloudstack_instance.vm: Still creating... (20s elapsed)
module.vm_instance_1.cloudstack_instance.vm: Still creating... (20s elapsed)
module.vm_instance_3.cloudstack_instance.vm: Still creating... (20s elapsed)
module.vm_instance_3.cloudstack_instance.vm: Creation complete
module.vm_instance_3.cloudstack_port_forward.pf_ssh: Creating...
  forward.#:                             "" => "1"
  forward.1057937353.private_port:       "" => "22"
  forward.1057937353.protocol:           "" => "tcp"
  forward.1057937353.public_port:        "" => "10024"
  forward.1057937353.uuid:               "" => "<computed>"
  forward.1057937353.virtual_machine_id: "" => "597c88c6-9bcf-44bf-8166-5412af937abb"
  ip_address_id:                         "" => "9692c7c9-f3ca-445a-b375-3a1ba488d886"
  managed:                               "" => "false"
module.vm_instance_1.cloudstack_instance.vm: Creation complete
module.vm_instance_1.cloudstack_port_forward.pf_ssh: Creating...
  forward.#:                             "" => "1"
  forward.3480124144.private_port:       "" => "22"
  forward.3480124144.protocol:           "" => "tcp"
  forward.3480124144.public_port:        "" => "10022"
  forward.3480124144.uuid:               "" => "<computed>"
  forward.3480124144.virtual_machine_id: "" => "b1e8ad78-9e3c-4798-9b3d-672cf3eb4363"
  ip_address_id:                         "" => "9692c7c9-f3ca-445a-b375-3a1ba488d886"
  managed:                               "" => "false"
module.vm_instance_2.cloudstack_instance.vm: Creation complete
module.vm_instance_2.cloudstack_port_forward.pf_ssh: Creating...
  forward.#:                             "" => "1"
  forward.1508310808.private_port:       "" => "22"
  forward.1508310808.protocol:           "" => "tcp"
  forward.1508310808.public_port:        "" => "10023"
  forward.1508310808.uuid:               "" => "<computed>"
  forward.1508310808.virtual_machine_id: "" => "5a7858c1-f84a-47b0-837f-4639530f6ef9"
  ip_address_id:                         "" => "9692c7c9-f3ca-445a-b375-3a1ba488d886"
  managed:                               "" => "false"
module.vm_instance_2.cloudstack_port_forward.pf_ssh: Creation complete
module.vm_instance_3.cloudstack_port_forward.pf_ssh: Creation complete
module.vm_instance_1.cloudstack_port_forward.pf_ssh: Creation complete

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

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

Outputs:

public_ipaddress = zzz.zzz.zzz.zzz
$

並列でVM作成などが行なわれていることがわかると思います。

Apply後の状態

VM一覧

見事にVMが作成されている!
画面で見るとやった感が出ます。
after_vm.png

IPアドレス

新しいPublicIPアドレスが取得されて、Firewallやポートフォワーディングが設定されていることがわかります。
after_ipaddress.png

おわりに

Terraformは状態を管理するものなのでそこが良いと思うし、GO製のツールなのでタスクが普通に並列化されるところが良いと思う。
ただIDCFクラウドで名前を管理するために利用しているTagの設定ができたらもっと良いかなと思うところ。
自分で実装しろよ、という話だけでも:smile:

0
1
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
0
1