Azure Kubernetes Service (AKS) に C# ASP.NET Core Webサービスをデプロイする (Docker Hub カスタムコンテナイメージ)

# Azure Kubernetes Service (AKS) に C# ASP.NET Core Webサービスをデプロイする (Docker Hub カスタムコンテナイメージ)

こんにちは、@studio_meowtoon です。今回は、Azure Kubernetes Service 環境で C# ASP.NET Core Web アプリケーションをコンテナとして起動する方法を紹介します。
![aspnet-core_on_azure-kubernetes-service.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/954ff1c1-e4eb-8ee0-fbce-e37f296d58c9.png)

## 目的

Windows 11 の Linux でクラウド開発します。

こちらから記事の一覧がご覧いただけます。

https://qiita.com/studio_meowtoon/items/6119ab05d8b13527efd8

## 実現すること

Microsoft Azure Kubernetes Service (AKS) に C# ASP.NET Core Web アプリケーションのカスタムコンテナイメージをデプロイします。  

:::note alert
この記事では、**Docker Hub** に公開する**パブリックアクセス可能なカスタムコンテナイメージ**を使用しています。実際のシステム開発では、どのレジストリからコンテナイメージを取得するかは異なる場合がありますので、適宜ご確認ください。
:::

## 技術トピック

### Microsoft Azure Kubernetes Service (AKS) とは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [Microsoft Azure Kubernetes Service (AKS)](https://learn.microsoft.com/ja-jp/azure/aks/intro-kubernetes)

:::note info
Azure 上で実行されるマネージド Kubernetes サービスです。AKS は、クラウド上で Kubernetes クラスターを簡単に展開、管理、スケーリングできるようにすることで、Kubernetes の運用を簡素化します。

|キーワード|内容|
|:--|:--|
|スケーラビリティ|AKS は、需要の変化に合わせてアプリケーションをスケールアップ/ダウンできます。Kubernetes の標準機能を使い、Pod の数を調整することで、クラスター全体のリソース使用率を最適化できます。|
|自動化|AKS は、クラスターのデプロイ、アップグレード、監視、スケーリング、および管理に関連するタスクを自動化します。これにより、ユーザーは Kubernetes に関連する複雑なタスクを簡素化できます。|
|セキュリティ|AKS は、ユーザーが Kubernetes クラスタをセキュアに設定するのに役立ちます。AKS は、構成可能なネットワークポリシー、構成済みのログイン、Role-Based Access Control (RBAC) などをサポートしています。|
|ポータビリティ|AKS は、クラウドネイティブアプリケーションをどこでも実行できるようにするため、オープンソースの Kubernetes を使用しています。AKS は、Azure の他のサービス、オンプレミス環境、および他のクラウドプロバイダーにデプロイできます。|
|プロダクションレディ|AKS は、高可用性、監視、スケーラビリティ、およびセキュリティに焦点を当て、プロダクションレディな Kubernetes クラスターを提供します。AKS は、SLA で 99.95％ の可用性を提供します。|
:::

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

.NET SDK [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/d68031c91462f6808dff)

```sh
$ dotnet --list-sdks
7.0.202 [/usr/share/dotnet/sdk]
$ dotnet --version
7.0.202
```

Docker [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe)

```sh
$ docker --version
Docker version 23.0.1, build a5ee5b1
```

Azure CLI [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/9721041f9e6e3e987cf0)

```sh
$ az --version
azure-cli                         2.45.0
core                              2.45.0
telemetry                          1.0.8
```

</details>

:::note warn
この記事では基本的に **Ubuntu のターミナルで操作**を行います。**Vim を使用してコピペする方法**を初めて学ぶ人のために、以下の記事で手順を紹介しています。ぜひ挑戦してみてください。
:::

https://qiita.com/studio_meowtoon/items/7771ad48676926e41463

## 作成する Web アプリケーションの仕様

|No|エンドポイント|HTTPメソッド|MIME タイプ|
|:--|:--|:--|:--|
|1|/api/data|GET|application/json|

## Hello World を表示する手順 

### ASP.NET Core Web サービスの作成  

こちらの関連記事で手順がご確認いただけます。

https://qiita.com/studio_meowtoon/items/d469de7f4cfe62aa5641

:::note info
ここまでの手順で、ローカル環境の **Ubuntu** にアプリの**カスタムコンテナイメージ**をビルドすることができました。
:::

### プロジェクトフォルダに移動

プロジェクトフォルダに移動します。  
※ ~/tmp/WebApp をプロジェクトフォルダとします。

```sh
$ cd ~/tmp/WebApp
```

### コンテナイメージの確認

Docker デーモンを起動します。

```sh
$ sudo service docker start
 * Starting Docker: docker      [ OK ]
```

:::note warn
Docker 環境をお持ちでない場合は、以下の関連記事から Docker Engine のインストール手順をご確認いただけます。
:::

https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe

コンテナイメージを確認します。

```sh
$ docker images | grep app-hello-aspnet-core
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
app-hello-aspnet-core   latest    63ec70def9d5   45 seconds ago   208MB
```

### Docker Hub にコンテナイメージを登録  

Docker Hub のアカウントを作成します。  

https://hub.docker.com/signup

Docker Hub にログインします。  

```sh
$ docker login
Login Succeeded
```

コンテナイメージにタグを付けます。

:::note alert
ここで **$USER** という埋め込み変数は、**Ubuntu** のシェル変数 **USER** のことです。つまり、カスタムコンテナイメージを **Docker Hub** にプッシュする際に指定する**アカウント名**と、**Ubuntu** の**ユーザー名**を同じにすると、イメージの取り扱いが簡単になります。ただし、この方法は検証用途に限定されます。
:::

```sh
$ docker tag app-hello-aspnet-core:latest $USER/app-hello-aspnet-core:latest
```

Docker Hub にコンテナイメージをプュシュします。

```sh
$ docker push $USER/app-hello-aspnet-core:latest
```

:::note info
ここまでの作業で、**Docker Hub** に $USER/app-hello-aspnet-core:latest として**カスタムコンテナイメージ**を公開できました。  
:::

### シェル変数の作成

:::note alert
**シェル変数**として、**Ubuntu** に以下の値を作成します。
:::

```sh
$ location_name=japaneast
$ resource_group_name=rg-hello
$ aks_name=aks-hello
```

### Azure 環境にサインイン

こちらの関連記事で手順がご確認いただけます。

https://qiita.com/studio_meowtoon/items/9721041f9e6e3e987cf0

Azure CLI でログインします。  

```sh
$ az login
```

### Azure 環境

#### リソースグループ

リソースグループを作成します。

```sh:Microsoft.Resources/resourceGroups
$ az group create \
    --name $resource_group_name \
    --location $location_name
```

#### AKS 環境

:::note alert
AKS を作成すると、Kubernetes クラスター用の多数のリソースが自動的に作成されます。これらのリソースは、Azure Portal 上で確認することができます。ただし、AKS のリソースを削除すると自動的に関連リソースも削除されますが、共有している Azure サブスクリプションで検証される場合など、ご注意ください。
:::

![aks.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/38d5b18f-0350-d765-01e4-01de32735c42.png)
AKS クラスターを作成します。  
※ Kubernetes 基盤の環境作成に少し時間がかかります。  

```sh:Microsoft.ContainerService/ManagedClusters
$ az aks create \
    --resource-group $resource_group_name \
    --name $aks_name \
    --node-count 1 \
    --generate-ssh-keys
```

AKS クラスターの一覧を表示してみます。

```sh
$ az aks list
```

設定ファイルの権限を修正します。 
※ 初回のみ必要です。 

```sh
$ chmod 600 /home/$USER/.kube/config
```

AKS クラスターと接続します。  
※ 既存設定の上書きを許可する為、yes を選択します。    

```sh
$ az aks get-credentials \
    --resource-group $resource_group_name \
    --name $aks_name
```

接続設定が成功した場合の出力は、以下のようになります。

```sh
Merged "aks-hello" as current context in /home/$USER/.kube/config
```

:::note info
ここまでで **Azure** 上に構築した **Kubernetes クラスターに接続**できました。ここからはその Kubernetes クラスターをリモートでコマンド操作していきます。
:::

### Kubernetes クラスター (AKS) の操作

:::note alert
AKS にデプロイするために、Minikube を経由して kubectl を使用する代わりに、Azure CLI で kubectl を直接インストールする手順を紹介しています。
:::

Kubernetes コマンドライン ツール、kubectl をインストールします。

```sh
$ sudo az aks install-cli
```

Minikube で使用したシンボリックリンクを削除します。

```sh
$ unalias kubectl
```

kubectl のバージョンを確認します。

```sh
$ kubectl version --short
Client Version: v1.26.2
Kustomize Version: v4.5.7
Server Version: v1.24.9
```

現在の接続先を確認します。

```sh
$ kubectl config current-context
aks-hello
```

:::note alert
**Kubernetes クラスターに接続していることを確認する**ことは、誤ったクラスターに対してコマンド操作を実行することを防ぐために重要です。例えば、誤ったクラスターに対してデプロイメントを適用したり、Pod を削除したりすると、**誤操作が原因**で**サービスの停止**や**データの消失**などの致命的な問題を引き起こす可能性があります。
:::

### Kubernetes マニフェスト作成  

Kubernetes のマニュフェストを作成します。

:::note alert
**$USER** の部分はご自身のコンテナリポジトリに**書き換えて**下さい。  
:::

```sh
$ vim kube-all-in-one.yaml
```

```yaml:kube-all-in-one.yaml
apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: app
        image: $USER/app-hello-aspnet-core:latest
        ports:
        - containerPort: 80
```

Kubernetes クラスターにデプロイします。

```sh
$ kubectl apply -f kube-all-in-one.yaml
service/app-service created
deployment.apps/app-deployment created
```

Kubernetes クラスターの状態を確認します。

```sh
$ kubectl get pods,services,deployments
NAME                      READY   STATUS    RESTARTS   AGE
pod/app-b69b5685c-wltz8   1/1     Running   0          14s

NAME                  TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)        AGE
service/app-service   LoadBalancer   10.0.251.6   xx.xx.xx.xx     80:32300/TCP   14s
service/kubernetes    ClusterIP      10.0.0.1     <none>          443/TCP        5m3s

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/app   1/1     1            1           14s
```

### コンテナ動作の確認

**app-service** サービスの EXTERNAL-IP を Ubuntu のシェル変数 **aks_external_ip** に取得します。

```sh
$ aks_external_ip=$(kubectl get svc app-service \
    -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
```

別ターミナルから curl コマンドで確認します。

```sh
$ curl http://$aks_external_ip/api/data -w "\n"
{"message":"Hello Blue!"}
```

<details>
<summary>詳細を表示します。</summary>

```
$ curl -v http://$aks_external_ip/api/data -w "\n"
*   Trying xx.xx.xx.xx:80...
* Connected to xx.xx.xx.xx (xx.xx.xx.xx) port 80 (#0)
> GET /api/data HTTP/1.1
> Host: xx.xx.xx.xx
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Sat, 25 Mar 2023 01:07:30 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host xx.xx.xx.xx left intact
{"message":"Hello World!"}
```

</details>

:::note info
ターミナルに **{"message":"Hello World!"}** と表示され、**JSON データ**を取得することが出来ました。  
:::

### コンテナに接続

**POD** の名前を Ubuntu のシェル変数 **aks_pod_name** に取得します。

```sh
$ aks_pod_name=$(kubectl get pods -o=name)
```

コンテナに接続します。

```sh
$ kubectl exec -it $aks_pod_name -- /bin/bash
```

コンテナに接続後にディレクトリを確認します。 
※ コンテナから出るときは ctrl + D を押します。  

```
# pwd
/App
# ls -lah
total 200K
drwxr-xr-x 1 root root 4.0K Mar 24 14:34 .
drwxr-xr-x 1 root root 4.0K Mar 24 23:00 ..
-rwxr-xr-x 1 root root 140K Mar 24 14:34 WebApp
-rw-r--r-- 1 root root  388 Mar 24 14:34 WebApp.deps.json
-rw-r--r-- 1 root root 8.5K Mar 24 14:34 WebApp.dll
-rw-r--r-- 1 root root  20K Mar 24 14:34 WebApp.pdb
-rw-r--r-- 1 root root  469 Mar 24 14:34 WebApp.runtimeconfig.json
-rw-r--r-- 1 root root  119 Mar 24 12:59 appsettings.Development.json
-rw-r--r-- 1 root root  142 Mar 24 12:59 appsettings.json
-rw-r--r-- 1 root root  482 Mar 24 14:34 web.config
```

top コマンドで状況を確認します。

```
# apt update
# apt install procps
# top
top - 01:05:25 up 14 min,  0 users,  load average: 0.28, 0.29, 0.37
Tasks:   3 total,   1 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu0  :  2.3 us,  1.0 sy,  0.0 ni, 96.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  1.7 us,  2.0 sy,  0.0 ni, 96.0 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
MiB Mem :   6949.5 total,   3818.9 free,    819.8 used,   2310.7 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   5859.8 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
      1 root      20   0   12.0g  69424  40820 S   0.0   1.0   0:00.84 dotnet
     69 root      20   0    4028   3472   2984 S   0.0   0.0   0:00.01 bash
    401 root      20   0    6996   3236   2752 R   0.0   0.0   0:00.00 top
```

コンテナの情報を表示してみます。

```
# cat /etc/*-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

:::note warn
このコンテナは Debian Linux をベースに作成されています。つまり、Debian Linux と同じように扱うことができます。
:::

## まとめ

Azure Kubernetes Service (AKS) 環境で、Dockerfile からビルドした C# ASP.NET Core Web サービスのカスタムコンテナイメージを起動することができました。

:::note warn
**.NET SDK**、**Docker**、**Azure CLI**、**kubectl** を使って、ASP.NET Core アプリの開発からコンテナイメージの作成、Azure 環境へのデプロイまで、**すべてをターミナルから行うことができます**。このように、クラウドでのシステム開発に必要なスキルや理解を深めることができます。初めての人でも簡単に手順を追うことができるので、ぜひ挑戦してみてください。
:::

どうでしたか？ 検証目的として、WSL Ubuntu で、C# ASP.NET Core Web アプリケーションを Azure Kubernetes Service (AKS) 環境でコンテナとして手軽に起動することができます。ぜひお試しください。今後も Azure の開発環境などを紹介していきますので、ぜひお楽しみにしてください。

## 関連記事

#### Java Spring Boot

https://qiita.com/studio_meowtoon/items/9b3a861bbaeba1f56640