nginx-ingress/kube-legoを複数系統で構築する

# :skull: 

悲しいことにこの記事を書いている時点(2018/02/08)ですでにkube-legoはDEPRECATEDな状態。

https://github.com/jetstack/kube-lego

> :warning: 
> kube-lego is in maintenance mode only. There is no plan to support any new features. The latest Kubernetes release that kube-lego officially supports is 1.8. The officially endorsed successor is [cert-manager](https://github.com/jetstack/cert-manager/).

Kubernetes1.8までしか正式にはサポートせず、cert-managerに引き継がれる模様。
1.9はすでにリリースされていて、ちょうど今日GKEも[全リージョン1.9対応が完了済み](https://cloud.google.com/kubernetes-engine/release-notes#february-5-2018) :skull: 

なので自分自身この構成で運用はすることなく終わるけど一応できたことのメモ。

基本的にHelmを使う。

# やりたかったこと

[NGINX Ingress Controller](https://github.com/kubernetes/ingress-nginx)を使うと、
各IngressでそのコントローラのService(type: LoadBalancer)を共有する形になり、GKEでいうとTCP LoadBalancerを共有し、IPも同じになる模様。

(それによるデメリットがどれほどかは明確にわかってない）

それを避けるために、複数のNGINX Ingress Controllerをデプロイする方法と、
そのとき苦戦したkube-lego側の設定のメモ。

# 環境

- Host: GKE
- Master version/Node version: 1.8.5-gke.0
- Helm: v2.8.0
- chart: stable/nginx-ingress:0.9.1
- chart: stable/kube-lego:0.4.0
- image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.10.2
- image: jetstack/kube-lego:**master-3418** (後述)

# 構成

- NGINX Ingress Controller
    - namespace: `ingress-nginx`

- kube-lego
    - namespace: `kube-lego`

- 通常の系統
    - `nginx-ingress`/`kube-lego`
- 別系統(社内用系統なイメージ)
    - `nginx-ingress-inhouse`/`kube-lego-inhouse`

# 手順

(helmのセットアップ手順は省略)


## namespace作成

```
# ingress-nginx
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/namespace.yaml \
  | kubectl apply -f -

# kube-lego
curl https://raw.githubusercontent.com/jetstack/kube-lego/master/examples/nginx/lego/00-namespace.yaml \
  | kubectl apply -f -
```

必須ではないけど、公式の命名に準じる。

## nginx-ingressのセットアップ

```
helm install stable/nginx-ingress \
  --name nginx-ingress \
  --namespace ingress-nginx \
  --set controller.replicaCount=2
```

`replicaCount` は任意で。

## nginx-ingress-inhouseのセットアップ

```
helm install stable/nginx-ingress \
  --name nginx-ingress-inhouse \
  --namespace ingress-nginx \
  --set controller.ingressClass=nginx-inhouse \
  --set controller.replicaCount=2
```

`ingressClass` をデフォルトの`nginx`とは別名に指定する。

## kube-legoのセットアップ

```
helm install stable/kube-lego \
  --name kube-lego \
  --namespace kube-lego \
  --set image.tag=master-3418 \
  --set config.LEGO_LOG_LEVEL=debug \
  --set config.LEGO_EMAIL=email@example.com \
  --set config.LEGO_URL=https://acme-v01.api.letsencrypt.org/directory
```

`LEGO_LOG_LEVEL` は任意で。
`LEGO_EMAIL` は自分のものに要変更。

`LEGO_URL` はまず指定せずにセットアップを検証してから（staging用apiになる)、本番用apiで構築する。

`image.tag=master-3418` は後述。

## kube-legoinhouseのセットアップ

```
helm install stable/kube-lego \
  --name kube-lego-inhouse \
  --namespace kube-lego \
  --set nameOverride=kube-lego-inhouse \
  --set image.tag=master-3418 \
  --set config.LEGO_LOG_LEVEL=debug \
  --set config.LEGO_EMAIL=email@example.com \
  --set config.LEGO_URL=https://acme-v01.api.letsencrypt.org/directory \
  --set config.LEGO_SUPPORTED_INGRESS_CLASS=nginx-inhouse \
  --set config.LEGO_DEFAULT_INGRESS_CLASS=nginx-inhouse \
  --set config.LEGO_DEFAULT_INGRESS_PROVIDER=nginx \
  --set config.LEGO_SECRET_NAME=kube-lego-inhouse-account \
  --set config.LEGO_SERVICE_NAME_NGINX=kube-lego-inhouse-nginx \
  --set config.LEGO_INGRESS_NAME_NGINX=kube-lego-inhouse-nginx
  --set config.LEGO_SERVICE_SELECTOR=kube-lego-inhouse \
```

`LEGO_SUPPORTED_INGRESS_CLASS` で `nginx-inhouse` ingress classを対象に取るように変更。

`LEGO_DEFAULT_INGRESS_CLASS`, `LEGO_DEFAULT_INGRESS_PROVIDER `は、kube-legoがACMEチャレンジのパス用に生成するingressの `kubernetes.io/ingress.class`, `kubernetes.io/ingress.provider` annotationの値に使われる。

`LEGO_SECRET_NAME`, `LEGO_SERVICE_NAME_NGINX`, `LEGO_INGRESS_NAME_NGINX` はkube-legoが生成するリソースの名前を主系統と重複させないために設定。

`LEGO_SERVICE_SELECTOR`も、主系統用とは別に設定。

苦戦したのが、これらのうち `LEGO_DEFAULT_INGRESS_PROVIDER` などの項目が、helmがデフォルトで参照する jetstack/kube-lego:0.1.5 イメージではまだ対応していない点。

最新バージョンのイメージを使うために https://hub.docker.com/r/jetstack/kube-lego/tags/ を参照しつつ最新の `image.tag=master-3418` (2018/02/08時点)を指定。

## nginx-ingress系統にingress追加

例

```
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: web-nginx
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: web
          servicePort: 80
  tls:
  - hosts:
    - example.com
    secretName: web-tls
```

`name`, `namespace`, `serviceName`, `servicePort`, `secretName` などは適宜命名。
`example.com` は要変更。

`kubernetes.io/ingress.class`が`nginx`なので主系統の`nginx-ingress`, `kube-lego`が捕捉して処理される。

## nginx-ingress-inhouse系統にingress追加

例

```
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: admin-nginx-inhouse
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx-inhouse
    kubernetes.io/ingress.provider: nginx
    kubernetes.io/tls-acme: "true"
spec:
  rules:
  - host: admin.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: admin-service
          servicePort: 80
  tls:
  - hosts:
    - admin.example.com
    secretName: admin-tls
```

`kubernetes.io/ingress.class`が`nginx-inhouse`なので`nginx-ingress-inhouse`, `kube-lego-inhouse`に捕捉される。

嵌ったのが、 `kubernetes.io/ingress.provider: nginx` を指定しないと、ingress.classがprovider名としても適用され、結果としてkube-legoのnginx providerの処理にのらず

```
disable provider no TLS hosts found
```

と出てkube-legoが動作してくれなかった。

（ソースコード的には[このあたり](https://github.com/jetstack/kube-lego/blob/020e4d59b82d75681a04c92d46d4ca89bbfa9f43/pkg/kubelego/configure.go#L66)。provider名での分岐に入ってくれない）

# おわり

`annotation.kubernetes.io/ingress.provider` が鍵。

且つそれがlatestイメージではサポートされていない罠。

そしていずれにせよDEPRECATEDな悲しみ。

次はcert-managerの方を調べる。