しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ACS Engine を使って Dv3 インスタンスベースの Kubernetes クラスタを作成する

Azure Portal から Azure Container Service を作ろうとすると Dv2 までしかインスタンスが選べないので、新しい Nested Virtualization に対応した Dv3 を使ってクラスタを作ることは出来ませんが、ACS Engine を使うと作成することが出来ます。

ACS Engine は ARM Template を作成するだけなので、ACS の制限とは無縁です。

セットアップは README などに書いてある通りなので省略します。前までは Azure Cloud Shell にインストール出来ましたが、最近のバージョンでは Go 1.8 が必要らしく手間がかかりそうです。

基本的に Windows ベースの Kubernetes しか作る気がないので、ACS Engine のリポジトリに用意されている Windows 向けのサンプルを修正して、D2 v3 を使うようにします。

"agentPoolProfiles": [
  {
    "name": "windowspool",
    "count": 2,
    "vmSize": "Standard_D2_v3",
    "availabilityProfile": "AvailabilitySet",
    "osType": "Windows"
  }
]

修正したサンプルから ACS Engine を使って ARM Template を作成し、リソースグループにデプロイを行うだけで Dv3 ベースの Kubernetes が完成します。

あまり関係ないですが、Managed Disk を使って VM が作成されていました。課金に注意ですね。

f:id:shiba-yan:20170817230128p:plain

作成された VM を確認すると、指定通り D2 v3 なインスタンスとなっています。Hyper Threading なインスタンスなので、コンテナを多く立ち上げる場合には少し不安があります。

f:id:shiba-yan:20170817225611p:plain

これで Hyper-V Containers が利用可能な Windows Server と Kubernetes な環境が手に入りました。

既に Kubernetes は Windows Server Containers と Hyper-V Containers に対応していると発表されています。

Support for Both Windows Server Containers and Hyper-V Containers - There are two types of containers in Windows Server 2016. Windows Containers is similar to Docker containers on Linux, and uses kernel sharing. The other, called Hyper-V Containers, is more lightweight than a virtual machine while at the same time offering greater isolation, its own copy of the kernel, and direct memory assignment. Kubernetes can orchestrate both these types of containers. 

対応していると発表されてはいますが、軽く調べた限りでは Hyper-V Containers を利用する方法は分かりませんでした。まともに Windows Containers を使う場合には Hyper-V が必要なので、何とかしたいところです。

Hyper-V Containers で実行する方法が分かれば続きを書く予定です。

App Service Premium V2 が Public Preview になった

あまりアップデートが無かった App Service 周りですが、ここ数日で一気にインフラ周りにアップデートがあって色々と見るのが大変です。割高感しかなかった Premium がついに改善です。

Premium V2 自体は App Service Environment で使える Isolated と同じインスタンスタイプです。具体的には D1-3 v2 が使われるようになりました。

現在利用可能なリージョンは South Central US / West Europe / North Europe / Australia East / Australia Southeast の 5 つだけです。EU と Australia は Premium ユーザーが多かったのかもしれないです。

検証のために、日本から比較的近そうな South Central US を使うことにしました。

f:id:shiba-yan:20170731163932p:plain

ちなみに気になる価格ですが、ぶちぞう RD 曰く Preview 中でも値引きされていないのではないかという話でした。なので GA 後もこれまでの Premium と同じ価格で Premium V2 が使えるかもしれません。

確かに価格に関しては 50% 引きになっているという記載はありませんでした。

GA 済みの Isolated より高くなることはないと思うので、多分このままの価格で行くのだと思います。

パフォーマンスを検証

CPU に関してはこれまでの A シリーズから Dv2 にアップグレードされているので、ACU 換算で倍にはなっているはずです。ちなみにリージョンによっては Premium と Premium V2 で同じ CPU が使われています。

使われているのは Intel(R) Xeon(R) CPU E5-2673 v3 @ 2.40GHz です。Azure で一番使われています。メモリも Premium と比べて 2 倍になっているので、これまでよりもバランスの良い構成になりました。

さて、App Service の最大の弱点としてストレージのパフォーマンスが悪いことがありました。

Premium V2 になっても共有ストレージのパフォーマンスは原理上変わらないはずですが、キャッシュなどで使われるストレージは Dv2 化の恩恵を受けているはずなので簡単に確認しました。

念のために Standard / Premium / Premium V2 の 3 環境でパフォーマンスを確認しました。

Standard

D:\local\Temp>StorageBenchmark.exe 1000 0
WriteFile : 929ms
ReadFile : 265ms
DeleteFile : 392ms

D:\local\Temp>StorageBenchmark.exe 1000 512
WriteFile : 1088ms
ReadFile : 245ms
DeleteFile : 326ms

Premium

D:\local\Temp>StorageBenchmark.exe 1000 0
WriteFile : 1120ms
ReadFile : 252ms
DeleteFile : 377ms

D:\local\Temp>StorageBenchmark.exe 1000 512
WriteFile : 1053ms
ReadFile : 221ms
DeleteFile : 516ms

Premium V2

D:\local\Temp>StorageBenchmark.exe 1000 0
WriteFile : 304ms
ReadFile : 102ms
DeleteFile : 169ms

D:\local\Temp>StorageBenchmark.exe 1000 512
WriteFile : 433ms
ReadFile : 95ms
DeleteFile : 140ms

Standard / Premium はハードウェアは全く同じなので、基本的に性能に差は出ないはずです。Premium V2 は他の 2 つに比べると 2-3 倍はストレージのパフォーマンスが改善しているようです。

ストレージのパフォーマンスが改善したと言っても共有ストレージには影響なさそうですが、最近の App Service では Dynamic Cache がデフォルトで有効化されるようなので効果があります。

同様にローカルストレージにファイルをキャッシュする Local Cache を使った場合にも効果があると思います。とは言え、大半のケースでは Dynamic Cache だけで有効だと思いますが。

Azure Container Instances の裏側を少しだけ覗いてみる

Azure Container Instances に sshd を有効化したイメージをデプロイすると、当然ながら ssh で接続出来るのでコンテナに関するいろいろな情報を収集できます。

とりあえず気になるインスタンスの情報について調べます。まずは CPU 周りから。

f:id:shiba-yan:20170731150030p:plain

Intel(R) Xeon(R) CPU E5-2673 v3 @ 2.40GHz が使われています。Haswell 世代のようです。ちなみに CPU コアのリクエスト数を 1 つにしても 4 つのコアが見えていました。

この時点ではまだ Dv3 なのか Dv2 の判別は付かないので、ぶちぞう RD に聞いてみました。

てっきりインスタンスメタデータはまだプレビューかと思っていたら、普通に使えるようになっていました。流石世界のぶちぞう RD、レドモンド行き待ったなし。

コンテナから実際にメタデータを取得してみると、いろんなことが分かりました。最初に分かることは、少なくとも Linux の場合は Hyper-V Containers を使った分離は行われていないということです。*1

インスタンスも予想された Dv3 / Ev3 ではなく Standard_D3_v2 でした。

{
  "compute": {
    "location": "westus",
    "name": "k8s-agentpool3-a02wus0l-4",
    "offer": "UbuntuServer",
    "osType": "Linux",
    "platformFaultDomain": "0",
    "platformUpdateDomain": "2",
    "publisher": "Canonical",
    "sku": "16.04-LTS",
    "version": "16.04.201706191",
    "vmId": "1901479c-26b8-42ac-92d8-81015daa970b",
    "vmSize": "Standard_D3_v2"
  },
  "network": {
    "interface": [
      {
        "ipv4": {
          "ipAddress": [
            {
              "privateIpAddress": "10.240.0.234",
              "publicIpAddress": ""
            }
          ],
          "subnet": [
            {
              "address": "10.240.0.0",
              "prefix": "16"
            }
          ]
        },
        "ipv6": {
          "ipAddress": []
        },
        "macAddress": "000D3A33B315"
      }
    ]
  }
}

ホスト OS が Linux になっているので、普通に Linux と Docker の組み合わせで ACI は作られているようです。そして実際に裏側でコンテナのオーケストレーションを行っているのは Kubernetes のようです。

3 行でまとめると以下のようになりました。

  • ACI の裏側は Ubuntu + Kubernetes が使われている
  • インスタンスは Nested Virtualization に非対応の Dv2
  • Hyper-V Containers は当然ながら使われていない

Cloud Native Computing Foundation にプラチナメンバーとして参加した理由もわかる結果となりました。Windows に関してはイメージを作る手間がかかるので、また今度試してみることにします。

*1:Hypervisor レベルでの分離というのが嘘っぽく感じる気が…

Azure Container Instances を Kubernetes の仮想ノードとして利用できる aci-connector-k8s を試してみた

Azure Container Instances は単体ではオーケストレーターが提供されてないですが、実験的に Kubernetes と連携して利用できる aci-connector-k8s が同時にリリースされています。

GitHub でソースコードが、Docker Hub でビルド済みイメージが公開されています。

https://github.com/Azure/aci-connector-k8s

利用するためにはサービスプリンシパルと Container Instances が実際にデプロイされるリソースグループが必要なので、予め Azure CLI などを使って作っておきます。

Cloud Shell で作るのが手っ取り早いかと思います。

公式に Docker Hub で提供されているイメージは、最新版の Resource Provider に対応していないようなので、そのままデプロイしてしまうと aci-connector-k8s でエラーになってしまいます。

仕方ないのでサクッと対応コードを書いて Docker Hub にプッシュしておきました。

https://hub.docker.com/r/shibayan/aci-connector-k8s/

サンプルにある YAML のイメージを変更するだけで使えます。Pull Request を送っているので、ひょっとしたら取り込まれて公式対応されるかもしれません。とりあえずの対応です。

他には作成しておいたサービスプリンシパルやリソースグループの名前などセットします。

apiVersion: v1
kind: Pod
metadata:
  name: aci-connector
  namespace: default
spec:
  containers:
  - name: aci-connector
    image: shibayan/aci-connector-k8s:latest
    imagePullPolicy: Always
    env:
      - name: AZURE_CLIENT_ID
        value: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      - name: AZURE_CLIENT_KEY
        value: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      - name: AZURE_TENANT_ID
        value: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      - name: AZURE_SUBSCRIPTION_ID
        value: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      - name: ACI_RESOURCE_GROUP
        value: YOURRESOURCEGROUP-NAME
  nodeSelector:
    beta.kubernetes.io/os: linux

nodeSelector は私が持っている Kubernetes クラスタが Windows なので指定しています。Linux のみの Kubernetes クラスタの場合は指定する必要はないはずです。

ダッシュボードからデプロイしてしばらく待つと、ノードに aci-connector が現れます。

f:id:shiba-yan:20170728004344p:plain

このノードに対するコンテナ起動リクエストは Azure Container Instances が透過的に使われるようになります。今後 Container Instances にネイティブ対応したオーケストレーターが出てくると楽しそうです。

実際に aci-connector に対してデプロイを行ってみます。これもサンプルの通りです。

apiVersion: v1
kind: Pod
metadata:
  name: nginx-aci
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    imagePullPolicy: Always
    name: nginx-aci
  dnsPolicy: ClusterFirst
  nodeName: aci-connector

nodeName として aci-connector を明示的に指定しているので注意です。

デプロイすると一瞬で新しい Pod が立ち上がり、パブリック IP アドレスも割り当てられます。

f:id:shiba-yan:20170728004613p:plain

パブリック IP アドレスにアクセスすると、Nginx が確かに立ち上がっていることが簡単に確認できます。

Kubernetes クラスタ内に Pod を作成する時よりも、コンテナの起動が早い気がします。

f:id:shiba-yan:20170728005721p:plain

Kubernetes の知識が少しあれば、バックエンドが Azure Container Instances かどうか全く意識することなく使うことが出来ました。リソース管理も不要ですし、これはかなり便利ですね。

ただし Kubernetes のマスターノードは作っておく必要があるので、Azure Container Services が管理された Kubernetes マスターを提供してくれたら最高です。

Azure Container Instances は Hypervisor レベルでの分離を備えた次世代のコンテナ実行環境

ASE v2 について書いたし、そろそろぶちぞう RD に後のことを任せて寝るかと思っていたら、突然 Azure Container Instances という新しいサービスが発表されました。コンテナと聞いたら寝るわけにはいかないので、例によって興味のある部分だけサクッと試しました。

まずは公式情報がいろいろと出てきているので、しっかりと目を通しておくべきです。要約すると Azure Container Instances こそ真の Container as a Service ということです。

ASE v2 よりもドキュメントが充実している気がします。しっかりと Linux と Windows に対応していますし、コンテナ間の分離にはハイパーバイザが使われているともあります。

要するに Hyper-V Container が使われているということでしょう。

何はともあれ触ってみたいという人は、Azure Cloud Shell に Container Instances 対応の Azure CLI 2.0 がインストールされているので、そこから簡単に試せます。Quick Start を読めば誰でも作れます。

一応 Azure Portal からも作れるようになってるみたいです。

そして気になる課金ですが、コンテナ作成リクエストと秒単位の課金で CPU コア数、そしてメモリ量によって決まります。ガンガン使ってガンガン捨てるような運用が容易です。

Pricing - Container Instances | Microsoft Azure

そして一番重要なポイントとしては Hyper-V Containers がおそらく使われているので、Windows Server Containers の面倒な制約を受けることなく、安全に Windows Containers を使うことが出来ることです。

まさに Windows Containers の実運用に必要なサービスが出てきたと感動しています。これからは ASP.NET アプリケーションも Dockernize して動かす時代になる、と思っています。

Azure Container Instances の特徴

最近は Windows Containers のクラスタ周りで苦しんでいたので、思いを書きすぎました。Azure Container Instances の何が素晴らしいのか、もうちょっと具体的に書きます。

  • クラスタレス
  • 高速なコンテナの起動
  • 秒単位での課金
  • ハイパーバイザで分離された実行環境
  • CPU / メモリの柔軟な割り当て
  • Linux / Windows に対応

どれを取っても最高ですね。要するに Hyper.sh と直接競合するサービスだと思います。

これまで Function as a Service が Serverless だと良く言われてきましたが、実際にホストするサーバーを全く意識することなく使える Container Instances や Hyper.sh こそが Serverless だと私は思っています。

喋りすぎなので、実際に Container Instances を使って Windows Server Core で動作する ASP.NET アプリケーションをデプロイしてみます。

Container を作成する

Cloud Shell とビルド済みのイメージがあれば、1 コマンドを実行するだけでデプロイ可能です。注意するポイントは --os-type で Windows を指定することです。

使用する Docker Image は AppVeyor を使って自動ビルドを行っている ASP.NET MVC アプリケーションです。本当に Windows Containers に欠けていたものは実行環境だけでした。

az container create -g ContainerInstance-RG --name aspnetapp --location westus \
    --image shibayan/appveyor-aspnet-docker:master-19 --ip-address public --os-type Windows

外部からアクセスするように IP アドレスは公開を指定して実行しました。

すると以下のエラーが表示されてしまいました。エラーメッセージを読むと Windows の場合には最低でも 2 コアと 3.5GB メモリを指定する必要があるらしいです。

The resource requirments '{"requests":{"memoryInGB":1.5,"cpu":1.0}}' is invalid. Windows containers can only request 2 CPU and 3.5GB memory.

なので --cpu と --memory を追加してコマンドの実行をやり直します。

az container create -g ContainerInstance-RG --name aspnetapp --location westus \
    --image shibayan/appveyor-aspnet-docker:master-19 --ip-address public --os-type Windows --cpu 2 --memory 3.5

今度はすんなりと上手くいって、すぐに確保された IP アドレスを含む情報が表示されました。

f:id:shiba-yan:20170727005212p:plain

まだこの時はコンテナの作成中なので IP アドレスにアクセスしても何も返ってきません。作成が完了したかを知るためには別のコマンドを使います。

Container Status を確認

作成リクエストを投げたコンテナの状態を知るためには container show コマンドを使います。名前とリソースグループ名を指定するだけの、非常に簡単なコマンドです。

az container show -n aspnetapp -g ContainerInstance-RG

実行するとステータスなどを含む JSON が表示されるので、ProvisioningState の値が Succeeded になっていることを確認すれば OK です。

f:id:shiba-yan:20170727014318p:plain

このコマンドの実行結果には docker 周りのイベントログも出てるので、以下のようなクエリを使うと簡単に見ることが出来ます。

az container show -n aspnetapp -g ContainerInstance-RG --query "containers[0].instanceView.events"

docker pull などに、どのくらい時間がかかっているかを簡単に把握できます。

f:id:shiba-yan:20170727005949p:plain

関係ないですが、同じ Docker を使った App Service on Linux は簡単にログを確認する方法が用意されていないので、GA までには Azure Portal から確認できるような機能が欲しいと思ってますし、要望もしてます。

Public IP にアクセス

コンテナの作成が完了後に Public IP にブラウザからアクセスすると、少し後に見慣れた ASP.NET MVC アプリケーションのページが表示されました。

手順としてはコマンドを 2 回打ち込んだだけです。本当にたったこれだけで ASP.NET アプリが動いてます。

f:id:shiba-yan:20170727010055p:plain

非常に簡単にコンテナのデプロイが行えるので、Azure Container Service を使ってクラスタから作成していた部分を全てスキップして、アプリケーションの開発をデプロイのみに集中することが出来そうです。

ただし実際に本番、運用環境で使う場合にはオーケストレーターが必要となります。何故なら作成したコンテナはイミュータブルであり、後からイメージのアップデートなどは出来ないからです。

オーケストレーションについて

Azure Container Instances 自体は Swarm や Kubernetes のようなオーケストレーション機能は持っておらず、非常に低レベルなコンテナを実行することに特化したサービスとなっています。

サンプルとして用意されている aci-connector を使うと、Kubernetes と Container Instances をシームレスに統合することが出来るようです。

個人的な認識としては Container Instances はコンテナ版 Cloud Services に近いかなと思います。各サービスの土台となるようなサービスという印象です。

まだプレビューリリースされたばかりの新しいサービスですが、今後の Microsoft と Azure が本気でコンテナに注力していることが分かりますね。私は Azure 最高の素晴らしいサービスだと思います。

App Service Environment v2 がリリースされたので v1 からの変更点を調べてみた

最近はあまり ASE について興味を持っていなかったのですが、今朝 ASE v2 がリリースされたと聞いたので v1 との違いに注目して調べてみることにしました。

ドキュメントは一部更新されているみたいですが、まだまだ足りていない印象です。

恐らく詳細に関しては @kosmosebi がブチザッキに書いてくれるはずなので、例によって自分の興味のある部分だけ見ています。ASE v1 は知っているという前提なので注意。

ASE v2 を作成するのは非常にシンプルで、名前とリソースグループ、そしてリージョンを選択するぐらいで作れます。既存の VNET への参加も勿論できます。

f:id:shiba-yan:20170726205531p:plain

今日は ASE v2 以外にも ILB ASE で WebJobs サポートなどもリリースされたらしいですね。

料金プランの変更

これまでの ASE v1 では Frontend Pool に最低でも P2 が 2 台、Worker Pool に P1 が 1 台必要でした。つまり 1 か月間動かす場合には最低でも 113,832 円必要です。*1

新しい ASE v2 では基本使用料的なものが導入されていました。正直まだよくわかっていませんが、説明を読む限りでは Frontend Pool や File Server などの費用が含まれているようです。

f:id:shiba-yan:20170726205459p:plain

App Service Plan の数に関係なく、最低でも上の金額が必要となるみたいですね。ちょっと自信ないです。月額で表示されていますが、一応時間割されるらしいです。

なので ASE v2 を使う場合には最低でも 130,533.89 円かかりそうです。ちょっと高くなりました。

インスタンスが Dv2 ベースに

ASE v1 と比べて v2 は少し価格は上がったように感じますが、v2 では利用可能なインスタンスが Dv2 にアップグレードされているので、パフォーマンスは確実に v1 よりも上がっています。

新しく App Service Plan を作成する際に I1 から I3 で選ぶことが出来ます。

f:id:shiba-yan:20170726221806p:plain

ストレージも v1 の 500GB から 1TB に増えているので、実質値下げと言っても過言ではないと思います。ローカルストレージも SSD になり、CPU とメモリのバランスも良くなりました。

ただし素の Dv2 から比べると結構高いので、ASE 専用インスタンスと捉える必要があります。

Worker Pool から Isolated Worker に

事前に 3 つの Worker Pool のどれかに対してインスタンスと数を割り当てておく必要があった ASE v1 に対して、ASE v2 では通常の App Service と同じように App Service Plan を作る形になりました。

作成されたインスタンスは ASE によって管理されているので、追加の作業が必要になることはないです。

f:id:shiba-yan:20170726231949p:plain

スケールアップも App Service Plan の設定から行えますが、例によってスケール完了までには時間がそれなりにかかるので注意が必要です。

しかし ASE であることを感じさせずに、Web App などを作れるので便利になりました。

Storage が Azure File から変更

料金部分に File Server とあったので気になっていましたが、v1 ではコンテンツのストレージとして Azure File が使われていたのに対して、v2 では通常の App Service と同じ方式になってました。

f:id:shiba-yan:20170726231034p:plain

Azure File を使うとシンプルになって良さそうでしたが、パフォーマンスが悪かったので仕方ないです。

Frontend Pool のスケールが自動に

確か v1 では手動でスケールさせる必要があったはずですが、v2 では App Service Plan のインスタンス数に応じて自動的にスケールさせることが出来るみたいです。

f:id:shiba-yan:20170726230955p:plain

標準では I2 が使われている感がありますが、更にパワフルな I3 を使うことも出来そうです。ただし追加料金が必要になるようなので、課金周りがはっきりするまでは選びにくいですね。

色々と書いてきましたが、個人的にはインスタンスが A シリーズから Dv2 シリーズに変更になったというのが一番魅力的でした。通常の App Service も早く Dv2 ベースに切り替わってもらいたいものです。

*1:\45,532.80 * 2 + \22,766.40 * 1 = \113,832

App Service on Linux が Japan East で利用可能になりました

これまで Southeast Asia が日本から一番近いリージョンでしたが、ついに Japan East に App Service on Linux がデプロイされたので、レイテンシを気にせず使えるようになりました。

今までは Japan East / West は割と後回しでしたが、今回は早期にデプロイされました。

それほど需要が多いと見込まれているらしいので、次の仕事では使っていきたいと思っていますが、まずは Southeast Asia と比較する形でパフォーマンス周りの確認をしたいですね。

ざっくりと確認する

今朝までは作成時にエラーになっていましたが、今は修正されています。帝国兵殿に感謝。

f:id:shiba-yan:20170712205800p:plain

新しく作成された stamp ということで、新しめのインスタンスで stamp が作成されていました。

Windows の App Service は AMD Opteron な stamp を引くこともありますが、Linux だと Intel っぽいです。

f:id:shiba-yan:20170712210132p:plain

とりあえず Japan East と Southeast Asia に S2 の App Service Plan を作成して、その上に同じ Docker Image をデプロイしたものを用意しました。ちなみに ASP.NET Core です。

それぞれに対して wrk でトラフィックを流し込んでレイテンシを確認してみました。

Japan East S2 の場合

Running 10s test @ http://docker-test-1.azurewebsites.net/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    28.28ms   12.03ms 130.25ms   74.04%
    Req/Sec   178.90     28.92   260.00     69.00%
  Latency Distribution
     50%   26.82ms
     75%   34.07ms
     90%   43.17ms
     99%   66.47ms
  3570 requests in 10.01s, 31.06MB read
Requests/sec:    356.49
Transfer/sec:      3.10MB

Southeast Asia S2 の場合

Running 10s test @ http://docker-test-2.azurewebsites.net/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    76.50ms    8.66ms 170.83ms   94.76%
    Req/Sec    65.50     12.07    90.00     59.18%
  Latency Distribution
     50%   74.43ms
     75%   76.88ms
     90%   81.23ms
     99%  100.75ms
  1292 requests in 10.01s, 11.24MB read
Requests/sec:    129.09
Transfer/sec:      1.12MB

ざっくりとした確認ですが、Southeast Asia に比べて Japan East の方が 3 倍近く処理出来ていました。当然ながらレイテンシは 1/3 ぐらいですが、Japan East は少しばらつきが大きいのが気になります。

個人的にですが App Service on Linux を使う場合には、最低でも S2 以上を使った方が良い気がしています。そろそろ App Service 全体的にインスタンスサイズの底上げをしてもらいたいですね。

無料で試したい人は

Azure サブスクリプション無しでも試す方法があるので、まずは 1 時間体験してみるのがおすすめです。

f:id:shiba-yan:20170711221134p:plain

Azure App Service Overview

サブスクリプションは必要ないですが Microsoft アカウントは必要です。

MSDN を持っている人は無料枠があるのでそれで試せます。ちなみに時間課金で今は 50% 引きです。

もう少し詳しく知りたい人は

これまでに何回か App Service on Linux について書いてきたので参考にしてください。主にカスタムランタイムの作り方や CI / CD について書いてきた気がします。

5 月に開催された de:code 2017 で使用したスライドも Speaker Deck に公開しているので、基本的な機能からデプロイや内部のアーキテクチャまで軽く触れています。

今回の Japan East 追加以外に特に目立ったアップデートはないので、情報は古くなっていないはずです。

最後に

ASP.NET Core アプリケーションと組み合わせるのが、個人的に使い勝手が良いと思いました。Visual Studio から直接デプロイも可能なので、Windows 版と同じぐらいの使い勝手です。

仕事で使ってみたい気持ちでいっぱいです。なので App Service on Linux の案件を募集しています。

Application Insights を Elastic Beanstalk にデプロイした ASP.NET アプリケーションから利用してみる

わざわざ Application Insights を Azure 以外から使う理由があるのかと言われそうですが、Application Insights Basic の課金はよくある APM のホスト数での課金ではなく、容量課金となっているので頻繁にインスタンスを使い捨てる場合などで、使いやすいことがあります。

Enterprise の場合はホスト数での課金 + 容量課金となるので少し注意です。

Application Insights 自体は Azure に限定したものではなく、オンプレミスや他のサービスでも使えるようになっているので問題ありません。設定は Visual Studio で完結します。

Azure Portal から Application Insights を単体で作成します。ASP.NET アプリケーションを選びます。

f:id:shiba-yan:20170626224318p:plain

リソースを作成したら、Visual Studio から Application Insights の設定を行います。Visual Studio 2017 では昔に比べて、かなり簡単にいろんな設定が行えるようになっていました。

公開コメントは無効にしても良いかもしれませんが、とりあえず有効のままです。

f:id:shiba-yan:20170626224841p:plain

このアプリケーションをそのまま Elastic Beanstalk にデプロイすれば、Application Insights で各パフォーマンスカウンターなどの値が確認できるようになります。

Cloud Role Instance は Windows のマシン名になっていますが、スケーリングした時には同じマシン名になるので、このままだと区別がつきません。

f:id:shiba-yan:20170626224335p:plain

この部分ですが Application Insights のデフォルトでは Azure Cloud Services と Web Apps だけの対応なので、EC2 向けに専用の TelemetryInitializer を作成して対応します。

EC2 は Instance Metadata を稼働中のインスタンス内から簡単に取れるようになってるので、それを利用してインスタンス ID を取得します。

public class AwsEc2EnvironmentTelemetryInitializer : ITelemetryInitializer
{
    public AwsEc2EnvironmentTelemetryInitializer()
    {
        try
        {
            _roleInstanceName = new WebClient().DownloadString("http://169.254.169.254/latest/meta-data/instance-id");
        }
        catch
        {
        }
    }
        
    private readonly string _roleInstanceName;

    public void Initialize(ITelemetry telemetry)
    {
        if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleInstance))
        {
            telemetry.Context.Cloud.RoleInstance = _roleInstanceName;
        }

        if (string.IsNullOrEmpty(telemetry.Context.GetInternalContext().NodeName))
        {
            telemetry.Context.GetInternalContext().NodeName = _roleInstanceName;
        }
    }
}

取得したインスタンス ID で RoleInstance と NodeName を置き換えます。

作成した TelemetryInitializer は ApplicationInsights.config に追加します。元々あった Cloud Services と Web Apps 用の Initializer は削除しておきます。

<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
  <TelemetryInitializers>
    <Add Type="Microsoft.ApplicationInsights.DependencyCollector.HttpDependenciesParsingTelemetryInitializer, Microsoft.AI.DependencyCollector" />
    <!--<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer" />-->
    <!--<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureWebAppRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer" />-->
    <Add Type="WebApplication1.AwsEc2EnvironmentTelemetryInitializer, WebApplication1" />
    <Add Type="Microsoft.ApplicationInsights.WindowsServer.BuildInfoConfigComponentVersionTelemetryInitializer, Microsoft.AI.WindowsServer" />
    <!-- 省略 -->
  </TelemetryInitializers>
</ApplicationInsights>

ローカルか EC2 上かの判別は組み込んでいないので、このままだといまいちですが上手く判別する方法が見つからなかったので、とりあえずは保留にします。

この変更を行った ASP.NET アプリケーションを Elastic Beanstalk にデプロイすると、インスタンス別にメトリックを確認できるようになりました。

f:id:shiba-yan:20170626231430p:plain

ASP.NET アプリケーション向けに使う場合には、Application Insights も割と良い選択肢になるかと思いました。エージェント無しでパフォーマンスに関するメトリックが取れるのは結構良いです。

Dependency 周りは別途インストールが必要かもしれないですが、確認はまた今度にします。

Azure Cloud Services の移行先として Kubernetes + Windows Server Containers を検討してみる

既に Azure Cloud Services をバリバリ使って開発している場合は、そろそろ新しいサービスに乗り換えたいという思いを持っていることが多いのではないかと思います。

確かに Cloud Services は純粋な PaaS としての機能は十分ですが、CI/CD も組みにくいし RoleEnvironment など専用の処理になってしまうところが個人的には気に食わないです。なので Kubernetes と Windows Server Containers の組み合わせを移行先に出来ないか検討しました。

Kubernetes と Windows Server Containers については既に何回か検証しています。

ホスト OS の Windows Update や Windows Server Core の Docker Image のアップデートに関して少し難があるという判断をしていますが、それ以外は十分 Cloud Services を置き換えることが出来ると思います。

実際に検討した結果を以下にまとめます。Kubernetes と Windows Server Containers をバリバリ使わせてくれる仕事を募集しています。

Cloud Services vs Kubernetes

Cloud Services には Web Role と Worker Role の 2 種類が用意されていて、VIP スワップによって Blue-Green Deployment が行えるようになっています。最低限、この機能は実現する必要があります。

大雑把ですが、Cloud Services と Kubernetes の機能を比較しました。

Cloud Services Kubernetes
Web アプリケーション Web Role Deployment
バッチ処理 Worker Role Deployment / Cron Job
リソースの利用 1 アプリケーション 1 VM クラスタ内に任意の数を配置
ステージング環境 本番と同じ数の VM を用意 Deployment を個別に用意する
Blue-Green Deployment VIP スワップによる切り替え selector を使ったルーティング
デプロイにかかる時間 5-10 分ぐらい イメージが pull 済みであれば数秒
pull が必要な場合は 10 分ほど
CI/CD AppVeyor を使う*1 CI SaaS と kubectl を使って実現
スケーリング Azure Portal から変更 ACS の場合は Portal から変更
クラスタに余裕があれば Pod を増やす
監視 / アラート Azure に組み込み 別途用意する必要あり
OS アップデート Azure により自動 現在は手動
コンポーネントの追加 Startup Task で頑張る Docker Image をカスタマイズ

大体の機能が Kubernetes で実現できることが分かると思います。特にステージング環境に追加のコストが発生しない点や Startup Task でいろいろと頑張る必要がない点などメリットも多いです。

とはいえ、まだ Kubernetes の Windows サポート自体が Preview という課題はあります。Windows Server Containers 自体が立ち上がったばかりというのもありますが、早めに手を出しておくべき技術だと思います。

Kubernetes に 移行する方法

基本的な考え方として、Kubernetes では Deployment を使って Cloud Services の Web Role と Worker Role の機能を実現します。Replica Set が作成されるので、設定した数の Pod が動作することを保証できます。

Web Role と Worker Role の区別も Kubernetes としては違いがなく、IIS が動作しているかどうかぐらいです。その辺りは Docker Image を作成する時に違いが出ます。

Startup Task を Dockerfile に置き換える

Startup Task を使っていた場合には、予めバッチファイルとして作成していた Startup Task を Dockerfile 側で使うように変更しておく必要があります。

特殊な記述が無ければ、そのまま Dockerfile で実行する方法もあります。

FROM microsoft/iis

COPY Startup.cmd .
RUN Startup.cmd

これで Startup.cmd を実行済みの Docker Image が作成されます。移行としてはわかりやすい部類です。

Web Role の場合

Web Role から Kubernetes に移行する場合には、Cloud Services 向けの処理を置き換えや削除していくことになります。RoleEntryPoint などのクラスは必要ないので削除します。

要するに普通の ASP.NET アプリケーションにするように修正していくことになります。なのでデフォルトで参照されている Microsoft.WindowsAzure.* といったライブラリの参照は不要となります。

ASP.NET アプリケーションの場合は修正点は少ないと思いますが、Configuration 周りだけは RoleEnvironment.GetConfigurationSettingValue に依存していると思うので、ConfigurationManager と環境変数を使って設定できるように修正する必要があります。

一般的な IIS 向けの Dockerfile を使って ASP.NET アプリケーションをビルドすれば OK です。この辺りは Visual Studio 2017 の Docker サポート機能を利用しても良いかもしれません。

Worker Role の場合

Web Role と異なり Worker Role は Cloud Services の機能にべったりと依存していますが、やっていることは無限に動き続けるコンソールアプリケーションに近いので、RoleEntryPoint 周りをごっそりコンソールアプリケーションに移行すれば良いです。

以下は Visual Studio のテンプレートで生成される Worker Role のコードです。

public class WorkerRole : RoleEntryPoint
{
    private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);

    public override void Run()
    {
        Trace.TraceInformation("WorkerRole1 is running");

        try
        {
            this.RunAsync(this.cancellationTokenSource.Token).Wait();
        }
        finally
        {
            this.runCompleteEvent.Set();
        }
    }

    private async Task RunAsync(CancellationToken cancellationToken)
    {
        // TODO: 次のロジックを自分で作成したロジックに置き換えてください。
        while (!cancellationToken.IsCancellationRequested)
        {
            Trace.TraceInformation("Working");
            await Task.Delay(1000);
        }
    }
}

OnStart / OnStop は長くなるので省略しましたが、いつの間にかに非同期対応になっていました。while で無限ループになっているのをコンソールアプリケーションに書き換えるのは簡単だと思います。

Kubernetes は Pod を停止する前に SIGTERM を送るので、それを処理することで Graceful Shutdown も行えるのですが、Windows の場合はよくわかりませんでした。

ビルドしたアプリケーションは以下のような Dockerfile を使ってビルドします。

FROM microsoft/dotnet-framework

COPY ConsoleApp.exe .

ENTRYPOINT ["ConsoleApp.exe"]

Docker Image のビルドは AppVeyor などで行わせるのが良いと思います。この Docker Image を Kubernetes にデプロイすると、Worker Role と同様に実行され続けます。

f:id:shiba-yan:20170621234403p:plain

標準出力に書き出すとログとして確認できるようになるので、結構シンプルに扱えます。

実行する Pod の数は Replica Set によって保証されているので、安心して使うことが出来ます。スケーリングも数秒で行えるので、かなり柔軟に使うことが出来ます。

Blue-Green Deployment

Kubernetes の標準では Blue-Green Deployment はサポートされていないですが、selector を使うことで任意の Pod へのルーティングが可能なので、それを使って切り替えを行います。

実現するためには Service 1 つと Deployment を 2 つ作成しておきます。作成するための YAML はそれぞれ以下のような形になります。

apiVersion: v1
kind: Service
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: webapp
    color: blue
  type: LoadBalancer
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: webapp-blue
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: webapp
        color: blue
    spec:
      containers:
      - name: aspnet
        image: shibayan/aspnet-demo:v1
        ports:
        - containerPort: 80
          name: http
      nodeSelector:
        beta.kubernetes.io/os: windows
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: webapp-green
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: webapp
        color: green
    spec:
      containers:
      - name: aspnet
        image: shibayan/aspnet-demo:v2
        ports:
        - containerPort: 80
          name: http
      nodeSelector:
        beta.kubernetes.io/os: windows

バージョンは Docker Image のタグとして表現しています。ラベルに含めても良いかなと思います。

この状態で Service を確認すると、ラベルとして color: blue を指定した Pod が関連付いていることが分かります。外部エンドポイントからのリクエストは、この color: blue の Pod にルーティングされます。

f:id:shiba-yan:20170621223614p:plain

ブラウザで外部エンドポイントにアクセスすると、v1 のアプリが表示されます。

f:id:shiba-yan:20170621234702p:plain

そこで Kubernetes のダッシュボードから Service の selector を変更してみます。今は color: blue となっていますが、次は green に切り替えたいので color: green に変更します。

f:id:shiba-yan:20170621223513p:plain

これでアップデートを行うと、即座に対象の Pod が切り替わっていることが分かります。

f:id:shiba-yan:20170621223623p:plain

同じようにブラウザで外部エンドポイントにアクセスすると、今度は v2 のアプリが表示されます。コネクションが切れるまでは古い方にルーティングされるみたいですが、しばらくすると切り替わります。

f:id:shiba-yan:20170621234837p:plain

実際には現在の Service が指している先を確認した後に、変更する Deployment を決めていけば Blue-Green Deployment を行うことが出来ますね。Cloud Services とは異なり、ステージング用に別のインスタンスを必要としないので効率的です。

App Service に移行すれば良いのではという声も聞こえてきそうですが、Worker Role の機能を Web Jobs で実現するのは地味に面倒だと思っています。個別にスケールさせることを考えると辛そうです。

Azure のアラートを Azure Functions を使って Slack に投げる

Azure のアラートは前から Webhook での通知に対応しているので、Logic Apps や Flow などで受け取ってあげれば、Slack に簡単に通知できます。

単純な通知なら問題ないですが、Slack のもうちょっとリッチな通知を使いたいので、Azure Functions を使って整形してから Slack に通知するようにします。

素晴らしいことに Azure のアラートはペイロードの JSON が統一されているので、単一の Function を用意するといろんな部分で使いまわせます。ペイロードはドキュメントにまとまってます。

オートスケールの場合にも Webhook で通知できるので、設定しておくと便利でした。

説明するよりコードを出した方が早いので、Function の実装を出しておきます。単純に Webhook を受け取って、Slack 向けに整形しているだけです。オートスケール向けに特殊対応はしています。

#r "Newtonsoft.Json"

using System;
using System.Net;
using System.Net.Http;

using Newtonsoft.Json;

const string SlackEndpoint = "https://hooks.slack.com/services/xxxx";

static HttpClient httpClient = new HttpClient();

public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info($"Webhook was triggered!");

    string jsonContent = await req.Content.ReadAsStringAsync();
    dynamic data = JsonConvert.DeserializeObject(jsonContent);

    if (data.status == null || data.context == null)
    {
        return req.CreateResponse(HttpStatusCode.BadRequest);
    }

    var payload = new
    {
        attachments = new []
        {
            new
            {
                pretext = $"{data.context.name} is {data.status}",
                color = (data.operation == "Scale In" || data.operation == "Scale Out") ? "#439FE0" : (data.status == "Activated" ? "danger" : "good"),
                title = data.context.resourceName,
                title_link = data.context.portalLink,
                text = data.context.description ?? data.context.details
            }
        }
    };

    var jsonString = JsonConvert.SerializeObject(payload);

    await httpClient.PostAsync(SlackEndpoint, new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("payload", jsonString)
    }));

    return req.CreateResponse(HttpStatusCode.OK);
}

オートスケールの時なのか判別するために、operation の値をチェックしています。ポータルのリンクも設定するので、アラートが飛んできたときにリンクをクリックすると、該当するリソースに飛べて便利です。

アラートには Description を設定できるので、そこに日本語で説明を入れることで分かりやすくします。通知には Description を含めてあります。

f:id:shiba-yan:20170606232922p:plain

設定後に負荷を掛けてアラートを飛ばしてみると、以下のような通知が Slack に飛んできます。

f:id:shiba-yan:20170606232805p:plain

アラートがアクティブになると赤で、解決すると緑で表示するので一目でわかるようになってます。Description を書いておくと、何のアラートなのか分かりやすいです。

オートスケールの場合は Description を設定できないので、代わりに details を通知に含めています。

f:id:shiba-yan:20170606220635p:plain

インスタンスの増減が Slack で確認できるようになりました。

全く関係ないですが、App Service on Linux でも App Service Plan に対してはアラートを設定できるようになってるみたいです。GA までには Web App に対してのメトリックが欲しいですね。