浅草橋青空市場

Microsoft Azure のニュースや情報を中心にあれこれと

Azure CLI 2.0 で仮想マシンのイメージを作る & イメージから仮想マシンを作る

少し手を加えた仮想マシンをイメージ化して展開、みたいなことをAzure CLI 2.0で行う手順です。

CLIによる仮想マシンの作り方はこちらからどうぞ。

asazure.hatenablog.jp

例によってAzure Cloud Shellを使います。

docs.microsoft.com

サブスクリプションの選択

(2017/08/01 追記)
複数のサブスクリプションを扱っている場合には明示的に指定した方が間違いが無いですね。

# 利用可能なサブスクリプションの一覧を見る
az account list -o table

# サブスクリプションを選択する
az account set --subscription [Subscription Id]

(追記ここまで)

仮想マシンのイメージを作る

対象のホストにログインしてdeprovisionを行います。 イメージ化(=汎用化)の前準備で、以下の設定が消えますのでご留意のほど。

  • 全てのSSHホストキー
  • /etc/resolv.conf 内のネームサーバー構成
  • /etc/shadow のrootパスワード
  • キャッシュされたDHCPクライアントのリース
  • ホスト名を localhost.localdomain にリセット

waagentのdeprovisioningオプションについて詳しくはこちらから。

docs.microsoft.com

sudo waagent -deprovision+user -force

この後はすべてAzure CLI上での操作です。
汎用化した後は起動できなくなりますのでご注意を。起動できなくなると困る場合は、先に仮想マシンを複製しましょう(末尾に参考URL記載)。

# 仮想マシンの停止
az vm deallocate --resource-group myRg --name myVm

# 仮想マシンの汎用化
az vm generalize --resource-group myRg --name myVm

# イメージの生成
az image create --resource-group myRg --name myImage --source myVm

イメージが生成できたら、元の仮想マシンは削除しても構いません。

イメージから仮想マシンを作る

続いてイメージから仮想マシンを作ります。 NICと、それに紐付く諸々はあらかじめ用意されている前提です。諸々の生成についてはこちらをどうぞ。 http://asazure.hatenablog.jp/entry/2017/07/31/213107

セキュリティグループとPublic IP、VNETはNICの側で指定しますので、仮想マシン生成時には指定できません。

# 仮想マシンの生成 (パスワード認証)
az vm create --name myVm --resource-group myRg --location japaneast --image myImage --size Standard_DS1 --nics myNic --admin-username myAdminName--admin-password myAdminPassword

# 仮想マシンの生成 (公開鍵認証)
az vm create --name myVm --resource-group myRg --location japaneast --image myImage --size Standard_DS1 --nics myNic --admin-username myAdminName--ssh-key-value ~/.ssh/id_rsa.pub

(参考) 汎用化しない仮想マシンの複製

そのままコピーする場合はこちらをどうぞ。Managed Disksを複製して、NIC等のリソースは新しく用意して作る手順です。Managed Disksのおかげでだいぶシンプルになりましたね。

docs.microsoft.com

Azure CLI 2.0 から手作業でVMを作る

ググると古い情報がヒットしたりしていまいち探しづらい気がしたのでまとめてみました。
イメージの元にするような用途を想定して、パブリックIPアドレスが1つだけ付いてsshができるLinux仮想マシンを立ち上げるところまで。ポータル上から使える Azure Cloud Shell から Azure CLI 2.0 を使います。

Azure Cloud Shell の使い方はこちらから。

docs.microsoft.com

ワンライナーで簡単に作るにはこちらの手順をどうぞ。

docs.microsoft.com

テンプレ用であればこのワンライナーでも事足りるのですが、今回は動作を理解することも兼ねているのと「名前を付けられるものには付けておきたいよね」ということで、暖かみを感じられる手組でやってみましょう。

サブスクリプションの選択

(2017/08/01 追記)
複数のサブスクリプションを扱っている場合には明示的に指定した方が間違いが無いですね。

# 利用可能なサブスクリプションの一覧を見る
az account list -o table

# サブスクリプションを選択する
az account set --subscription [Subscription Id]

(追記ここまで)

リソースグループの作成

az group create --name [リソースグループ名] --location [ロケーション]

既存のリソースグループを使う場合には不要です。
ロケーションの指定は、特に何も無ければjapaneastかjapanwestあたりで構わないかと思います。ロケーション一覧の取得方法は末尾に。
リソースグループのロケーションと中に入れるリソースのロケーションを別にすることもできますが、見た目としても分かりづらいですし、JSONのテンプレートからデプロイするようなときには「リソースグループと同じ場所」という指定をすることも多いので、できるだけ揃えた方が良いと思います。

仮想ネットワークの作成

シンプルにサブネットが1つだけある仮想ネットワークを作ります。

az network vnet create --name [仮想ネットワーク名] --resource-group [リソースグループ名] --location [ロケーション] --address-prefix [仮想ネットワークのアドレス帯/マスク] --subnet-name [サブネット名] --subnet-prefix [サブネットのアドレス帯/マスク]

()
az network vnet create --name myVnet --resource-group myRg --location japaneast --address-prefix 192.168.0.0/16 --subnet-name subnet1 --subnet-prefix 192.168.1.0/24

パブリックIPアドレスの作成

クラシック仮想マシンの時代はIPアドレスを確保する方法や割り当て状況によって料金が違っていたのですが、いまは確保する毎に一律で料金がかかるようなので、オプションはお好みで指定して大丈夫です。

az network public-ip create --name [IPアドレス名] --resource-group [リソースグループ名] --location [ロケーション] --allocation-method [割り当て方法(動的/静的)] --dns-name [DNS名]

()
az network public-ip create --name myPip --resource-group myRg --location japaneast --allocation-method static --dns-name myVm1

セキュリティグループとルールの作成

1行目でセキュリティグループを作成して、2行目でルールを設定しています。
例ではssh用にインバウンドの22番ポートを開放しています。

az network nsg create --name [セキュリティグループ名] --resource-group [リソースグループ名] --location [ロケーション]
az network nsg rule create --name [ルール名] --nsg-name [セキュリティグループ名] --resource-group [リソースグループ名] --access [Allow/Deny] --protocol [Tcp/Udp] --direction [Inbound/Outbound] --priority 1000 --source-address-prefix [Internet/発信元アドレス] --source-port-range ["*"/発信元ポート] --destination-address-prefix ["*"/宛先アドレス] --destination-port-range ["*"/宛先ポート]

()
az network nsg create --name myNsg --resource-group myRg --location japaneast
az network nsg rule create --name ssh --nsg-name myNsg --resource-group myRg --access allow --protocol Tcp --direction Inbound --priority 1000 --source-address-prefix Internet --source-port-range "*" --destination-address-prefix "*" --destination-port-range 22

NICの作成と仮想ネットワークへの接続、セキュリティグループやIPアドレスの割り当て

今まで作ったものをまとめてくっつけながらNICを生成します。
プライベートIPアドレスは、オプションを指定しなければ動的になります。ここも費用には関係しませんのでお好みで(どちらも無料)。 本番用だと、LBへの接続もここで行います。

az network nic create --name [NIC名] --resource-group [リソースグループ名] --vnet-name [NICを接続する] --subnet [サブネット名] --network-security-group [セキュリティグループ名] --public-ip-address [IPアドレス名] --private-ip-address [プライベートIPアドレス(固定する場合)]

()
az network nic create --name myNic --resource-group myRg --vnet-name myVnet --subnet subnet1 --network-security-group myNsg --public-ip-address myPip --private-ip-address 192.168.1.5

仮想マシンの作成

ここまでに作ったリソースを元にして仮想マシンを作成します。
この例ではAzure側で用意されているイメージから作っています。イメージ名には自作イメージも指定できます。
デフォルトで Managed Disks で作られます。オプションで明示的に指定するとUnmanaged Disksで作れますが、Managed Disksに流行って欲しいのでそちらは割愛。

az vm create --name [仮想マシン名] --resource-group [リソースグループ名] --location [ロケーション] --image [イメージ名] --size [仮想マシンのサイズ] --nics [NIC名] --admin-username [OSユーザー名] --admin-password [OSパスワード]

az vm create --name myVm1 --resource-group myRg --location japaneast --image UbuntuLTS --size Standard_DS1 --nics myNic --admin-username adminuser --admin-password admin.password

あとは完成までしばらくお待ち下さい。Linuxであれば1~2分程度でデプロイ完了です。
パスワードでは無く公開鍵認証を行う場合は「–admin-password」の代わりに「–ssh-key-value ~/.ssh/id_rsa.pub」のように公開鍵を指定して下さい。

おまけ: 各種一覧の取得方法

仮想マシンサイズの一覧はこんな感じで取得出来ます。ロケーションの指定は必須です。

az vm list-sizes --location japaneast -o table

使えるイメージの一覧はこんな感じで取得出来ます。

# ざっくり新しめのイメージを表示
az vm image list -o table

# 利用可能な全てのイメージを表示 ※非常に時間がかかります
az vm image list --all -o table

# パブリッシャーがマイクロソフトの東日本にあるUbuntuのイメージ全て
az vm image list --offer Ubuntu --all --location japaneast --publisher Microsoft -o table

使えるロケーション一覧の取得はこちらから。カレントのサブスクリプションで利用できるロケーションの一覧が表示されます。

az account list-locations -o table

今回は割愛しますが、オリジナルイメージの作り方はこちら。 https://docs.microsoft.com/ja-jp/azure/virtual-machines/linux/capture-image

Azure Container Instances が登場しましたね

急に登場したような気がするのですが「Azure Container Instances」のパブリックプレビューが始まっていました。 ホストVM無しでいきなりコンテナが動かせるというもの。また楽しそうなサービスが増えてしまいましたね。

azure.microsoft.com

既にいくつも日本語の記事が投稿されて、盛り上がってますね。

Azure Container Instances | ブチザッキ

Azure Container Instances は Hypervisor レベルでの分離を備えた次世代のコンテナ実行環境 - しばやん雑記

Azure Container Instances が出た! - 世界のやまさ

何番煎じか分かりませんが、自分用のメモも兼ねて記録しておきます。

対応リージョンは West US, East, US, West Europe の3つ。東南アジアもまだのようですね。

料金体系はこちら。 作成リクエスト毎に料金がかかるほか、メモリはGB/秒で、CPUはコア/秒で料金が発生します。メモリとCPUコア数はコンテナ作成時に指定できます。 1コア/1.5GBメモリで10,479円/月なので、立ち上げっぱなしだとVMよりだいぶ高いですね。

Pricing - Container Instances | Microsoft Azure

基本的なチュートリアルはこちらから。

Create your first Azure Container Instances container | Microsoft Docs

チュートリアルにある「aci-helloworld」程度だと数秒で起動してきます。リソースプロバイダーは「Microsoft.ContainerInstance」でした。IPアドレスも内包しており、単独のリソースとしては作られません。

オーケストレーターは含まれていません。 同時に公開された「Azure Container Instances Connector for Kubernetes」を使うと、既存のK8sクラスタのノードとしてACIを使うことができます。

いい感じにプリミティブで扱いやすそうなサービスが出てきましたね。今後が楽しみです。

Azure仮想マシンのコストパフォーマンス(Ev3, Dv3を含め)

こちらの記事の続きっぽいもの。 asazure.hatenablog.jp

Ev3、Dv3シリーズが出ましたので、カタログベースで簡単にコスパをまとめてみました。
まだ日本には来ていませんので、東南アジア(Southeast Asia)の金額です。 身の回りで使用頻度の高い、4コアのLinuxインスタンスで比較しています。

月額料金の安い順

f:id:yhara90:20170725230836p:plain

メモリ単価の安い順

f:id:yhara90:20170725230839p:plain

並べてみると、面白みはありませんがAzureの料金表ページに書いてある通りの結果になりました。
CPU性能が必要な場合は「コンピューティングの最適化」のFシリーズのお得感が際立ちます。Hyper Threadingが有効なDv3やEv3より割安なのは意外ですね。

メモリ容量が必要であれば「メモリの最適化」のEv3シリーズでしょうか。Nested Virtualization を活かしてDockerコンテナをたくさん載せたりするのに良いですかね。
DBサーバーに使う場合は、CPUやストレージ性能の高いLシリーズも良さそうです。

Markdownのテーブルだと横にはみ出して見づらかったので画像で貼りました。
テキスト版が必要な方はこちらからどうぞ。

Azure 4コアインスタンスの料金比較 · GitHub

他のサイズで比較したい場合は、そのうちこちらのサイト様が更新されるかと思いますので合わせてどうぞ。

http://azureprice.net/

Azure Ev3シリーズ仮想マシンのストレージベンチマーク

こちらの記事の続き。

asazure.hatenablog.jp

E4s_v3 Cドライブ

f:id:yhara90:20170722000247p:plain

E4s_v3 Dドライブ

f:id:yhara90:20170722000305p:plain

うーん、あんまり早くないですね…価格優先ということですかね。

▼米国東部2での比較

インスタンスサイズ 金額
DS12 v2 44,242.71/月
L4s 41,434.85/月
E4s v3 37,564.56/月

TerraformでAzure仮想マシンを作成する(Managed Disks編)

Terraformを使って、Managed Disksの付いた仮想マシンを作る簡単な手順です。 UnManaged Disksの場合はこちらの記事をどうぞ。

バーチャルマシンを ARM対応の Terraform を使ってプロビジョンしてみる - Qiita

ではさっそく。

Terraformのダウンロード

公式サイトから自分の環境用のバイナリを取得してパスの通った場所に置いて下さい。バイナリ1つで動作するのでセットアップの手間いらずでとても楽ですね。

Download Terraform - Terraform by HashiCorp

サービスプリンシパルの作成

Terraformを実行するためのアカウントのようなものですね。 CLI2.0で作るのが簡単なので、ポータルのAzure Cloud Shell から実行しましょう。 サブスクリプションが1つの環境であれば1行目は無くても大丈夫です。

az account set --subscription "[サブスクリプションID]"
az ad sp create-for-rbac --name "[アプリケーション名]" --role Contributor

2行目を実行したあとのレスポンスが大切です。以下の3項目はあとで使います。特に「password」はあとから参照できませんのでしっかり記録しておきましょう(パスワードリセットは可能です)。

  • appId
  • password
  • tenant

サービスプリンシパルそのものについての説明はこちらへどうぞ。

AzureのサービスプリンシパルをCLI2.0から発行する - 浅草橋青空市場

Terraformの設定ファイルを書く

ここからが本番です。「.tf」という拡張子のファイルにどんどん書いていきます。
サービスプリンシパルやOSアカウントなどの認証情報は「.tfvars」に外出しして .gitignore に入れておくのが良いでしょう。.tfvars のデフォルトは「terraform.tfvars」です。これ以外のファイル名の場合は、terraformコマンド実行時にオプションで明示的に指定する必要があります。

ではまず.tfvarsの書式から。

default_user = "OSユーザー名"
default_password = "OSパスワード"
subscription_id = "AzureのサブスクリプションID"
client_id       = "AzureのサービスプリンシパルのクライアントID(appId)"
client_secret   = "Azureのサービスプリンシパルのパスワード(password)"
tenant_id       = "AzureのサービスプリンシパルのテナントID(tenant)"

続いて.tfのコード例。
Azure側で用意されているUbuntu16.04LTSの最新版から、データディスク付きのVMを作ります。 ストレージはもちろんManaged Disks。
ほぼ公式のサンプルを踏襲していますので、IPアドレスにタグが付いていたりしますのでお好みで。

variable "default_user" {}
variable "default_password" {}
variable "subscription_id" {}
variable "client_id" {}
variable "client_secret" {}
variable "tenant_id" {}

provider "azurerm" {
  subscription_id = "${var.subscription_id}"
  client_id       = "${var.client_id}"
  client_secret   = "${var.client_secret}"
  tenant_id       = "${var.tenant_id}"
}

resource "azurerm_resource_group" "test" {
  name = "harayTerraformDemo"
  location = "Japan East"
}

resource "azurerm_virtual_network" "test" {
  name = "tfvnet1"
  address_space = ["10.0.0.0/16"]
  location = "${azurerm_resource_group.test.location}"
  resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_subnet" "test" {
  name = "tfsubnet1"
  resource_group_name = "${azurerm_resource_group.test.name}"
  virtual_network_name = "${azurerm_virtual_network.test.name}"
  address_prefix = "10.0.2.0/24"
}

resource "azurerm_public_ip" "test" {
    name = "haraytfvm01PIP"
    location = "${azurerm_resource_group.test.location}"
    resource_group_name = "${azurerm_resource_group.test.name}"
    public_ip_address_allocation = "static"
    domain_name_label = "haraytfvm01"
    tags {
        environment = "test"
    }
}


resource "azurerm_network_interface" "test" {
  name = "haraytfvm01nic1"
  location = "${azurerm_resource_group.test.location}"
  resource_group_name = "${azurerm_resource_group.test.name}"

  ip_configuration {
      name = "haraytfvm01ip"
      subnet_id = "${azurerm_subnet.test.id}"
      private_ip_address_allocation = "dynamic"
      public_ip_address_id = "${azurerm_public_ip.test.id}"
  }
}

resource "azurerm_virtual_machine" "test" {
  name                  = "haraytfvm01"
  location              = "${azurerm_resource_group.test.location}"
  resource_group_name   = "${azurerm_resource_group.test.name}"
  network_interface_ids = ["${azurerm_network_interface.test.id}"]
  vm_size               = "Standard_DS1_v2"

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

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

  storage_data_disk {
    name              = "datadisk_new"
    managed_disk_type = "Standard_LRS"
    create_option     = "Empty"
    lun               = 0
    disk_size_gb      = "32"
  }

  os_profile {
    computer_name  = "haraytfvm01"
    admin_username = "${var.default_user}"
    admin_password = "${var.default_password}"
  }

  os_profile_linux_config {
    disable_password_authentication = false
  }

  tags {
    environment = "test"
  }
}

Terraformコマンドを実行する

.tf と .tfvars を同じディレクトリに置いてください。 まず全体像はこんな感じです。

- terraform plan : いわゆるdry-runを実行します。
- terraform apply : 実際プロビジョニングを行います。
- terraform plan -destroy : リソース削除のdry-runです。
- terraform destroy :  リソースを削除します。

plan オプションで確認してから実行、という流れですね。 .tfvars のファイル名が「terraform.tfvars」じゃない場合には、ファイル名を明示的に指定して下さい。

terraform plan -var-file myvmvars.tfvars

Managed Disks の場合、ストレージアカウントを考慮する必要がなくなりますのでより扱いやすくなりますね。

AzureのサービスプリンシパルをAzure CLI2.0から発行する

AzureをTerraformやAnsibleから制御したかったり、Azure Container Services から Kubernetes のクラスをデプロイするときなんかに必要となるのが「サービスプリンシパル」。 言葉が分かりづらいですが、SaaSでよくある「アプリケーションパスワード」みたいなものですね。

以前はこれの発行がわりと面倒だったのですが、Azure CLI 2.0 になって劇的に楽になりました。 Azure Cloud Shell から実行する前提で、ログイン等は省略してあります。

サービスプリンシパルの発行(CLI2.0対応版)

az account set --subscription "[サブスクリプションID]"
az ad sp create-for-rbac --name "[アプリケーション名]" --role Contributor

これだけです。

(追記: 2017/06/02)

「--role」オプションでRBACベースのロールを指定します。
例では、Ansible等を想定してリソースの読み書きなどが可能な「Contributor(=共同作業者)」としてありますが、こちらを参考に適切なロールを指定して下さい。情報の取得のみであれば「Reader」あたりでも間に合いますね。

docs.microsoft.com

(追記以上)

こんな感じのレスポンスが返ってきます。パスワードはあとから照会できませんので控えておきましょう。 パスワード以外の項目は、ポータルの「Azure Active Directory → アプリの登録」から確認できます。

{
    "appId": "[アプリケーションID]",
    "displayName": "[表示名]",
    "name": "[アプリケーションID/URI]",
    "password": "[パスワード]",
    "tenant": "[テナントID]"
}

発行したサービスプリンシパルCLIからログインしてみる

az login --service-principal --username "[アプリケーションID/URI]" --password "[パスワード]" --tenant "[テナントID]"

公式のドキュメントはこちらからどうぞ。

docs.microsoft.com

(追記: 2017/06/01)

パスワードが分からなくなった場合は、ポータルからキーを新たに発行すれば良さそうです。 手順はこのあたりから。

docs.microsoft.com

複数のサブスクリプションを同じディレクトリ上で運用している場合、カレントのサブスクリプションにIAM(RBAC)権限が付与されるようです。思ったのと違うところに権限が付いた場合は、ポータル等から変更しておきましょう。

そもそもサービスプリンシパルって何?という説明はこちらの公式ドキュメントをどうぞ。

docs.microsoft.com

(追記: 2017/10/26)

Terraform が Azure CLI認証に対応したそうで、そもそもサービスプリンシパル無しで走らせられるようになりました。

TerraformをAzure CLI認証で使う