[MaaStep]Terraformを用いたVMのセットアップ
2019-06-12
azblob://2022/11/11/eyecatch/2019-06-12-maastep-terraform-azure-vm-000.jpg

はじめに

Azureを用いてMaaSに関するアプリの作成をしているときに作ったもの・触ったものをMaaSの歩み、MaaS+Step=「MaaStep」としてタグをつけて紹介していきます。
今回はOpenStreetMapサーバーを立てることを目標に、まずはTerraformを使ってAzure VMをセットアップしました。

実行環境

リンクはインストール方法へのリンクです。

1. TerraformでLinux仮想マシンの作成

1.0 目次

  • 必要な文字列の取得[コマンドプロンプト]
  • Terraformファイルの作成と適用[コマンドプロンプト]
  • VMへのSSH接続[コマンドプロンプト]
  • VMへのHTTP接続[Azure Portal]

1.1 必要な文字列の取得[コマンドプロンプト]

1.1.1 公開鍵

(1)コマンドプロンプトで公開鍵を作成

以下のコマンドで~/.ssh/id_rsa公開鍵と秘密鍵を生成します。

ssh-keygen -t rsa -b 2048
(2)公開鍵のコピー

公開鍵はVMにログインするための管理者アカウントを作成するときに用います。
そのため、以下のコマンドで公開鍵をコンソールに表示し、コピーしておきます。

cd ~/.ssh
type id_rsa.pub

1.1.2 サービスプリンシパル(オプション)

以下のコマンドでログインしてからTerraformの実行をする場合は不要なようです。

az login

ログインしない場合はAzure CLI でAzureサービス プリンシパルを作成するを参考にサービスプリンシパルを作成してください。
サービスプリンシパルが作成できない場合は、Azureサブスクリプションのアクセス許可を確認するでアカウントが共同作成者ロールに割り当てられていないか調べてください。
サービスプリンシパルは以下のようなものです。

{
  "appId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "displayName": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "password": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "tenant": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

1.1.3 Azureサブスクリプション(オプション)

上記のようにログインしない場合は以下のコマンドでAzureサブスクリプションIDとテナントIDを取得します。

az account show --query "{subscriptionId:id, tenantId:tenantId}"

このコマンドによって以下のように表示されます。

{
  "subscriptionId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "tenantId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

1.2 Terraformファイルの作成と適用[コマンドプロンプト]

TerraformによってAzureに完全なLinux仮想マシンのインフラストラクチャを作成するを参考に先ほど取得した文字列を使って、tfファイルを作成していきます。
まだまだTerraformについては勉強中で、ぼんやりとした理解ですが、大体以下のような構文になっていそうです。

# プロバイダーの設定:プロバイダーに接続するための設定などを書く
## az loginしない場合は以下を記述
### subscription_id:サブスクリプションID
### client_id:サービスプリンシパルのappId
### client_secret:サービスプリンシパルのpassword
### tenant_id:tenantId
provider "プロバイダー名" {
}

# リソースの作成:中身は作成するリソースによって必須なものとオプションが変わる
resource "作成するリソースの種類" "Terraform内での名前" {
    name     = "プロバイダーで実際に作成されるリソースの名前"
    ・・・
}

# リソースの定義で、すでに上で定義されたリソースの「プロバイダーで実際に作成されるリソースの名前」などを参照できる
## ${作成するリソースの種類.Terraform内での名前.name}でプロバイダーで実際に作成されるリソースの名前を参照
## 例
resource "azurerm_resource_group" "resourcegroup" {
    name     = "myResourceGroup"
    ・・・
}
resource "azurerm_virtual_network" "terraformnetwork" {
    ・・・
    resource_group_name = "${azurerm_resource_group.resourcegroup.name}"
    ・・・
}

今回記述したtfファイルは以下です。

# Azureプロバイダーの設定

provider "azurerm" {
}
# Azure上のリソースグループの作成
resource "azurerm_resource_group" "terraformgroup" {
  name     = "osmgroup"
  location = "japaneast"

  tags = {
    environment = "OSM"
  }
}

# 仮想ネットワークの作成
resource "azurerm_virtual_network" "terraformnetwork" {
  name                = "osmVnet"
  address_space       = ["10.0.0.0/16"]
  location            = "japaneast"
  resource_group_name = "${azurerm_resource_group.terraformgroup.name}"

  tags = {
    environment = "OSM"
  }
}
## 仮想ネットワークにサブネットを作成
resource "azurerm_subnet" "terraformsubnet" {
  name                 = "osmSubnet"
  resource_group_name  = "${azurerm_resource_group.terraformgroup.name}"
  virtual_network_name = "${azurerm_virtual_network.terraformnetwork.name}"
  address_prefix       = "10.0.2.0/24"
}

# パブリックIPアドレスの作成
resource "azurerm_public_ip" "terraformpublicip" {
  name                = "osmPublicIP"
  location            = "japaneast"
  resource_group_name = "${azurerm_resource_group.terraformgroup.name}"
  allocation_method   = "Dynamic"

  tags = {
    environment = "OSM"
  }
}

# ネットワーク セキュリティ グループの作成
resource "azurerm_network_security_group" "terraformnsg" {
  name                = "osmSecurityGroup"
  location            = "japaneast"
  resource_group_name = "${azurerm_resource_group.terraformgroup.name}"

  security_rule {
    name                       = "SSH"
    priority                   = 1001
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  tags = {
    environment = "OSM"
  }
}

# 仮想ネットワーク インターフェイス カードの作成
resource "azurerm_network_interface" "terraformnic" {
  name                      = "osmNIC"
  location                  = "japaneast"
  resource_group_name       = "${azurerm_resource_group.terraformgroup.name}"
  network_security_group_id = "${azurerm_network_security_group.terraformnsg.id}"

  ip_configuration {
    name                          = "osmNicConfiguration"
    subnet_id                     = "${azurerm_subnet.terraformsubnet.id}"
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = "${azurerm_public_ip.terraformpublicip.id}"
  }

  tags = {
    environment = "OSM"
  }
}

# ストレージアカウントの作成
## 一意なストレージアカウント名の生成
resource "random_id" "randomId" {
  keepers = {
    # Generate a new ID only when a new resource group is defined
    resource_group = "${azurerm_resource_group.terraformgroup.name}"
  }

  byte_length = 8
}
## ストレージアカウントの作成
resource "azurerm_storage_account" "storageaccount" {
  name                     = "diag${random_id.randomId.hex}"
  resource_group_name      = "${azurerm_resource_group.terraformgroup.name}"
  location                 = "japaneast"
  account_replication_type = "LRS"
  account_tier             = "Standard"

  tags = {
    environment = "OSM"
  }
}

# 仮想マシンの作成
resource "azurerm_virtual_machine" "terraformvm" {
  name                  = "osmVM"
  location              = "japaneast"
  resource_group_name   = "${azurerm_resource_group.terraformgroup.name}"
  network_interface_ids = ["${azurerm_network_interface.terraformnic.id}"]
  vm_size               = "Standard_B2s"

  storage_os_disk {
    name              = "myOsDisk"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }

  storage_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "16.04.0-LTS"
    version   = "latest"
  }

  os_profile {
    computer_name  = "myvm"
    admin_username = "azureuser"
  }

  os_profile_linux_config {
    disable_password_authentication = true
    ssh_keys {
      path     = "/home/azureuser/.ssh/authorized_keys"
      key_data = <ssh公開鍵>
    }
  }

  boot_diagnostics {
    enabled     = "true"
    storage_uri = "${azurerm_storage_account.storageaccount.primary_blob_endpoint}"
  }

  tags = {
    environment = "OSM"
  }
}

tfファイルのあるディレクトリに移動して、Terraformを適応していきます。

(1)Terraformの初期化

terraform init

実行すると、このような画面が表示されます。

>terraform init
Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "random" (terraform-providers/random) 2.1.2...
- Downloading plugin for provider "azurerm" (terraform-providers/azurerm) 1.30.1...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.azurerm: version = "~> 1.30"
* provider.random: version = "~> 2.1"

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.

(2)テンプレートの検証

次にTerraformでtfファイルで作成したテンプレートを検証させます。
構文などにミスがある場合、ここでエラーがでます。

terraform plan

検証が成功すると、以下の画面が表示されます。

・・・
  # random_id.randomId will be created
  + resource "random_id" "randomId" {
      + b64         = (known after apply)
      + b64_std     = (known after apply)
      + b64_url     = (known after apply)
      + byte_length = 8
      + dec         = (known after apply)
      + hex         = (known after apply)
      + id          = (known after apply)
      + keepers     = {
          + "resource_group" = "osmgroup"
        }
    }

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

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

(3)テンプレートの適用

最後に以下のコマンドでTerraformでテンプレートを適用させます。

terraform apply

大体5分くらいかかって、以下のように表示されます。

・・・
azurerm_virtual_machine.terraformvm: Still creating... [3m0s elapsed]
azurerm_virtual_machine.terraformvm: Still creating... [3m10s elapsed]
azurerm_virtual_machine.terraformvm: Still creating... [3m20s elapsed]
azurerm_virtual_machine.terraformvm: Still creating... [3m30s elapsed]
azurerm_virtual_machine.terraformvm: Creation complete after 3m40s [id=/subscriptions/b9d7332c-ab99-4096-b6e6-04b1b905736c/resourceGroups/osmgroup/providers/Microsoft.Compute/virtualMachines/osmVM]

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

1.3 VMへのSSH接続[コマンドプロンプト]

以下のコマンドでVMのパブリックIPアドレスを取得します。

az vm show --resource-group osmgroup --name osmVM -d --query [publicIps] --o tsv

そして、そのパブリックIPアドレスを用いて、VMにSSH接続できます。

ssh azureuser@xx.xxx.xxx.xxx

接続すると、以下のような画面になります。

1.4 VMへのHTTP接続[Azure Portal]

(1) Azure Portalで作成したリソースグループを開く

(2) ネットワークを開く

(3) 受信セキュリティ規則の追加で宛先ポート範囲を「80」で追加

(4) 同様に宛先ポート範囲を「8080」で追加

おわりに

以上で、Terraformを使ってAzure VMをセットアップすることができました。 次回はOpenStreetMapのセットアップをしていきます。