November 2018
Volume 33 Number 12
Containers - Azure Kubernetes Services を開始する
コンテナー オーケストレーションの世界は、Kubernetes の話でもちきりです。当初、Kubernetes は Google によって設計されたオープンソース プラットフォームで、仮想マシン (VM) のクラスター間で Docker (または Open Container Initiative) コンテナーを調整するためのものでした。デプロイ、ロールバック、スケーリングや、その他の機能のホストもサポートしています。Kubernetes クラスターの管理は手間のかかる作業となる場合があります。そこで、Azure チームは Azure Kubernetes Service (AKS) という管理ソリューションを提供し、プロセスを大幅に簡素化しました。
この記事では、AKS クラスターをデプロイする方法、セキュリティで保護された Azure Container Registry (ACR) を作成する方法、ASP.NET Web API アプリケーションをデプロイする方法、Kubernetes Ingress と Azure DNS によってインターネット上でそのアプリケーションを公開する方法を説明しています。この記事の目的は、Kubernetes の詳細を記すことではありません。Azure AKS オファリングを使用してアプリケーションを開始するための必要最低限の情報を記載しています。
Azure AKS の強みの 1 つは、マスター ノードと構成ノードで構成される制御プレーンが完全に管理されることです。通常、Kubernetes 制御プレーンは、少なくとも 1 つのマスター ノードと 1 つ、3 つまたは 5 つの etcd 構成ノードで構成されます。こうしたコア サービスの管理に手間とコストがかかることは容易に想像できます。AKS を使用すれば、ボタンをクリックするだけで、コア サービスをアップグレードしたり、追加の worker ノードをスケールアウトしたりすることができます。また、現時点では、これらの管理ノードに追加料金はかかりません。必要なのは、ご使用のサービスを実行する worker ノードのお支払いだけです。
この記事で解説するコードは bit.ly/2zaFQeq にあります。リポジトリには、ASP.NET Web API の基本アプリケーションと、Dockerfile および Kubernetes マニフェストが含まれています。
Kubernetes AKS クラスターの作成
Azure Cloud サブスクリプションでリソースを作成して管理するには、Azure CLI を使用します。これについては bit.ly/2OcwFQb をご覧ください。この記事では、これを利用してさまざまな Azure サービスを管理しています。Azure Cloud Shell (bit.ly/2yESmTP) は Web ベースのシェルです。開発者は CLI をローカルにインストールすることなくコマンドを実行できます。
早速、このコードを使ってみましょう。まずは、AKS クラスターとコンテナー レジストリを保持するリソース グループの作成です。
> group create --name my-aks-cluster --location eastus2
リソース グループを作成した後、1 つのノードを含むクラスターを作成します。B1 バースト対応可能イメージくらいの大きさの VM でも問題ありませんが、できれば、少なくとも 2 コア、7 GB のメモリ インスタンス (D シリーズ以降) を使用することをお勧めします。これより小さいと、クラスターのスケーリングとアップグレードの際の信頼性が低くなる傾向があります。また、AKS では現在、1 種類のノードしかサポートされていないので、後からより大きな VM インスタンスにスケールアップすることができません。今後、複数のノード プールでさまざまな種類のノードを追加できるようになる予定ですが、現時点では、実行するサービスのニーズに合わせてノードのサイズを選ぶ必要があります。
通常、クラスターの作成には 10 分から 12 分かかります。くつろぎながらお待ちください。処理は次のコードから始まります。
> az aks create --resource-group my-aks-cluster --name my-aks-cluster
--node-count 1 --generate-ssh-keys --kubernetes-version 1.11.2
--node-vm-size Standard_D2s_v3
Docker イメージを AKS クラスターに取り込むには、Docker レジストリを使用する必要があります。オープンソースのディストリビューションで Docker のパブリック レジストリを使用することは可能ですが、通常は、プロジェクトのアプリケーション コードをプライベート レジストリで保護します。
Azure では Azure Container Registry (ACR) を利用した、安全で管理された Docker レジストリ ソリューションが提供されています。ACR インスタンスをセットアップするには、次のコマンドを実行します。
> az acr create --resource-group my-aks-cluster
--name <REGISTRY_NAME> --sku Basic --admin-enabled true
レジストリ名は、Azure 上のすべての ACR でホストされたレジストリ名の中で一意である必要があります。
Kubernetes の CLI
AKS クラスターとの対話には kubectl を使用します。これは、どの OS でも使用でき、さまざまな方法でインストールできます。詳細については、bit.ly/2Q58CnJ を参照してください。Web ベースのダッシュボードは、クラスターの状態をすばやく概観するのには非常に便利ですが、利用できるすべての API 操作をカバーしているわけではありません。そのため、結局 kubectl CLI に戻らなければならないこともあります。コマンドライン操作が好きというわけではなくても、時がたつうちに、kubectl の価値を実感することでしょう。Azure CLI と組み合わせることで、シェルから離れずにあらゆる操作を実行できます。
kubectl のインストール完了後、次のコマンドを使用して、資格情報をローカルにインポートしてクラスターに対して認証できます。
> az aks get-credentials --resource-group my-aks-cluster
--name my-aks-cluster
このコマンドを実行すると、~/.kube/config がクラスター URI と署名機関および資格情報で更新されます。また、クラスターを現在の構成として設定するためのコンテキストも追加されます。kubectl の構成では、複数のクラスターのコンテキストを保持できます。コンテキストは、kubectl config コマンドを使用して簡単に切り替えることができます。さらに、オープンソース ユーティリティ (kubectx と kubectxwin) もあり、それを使用することによってもコンテキストを簡単に切り替えられます。
資格情報のインポート後、kubectl get nodes コマンドを使用して実行中のノードをリストすることによって、クラスターへの接続をテストできます。次のように表示されます。
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-default-34827916-0 Ready agent 1d v1.11.2
デプロイのためのコンテナー レジストリ シークレットの追加
Kubernetes はシークレットの使用による安全な方法で機密データを格納します。たとえば、ACR 資格情報がソース コードに格納されないようにするには、シークレットを作成し、Kubernetes 配置マニフェストから参照されるよう設定する必要があります。ACR サービスの資格情報を取得するには、次のコマンドを実行します。
> az acr credential show --name <REGISTRY_NAME>
次に、kubectl を使用して、特別なシークレット (docker-registry) を生成します。これは、Docker によって提供される資格トークンを格納するために特別に準備されたものです。次のコードは、ACR クエリから取得した資格情報を使用して my-docker-creds というシークレットを作成します。ユーザー名には大文字と小文字の区別があります。組み込みの管理者アカウントでは、ACR によって既定で小文字になります。
> kubectl create secret docker-registry my-docker-creds
--docker-server=<REGISTRY_NAME>.azurecr.io --docker-username=<REGISTRY_USERNAMEE>
--docker-password=<REGISTRY_PASSWORD> --docker-email=<ANY_EMAIL>
最後に、次のコマンドを実行してシークレットが作成されたことを確認します。
> kubectl describe secrets my-docker-creds
Name: my-docker-creds
Namespace: default
Type: kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson: 223 bytes
Docker コンテナーの作成
AKS のアプリケーションはすべて Docker コンテナーとしてデプロイされます。クラスターへ送られた Docker イメージを作成する Dockerfile のコードは次のとおりです。
FROM microsoft/dotnet:2.1-sdk AS builder
COPY . /app
WORKDIR /app
RUN dotnet publish -f netcoreapp2.1 -c Debug -o /publish
FROM microsoft/dotnet:2.1.3-aspnetcore-runtime
COPY --from=builder /publish .
ENTRYPOINT ["/bin/bash", "-c", "dotnet WebApiApp.dll"]
この Dockerfile では、ビルド用ステージとランタイム用ステージのそれぞれにビルドを分割するマルチステージ アプローチが採用されています。このアプローチでは SDK 全体をディストリビューションに含めないため、イメージ全体のサイズが大幅に縮小されます。
レジストリへのイメージのプッシュ
Docker は、ローカルのイメージとコンテナーの概念に基づいてイメージを実行します。Docker イメージをクラスターに直接プッシュすることはできません。そのため、Kubernetes がアクセスできる場所にイメージを格納して、イメージをローカルのクラスターにプルする必要があります。ACR レジストリは、開発環境、継続的インテグレーション環境、クラスター環境の間でイメージを集中管理できる安全な場所です。
イメージのアップストリームのプッシュ先を Docker が識別できるよう、イメージをビルドして <REGISTRY>/<REPOSITORY>/<IMAGE>:<TAG> の形式でタグ付けする必要があります。任意の名前を指定できるリポジトリでは、レジストリ イメージを論理グループに分けることができます。次のコードは、ACR にプッシュする前にイメージをビルドしてタグ付けする方法を示しています。最新のタグがサポートされていますが、Kubernetes で作業する場合はセマンティック バージョニングを使用することを強くお勧めします。バージョン番号を使用すれば、デプロイやロールバックの管理がずっと簡単になります。コードは次のようになります。
> az acr login --name <REGISTRY_NAME>
> docker build -t <REGISTRY_NAME>.azurecr.io/api/my-webapi-app:1.0 .
> docker push <REGISTRY_NAME>.azurecr.io/api/my-webapi-app:1.0
baf6b1178a5b: Pushed
b3f8eefa2758: Pushed
393dd8f4a879: Pushed
0ad9ffac9ae9: Pushed
8ea427f58308: Pushed
cdb3f9544e4c: Pushed
1.0: digest: sha256:47399f3b2365a9 size: 1579
次のコードを実行して、イメージがアップストリームにプッシュされたことを確認します。
> az acr login --name <REGISTRY_NAME>
> az acr repository list --name <REGISTRY_NAME>
アプリケーションの配置
Kubernetes では、マニフェストを使用してクラスター内のすべてのオブジェクトを記述します。マニフェストは、Kubernetes API によって管理される yaml ファイルです。配置マニフェストの種類は、アプリケーションのリソース、イメージのソース、必要な状態を記述するために使用されます。図 1 は、使用するコンテナーの名前、実行する必要があるコンテナーの数、クラスター内のアプリケーションの記述に役立つラベルを Kubernetes に知らせるシンプルなマニフェストです。また、リモート イメージをプルするときに ACR に対して認証するシークレットの名前も追加されています。
図 1 Deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-webapi-app
spec:
selector:
matchLabels:
app: my-webapi-app
replicas: 2
template:
metadata:
labels:
app: my-webapi-app
spec:
containers:
- name: my-webapi-app
image: <REGISTRY_NAME>.azurecr.io/api/my-webapi-app:1.0
livenessProbe:
initialDelaySeconds: 10
path: /health
periodSeconds: 5
ports:
- containerPort: 80
imagePullSecrets:
- name: my-docker-creds
マニフェストを配置するには、次のコマンドを使用します。
> kubectl apply -f ./deployment.yaml
Kubernetes は、ポッドという概念を使用して、1 つ以上のコンテナーを、クラスター内の論理的でスケーラブルなインスタンスにグループ化します。通常、ポッドにはそれぞれ 1 つのコンテナーがあります。これを利用して、アプリケーションのサービスを個別にスケーリングすることができます。アプリケーションのすべてのサービス (Web アプリケーションやデータベースなど) を単一のポッドに入れると思われがちですが、これは間違いです。そのようにすると Web フロントエンドでデータベースから個別にスケーリングできなくなり、Kubernetes の多くの利点が失われてしまいます。
一般的なシナリオでは、ポッドに複数のコンテナーを含めることができます。これは、サイドカーと呼ばれる概念です。アプリケーション コンテナーを監視するコンテナーと、メトリックまたはログを提供するコンテナーがあるとします。この場合、両方のコンテナーを 1 つのポッドに配置することには大きなメリットがあります。それ以外の場合、コンテナーのグループ化の制限 (およびメリット) を確実に理解するまでは、ポッドにつきコンテナー 1 つという比率のままにするのが通常はベストです。
配置完了後は、次のコマンドでアプリケーション ポッドの状態を確認できます。
> kubectl get pods
NAME READY STATUS RESTARTS AGE
my-webapi-app-64cdf6b449-9hsks 2/2 Running 0 2m
レプリカ セットを満たすためにポッドの 2 つのインスタンスが実行されていることにご注意ください。
サービスの作成
これで、アプリケーション Docker コンテナーがクラスターにデプロイされました。次に、サービスでその Docker コンテナーを検出できるようにする必要があります。Kubernetes Service を使用して、自分のポッドを、クラスター内の他のポッドが検出できるようにします。これを行うには、クラスターの内部 DNS に自身を登録します。また、このサービスを使用することで、すべてのポッド レプリカの間で負荷を分散し、ポッドをアップグレードするときにポッドの可用性を管理できます。サービスは非常に強力な Kubernetes の概念であり、ローリング、ブルー/グリーン、カナリア デプロイでアプリケーションをアップグレードする際に可用性を実現するために必須です。次のコマンドは、デプロイ用のサービスを作成します。
> kubectl expose deployment/my-webapi-app
service "my-webapi-app" exposed
ここで、次のコマンドを実行して、クラスターで実行されているサービスを表示してみましょう。
> kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-web-api ClusterIP 10.0.0.157 <none> 443/TCP 1d
既定では、サービスはクラスター内からしかアクセスできないため、外部 IP は存在しません。kubectl CLI を使用すると、簡単にローカル コンピューターとクラスターの間でプロキシを開いて実行状況を対話的にチェックできます。たとえば、次のコードのようになります。
> kubectl port-forward services/my-webapi-app 8080:80
> curl http://localhost:8080
StatusCode : 200
StatusDescription : OK
Content : Hello from our Kubernetes hosted service!
HTTP ルーティングの追加
Kubernetes は既定でセキュリティによって保護されています。そのため、クラスターの外からアクセスするサービスは明示的に公開する必要があります。これは、セキュリティの観点からすれば優れた機能設計ですが、初めての方は少し難しく感じるかもしれません。クラスター内の HTTP ベースのサービスにアクセスする最も一般的な方法は、Kubernetes Ingress コントローラーを使用する方法です。Ingress コントローラーを使用すると、HTTP プロキシのエントリ ポイントを経由してホスト名とパスに基づいて要求を内部サービスにルーティングできます。
Ingress が Kubernetes に追加される前は、LoadBalancer サービス タイプの使用が、サービスを公開するための主要な方法でした。ただ、この方法では、ロード バランサーがサービスにつき 1 つずつ増え、それぞれを個別に管理しなければなりませんでした。Ingress を使用すると単一の Azure Load Balancer がクラスター内のすべてのサービスにアクセスできるため、コストも複雑さも大幅に削減されます。
こうした要求を処理するために、AKS には Ingress コントローラーとして機能する Nginx プロキシでクラスターを拡張できる有用なアドオンがあります。これは、Azure CLI の次のコマンドで有効にできます。
> az aks enable-addons --resource-group my-aks-cluster
--name my-aks-cluster --addons http_application_routing
ルーティング サービスの稼働状況は、図 2 のコマンドを発行して確認できます。
図 2 稼働ルーティング サービスの確認
> kubectl get pods --all-namespaces
NAMESPACE NAME READY
kube-system addon-http-application-routing-default-http-backend-74d455htfw9 1/1
kube-system addon-http-application-routing-external-dns-7cf57b9cc7-lqhl5 1/1
kube-system addon-http-application-routing-nginx-ingress-controller-5595b2v 1/1
リストには 3 つの新しいコントローラーが表示されています。Ingress コントローラー、外部 DNS、既定のバック エンドです。既定のバック エンドは、既存のサービスにルートが見つからない場合にクライアントに応答するために使用されます。これは、別の Docker コンテナーとして実行されることを除けば、標準 ASP.NET アプリケーションの 404 Not Found ハンドラーとほぼ同じです。HTTP アドオンは実験には最適ですが、運用環境での使用は意図されていないことにご留意ください。
サービスの公開
Ingress は、Ingress コントローラーと Ingress 定義を組み合わせたものです。各サービスには、サービスを公開する方法を Ingress コントローラーに指示する Ingress 定義があります。次のコマンドは、クラスターの DNS 名を取得します。
> az aks show --resource-group my-aks-cluster
--name my-aks-cluster --query
addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName
-o table
Result
--------------------------------------
<CLUSTER_PREFIX>.eastus2.aksapp.io
図 3 に示すように、Ingress 注釈の kubernetes.io/ingress.class が Ingress コントローラーにこの仕様を処理するよう通知します。サブドメインは、既に解決済みのクラスター DNS 名を使用して、ルート パス「/」とともにホストに追加されます。さらに、クラスター内の要求ルーティング先を Ingress コントローラーに指示するために、サービス名とその内部で公開されているポートを追加する必要もあります。
図 3 Ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-webapi-app
annotations:
kubernetes.io/ingress.class: addon-http-application-routing
spec:
rules:
- host: my-webapi-app.<CLUSTER_PREFIX>.eastus2.aksapp.io
http:
paths:
- path: /
backend:
serviceName: my-webapi-app
servicePort: 80
その後、次のコマンドを使用して、Ingress マニフェストを適用できます。
> kubectl apply -f ./ingress.yaml
DNS エントリが作成、反映されるまでに数分かかることがありますので、しばらくお待ちください。Azure portal 内から DNS サービスの状態を次のように確認できます。
> curl http://my-webapi-app.<CLUSTER_PREFIX>.eastus2.aksapp.io
StatusCode : 200
StatusDescription : OK
Content : Hello from our Kubernetes hosted service!
まとめ
この時点で、単一ノードの AKS クラスターが実行され、ACR サービスがアプリケーション Docker イメージをセキュリティで保護されたシークレットと共にホストしています。ここを出発点として、Azure AKS Kubernetes が提供できる多くの追加機能を探すことができます。私は、「自分の能力を高めてくれるものにお金を使い、自分にしかできないものを作る」というシンプルな哲学をモットーにしています。 ご覧いただいたように、Azure によって Kubernetes はより利用しやすくなり、開発者と DevOps 専門家の両者が、より重要な作業に集中できるようになっています。
Chander Dhall 氏は Cazton の CEO であり、Microsoft MVP を 8 度受賞しています。Google 開発の専門家でもあり、ソリューションのアーキテクトと実装において世界的に有名なテクノロジ リーダーです。
この記事の技術校閲者を務めてくれた Microsoft に感謝します。Brendan Burns 氏