Minikube で快適に Ingress を利用する

## TL;DR

- `minikube addons enable ingress` で Minikube で簡単に Ingress を使いはじめることができる
- ローカルのネームサーバで Ingress に設定したホスト名を名前解決させてより快適に利用する
- Minikube 標準の Ingress Controller の代わりに nghttpx-ingress-controller を使うのも簡単、そのときカスタム Cluster Add-on を使うとより快適に利用できる

## はじめに

プロダクションにおいて Ingress を利用しているならローカルの Minikube においても Ingress を利用して動作を検証したい。Minikube は、ローカルにシングルノード構成の Kubernetes クラスタを簡単に構築できるツールである。

Ingress を使うには、作成されている Ingress オブジェクトに基づいて L7 LB の設定を行う Ingress Controller が必要である。GKE においては、Google Cloud Load Balancer (GCLB) を制御する Ingress Controller があり、Ingress オブジェクトの定義にしたがって GCLB の設定を行う。Minikube により作成されたローカルのクラスタには当然ながら L7 LB は備わっていない。

Minikube には addons という機能が提供されており、Kubernetes Dashboard や heapstar, kube-dns などのアドオンを管理することができる。アドオンのひとつとして nginx を Ingress の L7 LB として利用する nginx-ingress-controller が提供されているので、簡単に Ingress の利用を開始できる。

- [kubernetes/minikube: Run Kubernetes locally](https://github.com/kubernetes/minikube)
- [nginxinc/kubernetes-ingress: NGINX and NGINX Plus Ingress Controllers for Kubernetes](https://github.com/nginxinc/kubernetes-ingress)

## Ingress を有効にする

Ingress を有効にする前に、クラスタが作成されていなければ事前に作成しておく必要がある。

```
$ minikube start
```

クラスタが作成されたら、`minikube addons` コマンドを使い、Ingress を有効にする。

```
$ minikube addons enable ingress
```

しばらくすると nginx-ingress-controller と default-http-backend が kube-system ネームスペースに作成される。

```
$ kubectl get po -n kube-system
NAME                             READY     STATUS    RESTARTS   AGE
default-http-backend-2dmmw       1/1       Running   0          12m
kube-addon-manager-minikube      1/1       Running   0          13m
kube-dns-v20-1rr8n               3/3       Running   0          13m
nginx-ingress-controller-85dql   1/1       Running   0          12m
```

*Ingress アドオンは [v0.14.0 で提供された](https://github.com/kubernetes/minikube/blob/master/CHANGELOG.md#version-0140---12142016)。有効にならないときは、バージョンを確認する。*

## Ingress の動作を確認する

動作を確認するために、ここでは nginx をデプロイして Ingress LB 経由でアクセスしてみる。`nginx.minikube.dev` というホスト名で全てのパスのリクエストを `nginx` サービスの80番ポートにフォワードするように定義する。

*ここでは、利便性を優先し dev ドメインを利用していますが、このような開発用途でのこのドメインの利用は推奨されていません。必要に応じて test など他のドメインを利用してください。*

```
$ kubectl run nginx --image=nginx
$ kubectl expose deploy nginx --port=80 --target-port=80
$ cat <<EOL | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
  - host: nginx.minikube.dev
    http:
      paths:
      - backend:
          serviceName: nginx
          servicePort: 80
EOL
```


Minikube で作成されたクラスタはひとつのマスタ兼ノードのマシンで動作しており、当然 Ingress の使う L7 LB (nginx) もそのマシンで動作している。マシンの IP アドレスは、`minikube ip` コマンドで取得できるので、この IP アドレスに Host ヘッダ `nginx.minikube.dev` を付与してアクセスする。

```
$ curl -I http://`minikube ip` -H 'Host: nginx.minikube.dev'
HTTP/1.1 200 OK
Server: nginx/1.11.3
Date: Fri, 17 Feb 2017 07:59:13 GMT
Content-Type: text/html
Content-Length: 612
Connection: keep-alive
Last-Modified: Tue, 14 Feb 2017 15:36:04 GMT
ETag: "58a323e4-264"
Accept-Ranges: bytes
```

nginx-ingress-controller の L7 LB はホストポートの 80, 443 を使っているため、この方法でアクセスできる。気になる人は、`kubectl describe nginx-ingress-controller -n kube-system` で定義内容を確認してみるとよい。

ここまでで利用することは出来ているが、curl ではなく例えばブラウザからアクセスしたいときなど Host ヘッダを付与しなければならないのは面倒である。次にホストマシンで動作するネームサーバを使い Ingress オブジェクトに定義したホスト名で名前解決できるようにする。

## Ingress のホスト名を名前解決する

名前解決するためにはネームサーバが必要であり、ここではホストマシンにインストールされた dnsmasq をそれに利用する。

**Ubuntu 16.04 LTS / 14.04 LTS**

```
$ sudo bash -c "echo address=/minikube.dev/`minikube ip` > /etc/NetworkManager/dnsmasq.d/minikube.conf"
# for 16.04 LTS
$ sudo systemctl restart NetworkManager
# for 14.04 LTS
$ sudo restart network-manager
```

**macOS**

```
$ brew install dnsmasq
$ echo bind-interfaces > /usr/local/etc/dnsmasq.conf
$ echo listen-address=127.0.0.1 >> /usr/local/etc/dnsmasq.conf
$ echo address=/minikube.dev/`minikube ip` >> /usr/local/etc/dnsmasq.conf
$ sudo brew services start dnsmasq
$ sudo mkdir -p /etc/resolver/
$ sudo bash -c "echo 'nameserver 127.0.0.1' > /etc/resolver/minikube.dev"
```

上記の設定で、`minikube.dev.` の名前解決の結果に `minikube ip` で出力されるマシンの IP アドレスが返却されるようになり、Ingress で定義したホスト名でアクセスできるようになる。

```
$ nslookup nginx.minikube.dev
Server:         127.0.1.1
Address:        127.0.1.1#53

Name:   nginx.minikube.dev
Address: 192.168.99.100
$ curl -I http://nginx.minikube.dev/
HTTP/1.1 200 OK
Server: nginx/1.11.3
Date: Fri, 17 Feb 2017 08:20:45 GMT
Content-Type: text/html
Content-Length: 612
Connection: keep-alive
Last-Modified: Tue, 14 Feb 2017 15:36:04 GMT
ETag: "58a323e4-264"
Accept-Ranges: bytes

```

もちろんこの設定によってブラウザからも正しくアクセスすることができる。

![It works!](https://qiita-image-store.s3.amazonaws.com/0/3780/cb981771-cba2-48e9-015f-fc42ef2e3c43.png)

`dnsmasq.conf` に設定したマシンの IP アドレスは、クラスタを再作成すると変わることがある。そのときは再度上書きし、dnsmasq を再起動する必要がある。そこで `minikube start` 実行後に自動的に作成されたクラスタの IP アドレスを取得し、dnsmasq に再設定、リスタートするスクリプトを作成した。スクリプトを minikube コマンドのエイリアスとして設定するだけ動作する。詳しい使い方は下記リポジトリを参照してほしい。

- https://github.com/superbrothers/minikube-ingress-dns

## [PR] HTTP/2 でフォワードできる nghttpx-ingress-controller を使う

nghttpx-ingress-controller は、nghttpx を L7 LB として利用する Ingress Controller である。nghttpx は、フロントエンドとバックエンドともに HTTP/2 をサポートしており、gRPC のような HTTP/2 を使ったリクエストをバックエンドまで HTTP/2 で通信することができるなどの特徴がある。またこの Ingress Controller 自体も nghttpx の開発者自身が開発しており、nghttpx の機能を余すところなく利用できる拡張性がある。

- [nghttpx \- HTTP/2 リバースプロキシー / L7 ロードバランサー \- Qiita](http://qiita.com/tatsuhiro-t/items/99a2fd61d0fb16d7241b)
- [zlabjp/nghttpx\-ingress\-lb: nghttpx ingress controller for Kubernetes](https://github.com/zlabjp/nghttpx-ingress-lb)
- [nghttpx Ingress controller // Speaker Deck](https://speakerdeck.com/tatsuhirot/nghttpx-ingress-controller)

nginx-ingress-controller の代わりに nghttpx-ingress-controller でももちろん問題なく動作する。nginx-ingress-controller を無効にして、nghttpx-ingress-controller をデプロイするだけである。ただこれだとクラスタを作成するたびにデプロイし直す必要があり面倒である。

Minikube では `~/.minikube/addons` にマニフェストファイルを置いておくことで自動的にクラスタにデプロイされる。これは Minikube 組み込みのアドオンも同様だが、[addon-manager](https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/addon-manager) で実現されており、単にマシン内の `/etc/kubernetes/addons` にマニフェストファイルがコピーされているだけである。
addon-manager の仕様としてマニフェストを Cluster Add-on として登録するには、登録する全てのマニフェストのラベルに `kubernetes.io/cluster-service: true` と指定されている必要がある。指定済の nghttpx-ingress-controller のマニフェストを gist に置いておいたのでクローンして使う。

```
$ minikube addons disable ingress
```

これまでの続きで作業しているなら、最初に nginx-ingress-controller を無効化する。ひとつのクラスタにある複数の ingress-controller を使い分ける機能も存在はするが、混乱の元なので必要なければ削除しておいたほうがよい。

```
$ wget -O ~/.minikube/addons/nghttpx-ingress-lb.yaml https://gist.githubusercontent.com/superbrothers/1b8b1e6e0dd13acf62be93c6cd329d92/raw/e7a30cded7cff675d598199bf07f57e4b7318f8c/nghttpx-ingress-lb.yaml
$ minikube stop && minikube start
```

クラスタの起動時に `~/.minikube/addons` にインストールしたマニフェストファイルがマシン内の `/etc/kubernetes/addons` にコピーされ、addon-manager により自動的にデプロイされる。これで次回からクラスタを作成するときに自動的に nghttpx-ingress-controller がデプロイされるようになる。
アクセスすると、nghttpx のヘッダが出力されており、正しく動作していることが確認できる。

```
$ curl -I http://nginx.minikube.dev
HTTP/1.1 200 OK
Date: Fri, 17 Feb 2017 08:33:03 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 14 Feb 2017 15:36:04 GMT
Etag: "58a323e4-264"
Accept-Ranges: bytes
Server: nghttpx nghttp2/1.19.0
Via: 1.1 nghttpx

```