1. 概要
Microsoft .NET Core は、コンテナ内でネイティブに実行できる .NET のオープンソースかつクロス プラットフォーム バージョンです。.NET Core は GitHub で提供され、Microsoft と .NET コミュニティによって管理されています。このラボでは、コンテナ化された .NET Core アプリを Google Kubernetes Engine(GKE)にデプロイします。
このラボでは、アプリケーションをデベロッパーのローカル環境で開発してから本番環境にデプロイする一般的な開発パターンに従います。ラボの前半では、Cloud Shell で実行されているコンテナを使用して、サンプルの .NET Core アプリケーションを検証します。検証が完了すると、アプリは GKE を使用して Kubernetes にデプロイされます。このラボには、GKE クラスタを作成する手順が含まれています。
ラボの後半では、アプリに小さな変更を加え、そのアプリ インスタンスを実行しているコンテナのホスト名を表示します。更新されたアプリが Cloud Shell で検証され、新しいバージョンを使用するように Deployment が更新されます。次の図は、このラボで行う一連のアクティビティを示しています。
料金
記載どおりにこのラボを実行する場合は、次のサービスの通常の費用が適用されます。
2. 設定と要件
前提条件
このラボを完了するには、Google Cloud アカウントとプロジェクトが必要です。新しいプロジェクトの作成手順について詳しくは、こちらの Codelab をご覧ください。
このラボでは、Cloud Shell で実行される Docker を使用します。Cloud Shell は Google Cloud コンソールから利用でき、gcloud や Docker など多くの便利なツールがあらかじめ構成されています。Cloud Shell にアクセスする方法を以下に示します。右上にある Cloud Shell アイコンをクリックすると、コンソール ウィンドウの下部ペインに Cloud Shell が表示されます。
GKE クラスタの代替構成オプション(省略可)
このラボでは Kubernetes クラスタが必要です。次のセクションでは、シンプルな構成の GKE クラスタを作成します。このセクションでは、GKE を使用して Kubernetes クラスタをビルドするときに使用する代替構成オプションを提供する、いくつかの gcloud
コマンドについて説明します。たとえば、以下のコマンドを使用すると、さまざまなマシンタイプ、ゾーン、さらには GPU(アクセラレータ)を識別できます。
gcloud compute machine-types list
コマンドでマシンタイプを一覧表示します。- 次のコマンドで GPU を一覧表示する
gcloud compute accelerator-types list
- 次のコマンドでコンピューティング ゾーンを一覧表示します。
gcloud compute zones list
- gcloud コマンドのヘルプを表示する
gcloud container clusters --help
- たとえば、Kubernetes クラスタ
gcloud container clusters create --help
の作成に関する詳細がわかります。
- たとえば、Kubernetes クラスタ
GKE の構成オプションの一覧については、このドキュメントをご覧ください。
Kubernetes クラスタの作成を準備する
Cloud Shell で、いくつかの環境変数を設定し、gcloud クライアントを構成する必要があります。これを行うには、次のコマンドを使用します。
export PROJECT_ID=YOUR_PROJECT_ID
export DEFAULT_ZONE=us-central1-c
gcloud config set project ${PROJECT_ID}
gcloud config set compute/zone ${DEFAULT_ZONE}
GKE クラスタを作成する
このラボでは .NET Core アプリを Kubernetes にデプロイするため、クラスタを作成する必要があります。次のコマンドを使用して、GKE を使用して Google Cloud に新しい Kubernetes クラスタを作成します。
gcloud container clusters create dotnet-cluster \
--zone ${DEFAULT_ZONE} \
--num-nodes=1 \
--node-locations=${DEFAULT_ZONE},us-central1-b \
--enable-stackdriver-kubernetes \
--machine-type=n1-standard-1 \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--enable-ip-alias
--num-nodes
は、ゾーンごとに追加するノード数で、後でスケーリングできます。--node-locations
は、ゾーンのカンマ区切りのリストです。この場合、上記の環境変数で特定したゾーンとus-central1-b
が使用されます。- 注: このリストには重複した項目を含めることはできません
--workload-pool
は、GKE ワークロードが Google Cloud サービスにアクセスできるように Workload Identity を確立します。
クラスタのビルド中は、次の画面が表示されます。
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
kubectl を構成する
Kubernetes クラスタを操作する基本的な方法は、kubectl
CLI です。作成した新しいクラスタで使用するには、そのクラスタに対して認証を行うように構成する必要があります。これを行うには、次のコマンドを使用します。
$ gcloud container clusters get-credentials dotnet-cluster --zone ${DEFAULT_ZONE}
Fetching cluster endpoint and auth data.
kubeconfig entry generated for dotnet-cluster.
これで、kubectl
を使用してクラスタを操作できるようになりました。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-dotnet-cluster-default-pool-02c9dcb9-fgxj Ready <none> 2m15s v1.16.13-gke.401
gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 Ready <none> 2m24s v1.16.13-gke.401
3. ローカルでテストして目的の機能を確認する
このラボでは、Docker Hub の公式 .NET リポジトリにある以下のコンテナ イメージを使用します。
コンテナをローカルで実行して機能を確認する
Cloud Shell で次の Docker コマンドを実行して、Docker が正常に動作していること、.NET コンテナが期待どおりに動作することを確認します。
$ docker run --rm mcr.microsoft.com/dotnet/samples
Hello from .NET!
__________________
\
\
....
....'
....
..........
.............'..'..
................'..'.....
.......'..........'..'..'....
........'..........'..'..'.....
.'....'..'..........'..'.......'.
.'..................'... ......
. ......'......... .....
. ......
.. . .. ......
.... . .......
...... ....... ............
................ ......................
........................'................
......................'..'...... .......
.........................'..'..... .......
........ ..'.............'..'.... ..........
..'..'... ...............'....... ..........
...'...... ...... .......... ...... .......
........... ....... ........ ......
....... '...'.'. '.'.'.' ....
....... .....'.. ..'.....
.. .......... ..'........
............ ..............
............. '..............
...........'.. .'.'............
............... .'.'.............
.............'.. ..'..'...........
............... .'..............
......... ..............
.....
Environment:
.NET 5.0.1-servicing.20575.16
Linux 5.4.58-07649-ge120df5deade #1 SMP PREEMPT Wed Aug 26 04:56:33 PDT 2020
ウェブアプリの機能を確認する
のサンプル ウェブ アプリケーションは、Cloud Shell でも検証できます。次の Docker の実行コマンドでは、ポート 80
を公開し、localhost
ポート 8080
にマッピングする新しいコンテナを作成します。この場合の localhost
は Cloud Shell 内にあることを思い出してください。
$ docker run -it --rm -p 8080:80 --name aspnetcore_sample mcr.microsoft.com/dotnet/samples:aspnetapp
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {64a3ed06-35f7-4d95-9554-8efd38f8b5d3} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
これはウェブアプリであるため、ウェブブラウザで表示および検証する必要があります。次のセクションでは、ウェブでプレビューを使用して、Cloud Shell でその確認を行う方法を説明します。
4. 「ウェブでプレビュー」を使用して Cloud Shell からサービスにアクセスする
Cloud Shell にはウェブでプレビュー機能があり、Cloud Shell インスタンスで実行中のプロセスをブラウザを使用して操作できます。
[ウェブでプレビュー] を使用するCloud Shell でアプリを表示する
Cloud Shell で [ウェブでプレビュー] ボタンをクリックし、[ポート 8080 でプレビュー] を選択します。(または [Web Preview] に設定されている任意のポート)。
これにより、次のようなアドレスのブラウザ ウィンドウが開きます。
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/?authuser=0
ウェブでプレビューを使用して .NET サンプル アプリケーションを表示する
前のステップで開始したサンプルアプリを表示するには、ウェブ プレビューを開始し、指定された URL を読み込みます。次のようになります。
5. Kubernetes にデプロイする
YAML ファイルをビルドして適用する
次のステップでは、2 つの Kubernetes リソース(Deployment と Service)を記述する YAML ファイルが必要です。Cloud Shell で dotnet-app.yaml
という名前のファイルを作成し、次の内容を追加します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: dotnet-deployment
labels:
app: dotnetapp
spec:
replicas: 3
selector:
matchLabels:
app: dotnetapp
template:
metadata:
labels:
app: dotnetapp
spec:
containers:
- name: dotnet
image: mcr.microsoft.com/dotnet/samples:aspnetapp
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: dotnet-service
spec:
selector:
app: dotnetapp
ports:
- protocol: TCP
port: 8080
targetPort: 80
次に、kubectl
を使用して、このファイルを Kubernetes に適用します。
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment created
service/dotnet-service created
目的のリソースが作成されたことを示すメッセージが表示されます。
結果のリソースを確認する
kubectl
CLI を使用して、上記で作成したリソースを調べることができます。まず、Deployment リソースを調べて、新しい Deployment が存在することを確認します。
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
次に、ReplicaSet を確認します。上記の Deployment で作成された ReplicaSet が必要です。
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
最後に、Pod を確認します。Deployment には 3 つのインスタンスがあることが示されています。以下のコマンドは、インスタンスが 3 つあることを示しているはずです。-o wide
オプションが追加されるため、これらのインスタンスが実行されているノードが表示されます。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Running 0 2m25s 10.16.0.8 gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 <none> <none>
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Running 0 2m25s 10.16.1.7 gke-dotnet-cluster-default-pool-02c9dcb9-fgxj <none> <none>
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Running 0 2m25s 10.16.0.7 gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 <none> <none>
Service リソースを確認する
Kubernetes の Service リソースはロードバランサです。エンドポイントは Pod のラベルによって決まります。このようにして、上記の kubectl scale deployment
オペレーションで新しい Pod が Deployment に追加されると、すぐに結果の Pod をその Service によって処理されるリクエストに使用できるようになります。
次のコマンドで Service リソースが表示されます。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
次のコマンドを使用すると、Service の詳細を確認できます。
$ kubectl describe svc dotnet-service
Name: dotnet-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=dotnetapp
Type: ClusterIP
IP: 10.20.9.124
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.16.0.7:80,10.16.0.8:80,10.16.1.7:80
Session Affinity: None
Events: <none>
Service のタイプは ClusterIP
です。つまり、クラスタ内のどの Pod も、Service 名 dotnet-service
をその IP アドレスに解決できます。サービスに送信されたリクエストは、すべてのインスタンス(Pod)間でロードバランスされます。上記の Endpoints
値は、このサービスで現在利用可能な Pod の IP を示しています。これらを上記で出力された Pod の IP と比較します。
実行中のアプリを確認する
この時点でアプリケーションは稼働しており、ユーザー リクエストを受け付ける準備が整っています。これにアクセスするには、プロキシを使用します。次のコマンドは、ポート 8080
でリクエストを受け入れて Kubernetes クラスタに渡すローカル プロキシを作成します。
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
次に、Cloud Shell でウェブでプレビューを使用して、ウェブ アプリケーションにアクセスします。
ウェブ プレビューで生成された URL に /api/v1/namespaces/default/services/dotnet-service:8080/proxy/
を追加します。結果は次のようになります。
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
これで、Google Kubernetes Engine に .NET Core アプリをデプロイできました。次に、アプリに変更を加えて再デプロイします。
6. アプリを変更する
このセクションでは、インスタンスが実行されているホストを表示するようにアプリケーションを変更します。これにより、ロード バランシングが機能していることと、使用可能な Pod が期待どおりに応答していることを確認できます。
ソースコードを取得する
git clone https://github.com/dotnet/dotnet-docker.git
cd dotnet-docker/samples/aspnetapp/
アプリを更新してホスト名を含める
vi aspnetapp/Pages/Index.cshtml
<tr>
<td>Host</td>
<td>@Environment.MachineName</td>
</tr>
新しいコンテナ イメージをビルドし、ローカルでテストする
更新されたコードで新しいコンテナ イメージをビルドします。
docker build --pull -t aspnetapp:alpine -f Dockerfile.alpine-x64 .
先ほどと同様に、新しいアプリケーションをローカルでテストします。
$ docker run --rm -it -p 8080:80 aspnetapp:alpine
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {f71feb13-8eae-4552-b4f2-654435fff7f8} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
以前と同様に、ウェブ プレビューを使用してアプリにアクセスできます。今回は、以下のように Host パラメータが表示されます。
Cloud Shell で新しいタブを開いて docker ps
を実行し、コンテナ ID が上記の [ホスト] の値と一致していることを確認します。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab85ce11aecd aspnetapp:alpine "./aspnetapp" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp relaxed_northcutt
イメージにタグを付けて push し、Kubernetes で使用できるようにする
Kubernetes がイメージを pull できるようにするには、イメージにタグを付けて push する必要があります。まず、コンテナ イメージを一覧表示し、目的のイメージを特定します。
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
aspnetapp alpine 95b4267bb6d0 6 days ago 110MB
次に、そのイメージにタグを付けて Google Container Registry に push します。上記の IMAGE ID を使用すると、次のようになります。
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. 更新したアプリケーションを再デプロイする
YAML ファイルを編集する
ファイル dotnet-app.yaml
が保存されているディレクトリに戻ります。YAML ファイルで次の行を見つけます。
image: mcr.microsoft.com/dotnet/core/samples:aspnetapp
これは、上で作成して gcr.io に push したコンテナ イメージを参照するように変更する必要があります。
image: gcr.io/PROJECT_ID/aspnetapp:alpine
PROJECT_ID
を使用するように変更することを忘れないでください。完了すると、次のようになります。
image: gcr.io/myproject/aspnetapp:alpine
更新された YAML ファイルを適用する
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment configured
service/dotnet-service unchanged
Deployment リソースには更新済みであることが表示され、Service リソースに変更がないことがわかります。更新された Pod は、以前と同様に kubectl get pod
コマンドで表示できますが、今回は -w
を追加します。これにより、すべての変更が発生するのを監視します。
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Running 0 34m
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Running 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Running 0 34m
dotnet-deployment-85f6446977-tmbdq 0/1 ContainerCreating 0 4s
dotnet-deployment-85f6446977-tmbdq 1/1 Running 0 5s
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Terminating 0 34m
dotnet-deployment-85f6446977-lcc58 0/1 Pending 0 0s
dotnet-deployment-85f6446977-lcc58 0/1 Pending 0 0s
dotnet-deployment-85f6446977-lcc58 0/1 ContainerCreating 0 0s
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-lcc58 1/1 Running 0 6s
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 0s
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 0s
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 2s
dotnet-deployment-85f6446977-hw24v 0/1 ContainerCreating 0 2s
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 1/1 Running 0 3s
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-httw6 0/1 Terminating 0 34m
上記の出力には、実行中のローリング アップデートが表示されます。まず、新しいコンテナが起動し、実行中に古いコンテナは終了します。
実行中のアプリを確認する
この時点でアプリケーションが更新され、ユーザー リクエストを受け付ける準備が整いました。以前と同様に、プロキシを使用してアクセスできます。
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
次に、Cloud Shell でウェブでプレビューを使用して、ウェブ アプリケーションにアクセスします。
ウェブ プレビューで生成された URL に /api/v1/namespaces/default/services/dotnet-service:8080/proxy/
を追加します。結果は次のようになります。
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Kubernetes Service が負荷を分散していることを確認する
この URL を数回更新すると、Service によってリクエストが複数の Pod に負荷分散されるため、ホストが変わることがわかります。ホストの値と上記の Pod のリストを比較して、すべての Pod がトラフィックを受信していることを確認します。
インスタンスをスケールアップする
Kubernetes でのアプリのスケーリングは簡単です。次のコマンドは、アプリケーションのデプロイを最大 6 つのインスタンスにスケーリングします。
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
次のコマンドを使用すると、新しい Pod とその現在の状態を表示できます。
kubectl get pod -w
同じブラウザ ウィンドウを更新すると、すべての新しい Pod にトラフィックが分散されていることがわかります。
8. 完了
このラボでは、開発環境で .NET Core サンプル ウェブ アプリケーションを検証し、GKE を使用して Kubernetes にデプロイしました。その後、アプリは、実行されていたコンテナのホスト名を表示するように修正されました。その後、Kubernetes Deployment を新しいバージョンに更新し、アプリをスケールアップして、追加のインスタンス間で負荷がどのように分散されるかを示しています。
.NET と Kubernetes の詳細については、以下のチュートリアルを検討してください。このラボで学習した内容に基づき、より高度なルーティングと復元力のパターンのために Istio サービス メッシュを導入しました。
9. クリーンアップ
予期しない費用の発生を避けるために、次のコマンドを使用して、このラボで作成したクラスタとコンテナ イメージを削除します。
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine