読者です 読者をやめる 読者になる 読者になる

しばやん雑記

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

Kubernetes で .NET アプリケーションな Job を動かしてみる

Kubernetes には Job という機能があることを知ったので、Windows Server Containers でも試してみました。使いどころはわかってないですが、Windows でも動くことを確認したかったのです。

動き続けるタイプの Job を作りたい場合には、これまで通り Deployment と Replica Set を使った方が良さそうです。Job のイメージ定義は immutable なので後から変更できません。

とりあえず適当にコンソールアプリケーションを作成します。標準出力にメッセージを書くだけのアプリケーションをサクッと用意してビルドします。

using System;
using System.Threading;

namespace JobApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello Job : " + DateTime.Now);
        }
    }
}

このアプリケーションをイメージ化するために用意した Dockerfile は以下の通りです。ENTRYPOINT としてビルドしたアプリケーションを指定して、実行が終わったらコンテナも終わるようにします。

FROM では .NET Framework 4.6.2 のイメージを明示的に指定しておきます。ちなみに microsoft/dotnet は .NET Core を使うイメージなので注意。

FROM microsoft/dotnet-framework:4.6.2

COPY app /app

WORKDIR /app

ENTRYPOINT ["JobApplication.exe"]

作成したイメージを Docker Hub に push し、Kubernetes にデプロイするための YAML を用意します。ダッシュボードから作れないので YAML を用意する必要がありました。

最重要ポイントとして nodeSelector で os を windows として明示的に指定しないと、Linux で動作しているマスターにデプロイされて詰みます。

apiVersion: batch/v1
kind: Job
metadata:
  name: dotnet-job
spec:
  template:
    metadata:
      name: dotnet-job
    spec:
      containers:
      - name: dotnet-job
        image: shibayan/dotnet-job
      restartPolicy: OnFailure
      nodeSelector:
        beta.kubernetes.io/os: windows

これに気が付かずに何回も pull 時にエラーを出してしまって悩みました。デプロイメントを作成した時に上手くいった理由はよくわからないですが、Windows Server Containers を使うので指定は必要だと思います。

YAML を作成したら Kubernetes のダッシュボードからアップロードするだけです。

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

デプロイが完了すると Job が実行されて、ログが見れるようになります。

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

とりあえず Windows Server Containers でも動作はしましたが、再実行するには Job の再デプロイが必要なので、いまいち使いどころがやっぱりわかりませんでした。

ちょっと前に追加された Cron Jobs の方が使いどころが多いと思いました。

しかし、現在の Azure Container Service で作られた Kubernetes では有効化されていないので、残念ながらデプロイのタイミングで検証エラーになってしまいます。

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

acs-engine を使えばカスタマイズ出来そうですが、それはまた今度試してみることにします。

nodeSelector について学べたので、今回はそれで良しとしておきます。

AppVeyor から Azure Container Service 上の Kubernetes へ自動でデプロイを行う

これまで AppVeyor と Kubernetes でやってきたことの集大成として、AppVeyor から Azure Container Service 上の Kubernetes への自動デプロイを試してみます。

やってきたことは以下のエントリを参照してください。Kubernetes はかなり良いですね。

AppVeyor から Kubernetes にデプロイするためには、認証情報が必要なので先に作成しておきます。

Service Account Token を取得

トークンを作成するためには kubectl を使って Service Account をまず作成する必要があります。

kubectl create serviceaccount appveyor

コマンドで Service Account を作成したら、ダッシュボードでトークンを確認できるので簡単です。

コンフィグの中にあるシークレットから、Base64 デコード済みのトークンをコピーできます。

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

作成はコマンドで作りますが、削除と編集はダッシュボードから簡単に行えるのがアンバランスでした。

appveyor.yml に処理を追加

現在の Kubernetes ダッシュボードは以下のようになっています。基本的にアップデートを行うだけなので、予めアプリケーションはデプロイしてあります。

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

appveyor.yml には kubectl のダウンロードと、イメージの更新コマンドを追加します。

version: '{build}'

image: Visual Studio 2017

environment:
  DOCKER_USER: XXXXX
  DOCKER_PASS:
    secure: XXXXX
  DOCKER_IMAGE_NAME: shibayan/appveyor-aspnet-docker:$(APPVEYOR_REPO_BRANCH)-$(APPVEYOR_BUILD_NUMBER)
  KUBE_MASTER: https://XXXXX
  KUBE_TOKEN:
    secure: XXXXX

install:
  - curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.6.2/bin/windows/amd64/kubectl.exe

before_build:
  - nuget restore

build_script:
  - msbuild .\AspNetApplication\AspNetApplication.csproj /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="..\publish";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release
  - docker build -t %DOCKER_IMAGE_NAME% .

test_script:
  - ps: |
      $cid = docker run -d $env:DOCKER_IMAGE_NAME
      $hostip = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" $cid
      curl "http://${hostip}" -UseBasicParsing

deploy_script:
  - docker login -u %DOCKER_USER% -p %DOCKER_PASS%
  - docker push %DOCKER_IMAGE_NAME%
  - kubectl set image deployment/aspnet aspnet=%DOCKER_IMAGE_NAME% --server=%KUBE_MASTER% --token=%KUBE_TOKEN% --insecure-skip-tls-verify=true

kubectl は curl でダウンロードするだけなので簡単です。現在のデプロイメントに対してイメージを更新するコマンドは kubectl set image になります。

オプションとして --server と --token を追加して、Kubernetes のマスターの URL とさっき作成したトークンを渡します。証明書の検証が問題になるので --insecure-skip-tls-verify=true を指定してスキップします。

AppVeyor で実際にビルド

appveyor.yml の修正を GitHub にコミットして、実際に AppVeyor でビルドを行いました。kubectl の実行結果から成功したことが分かります。たったこれだけでアップデートが行われます。

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

アップデート中にダッシュボードを確認すると、新しいレプリカセットとポッドが作成されています。

既にベースイメージは pull されているので、コンテナの起動は非常に高速です。

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

アップデートが完了したら、ブラウザで外部エンドポイントにアクセスしてみます。アプリケーションに違いがないと、本当にアップデートが行われたのか確認しにくいので、テキストを少しだけ変えておきました。

例によって Razor のプリコンパイルを行っていないので、起動に少し時間がかかりますが表示されます。拍子抜けするぐらいあっさりと AppVeyor からのデプロイが行えました。

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

ちなみに GitHub に push してから AppVeyor でのビルドが完了するまで 2 分ほどです。Docker Hub よりネットワーク的に近い Azure Container Registry を使うと pull は早くなりそうです。

今回は 1 エージェントかつ 1 レプリカで試しましたが、Kubernetes はまだ VMSS に対応していないみたいなので、エージェントを増やす手間がかかります。レプリカに関しては別の機会にまとめます。

Azure Container Service を使って Kubernetes と Windows Server Containers を試してみる

気が付いていなかったのですが、Azure Container Service のオーケストレータとして Kubernetes を選ぶと、Windows が選択できるようになっていたらしいです。もう 2 か月ぐらい前というのが驚きです。

まだ Public Preview になってないだろと思っていたら、ちゃんとドキュメントもありました。確か Swarm が Private Preview だったはずなんですが、いったいどこに行ってしまったのやら。

Windows Server Containers が使えるコンテナサービスを待望している人間としては、これは試しておかないといけないと思ったので、実際にクラスタを作成して試します。

Kubernetes クラスタを作成

Azure Portal から ACS を作成する際に Kubernetes を選ぶのは必須です。

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

例によって Japan East は VM が高いので、少し安い Japan West を選んでおきました。

次に Kubernetes のマスター向け設定を行います。マスターは Linux で動作するので SSH の公開鍵が必要です。そして Kubernetes が Azure のリソースを操作するために必要なサービスプリンシパルも設定します。

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

サービスプリンシパルは Azure CLI 2.0 を使うと簡単に作れます。*1

最後はエージェントの設定なので、ここで Windows を選びます。Windows を選ぶと RDP で接続するための認証情報を入力する必要があるので、適当に埋めておきます。

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

VM のサイズは中途半端にけちると苦労するので、デフォルトのままにしておきました。これでデプロイすると、Windows Server Containers 対応の Kubernetes が上がってきます。

Kubernetes への接続

Kubernetes への接続を行うためには kubectl と認証情報のダウンロードが必要らしいです。幸い Azure CLI 2.0 を使うと簡単にダウンロード出来るので、ドキュメントを紹介しておきます。

手元の PC には Azure CLI 2.0 を入れていなかったので、手動で kubectl をダウンロードして認証情報も SCP で拾ってきました。kubectl は単一の実行ファイルなので楽ですね。

kubectl と認証情報の設定が終わったので、接続テストとしてノードの情報を確認しておきました。

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

マスターとエージェントの情報が返ってきたので、認証情報に問題がないことが確認できました。

最初からコマンドで全て操作するのはしんどいので、Kubernetes のダッシュボードを使います。kubectl の proxy コマンドを使うとダッシュボードへの接続が行えます。

kubectl proxy

表示されたアドレスにブラウザでアクセスすると、Kubernetes のダッシュボードが表示されます。

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

Azure Container Service のドキュメントでは YAML ベースでデプロイを行っていましたが、折角なのでダッシュボードを使って試します。

アプリケーションをデプロイ

Kubernetes の用語については説明を省きますが、基本的に画面の指示通りに進めていけば問題ないです。

ナビゲーションからノードを選択すると稼働中のインスタンスとメトリックを確認できます。ECS はメトリックの表示に対応していなかったですが、Kubernetes はちゃんと確認することが出来て良いですね。

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

それはともかく、まずはアプリケーションをデプロイしてみます。デプロイするのは前回 AppVeyor で作成した ASP.NET MVC アプリケーションのイメージです。

JSON / YAML のアップロードも出来ますが、UI でポチポチ設定してみます。

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

重要な設定としてはサービスを外部にして、ターゲットポートを 80 に設定することです。

外部にしないと外からアクセス出来なくなりますし、作成したイメージは 80 でリクエストを待機しているので、ターゲットポートを間違えると繋がりません。

実際に配備を行うと、最初は例によってイメージの pull で時間がかかりました。これは ACS で使われている Windows Server 2016 のイメージが古いのが最大の原因だと思われます。

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

使われている Windows Server 2016 と Docker のバージョンは AppVeyor と比べて古いものだったので、公式らしく最新に追従する姿勢を見せてほしいところです。

ワークロードを選択すると、一度にステータスの確認が出来るので便利です。サービスを含め全てが緑色になればデプロイ完了なので、外部エンドポイントが表示されるようになります。

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

この外部エンドポイントは Azure Load Balancer を指しています。Load Balancer と Public IP Address は Kubernetes によって自動的に作成されていました。

外部エンドポイントにアクセスすると、前回作成した ASP.NET MVC アプリケーションが表示されました。

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

前に試した ECS よりも Kubernetes の方が Windows Server Containers への対応が進んでいる印象を受けました。まだ対応自体はプレビューですが、ダッシュボードから操作が行えるのは好印象です。

そろそろ Azure Container Service も Managed Disk を使って、いい感じにリソースを整理して欲しい気持ちでいっぱいです。あとベースイメージのバージョンアップは必須だと思います。