ASP.NET Core のWebアプリケーションを Kubernetes で構築する

# 概要
(※) [ASP.NET Core のWebアプリケーションを docker-compose で Dockerアプリケーション として構築する](https://qiita.com/wasimaru/items/4b160b48dae7f2618074)
の続編になります。

ASP.NET CoreのサンプルWebアプリケーションを題材として、Kubernetes の動作環境を構築する例を紹介します。

筆者は単なるアプリ屋ですが、「そろそろ Kubernetes に入門しておかないとヤバイかな…」と思い、
Kubernetes初心者ながら、本記事を書くに至りました。

Kubernetesの概念やリソース等は、ドキュメントや文献を参考にしつつ記載していますが、
間違いや不備があれば、ご指摘いただけると助かります。

# 環境
- Windows10
- Visual Studio 2019
- ASP.NET Core 3.1
- Docker for Windows

# サンプルアプリケーション
題材にしたサンプルアプリケーションです。
https://github.com/tYoshiyuki/dotnet-core-web-sample

docker-composeの際に利用したToDoのWebアプリケーションを今回も利用します。
トランザクションデータはデータベース (SQL Server) に保存しています。

# システム構成
Docker for Windows を利用して、ローカルPC上にKubernetes環境を構築します。
ローカル開発時は、IIS Express と LocalDB で構成していました。これを、下記図のイメージで構築します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353069/abcff90d-84e4-dce8-89a5-77046af835ef.png)

「Nginx + ASP.NET Coreアプリケーション」 と 「SQL Server Express」 で2つのPodを作成します。

Podとは、Kubernetesにおけるコンテナ集合体の単位になります。
コンテナ単体では扱い辛いため、利用したいコンテナをグルーピングして利用するイメージになります。

次に、「Web + アプリケーション部分を担当する dotnet-core-web-sample」 と
「DB部分を担当する sqlexpress」 の2つのServiceを定義します。

Serviceは、Kubernetesクラスタ内において、Podに対してのアクセス経路を定義するリソースになります。

Kubernetesにおいて、コンテナが実際に動作する環境を Node と言います。
PodはNodeに対して分散配置されるため、Pod間で通信を行うことを考えた場合に、
Podの実IPアドレスを意識したアクセスを行うことは困難です。

そのため、Podに対するアクセスを抽象化して提供する仕組みが Service ということになります、 

また、外部からのHTTPアクセス用に Ingress を導入します。
Ingressは、ServiceをL7層のレベルで外部に公開するためのリソースです。
今回は Nginx Ingress Controller を利用します。

# 解説

Docker for Windows の設定から、Kubernetesを有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353069/568b0cfb-87e3-10e9-737d-f67ba9afd967.png)

またGUIツールとして、Visual Studio Codeの拡張機能を利用しました。
Kubernetesのリソースを視覚的に確認できるため、導入しておくと便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353069/4cb02e5c-e040-8c15-201d-4ae80ec9b770.png)

## 1. app.yaml

まずは、Nginx、ASP.NET Coreアプリケーションの部分の定義を作成します。
Kubernetesのリソースは、YAMLで定義していきます。

```yaml:app.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dotnet-core-web-sample
  labels:
    app: dotnet-core-web-sample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dotnet-core-web-sample
  template:
    metadata:
      labels:
        app: dotnet-core-web-sample
    spec:
      containers:
      - name: web
        image: dotnet-core-web-sample_web
        imagePullPolicy: IfNotPresent
        env:
        - name: BACKEND_HOST
          value: localhost:5000  
        ports:
        - containerPort: 80
      - name: app
        image: dotnet-core-web-sample_app
        imagePullPolicy: IfNotPresent
        env:
        - name: ConnectionStrings__DefaultConnection
          value: "Server=sqlexpress;Database=master;User ID=sa;Password=P@ssw0rd;initial catalog=dotnetcorewebsample;MultipleActiveResultSets=True;App=EntityFramework;"
        ports:
        - containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
  name: dotnet-core-web-sample
spec:
  selector:
    app: dotnet-core-web-sample
  ports:
  - name: http
    port: 80

```

前半部分はDeployment、後半部分はServiceの定義になります。

Deploymentは、Deployment > ReplicaSet > Pod というように、Podを管理するための上位概念になります。
ReplicaSetは、同じ仕様のPodを複数生成・管理するためのリソースになります。
Deploymentは、ReplicaSetを世代管理するためのリソースです。
本記事では触れませんが、デプロイ時のロールバックやRollingUpdateの機能を定義することが出来ます。

Podに配置するコンテナの定義は `containers:` の部分で記載しています。
Podで利用するdockerイメージは、[前回の記事](https://qiita.com/wasimaru/items/4b160b48dae7f2618074)で作成したローカルのイメージを利用します。
`imagePullPolicy: IfNotPresent`を記載することで、ローカルイメージが存在する場合に、それを利用するようにしています。
dotnet-core-web-sample_webはNginxのdockerイメージ、dotnet-core-web-sample_appはASP.NET Coreのdockerイメージです。

各設定内容は docker-compose とほぼ同じですが、BACKEND_HOST に localhost:5000 を設定します。
これは、同一Podにおける宛先を localhost で解決出来るためです。

Serviceでは、dotnet-core-web-sampleという名称で、80番ポートを公開しています。

## 2. sqlexpress.yaml

次に、SQL Server Expressの定義です。

```yaml:sqlexpress.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sqlexpress
  labels:
    app: sqlexpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sqlexpress
  template:
    metadata:
      labels:
        app: sqlexpress
    spec:
      containers:
      - name: sqlexpress
        image: dotnet-core-web-sample_sqlexpress
        imagePullPolicy: IfNotPresent        
        env:
        - name: ACCEPT_EULA
          value: "Y"
        - name: MSSQL_PID
          value: Express
        - name: SA_PASSWORD
          value: P@ssw0rd
        ports:
        - containerPort: 1433
---
apiVersion: v1
kind: Service
metadata:
  name: sqlexpress
spec:
  selector:
    app: sqlexpress
  ports:
  - name: "1433"
    port: 1433
    targetPort: 1433

```

前半部分はDeployment、後半部分はServiceの定義になります。
構成内容としては、app.yamlとほぼ同様になっています。
Serviceでは、sqlexpressという名称で、SQL Server用の1433番ポートを公開しています。


## 3. Nginx Ingress Controllerの導入
Ingressを使用するために、Nginx Ingress Controllerを導入します。

Githubのドキュメントを参考にコマンドを実行します。
https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md

```
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
```

ドキュメントには Docker for Mac と書かれていますが、Docker for Windowsでも大丈夫でした。

## 4. ingress.yaml

最後に、ingress.yamlを作成します。

```yaml:ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dotnet-core-web-sample
spec:
  rules:
  - host: dotnetcorewebsample.local
    http:
      paths:
      - path: /
        backend:
          serviceName: dotnet-core-web-sample
          servicePort: 80

```

`host: dotnetcorewebsample.local`は、バーチャルホスト名になります。
`path: /`は、httpアクセス時のパス情報になります。

## 5. 動作確認

これまで作成してきた定義を、Kubernetesに適用させます。
kubectlコマンドを利用します。

```powershell
> kubectl apply -f .\sqlexpress.yaml
deployment.apps/sqlexpress created
service/sqlexpress created

> kubectl apply -f .\app.yaml       
deployment.apps/dotnet-core-web-sample created
service/dotnet-core-web-sample created

> kubectl apply -f .\ingress.yaml
ingress.extensions/dotnet-core-web-sample created
```

稼働確認のため、hostsファイルに下記を記載します。

```
127.0.0.1 dotnetcorewebsample.local
```

ブラウザでアクセスすると、無事アクセスすることが出来ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353069/d4c59d3b-722f-5634-ec2e-0842122af2d1.png)

# まとめ
Kubernetesの概念は当初複雑に感じていましたが、実際に触ってみると理解が進み易いと感じました。
また、コンテナ技術を扱っている関係上、docker や docker-compose に慣れておくと入門し易いと思います。