【Kubernetes】NginxのIngress Controllerをビルド＆デプロイする

# はじめに

Kubernetesでコンテナで動かしているサービスを公開する方法としてIngressがあります。
Ingressを動かすにはIngress Controllerが必要です。この記事ではNginxのIngress Controllerのコンテナイメージをビルドしてデプロイ（Kubernetesクラスタ上に作成）してみます。

# 使用するIngress Controller

Ingressではクラスタ内のサービスへのアクセス制御できます。

https://kubernetes.io/ja/docs/concepts/services-networking/ingress/

![ingress](https://d33wubrfki0l68.cloudfront.net/4035ad24be60d30ca4b0cdc6616e35b7722b38d3/d25bc/ja/docs/images/ingress.svg)

Ingressを動かすにはIngress Controllerが必要です。

https://kubernetes.io/ja/docs/concepts/services-networking/ingress-controllers/

Ingress Controllerにはオープンソースなもの、クラウド毎に提供しているものなどがあります。
今回はNginxを基にした無料版のIngress Controllerを使ってみます。
有料版ではWAFの機能を備えたNginx App Protectが使えます。

https://docs.nginx.com/nginx-ingress-controller/intro/overview/

# 作成する構成図

以下の構成を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474054/ea9c54e9-0463-1e92-102e-9e6c0ec1129a.png)

今回はminikubeでクラスタを作成します。インストール手順は省略しますが`alias kubectl="minikube kubectl --"`としています。
NginxのPODを作成し、Serviceでポート80を使ってアクセスできるようにします。VirtualServerはIngressと同じ役割で、ホスト名を定義してServiceへルーティングします。NodePort Serviceにてクラスタを動かしているノードのポートにマッピングして、クラスタ外からアクセスできるようにします。

VirtualServerではIngressよりも詳細にNginxの設定を行うことができるようになっています。

# 実践

## Nginxサービスをデプロイする

```bash
$ kubectl create ns myns
namespace/myns created
$ kubectl run nginx --image=bitnami/nginx --port=8080 --namespace=myns
pod/nginx created
$ kubectl expose pod nginx -n myns --name=mysvc --port=80 --target-port=8080
service/mysvc exposed
$ kubectl get all -n myns
NAME        READY   STATUS    RESTARTS   AGE
pod/nginx   1/1     Running   0          116m

NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/mysvc   ClusterIP   10.96.165.72   <none>        80/TCP    20s
```

それますが、今回はyamlファイルを作成せずデプロイする練習をしています。Kubernetesの試験の勉強のためです。

この段階ではクラスタ内からアクセスできるようになっています。
以下のコマンドで一時的にPODを立ち上げてクラスタ内からアクセスできます。

```bash
$ kubectl run busybox --image busybox -it  --rm --restart=Never -- wget -O - mysvc.myns
```

## Ingress Controllerのコンテナイメージをビルドする

ドキュメントに従ってビルドします。ビルド方法は複数種類あるので必要に応じてベースイメージ、ツールなどを変更します。今回はローカルにgoをインストールしてビルドしてみます。

https://docs.nginx.com/nginx-ingress-controller/installation/building-ingress-controller-image/

gitをインストールします。

```bash
$ sudo yum install -y git-2.38.1
```

goをインストールします。

```bash
$ sudo amazon-linux-extras install epel -y
$ sudo yum install -y golang-1.18.8
```

gitリポジトリをクローンしてビルドします。

```bash
$ git clone https://github.com/nginxinc/kubernetes-ingress.git --branch v2.4.2
$ cd kubernetes-ingress
$ make debian-image TARGET=local
```

ビルドする方法はTARGETにて選べます。

|TARGETの値|説明|
|---|---|
|local|ローカルのgoを使ってビルドする|
|container|goコンテナを使ってビルドする|
|download|バイナリファイルのコンパイルを行わずにビルドする|

NginxのIngress ControllerのGithubは以下の通りです。

https://github.com/nginxinc/kubernetes-ingress

minikubeでビルドしたイメージを使うため、ロードする。

```bash
$ minikube image load nginx/nginx-ingress:$(docker images | grep nginx-ingress | awk '{print $2}')
```

## Ingress Controller関連リソースをデプロイする

コンテナやサービスなどのリソースを以下のドキュメントに従って作成する。
複数の方法がありますが、今回はyamlファイルから作成します。
また、ロードバランサーは作成しないため、ノードポートを使用します。

https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/

```bash
cd kubernetes-ingress
kubectl apply -f deployments/common/ns-and-sa.yaml
kubectl apply -f deployments/rbac/rbac.yaml
kubectl apply -f deployments/rbac/apdos-rbac.yaml
kubectl apply -f deployments/common/default-server-secret.yaml
kubectl apply -f deployments/common/nginx-config.yaml
kubectl apply -f deployments/common/ingress-class.yaml
kubectl apply -f deployments/common/crds/k8s.nginx.org_virtualservers.yaml
kubectl apply -f deployments/common/crds/k8s.nginx.org_virtualserverroutes.yaml
kubectl apply -f deployments/common/crds/k8s.nginx.org_transportservers.yaml
kubectl apply -f deployments/common/crds/k8s.nginx.org_policies.yaml
kubectl apply -f deployments/deployment/nginx-ingress.yaml
kubectl apply -f deployments/service/nodeport.yaml
```

コマンドが多いのでミスがないか確認したい場合は、Ingress ControllerのPODのログを見るとよいと思います。

```bash
$ kubectl logs -n nginx-ingress $(kubectl get pod -n nginx-ingress | grep nginx-ingress | awk '{print $1}')
```

注意点として、コンテナはminikube本体のコンテナ上で動いています。そのため、ノードポートはminikube本体のコンテナのポートにマッピングしています。
どのポートにマッピングしているかはNodePort Serviceを見るとわかります。
下記の場合、HTTPは32440ポート、HTTPSは30230ポートにマッピングしています。

```bash
$ kubectl get svc -n nginx-ingress
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
nginx-ingress   NodePort   10.104.212.55   <none>        80:32440/TCP,443:30230/TCP   62s
```

まだバックエンドの設定ができていませんが、404のレスポンスは取得できます。

```bash
$ sudo docker exec minikube curl -s localhost:32440
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.23.2</center>
</body>
</html>
```

## VirtualServerをデプロイする

NginxのService用のVirtualServerのyamlファイルを作成します。

```yaml:virtualserver.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: mynginx-co-jp-vs #リソース名の定義
  namespace: myns
spec:
  host: mynginx.co.jp #ホスト名の定義
  tls:
    secret: mynginx-co-jp-cert #HTTPSで用いる証明書を入れたSecret名
  ingressClassName: nginx #使用するIngressClass名
  upstreams:
    - name: mynginx-co-jp #バックエンド名の定義
      service: mysvc #バックエンドのサービス名
      port: 80 #使用するポート
  routes:
    - path: / #パス「/」の場合にバックエンド「mynginx-co-jp」に送信する。
      action:
        pass: mynginx-co-jp
```

HTTPSのアクセスを受け付けるため、自己証明書を用意します。

```bash
$ openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout private.key \
-out server.crt \
-subj "/CN=mynginx.co.jp/O=mynginx.co.jp" &> /dev/null
$ kubectl create secret -n myns tls mynginx-co-jp-cert \
--key private.key \
--cert server.crt
```

その後、VirtualServerを作成します。

```bash
$ kubectl apply -f virtualserver.yaml
```

エラーが出ていないか確認します。VirtualServerのSTATEがValidになっていることを確認します。なっていない場合はdescribeコマンドで原因を調べます。

```bash
$ kubectl get vs -n myns
NAME               STATE   HOST            IP    PORTS   AGE
mynginx-co-jp-vs   Valid   mynginx.co.jp                 2m40s
```

これでNodePort Serviceを通してNginxのServiceにアクセスできるようになっているはずです。
HTTPSでアクセスしてみます。

```bash
$ sudo docker exec minikube curl -sk https://localhost:30230 -H"Host:mynginx.co.jp"
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

できました。
HTTPでも以下のようにアクセスできます。

```bash
$ sudo docker exec minikube curl -s http://localhost:32440 -H"Host:mynginx.co.jp"
```

# おわりに

勉強するまでよくわからなかったNginxとIngress Controllerについて触れました。
まだまだ理解しきれていませんが、Nginxがわかればネットワーク関連でできることが増えるので勉強していきたいです。