kubernetesにNGINX Ingress Controllerを追加して、ホスト名でポッドにルーティングできるようにする

[この記事](https://qiita.com/megasys1968/items/b84f79b9c3f77de3d4a3)に続いて、OpenShiftのルータと同様、ホスト名でポッドにアクセスできるようにします。
OpenShiftはHA Proxyを使ってましたが、KubernetesではNGINX Ingress Controllerを使う記事が多いので、それにしました。

今回は、以下を参照しています。

* [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/)
    * [Installation Guide](https://kubernetes.github.io/ingress-nginx/deploy/)
    * [Bare-metal considerations](https://kubernetes.github.io/ingress-nginx/deploy/baremetal/)
* [MetalLB](https://metallb.universe.tf)
* [オンプレK8sで使えるGoogle製External Load Balancer: MetalLB](https://qiita.com/tmatsu/items/f45f0ca07b4f8489df85)
* [ingress-nginxを使用してオンプレでもIngressを使用する](https://blog.web-apps.tech/ingress-nginx-on-prem/)

### MetalLBの構築
実際にはNGINX Ingress Controllerを先に構築して、上手く動かなくて調べてる中で、目的のためにMetalLBが必要とうことが判りました。他にも選択肢があるのかもしれませんが、NGINX Ingress ControllerのドキュメントでベアメタルではMetalLBを使うと解決するって書いてあるので、それに従いました。

ます、以下のコマンドでインストール。

```shell-session
# kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.1/manifests/metallb.yaml
namespace/metallb-system created
podsecuritypolicy.policy/speaker created
serviceaccount/controller created
serviceaccount/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
role.rbac.authorization.k8s.io/config-watcher created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/config-watcher created
daemonset.apps/speaker created
deployment.apps/controller created
```
次にMetalLBの設定を行います。

```shell-session
# cat <<EOF > metallb-l2.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.122.64-192.168.122.253
EOF
# kubectl apply -f metallb-l2.yaml
```
* MetalLBではBGPとL2の2つのモードがありますが、今回の環境で使用しているCNIのcalicoとBGPモードには[特定の使い方](https://metallb.universe.tf/configuration/calico/)で問題があるとのことです。
今回は無難にL2モードにしました。
* addressesの値は、いろいろとハマりました。
    * Bare-metal considerationsの説明には、ワーカーノードのIPアドレスを指定する例がありますが、それでは上手く行きませんでした。
    * 外部に公開するIPアドレスの範囲を指定するとあるので、仮想サーバのネットワークである192.168.121.0/24を指定したら、サービスのEXTERNAL-IPとして192.168.121.0が割り当たってしまいました。そのせいだと思うのですが通信ができませんでした。
    * ~~そこで物理ホスト側で仮想サーバに割り当てるアドレスを192.168.121.200〜254となるようにvirsh net-editで変更し、192.168.121.1はゲートウェイで使われているので、残りを指定しました。~~(2019/8/26追記)そこで仮想サーバには静的なアドレスを割り当てるようにしました。

### NGINX Ingress Controllerの構築
以下のコマンドでインストール。

```shell-session
# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created
# curl -L -O https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
# vi cloud-generic.yaml
  * spec:配下にloadBalancerIP: 192.168.122.64を追記
# kubectl apply -f cloud-generic.yaml
service/ingress-nginx created
# kubectl -n ingress-nginx get svc
NAME            TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                      AGE
ingress-nginx   LoadBalancer   10.101.221.156   192.168.122.64   80:31288/TCP,443:32439/TCP   172m
```
* この2つめのサービスの作成もハマりました。Installation GuideでベアメタルならこのURLでサービスを作成すると記述があり、Bare-metal considerationsにサービスの作成について言及がありません。これで作成されるサービスはNodePortでNGINX Ingress Controllerが外部に公開されません。
で、[こちらの記事](https://blog.web-apps.tech/ingress-nginx-on-prem/)を参考にcloud-generic.yamlを指定したら、上手く行きました。
* (2019/8/26追記)ingress-nginxサービスの外部アドレスが動的なのがムズムズするので、loadBalancerIPを指定するようにしました。指定するアドレスは、MetalLBで指定したプール内でなければならないです。

### 物理ホストからNGINX Ingress Controllerへ転送
(2019/8/22追記)
物理ホストには既にkube-apiserver用ロードバランサーとして建てたnginxがあるので、これを使ってNGINX Ingress Controllerへ転送させます。

```conf:nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

stream {
    upstream backend {
        least_conn;
        server k8s-master1:6443;
        server k8s-master2:6443;
    }
    server {
        listen     6443;
        proxy_pass backend;
    }
    server {
        listen     80;
        proxy_pass 192.168.122.64:80;
    }
    server {
        listen     443;
        proxy_pass 192.168.122.64:443;
    }
}
```
* httpディレクティブは消して、http/httpsポートをそのままNGINX Ingress Controllerへ転送するようにしました。
* 今回はNGINX Ingress Controllerのサービスに払いだされたアドレスを設定していますが、ちょっとムズムズするので、別途、サービスのアドレスを固定化する方法を検討したいと思います。

### hello worldアプリケーションで確認
前回と同じhello worldアプリケーションをデプロイして、疎通を確認しました。

```shell-session
# cat <<EOF > hello-world.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: hello
  name: hello-world
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - image: gcr.io/hello-minikube-zero-install/hello-node
        name: hello-world
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  ports:
  - name: http
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello-world
  type: NodePort
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-router
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: hello-world
          servicePort: 8080
    host: hello-world.kubernetes.local
EOF
# kubectl apply -f hello-world.yaml
# curl hello-world.kubernetes.local
Hello World!
```