ROMANCE DAWN for the new world

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

Global Azure Bootcamp 2017 に参加して LT してきました

jazug.connpass.com

先週の土曜日、Global Azure Bootcamp 2017 に参加して LT してきました。

speakerdeck.com

2週間ぐらい前に Microsoft が Deis を買収した発表があったので、Helm の話をしました。Helm は、Kubernetes のパッケージマネージャーなので、install コマンドでサクッとデプロイできます。YAML をゴリゴリ書くのは大変なので、Charts Repository を参考にするのもありです。今後、Kubernetes を便利に使えるようになっていくことに期待したいです。

本編としては、Azure 最新情報のまとめは、JAZUG 女子部のお二人による掛け合いが流石でした。スライドもゆるまとめと言いつつも、とても分かりやすい内容です。
bitFlyer さんによる障害復旧の話は、大変興味深かったです。一部のサービスに障害が発生しても、顧客にサービスを提供し続けられるアーキテクチャは重要です。
DocumentDB の話は、タイトル通り DeepDive でした。PaaS で提供される NoSQL でポテンシャルの高いサービスなので、プロダクション環境で使ってみたいところです。

そういえば、アンケートを作り忘れました。多くの方にご参加頂けたので、感想や今後のリクエストなどを聞いてみたかったです。今回初めて参加された方も多い印象でした。何かあれば、お気軽に Facebook の JAZUG グループや Twitter の #jazug までお願いします。

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