ROMANCE DAWN for the new world

Microsoft Azure を中心とした技術情報を書いています。

Azure Container Service の Kubernetes に YAML ファイルを使ってデプロイする

前回の記事では、Azure Container Service に Kubernetes を展開し、ASP.NET Core アプリケーションをデプロイしました。
gooner.hateblo.jp

今回は、kubectl の run コマンドを使わずに、YAML ファイルを使ってデプロイしてみます。元ネタは、こちらの公式ドキュメントになります。

デプロイに関連する用語

Pod

Pod は、複数のコンテナをまとめて管理できます。Pod 内のコンテナは同じホストにデプロイされるため、複数のコンテナでディレクトリなどを共有できます。
f:id:TonyTonyKun:20170410091436p:plain

Replica Set

Replica Set は、Pod のレプリカを管理します。常時レプリカを監視しているので、障害が発生したら Pod を破棄して、新しい Pod を立ち上げてくれます。
f:id:TonyTonyKun:20170410091516p:plain

Deployment

Deployment は、Replica Set を管理します。Replica Set の状態を履歴として管理しているので、更新やロールバックを行うことができます。
コンテナの Image バージョンを更新する Kubectl コマンドを実行すると、新しい Replica Set B が作成され、古い Replica Set A は破棄されます。
f:id:TonyTonyKun:20170410092559p:plain
Deployment は、Replica Set を宣言的に定義でき、ロールアウト履歴なども管理できるため、次世代 Replication Controller と呼ばれています。

Deployment の作成

3つの nginx の Pod を持つ Replica Set を管理する Deployment を作成する場合には、以下のような YAML ファイルを定義します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        ports:
        - containerPort: 80

Azure Container Service に SSH 接続して、kubectl の create コマンドを実行します。今回は、YAML ファイルを GitHub で管理しています。

$ git clone https://github.com/thara/k8s-samples.git
$ cd k8s-samples
$ kubectl create -f nginx/deployment-nginx.yaml --record
deployment "nginx-deployment" created

パラメータの「--record」は必須ではありませんが、指定することで kubectl コマンドの実行履歴を記録できます。
Deployment、Replica Set、Pod が作成された結果を確認します。

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           1m

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3450387708   3         3         3         1m

$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-3450387708-06x07   1/1       Running   0          2m        app=nginx,pod-template-hash=3450387708
nginx-deployment-3450387708-dv8sq   1/1       Running   0          2m        app=nginx,pod-template-hash=3450387708
nginx-deployment-3450387708-g7v2b   1/1       Running   0          2m        app=nginx,pod-template-hash=3450387708

Deployment の更新

Pod で使われている Image を更新する場合には、以下のような YAML ファイルを定義します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.11 # Update the version of nginx from 1.10 to 1.11
        ports:
        - containerPort: 80

GitHub から更新した YAML ファイルを Pull した後、kubectl の apply コマンドを実行します。

$ git pull
$ kubectl apply -f nginx/deployment-nginx.yaml --record
deployment "nginx-deployment" configured

Replica Set と Pod が更新された結果を確認します。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3450387708   0         0         0         20m
nginx-deployment-3531652349   3         3         3         7m

$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-3531652349-7wrw8   1/1       Running   0          7m        app=nginx,pod-template-hash=3531652349
nginx-deployment-3531652349-chpw0   1/1       Running   0          7m        app=nginx,pod-template-hash=3531652349
nginx-deployment-3531652349-g2fqc   1/1       Running   0          7m        app=nginx,pod-template-hash=3531652349

古い Replica Set(nginx-deployment-3450387708)の Pod が破棄され、新しい Replica Set(nginx-deployment-3531652349)の Pod が作成されたことが分かります。

Deployment の履歴

kubectl の rollout history コマンドで実行履歴を確認してみます。

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION        CHANGE-CAUSE
1               kubectl create -f nginx/deployment-nginx.yaml --record
2               kubectl apply -f nginx/deployment-nginx.yaml --record

履歴の一覧を表示する以外にも、特定の Revision の履歴を確認することもできます。

$ kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
  Labels:       app=nginx
        pod-template-hash=3531652349
  Annotations:  kubernetes.io/change-cause=kubectl apply -f nginx/deployment-nginx.yaml --record
  Containers:
   nginx:
    Image:      nginx:1.11
    Port:       80/TCP
    Volume Mounts:      <none>
    Environment Variables:      <none>
  No volumes.

$ kubectl rollout history deployment/nginx-deployment --revision=1
deployments "nginx-deployment" with revision #1
  Labels:       app=nginx
        pod-template-hash=3450387708
  Annotations:  kubernetes.io/change-cause=kubectl create -f nginx/deployment-nginx.yaml --record
  Containers:
   nginx:
    Image:      nginx:1.10
    Port:       80/TCP
    Volume Mounts:      <none>
    Environment Variables:      <none>
  No volumes.

Pod で使われている nginx の Image が更新されたことが分かります。

Deployment のロールバック

Deployment の更新後に予期せぬ問題が発生し、切り戻したいこともあるでしょう。kubectl の rollout undo コマンドでロールバックしてみます。

$ kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back

Replica Set と Pod がロールバックされた結果を確認します。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3450387708   3         3         3         32m
nginx-deployment-3531652349   0         0         0         19m

$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-3450387708-0jmtc   1/1       Running   0          1m        app=nginx,pod-template-hash=3450387708
nginx-deployment-3450387708-rvrn9   1/1       Running   0          1m        app=nginx,pod-template-hash=3450387708
nginx-deployment-3450387708-wsnw7   1/1       Running   0          1m        app=nginx,pod-template-hash=3450387708

新しい Replica Set(nginx-deployment-3531652349)の Pod が破棄され、古い Replica Set(nginx-deployment-3450387708)に新しい Pod が作成されたことが分かります。
1つ前の Revision だけでなく、特定の Revision にロールバックすることも可能です。

まとめ

今回は、kubectl の run コマンドを使わずに、YAML ファイルを使ってデプロイしてみました。
kubernetes にコンテナをデプロイして運用するには、Deployment や Replica Set の概念を理解しておく必要があります。YAML ファイルに定義することで、デプロイ環境の作成や破棄が簡単にできますし、kubernetes 側で宣言的な Pod の構成を維持してくれます。
次回は、ボリュームをマウントすることで、コンテナ間で共有したり永続化する方法を試してみたいと思います。
gooner.hateblo.jp

Azure Container Service の Kubernetes に ASP.NET Core アプリケーションをデプロイする

前回の記事では、Azure Container Service に Kubernetes を展開し、nginx をデプロイしました。
gooner.hateblo.jp
今回は、ASP.NET Core アプリケーションをデプロイして、スケールアウトやアプリケーションの更新を試してみます。

ASP.NET Core アプリケーションを作る

先月アップデートされた dotnet コマンドを使って、ASP.NET Core MVC のテンプレートでアプリケーションを作ります。

$ dotnet new mvc -o mvcapp
$ cd mvcapp
$ dotnet restore
$ dotnet build
$ code .

Docker イメージを作りたいので、テンプレートを少し修正します。
まずは、Visual Studio Code を開いて、Dockerfile を追加します。

FROM microsoft/dotnet:1.1.1-sdk
COPY . /app
WORKDIR /app

RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]

EXPOSE 5000/tcp
ENV ASPNETCORE_URLS http://*:5000

ENTRYPOINT ["dotnet","run"]

次に、Dockerfile で環境変数に指定したURL(ポート番号)をアプリケーションの起動時に設定します。
Program.cs を開いて、環境変数から取得した値を UseUrls() メソッドで設定します。

public class Program
{
    public static void Main(string[] args)
    {
        // 環境変数からURLを取得する
        var config = new ConfigurationBuilder()
            .AddEnvironmentVariables("")
            .Build();
        var url = config["ASPNETCORE_URLS"] ?? "http://*:5000";
        
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseUrls(url)           // URLを設定する
            .Build();

        host.Run();
    }
}

Docker イメージをビルドします。

$ docker build -t thara0402/mvcapp:v1 ./

ビルドした Docker イメージを Docker Hub にプッシュします。

$ docker login
Username: thara0402
Password: 
Login Succeeded
$ docker push thara0402/mvcapp:v1

f:id:TonyTonyKun:20170320110613p:plain

ASP.NET Core アプリケーションをデプロイする

Azure Container Service に SSH 接続します。

$ ssh thara@gooner0318mgmt.eastus.cloudapp.azure.com -A

先ほどプッシュした Docker Hub のイメージを使って、Kubernetes に ASP.NET Core アプリケーションをデプロイします。Pod が作成されるので、外部から接続できるように Service で 5000 ポートを公開します。

$ kubectl run mvcapp --image thara0402/mvcapp:v1
deployment "mvcapp" created

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
mvcapp-3237912479-6b159   1/1       Running   0          1m

$ kubectl expose deployments mvcapp --port=5000 --type=LoadBalancer
service "mvcapp" exposed

$ kubectl get svc
NAME         CLUSTER-IP   EXTERNAL-IP    PORT(S)          AGE
kubernetes   10.0.0.1     <none>         443/TCP          1h
mvcapp       10.0.47.43   52.168.28.12   5000:32648/TCP   2m

ブラウザで 52.168.28.12:5000 に接続すると、ASP.NET Core MVC のテンプレートが表示されます。

f:id:TonyTonyKun:20170320110631p:plain

ASP.NET Core アプリケーションをスケールアウトする

デプロイした ASP.NET Core アプリケーションをスケールアウトしてみます。

$ kubectl scale deployment mvcapp --replicas=3
deployment "mvcapp" scaled

$ kubectl get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
mvcapp    3         3         3            3           12m

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
mvcapp-3237912479-1mrkl   1/1       Running   0          13s
mvcapp-3237912479-5k3bq   1/1       Running   0          13s
mvcapp-3237912479-6b159   1/1       Running   0          12m

Pod が2つ追加されて、3つにスケールアウトされました。Service がロードバランサーとなり、リクエストを割り振ってくれます。数秒でイメージを複製できるのはコンテナの便利なところです。

ASP.NET Core アプリケーションを更新する

ローカルで ASP.NET Core アプリケーションを更新して、Docker イメージをビルドします。タグは、V2 に変更しています。ビルドした Docker イメージを Docker Hub にプッシュします。

$ docker build -t thara0402/mvcapp:v2 ./
$ docker push thara0402/mvcapp:v2

f:id:TonyTonyKun:20170320110654p:plain

Azure Container Service に SSH 接続して、Pod にデプロイされている Docker イメージを更新します。

$ kubectl set image deployments/mvcapp mvcapp=thara0402/mvcapp:v2

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
mvcapp-3312820128-400cp   1/1       Running   0          30s
mvcapp-3312820128-8wz2f   1/1       Running   0          30s
mvcapp-3312820128-tgvll   1/1       Running   0          30s

Pod の名前が変わっていることから、自動的に古い Pod が破棄され、新しい Docker イメージを使った Pod が作成されたことが分かります。
ブラウザで 52.168.28.12:5000 に接続すると、「mvcapp v2」に変更された ASP.NET Core MVC のアプリケーションが表示されます。

f:id:TonyTonyKun:20170320110712p:plain

ASP.NET Core アプリケーションを削除する

最後に、デプロイした ASP.NET Core アプリケーションを削除します。

$ kubectl delete service,deployment mvcapp
service "mvcapp" deleted
deployment "mvcapp" deleted

まとめ

今回は、Kubernetes に ASP.NET Core アプリケーションをデプロイして、スケールアウトやアプリケーションの更新を試してみました。

f:id:TonyTonyKun:20170321003533p:plain

ASP.NET Core アプリケーションの Docker イメージを Docker Hub 経由で、Pod にコンテナをデプロイしました。宣言的なコマンドを実行するだけで、簡単に Pod のレプリケーションを作成し、スケールアウトを構成できます。
ASP.NET Core アプリケーションの更新は、新しい Docker イメージを設定することで、自動的に古い Pod が破棄され、新しい Pod が作成されます。Pod を1つずつ更新したり、スワップする必要もありません。
次回以降は、もう少し実践的な内容を試してみたいと思います。
gooner.hateblo.jp

Azure Container Service で Kubernetes を試してみた

先週、Azure Container Service の Kubernetes 対応が GA したので、試してみました。
azure.microsoft.com

Azure Container Service は、Docker コンテナ上で動かすアプリケーションをホストできるサービスです。コンテナ オーケストレーションである Docker Swarm、DC/OS、Kubernetes を選択でき、コンテナ環境を簡単に構築できます。Azure の仮想マシンや仮想ネットワークなどのインフラストラクチャーの上に、オープンソースのコンテナ オーケストレーションが展開されます。
Kubernetes は、Google が公開しているオープンソースのコンテナ オーケストレーションの1つです。Docker だけでもコンテナーの作成してイメージを展開できますが、複数のコンテナを管理するにはオーケストレーションがあると便利です。

事前準備

Kubernetes を選択した場合のみ、 Azure Active Directory のサービス プリンシパルが必要となります。サービス プリンシパルの作成方法にはいくつかありますが、今回は Docker コンテナで Azure CLI 2.0 を使います。Docker for Mac や Docker for Windows をインストールした環境で、次のコマンドを実行します。

$ docker run -it azuresdk/azure-cli-python:latest bash

Azure にログインします。

$ az login
To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code GZGP8E89A to authenticate.

メッセージに表示されているURLにブラウザでアクセスしてコードを入力し、Azure アカウントでログインします。

次のコマンドの "mySubscriptionID" に Azure のサブスクリプションIDをセットして実行すると、サービス プリンシパルを作成できます。

$ az account set --subscription "mySubscriptionID"
$ az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/mySubscriptionID"
{
  "appId": "e8bbb9a0-xxxx-xxxx-xxxx-8eb054fc5ef2",
  "displayName": "azure-cli-2017-02-26-15-18-01",
  "name": "http://azure-cli-2017-02-26-15-18-01",
  "password": "378022a9-xxxx-xxxx-xxxx-127bb49cb1eb",
  "tenant": "2f2404eb-xxxx-xxxx-xxxx-6454b99e51a5"
}

"appId" と "password" を使いますので、メモしておきます。"password" は、このタイミングでしか取得できません。

Azure Container Service を作成する

Azure ポータルから、New - Compute - Azure Container Service を選択します。Orchestrator で Kubernetes を選択し、リソースグループやロケーションを指定します。

f:id:TonyTonyKun:20170227025056p:plain

DNS Name prefix には任意の名前を指定し、SSH で接続するユーザー名と公開キーを入力します。サービス プリンシパルの "clinet ID" には "appId"、"client secret" には "password" を入力します。

f:id:TonyTonyKun:20170227030435p:plain

Agent の数や仮想マシンのサイズは、初期値のまま進めます。Operating System では、プレビューで対応している Windows Server Container を選択することも可能ですが、今回は Linux を選択します。

f:id:TonyTonyKun:20170227030257p:plain

最後に、ここまでの設定内容を確認して、作成を開始します。

f:id:TonyTonyKun:20170227030500p:plain

数分待つと作成が完了し、リソースグループ内に作成された一覧が表示されます。仮想マシンや仮想ネットワークを利用して、Azure Container Service のクラスターが構築されたことを確認できます。

f:id:TonyTonyKun:20170227033410p:plain

Deployments の "2 Succeeded" のリンクをクリックすると、デプロイされた構成情報を確認できます。SSH 接続するための情報は、SSHMASTER0 から取得できます。

f:id:TonyTonyKun:20170227033433p:plain

Azure Container Service に接続する

Azure Container Service に SSH 接続します。

$ ssh thara@gooer0227mgmt.eastus.cloudapp.azure.com -A

Kubernetes のコマンド ライン クライアントである kubectl を使って、構成を確認してみます。
バージョンを確認すると、Ver.1.5 がデプロイされていることがわかります。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.3", GitCommit:"029c3a408176b55c30846f0faedf56aae5992e9b", GitTreeState:"clean", BuildDate:"2017-02-15T06:40:50Z", GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.3", GitCommit:"029c3a408176b55c30846f0faedf56aae5992e9b", GitTreeState:"clean", BuildDate:"2017-02-15T06:34:56Z", GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}

次に、ノードを確認すると、マスターとエージェントの2つの仮想マシンが構成されていることがわかります。

$ kubectl get nodes
NAME                    STATUS                     AGE
k8s-agent-d8d36729-0    Ready                      38m
k8s-master-d8d36729-0   Ready,SchedulingDisabled   38m

nginx をデプロイする

コンテナ上で動かすアプリケーションを手っ取り早く確認したいので nginx をデプロイしてみます。

$ kubectl run nginx --image nginx
deployment "nginx" created

nginx の Docker コンテナが Pod で起動していることを確認します。Pod は、複数のコンテナをまとめて管理できます。Pod 内のコンテナは同じホストにデプロイされるため、複数のコンテナでストレージなどを共有できます。現在の環境は、1つの Pod に1つの nginx コンテナが実行されています。

$ kubectl get pods
NAME                    READY     STATUS    RESTARTS   AGE
nginx-701339712-l7804   1/1       Running   0          3m

nginx の Docker コンテナを外部に80番ポートで公開します。Pod にはプライベートな IP アドレスが割り当てられているので、Service として外部に公開する必要があります。Service には、ロードバランサー的な役割もあります。

$ kubectl expose deployments nginx --port=80 --type=LoadBalancer
service "nginx" exposed

Azure のロードバランサーにルールを追加したりしてるので、設定が反映するまでに少し時間がかかります。

$ kubectl get svc
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   10.0.0.1       <none>        443/TCP        1h
nginx        10.0.211.143   <pending>     80:30445/TCP   42s

少し待つと、パブリックIPアドレスが割り当てられます。

$ kubectl get svc
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   10.0.0.1       <none>        443/TCP        1h
nginx        10.0.211.143   13.92.95.76   80:30445/TCP   8m

ブラウザでパブリックIPアドレスにアクセスすると、nginx の画面が表示されます。

f:id:TonyTonyKun:20170227031516p:plain

まとめ

Azure Container Service の Kubernetes について、今回の内容を整理します。

f:id:TonyTonyKun:20170321001631p:plain

kubernetes には、複数のコンテナをまとめて管理する Pod があり、外部公開するためのロードバランサーの役割を Service が担います。kubectl コマンドを使うことで、kubernetes を操作できます。
Azure Container Service は、Azure の仮想マシンや仮想ネットワークなどをテンプレート化した環境に kubernetes を配置してくれるので、インフラの構築など面倒なことを考えずに、すぐに kubernetes を使い始めることができます。
今回は初歩的な内容を紹介しましたが、次回以降はもう少し詳しく見ていきたいと思います。
gooner.hateblo.jp