22 Dec 2018, 20:00

GitHub ActionsでAzure CLIとkubectlを動かす

GitHub Actionsのプレビュー招待がきた

ので試します。プレビュー中なので細かいことは抜きに、ざっくりどんなことができるか。

GitHub Actions

数時間、触った印象。

  • GitHubへのPushなどイベントをトリガーにWorkflowを流せる
  • シンプルなWorkflow記法 (TerraformのHCLに似ている)
  • Workflowから呼び出すActionはDockerコンテナー
  • Dockerコンテナーをビルドしておかなくてもいい (Dockerfileをリポジトリに置けば実行時にビルドされる)

Dockerに慣れていて、ちょっとしたタスクの自動化を、GitHubで完結したい人に良さそうです。

Azure CLI/Kubernetes(AKS) kubectlサンプル

こんなことを試してみました。

  • KubernetesのマニフェストをGitHubリポジトリへPush
  • PushイベントをトリガーにWorkflowを起動
  • Azure CLIを使ってAKSクラスターのCredentialを取得
  • イベント発生元がmasterブランチであれば継続
  • kubectl applyでマニフェストを適用

kubectlを制限したい、証明書を配るのめんどくさい、なのでGitHubにPushされたらActionsでデプロイ、ってシナリオです。がっつり使うにはまだ検証足らずですが、ひとまずできることは確認しました。

コードは ここ に。

ディレクトリ構造は、こうです。

.
├── .git
│   └── (省略)
├── .github
│   └── main.workflow
├── LICENSE
├── README.md
├── azure-cli
│   ├── Dockerfile
│   └── entrypoint.sh
└── sampleapp.yaml
  • .github の下にWorkflowを書きます
  • azure-cli の下に自作Actionを置きました
  • sampleapp.yaml がkubernetesのマニフェストです

Workflow

まず、 .github/main.workflow を見てみましょう

workflow "Deploy app to AKS" {
  on = "push"
  resolves = ["Deploy to AKS"]
}

action "Load AKS credential" {
  uses = "./azure-cli/"
  secrets = ["AZURE_SERVICE_APP_ID", "AZURE_SERVICE_PASSWORD", "AZURE_SERVICE_TENANT"]
  args = "aks get-credentials -g $AKS_RG_NAME -n $AKS_CLUSTER_NAME -a"
  env = {
    AZ_OUTPUT_FORMAT = "table"
    AKS_RG_NAME = "your-aks-rg"
    AKS_CLUSTER_NAME = "youraks"
  }
}

action "Deploy branch filter" {
  uses = "actions/bin/filter@master"
  args = "branch master"
}

action "Deploy to AKS" {
  needs = ["Load AKS credential", "Deploy branch filter"]
  uses = "docker://gcr.io/cloud-builders/kubectl"
  runs = "sh -l -c"
  args = ["cat $GITHUB_WORKSPACE/sampleapp.yaml |  sed -e 's/YOUR_VALUE/'\"$YOUR_VALUE\"'/' -e 's/YOUR_DNS_LABEL_NAME/'$YOUR_DNS_LABEL_NAME'/' | kubectl apply -f - "]
  env = {
    YOUR_VALUE = "Ale"
    YOUR_DNS_LABEL_NAME = "yournamedispvar"
  }
}

シンプルですね。全体を定義するworkflowブロックと、それぞれのActionを書くactionブロックがあります。記法やオプションはドキュメントを読めばだいたい分かります。依存関係はneedsで書ける。

それぞれのactionブロックでDockerコンテナーを呼び出します。usesで指定したディレクトリにDockerファイルをおいておけばビルドされ、そのactionで使えます。”Load AKS credential”ブロックがその例です。”./azure-cli/“にDockerfileとエントリーポイントとなるbashスクリプトを置きます。

“Deploy branch filter”ブロックはGitHub Actionsが提供しているコンテナーの、”Deploy to AKS”は外部Dockerレジストリーを利用した例です。詳しくは後ほど。

リポジトリでPushイベントが発生すると、Workflowが実行されます。リポジトリの”Actions”タブで実行結果を確認できます。

Workflow

できた。Azure CLIとkubectlが使えるなら、他にも応用できそう。

以下、actionブロックを補足します。

Load AKS credential

Azure CLIを使ってAKSクラスターのCredentialを取得し、kubectlのコンテキスト設定します。envでクラスターのリソースグループ名とクラスター名を渡しています。

GitHub ActionsがAzure CLI Actionを提供しているのですが、Azure CLIのバージョンを最新にしたかったのと、動的にコンテナーを作ってみたかったので ./azure-cli にDockerfileを書きました

試す時は、みなさんの環境向けにenvを書き換えてください。なお、AKSクラスターからCredentialを取得する権限が必要です。権限を持ったサービスプリンシパルのアプリケーションID/パスワード、テナントIDをGitHubリポジトリのSecretに設定してください。actionブロックのsecretsで指定している通りです。

Deploy branch filter

bin/filterはGitHub Actionsが提供しているユーティリティです。この例では、イベントがmasterブランチで発生した場合のみWorkflowを継続します。

Deploy to AKS

gcr.ioからkubectlコンテナーを取得し、実行しています。マニフェストの一部を動的に変えたいことは多いので、sedでマニフェストの一部を置換する例にしました。

このactionが実行されると、envの”YOUR_VALUE”にセットした文字列を表示するGolang WebアプリのDeploymentと公開用のServiceができます。”YOUR_DNS_LABEL_NAME”にはDNSラベル名を指定でき、FQDNはAKSクラスターの配置リージョンに応じて決定されます。東日本リージョンの場合、YOUR_DNS_LABEL_NAME.japaneast.cloudapp.azure.com となります。

アプリのパスは /dispvar です。curlしてみると。

$ curl http://yournamedispvar.japaneast.cloudapp.azure.com/dispvar
Hello. You set "Ale"

まとめ

Dockerに慣れていれば便利に使えるかな、という印象です。Terraformのfmt/validate/plan用Actionなども公開されています。がっつりパイプラインを作らないとしても、コードのフォーマットや構文チェックに良さそうです。

02 May 2018, 17:00

WSLENVでWSLとWindowsの環境変数を共有する(Go開発環境編)

見た目は地味だが役に立つ

Windows 10 April 2018 Update (別名: バージョン1803)がリリースされました。タイムラインなど目立つ機能が注目されていますが、開発者支援系の機能、ツールも拡充されています。特に、WSL/Windowsの連携、相互運用まわりは着実に進化しています。そのうちのひとつが、このエントリーで紹介するWSLENVです。

WSLENVは、WSL/Windows間で環境変数を共有する仕組みです。ただ単純に共有するだけでなく、ルールに従って変換も行います。これが地味に便利。でも地味だから、あまり話題になっていない。なので具体例で紹介しよう、というのがこのエントリーの目的です。

TL;DR

英語が読めて、「あ、それ便利ね」とピンとくる人は以下を。

Share Environment Vars between WSL and Windows

Go開発環境を例に

前述のリンクでも紹介されていますが、Goの開発環境はWSLENVの代表的なユースケースです。GOPATHをいい感じにWSL/Windowsで共有できます。掘り下げていきましょう。

想定開発者像、ペルソナ

  • Windows端末を使っている
  • Go言語を使っている
  • CLIはbash/WSL中心
    • スクリプト書くならPowerShellもいいけど、インタラクティブな操作はbashが楽
    • アプリをDockerコンテナーとしてビルドするなど、OSSエコシステム、ツールとの連携を考慮
  • とはいえエディタ/IDEはWindows側で動かしたい、最近はVS Code中心

前提条件

  • WSL、WindowsそれぞれにGoを導入
    • バージョン管理のためにも、パッケージマネージャーがおすすめ
    • わたしはWSL(Ubuntu)でapt、WindowsではChocolateyを使ってGoを導入しています
  • GOPATHは %USERPROFILE%go とする
    • ユーザー名を tomakabeとすると C:\Users\tomakabe\go
    • setx GOPATH “$env:USERPROFILE\go” で設定
    • WSLでもこのディレクトリをGOPATHとする
  • VS Code + Go拡張をWindowsに導入
  • WindowsのCLIはPowerShellを利用

そぞろ歩き その1(WindowsでのGo開発)

では、何が課題で、WSLがどのようにそれを解決するか、見ていきましょう。

まず、Windowsで環境変数GOPATHを確認します。

PS C:\WINDOWS\system32> Get-ChildItem env:GOPATH

Name                           Value
----                           -----
GOPATH                         C:\Users\tomakabe\go

GOPATHに移動し、ディレクトリ構造を確認します。この環境にはすでにディレクトリbinとsrcがあり、binにはいくつかexeが入っています。VS CodeのGo拡張を入れると導入を促されるツール群は、ここに格納され、構文チェックや補完でVS Codeと連動します。

PS C:\WINDOWS\system32> cd C:\Users\tomakabe\go
PS C:\Users\tomakabe\go> ls


    ディレクトリ: C:\Users\tomakabe\go


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2018/05/02     11:10                bin
d-----       2018/05/02     11:06                src

PS C:\Users\tomakabe\go> ls .\bin\


    ディレクトリ: C:\Users\tomakabe\go\bin


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/05/02     11:10       14835200 dlv.exe
-a----       2018/05/02     11:09        4239360 go-outline.exe
-a----       2018/05/02     11:09        4045824 go-symbols.exe
-a----       2018/05/02     11:08       11094528 gocode.exe
-a----       2018/05/02     11:09        5708288 godef.exe
[snip]

サンプルコードのディレクトリへ移動し、中身を確認します。シンプルな挨拶アプリです。

PS C:\Users\tomakabe\go> cd .\src\github.com\ToruMakabe\work\
PS C:\Users\tomakabe\go\src\github.com\ToruMakabe\work> cat .\hello.go
package main

import "fmt"

func main() {
        fmt.Println("Hello Go on the new WSL")
}

ビルドして動かしてみましょう。Windows環境ではデフォルトで実行ファイルとしてexeが作られます。

PS C:\Users\tomakabe\go\src\github.com\ToruMakabe\work> go build .\hello.go
PS C:\Users\tomakabe\go\src\github.com\ToruMakabe\work> ls


    ディレクトリ: C:\Users\tomakabe\go\src\github.com\ToruMakabe\work


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/05/02     11:54        2049536 hello.exe
-a----       2018/05/02     11:10             91 hello.go


PS C:\Users\tomakabe\go\src\github.com\ToruMakabe\work> .\hello.exe
Hello Go on the new WSL

ここまでは従来のWindowsにおけるGo開発環境です。ではWSLに話を移しましょう。

そぞろ歩き その2(WSLでのGo開発)

WSLにつなぎます。ターミナルは任意ですが、わたしはVS Codeの統合ターミナルが好きです。コードを書きながら操作できるので。

GOPATHを確認します。空っぽです。WSLは既定でWindowsから環境変数PATHを受け取ります。PATHは特別扱いです。ですが、他の環境変数は、指定しないと渡されません。よってWindowsで設定していても、WSLから見るとGOPATHは空っぽです。

~ $ echo $GOPATH

$HOMEもきれいな状態です。

~ $ ls
~ $

ではGOPATHに指定したい、先ほどWindowsで確認したディレクトリへ移動します。ちなみにWindowsのCドライブはWSLで/mnt/c/に変換されます。先ほど確認したbin、srcが見えています。

~ $ cd /mnt/c/Users/tomakabe/go/
/mnt/c/Users/tomakabe/go $ ls
bin  src

ではここで実験。試しにパッケージをインポートしてみましょう。定番のgoimportsをインポートしてみます。わざとらしいですが、なんだか嫌な予感がします。

/mnt/c/Users/tomakabe/go $ go get -v golang.org/x/tools/cmd/goimports
Fetching https://golang.org/x/tools/cmd/goimports?go-get=1
Parsing meta tags from https://golang.org/x/tools/cmd/goimports?go-get=1 (status code 200)
get "golang.org/x/tools/cmd/goimports": found meta tag get.metaImport{Prefix:"golang.org/x/tools", VCS:"git", RepoRoot:"https://go.googlesource.com/tools"} at https://golang.org/x/tools/cmd/goimports?go-get=1
get "golang.org/x/tools/cmd/goimports": verifying non-authoritative meta tag
Fetching https://golang.org/x/tools?go-get=1
Parsing meta tags from https://golang.org/x/tools?go-get=1 (status code 200)
golang.org/x/tools (download)
created GOPATH=/home/tomakabe/go; see 'go help gopath'

嫌な予感は予定調和で的中します。GOPATHがいらっしゃらないので、/home/tomakabe/go とみなしてしまいました。先ほど確認した際、$HOMEはきれいな状態でした、が。新たにお作りになられたようです。

/mnt/c/Users/tomakabe/go $ ls ~/
go
/mnt/c/Users/tomakabe/go $ ls ~/go
bin  src

これではWSLとWindowsで、ソースもバイナリーも別々の管理になってしまいます。これはつらい。ああ、GOPATHを共有できればいいのに。

そぞろ歩き その3(解決編)

そこで登場するのが、WSLENVです。Windowsで作業します。Windowsの環境変数GOPATHを、環境変数WSLENVへスイッチとともに設定します。/pスイッチは、「この環境変数はパスを格納しているから、いい感じにして」という指定です。

PS C:\Users\tomakabe\go\src\github.com\ToruMakabe\work> setx WSLENV "$env:WSLENV`:GOPATH/p"

成功: 指定した値は保存されました。

いい感じって何よ。それは環境に合わせたパス表現の変換です。WSLで見てみましょう。WSLENVを読ませる必要があるため、VS Codeを再起動します。そして、ターミナルで確認します。

/mnt/c/Users/tomakabe/go $ echo $GOPATH
/mnt/c/Users/tomakabe/go

GOPATHが読めるようになりました。かつ、Windowsのパス表現であるC:\Users\tomakabe\goから、WSLの表現である/mnt/c/Users/tomakabe/goへと変換して渡しています。素晴らしい。これでGOPATHはひとつになり、ソースやバイナリー、パッケージの管理を統一できます。

ではWSLでサンプルコードを触ってみましょう。ソースのあるディレクトリへ移動します。ソースと先ほどビルドしたexeがあります。

/mnt/c/Users/tomakabe/go $ cd src/github.com/ToruMakabe/work/
/mnt/c/Users/tomakabe/go/src/github.com/ToruMakabe/work $ ls
hello.exe  hello.go

WSL上でビルドします。ELFバイナリー hello が作られました。

/mnt/c/Users/tomakabe/go/src/github.com/ToruMakabe/work $ go build hello.go
/mnt/c/Users/tomakabe/go/src/github.com/ToruMakabe/work $ ls
hello  hello.exe  hello.go
/mnt/c/Users/tomakabe/go/src/github.com/ToruMakabe/work $ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
/mnt/c/Users/tomakabe/go/src/github.com/ToruMakabe/work $ ./hello
Hello Go on the new WSL

まとめ

代表例としてGoの開発環境で説明しましたが、WSLENVは他の用途でも応用できるでしょう。スイッチの説明など、詳細は先ほど紹介した、こちらを。

27 Apr 2018, 17:00

TerraformでAzureのシークレットを受け渡す(ACI/AKS編)

動機

システム開発、運用の現場では、しばしばシークレットの受け渡しをします。代表例はデータベースの接続文字列です。データベース作成時に生成した接続文字列をアプリ側で設定するのですが、ひとりでコピペするにせよ、チームメンバー間で受け渡すにせよ、めんどくさく、危険が危ないわけです。

  • いちいちポータルやCLIで接続文字列を出力、コピーして、アプリの設定ファイルや環境変数にペーストしなければいけない
    • めんどくさいし手が滑る
  • データベース管理者がアプリ開発者に接続文字列を何らかの手段で渡さないといけない
    • メールとかチャットとかファイルサーバーとか勘弁
  • もしくはアプリ開発者にデータベースの接続文字列が読める権限を与えなければいけない
    • 本番でも、それやる?
  • kubernetes(k8s)のSecretをいちいちkubectlを使って作りたくない
    • Base64符号化とか、うっかり忘れる

つらいですね。シークレットなんて意識したくないのが人情。そこで、Terraformを使った解決法を。

シナリオ

Azureでコンテナーを使うシナリオを例に紹介します。ACI(Azure Container Instances)とAKS(Azure Container Service - k8s)の2パターンです。

  • Nodeとデータストアを組み合わせた、Todoアプリケーション
  • コンテナーイメージはDocker Hubにある
  • コンテナーでデータストアを運用したくないので、データストアはマネージドサービスを使う
  • データストアはCosmos DB(MongoDB API)
  • Cosmos DBへのアクセスに必要な属性をTerraformで参照し、接続文字列(MONGO_URL)を作る
    • 接続文字列の渡し方はACI/AKSで異なる
    • ACI
      • コンテナー作成時に環境変数として接続文字列を渡す
    • AKS
      • k8sのSecretとして接続文字列をストアする
      • コンテナー作成時にSecretを参照し、環境変数として渡す

検証環境

  • Azure Cloud Shell
    • Terraform v0.11.7
    • Terraformの認証はCloud Shell組み込み
  • Terraform Azure Provider v1.4
  • Terraform kubernetes Provider v1.1
  • AKS kubernetes 1.9.6

ACIの場合

ざっと以下の流れです。

  1. リソースグループ作成
  2. Cosmos DBアカウント作成
  3. ACIコンテナーグループ作成 (Cosmos DB属性から接続文字列を生成)

var.で参照している変数は、別ファイルに書いています。

[main.tf]

resource "azurerm_resource_group" "rg" {
  name     = "${var.resource_group_name}"
  location = "${var.resource_group_location}"
}

resource "random_integer" "ri" {
  min = 10000
  max = 99999
}

resource "azurerm_cosmosdb_account" "db" {
  name                = "your-cosmos-db-${random_integer.ri.result}"
  location            = "${azurerm_resource_group.rg.location}"
  resource_group_name = "${azurerm_resource_group.rg.name}"
  offer_type          = "Standard"
  kind                = "MongoDB"

  enable_automatic_failover = true

  consistency_policy {
    consistency_level       = "BoundedStaleness"
    max_interval_in_seconds = 10
    max_staleness_prefix    = 200
  }

  geo_location {
    location          = "${azurerm_resource_group.rg.location}"
    failover_priority = 0
  }

  geo_location {
    location          = "${var.failover_location}"
    failover_priority = 1
  }
}

resource "azurerm_container_group" "aci-todo" {
  name                = "aci-todo"
  location            = "${azurerm_resource_group.rg.location}"
  resource_group_name = "${azurerm_resource_group.rg.name}"
  ip_address_type     = "public"
  dns_name_label      = "yourtodo"
  os_type             = "linux"

  container {
    name   = "todo"
    image  = "torumakabe/nodetodo"
    cpu    = "1"
    memory = "1.5"
    port   = "8080"

    environment_variables {
      "MONGO_URL" = "mongodb://${azurerm_cosmosdb_account.db.name}:${azurerm_cosmosdb_account.db.primary_master_key}@${azurerm_cosmosdb_account.db.name}.documents.azure.com:10255/?ssl=true"
    }
  }
}

containerのenvironment_variablesブロックでCosmos DBの属性を参照し、接続文字列を生成しています。簡単ですね。これで接続文字列コピペ作業から解放されます。

AKS

AKSの場合、流れは以下の通りです。

  1. リソースグループ作成
  2. Cosmos DBアカウント作成
  3. AKSクラスター作成
  4. k8s Secretを作成 (Cosmos DB属性から接続文字列生成)
  5. k8s Secretをコンテナーの環境変数として参照し、アプリをデプロイ

[main.tf]

resource "azurerm_resource_group" "rg" {
  name     = "${var.resource_group_name}"
  location = "${var.resource_group_location}"
}

resource "random_integer" "ri" {
  min = 10000
  max = 99999
}

resource "azurerm_cosmosdb_account" "db" {
  name                = "your-cosmos-db-${random_integer.ri.result}"
  location            = "${azurerm_resource_group.rg.location}"
  resource_group_name = "${azurerm_resource_group.rg.name}"
  offer_type          = "Standard"
  kind                = "MongoDB"

  enable_automatic_failover = true

  consistency_policy {
    consistency_level       = "BoundedStaleness"
    max_interval_in_seconds = 10
    max_staleness_prefix    = 200
  }

  geo_location {
    location          = "${azurerm_resource_group.rg.location}"
    failover_priority = 0
  }

  geo_location {
    location          = "${var.failover_location}"
    failover_priority = 1
  }
}

resource "azurerm_kubernetes_cluster" "aks" {
  name                = "yourakstf"
  location            = "${azurerm_resource_group.rg.location}"
  resource_group_name = "${azurerm_resource_group.rg.name}"
  dns_prefix          = "yourakstf"
  kubernetes_version  = "1.9.6"

  linux_profile {
    admin_username = "${var.admin_username}"

    ssh_key {
      key_data = "${var.key_data}"
    }
  }

  agent_pool_profile {
    name            = "default"
    count           = 3
    vm_size         = "Standard_B2ms"
    os_type         = "Linux"
    os_disk_size_gb = 30
  }

  service_principal {
    client_id     = "${var.client_id}"
    client_secret = "${var.client_secret}"
  }
}

provider "kubernetes" {
  host = "${azurerm_kubernetes_cluster.aks.kube_config.0.host}"

  client_certificate     = "${base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.client_certificate)}"
  client_key             = "${base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.client_key)}"
  cluster_ca_certificate = "${base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.cluster_ca_certificate)}"
}

resource "kubernetes_secret" "cosmosdb_secret" {
  metadata {
    name = "cosmosdb-secret"
  }

  data {
    MONGO_URL = "mongodb://${azurerm_cosmosdb_account.db.name}:${azurerm_cosmosdb_account.db.primary_master_key}@${azurerm_cosmosdb_account.db.name}.documents.azure.com:10255/?ssl=true"
  }
}

Cosmos DB、AKSクラスターを作ったのち、kubernetesプロバイダーを使ってSecretを登録しています。複数のプロバイダーを組み合わせられる、Terraformの特長が活きています。

そしてアプリのデプロイ時に、登録したSecretを指定します。ここからはkubernetesワールドなので、kubectlなどを使います。マニフェストは以下のように。

[todo.yaml]

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: todoapp
spec:
  selector:
    matchLabels:
      app: todoapp
  replicas: 2
  template:
    metadata:
      labels:
        app: todoapp
    spec:
      containers:
        - name: todoapp
          image: torumakabe/nodetodo
          ports:
            - containerPort: 8080
          env:
            - name: MONGO_URL
              valueFrom:
                secretKeyRef:
                  name: cosmosdb-secret
                  key: MONGO_URL
---
apiVersion: v1
kind: Service
metadata:
  name: todoapp
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: todoapp

シークレットの中身を見ることなく、コピペもせず、もちろんメールやチャットやファイルも使わず、アプリからCosmos DBへ接続できました。

シークレットに限らず、Terraformの属性参照、変数表現は強力ですので、ぜひ活用してみてください。数多くのAzureリソースが対応しています。

09 Apr 2018, 15:00

俺のAzure CLI 2018春版

春の環境リフレッシュ祭り

最近KubernetesのCLI、kubectlを使う機会が多いのですが、なかなかイケてるんですよ。かゆい所に手が届く感じ。そこで、いい機会なのでAzure CLIまわりも最新の機能やツールで整えようか、というのが今回の動機。気づかないうちに、界隈が充実していた。

俺のおすすめ 3選

  • デフォルト設定
    • リソースグループやロケーション、出力形式などのデフォルト設定ができる
  • エイリアス
    • サブコマンドにエイリアスを付けられる
    • 引数付きの込み入った表現もできる
  • VS Code プラグイン
    • Azure CLI Toolsプラグイン でazコマンドの編集をコードアシストしてくれる
    • 編集画面上でコマンド選択して実行できる

デフォルト設定

$AZURE_CONFIG_DIR/configファイルで構成設定ができます。$AZURE_CONFIG_DIR の既定値は、Linux/macOS の場合$HOME/.azure、Windowsは%USERPROFILE%.azure。

Azure CLI 2.0 の構成

まず変えたいところは、コマンドの出力形式。デフォルトはJSON。わたしのお気持ちは、普段はTable形式、掘りたい時だけJSON。なのでデフォルトをtableに変えます。

[core]
output = table

そしてデフォルトのリソースグループを設定します。以前は「デフォルト設定すると、気づかないところで事故るから、やらない」という主義だったのですが、Kubernetesのdefault namespaceの扱いを見て「ああ、これもありかなぁ」と改宗したところ。 軽く事故ったので、リソースグループのデフォルト設定をいまはやめています。デフォルトのご利用は計画的に。

[defaults]
group = default-ejp-rg

他にもロケーションやストレージアカウントなどを設定できます。ロケーションはリソースグループの属性を継承させたい、もしくは明示したい場合が多いので、設定していません。

ということで、急ぎUbuntuの仮想マシンが欲しいぜという場合、az vm createコマンドの必須パラメーター、-gと-lを省略できるようになったので、さくっと以下のコマンドでできるようになりました。

デフォルト指定したリソースグループを、任意のロケーションに作ってある前提です。

az vm create -n yoursmplvm01 --image UbuntuLTS

エイリアス

$AZURE_CONFIG_DIR/aliasにエイリアスを書けます。

Azure CLI 2.0 のエイリアス拡張機能

前提はAzure CLI v2.0.28以降です。以下のコマンドでエイリアス拡張を導入できます。現時点ではプレビュー扱いなのでご注意を。

az extension add --name alias

ひとまずわたしは以下3カテゴリのエイリアスを登録しました。

頻繁に打つからできる限り短くしたい系

[ls]
command = list

[nw]
command = network

[pip]
command = public-ip

[fa]
command = functionapp

例えばデフォルトリソースグループでパブリックIP公開してるか確認したいな、と思った時は、az network public-ip listじゃなくて、こう打てます。

$ az nw pip ls
Name                  ResourceGroup    Location    Zones    AddressVersion    AllocationMethod      IdleTimeoutInMinutes
ProvisioningState
--------------------  ---------------  ----------  -------  ----------------  ------------------  ----------------------
-------------------
yoursmplvm01PublicIP  default-ejp-rg   japaneast            IPv4              Dynamic                                  4
Succeeded

クエリー打つのがめんどくさい系

VMに紐づいてるパブリックIPを確認したいときは、こんなエイリアス。

[get-vm-pip]
command = vm list-ip-addresses --query [].virtualMachine.network.publicIpAddresses[].ipAddress

実行すると。

$ az get-vm-pip -n yoursmplvm01
Result
-------------
52.185.133.68

引数を確認するのがめんどくさい系

リソースグループを消したくないけど、中身だけ消したいってケース、よくありますよね。そんなエイリアスも作りました。–template-uriで指定しているGistには、空っぽのAzure Resource Manager デプロイメントテンプレートが置いてあります。このuriをいちいち確認するのがめんどくさいので、エイリアスに。

[empty-rg]
command = group deployment create --mode Complete --template-uri https://gist.githubusercontent.com/ToruMakabe/28ad5177a6de525866027961aa33b1e7/raw/9b455bfc9608c637e1980d9286b7f77e76a5c74b/azuredeploy_empty.json

以下のコマンドを打つだけで、リソースグループの中身をバッサリ消せます。投げっぱなしでさっさとPC閉じて帰りたいときは –no-waitオプションを。

$ az empty-rg

位置引数Jinja2テンプレートを使ったエイリアスも作れるので、込み入ったブツを、という人は挑戦してみてください。

VS Code プラグイン (Azure CLI Tools )

Azure CLIのVS Code向けプラグインがあります。コードアシストと編集画面からの実行が2大機能。紹介ページのGifアニメを見るのが分かりやすいです。

Azure CLI Tools

プラグインを入れて、拡張子.azcliでファイルを作ればプラグインが効きます。長いコマンドを補完支援付きでコーディングしたい、スクリプトを各行実行して確認しながら作りたい、なんて場合におすすめです。

注意点

  • エイリアスには補完が効かない
    • bashでのCLI実行、VS Code Azure CLI Toolsともに、現時点(20184)でエイリアスには補完が効きません
  • ソースコード管理に不要なファイルを含めない
    • $AZURE_CONFIG_DIR/ 下には、aliasやconfigの他に、認証トークンやプロファイルといったシークレット情報が置かれます。なのでGitなどでソースコード管理する場合は、aliasとconfig以外は除外したほうがいいでしょう

06 Apr 2018, 18:00

TerraformでAzure VM/VMSSの最新のカスタムイメージを指定する方法

カスタムイメージではlatest指定できない

Azure Marketplaceで提供されているVM/VMSSのイメージは、latest指定により最新のイメージを取得できます。いっぽうでカスタムイメージの場合、同様の属性を管理していないので、できません。

ではVM/VMSSを作成するとき、どうやって最新のカスタムイメージ名を指定すればいいでしょうか。

  1. 最新のイメージ名を確認のうえ、手で指定する
  2. 自動化パイプラインで、イメージ作成とVM/VMSS作成ステップでイメージ名を共有する

2のケースは、JenkinsでPackerとTerraformを同じジョブで流すケースがわかりやすい。変数BUILD_NUMBERを共有すればいいですね。でもイメージに変更がなく、Terraformだけ流したい時、パイプラインを頭から流してイメージ作成をやり直すのは、無駄なわけです。

Terraformではイメージ名取得に正規表現とソートが可能

Terraformでは見出しの通り、捗る表現ができます。

イメージを取得するとき、name_regexでイメージ名を引っ張り、sort_descendingを指定すればOK。以下の例は、イメージ名をubuntu1604-xxxxというルールで作ると決めた場合の例です。イメージを作るたびに末尾をインクリメントしてください。ソートはイメージ名全体の文字列比較なので、末尾の番号の決めた桁は埋めること。

ということで降順で最上位、つまり最新のイメージ名を取得できます。

data "azurerm_image" "poc" {
  name_regex          = "ubuntu1604-[0-9]*"
  sort_descending     = true
  resource_group_name = "${var.managed_image_resource_group_name}"
}

あとはVM/VMSSリソース定義内で、取得したイメージのidを渡します。

  storage_profile_image_reference {
    id = "${data.azurerm_image.poc.id}"
  }

便利である。