annotationではなくIngressClassリソースを使う(nginx-ingress)

### 経緯
kubernetesのバージョンが1.18以降でIngressリソースにおけるIngress.Classというアノテーションが非推奨になる、
ということで調査しておく必要があるという話になったため。

Ingressとはなにかがよくわかる図が載っている記事
https://qiita.com/MahoTakara/items/cdfa379f2280a58fdd6d

### 調査
k8s公式にはIngress.Classのアノテーションはもう非推奨だしIngressClassリソースつかえやみたいなの書いてます。
https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/
https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class
いまは公式のマニュアルにもIngressClassのことが書かれている
https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/

Deplicationガイド
https://kubernetes.io/docs/reference/using-api/deprecation-guide/

### 環境固有の前提
- AKSを使っている
- helmチャートでの更新（helm 3.2.0)
- 使っているイングレスはGoogleのingress-nginx
- helmチャートは一つでingressのチャートはchartsのなかに入れてるし2つのコントローラのためにディレクトリコピーして名前変えてる
- 本体チャートのvalues.yamlやhelm upgrade時の--valuesオプションで指定するenv名yamlとかでcharts内の依存チャートの変数を上書きしている
- LoadbalancerIPを固定するためにAzureポータルでPublicIPのserviceラベルを剝がしている


### AKS1.20, ingress-nginx4.0.6のケース
ぼんやりしてたらクラスタとチャートのバージョンが結構上がってしまって検証がやりなおしに。
kubernetes関連は半年もたつとEOLになりがちなのでカスタマイズと新機能上等で大規模に色々やりたいのでないならECSとかContainer Instancesにするほうが人件費が減るのかも。

#### 変更があったファイルとパラメータなど
おおざっぱな変更点はchartをpullしてきてCHANGELOG.mdから辿れるのでみるとよさそう。
あとは上書きしているvalues.yamlのパラメータが変わってないかどうかをガン見するとよさそう。
ここでいう変更点とは、ingress-nginxのhelmチャートのバージョン3.33.0から4.0.6に変えた場合のことを指す。

```
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx  #only once at initial
helm repo update
helm pull ingress-nginx/ingress-nginx --untar --version 4.0.6 --untar
view ingress-nginx/CHANGELOG.md
```
- values.yaml（アプリ側のチャートの直下のやつ）

```yaml:values.yaml
ingress-nginx-internal:
  controller:
    electionID: ingress-internal-controller-leader #※1
    ingressClassByName: true #※2
    ingressClassResource:#※3
      name: nginx-internal#※3
      enabled: true
      controllerValue: "k8s.io/ingress-nginx-internal" #※4
    config:
      server-tokens: "false"
    service:
      annotations:
        service.beta.kubernetes.io/azure-load-balancer-internal: "true"
    admissionWebhooks: #※5
      enabled: false #※5

ingress-nginx:
  controller:
    electionID: ingress-controller-leader
    ingressClassByName: true
    ingressClassResource:
      name: nginx
      enabled: true
      controllerValue: "k8s.io/ingress-nginx"
    config:
      server-tokens: "false"
    admissionWebhooks:
      enabled: false
  defaultBackend:
    enabled: "true"
akv2k8s:
  env_injector:
  # -- Whether to install the env-injector
    enabled: false
```
**※1: electionID**
--election-id はHPAやreplica数を増やした場合にリーダー選出するためのidである。
https://blog.sakamo.dev/post/ingress-nginx/#leader-election
Leaderとなった Pod は Ingress のステータスを毎秒同期している。
client-go で実装されていて、 ConfigMap を使いロックをしている。
Leaderが落ちたりして renewTime の更新ができなかった場合は他の Pod が renewTime と holderIdentity を更新しLeaderに昇格する。
→default値のままでどうせ定義されるので、それよりはinternalだけは変えたほうがいいんだろうと思われる
　定義しなくてもイングレスコントローラのPodが1つのケースだと特に問題は確認できなかったがテスト以外で1つのケースはそんなに無さそう。

**※2: ingressClassByName**
https://kubernetes.github.io/ingress-nginx/#how-to-easily-install-multiple-instances-of-the-ingress-nginx-controller-in-the-same-cluster
https://github.com/kubernetes/ingress-nginx/blob/main/cmd/nginx/flags.go#L72
IngressControllerがControllerClassと一緒に名前でIngressClassを監視するかどうかを定義します。
イングレスコントローラのPodが起動するときに、そのIngressClass名で指定したものとPodの.spec.controller（（上のオプションの説明によるとこれはControllerClassにあたると思われる）が一致するのを監視してくれる模様
https://github.com/kubernetes/ingress-nginx/pull/7609

```
$ kubectl get po tttest-chart-ingress-nginx-internal-controller-659dccf475-jxndm -o yaml|grep class
    - --controller-class=k8s.io/ingress-nginx-internal
    - --ingress-class-by-name=true
```
この値がfalseだと複数イングレスの内部側が404になるがtrueだと404にならずに目的のServiceのPodに到達できていた。

**※3: ingressClassResource.name**
これは昔のチャートにおいては、「ingressClass」というパラメータだった。それをこれに変えたみたいですね。

**※4: ingressClassResource.controllerValue**
ingressClassリソースをgetでみたときのCONTROLLERに表示される値で複数IngressClassの手順にかかれていて必要なパラメータ
https://kubernetes.github.io/ingress-nginx/#how-to-easily-install-multiple-instances-of-the-ingress-nginx-controller-in-the-same-cluster
https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/

**※5: admissionWebhooks**
事例がかかれていた：https://logmi.jp/tech/articles/323444#s7
有効になってる場合は有用らしいようなことが公式にはかかれている
    https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook
詳しく書いてる人によると、https://blog.sakamo.dev/post/ingress-nginx/
nginx -tとかvalidationしてくれるらしい
https://github.com/kubernetes/ingress-nginx/blob/ingress-nginx-2.7.0/internal/ingress/controller/controller.go#L201
けど具体的な使い方がよくわからないのでとりあえず止めている（オプション有効なままほったらかして大丈夫なやつかどうか確認できてない）

- ingress.yaml（アプリ側のチャートのtemplatesの下にあるやつ）

```yaml:${chartname}/templates/ingress.yaml 
{{- if or (eq .Values.env "tmp") (eq .Values.env "stg") }}
---
#apiVersion: extensions/v1beta1
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  labels:
    app: ingress
  annotations:
    #kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.publicWhitelist }}
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    nginx.ingress.kubernetes.io/custom-http-errors: 502,503,504,405
    #nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
    #nginx.ingress.kubernetes.io/configuration-snippet: |
    #  recursive_error_pages on;
    nginx.ingress.kubernetes.io/server-snippet: |
      error_page 405 = @custom_custom-default-backend-tttest-ingress-error_405;
    nginx.ingress.kubernetes.io/default-backend: tttest-ingress-error
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    #kubernetes.io/ingress.class: addon-http-application-routing
spec:
  ingressClassName: nginx
  rules:
    - http:
        {{ include "tttest.public-ingress" . | indent 8 }}
{{- end }}
---
#apiVersion: extensions/v1beta1
apiVersion: networking.k8s.io/v1 #※7
kind: Ingress
metadata:
  name: ingress-internal
  labels:
    app: ingress-internal
  annotations:
    #kubernetes.io/ingress.class: nginx-internal
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
#    nginx.ingress.kubernetes.io/default-backend: tttest-ingress-error
spec:
  ingressClassName: nginx-internal #※7
  rules:
    - host: tttest-static-int.default.svc.cluster.local #※6
      http:
        paths:
          - path: /(.*)
            pathType: ImplementationSpecific #※7
            backend:
#              serviceName: tttest-static-int
#              servicePort: 80
              service: #※7
                name: tttest-static-int #※7
                port: #※7
                  number: 80 #※7
```

**※6: host**
host指定しないとingressリソースをgetでみたときのADDRESSがおかしなことになって内部イングレスコントローラを介して裏にいるサービスに到達できなくてhttpエラーコード404でていた（アノテーションだと到達できる）
あと404はルートが存在しないと出るということでサービス名かポートかパスかDNSのどれかがダメというのを調べていく感じで、古い方のイングレスでTopの/しか表示しないようなサービスで`path: /(.*)`だと404 でたりしていた。
https://github.com/nginxinc/kubernetes-ingress/issues/966

**※7: apiVersionとingressClassNameとservice.nameとservice.port**
これらはAKS1.19かつhelmチャートバージョンが3.33で対応したときとおなじように変えるだけ
https://kubernetes.io/ja/docs/concepts/services-networking/ingress/#%E3%83%91%E3%82%B9%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%97

- networkpolicy.yaml
イングレスコントローラのPodのラベルが変わるからfromのイングレスからtoのアプリのPodのラベルめがけて接続許可しているルールのfromのラベル変える必要があった

- ingressClass.yaml
helmチャートのtemplatesのあたりにIngressClassリソースのマニフェストのテンプレートが同梱されていて勝手にIngressClassリソースが作られるっぽいので自分でかかなくてよくなってたので消した

- env/${environment}.yaml
環境毎に変える必要があるパラメータをvalues.yamlとは別に定義していてhelmのdiffとかupgrade打つときの--valuesオプションに指定してるファイル。特に何も変えていないが一応貼っておく

```env/${environment}.yaml
# Public Nginx ingress
ingress-nginx:
#nginx-ingress:
  defaultBackend:
    replicaCount: 1
  controller:
    replicaCount: 1
    config:
      log-format-upstream: '$http_x_forwarded_for - $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id'
    service:
      loadBalancerIP: '*******' ##test-je-cluster2-ing-pip
      externalTrafficPolicy: "Local"

# Internal Nginx ingress defaults
ingress-nginx-internal:
#nginx-ingress-internal:
  defaultBackend:
    replicaCount: 1
  controller:
    replicaCount: 1
    config:
      log-format-upstream: '$http_x_forwarded_for - $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id'
    service:
      loadBalancerIP: '100.98.0.35'
      externalTrafficPolicy: "Local"

# Ingress values
## - Front Door ref: https://docs.microsoft.com/en-us/azure/frontdoor/front-door-faq#how-do-i-lock-down-the-access-to-my-backend-to-only-azure-front-door
## - Special IP ref: https://docs.microsoft.com/ja-jp/azure/virtual-machines/windows/instance-metadata-service
publicWhitelist: "****"
publicHost: '****.japaneast.azmk8s.io'
```



#### 確認コマンド
意図したとおりにリソースができててLoadbalancerIPがサービスについてるかなどなどをザっと見るのとログ見る

```shell:kubectlっぽいの
$ kubectl get po,ing,ingressClass,service,deploy  #-o wideつけるとPodのIPとかもでる
$ kubectl describe ing <イングレス名> 
$ kubectl logs -f --tail=20 <イングレスコントローラのPod名>
```
ログをtailしておいて別窓で一時的にたてたcurl打つだけ用のPodからcurlで内部コントローラ越しに目的のPodにたどり着いてログが出るかを確認する。

```shell:到達を確認するcurl
$ kubectl run curl-komi --image=radial/busyboxplus:curl -i --tty --rm --labels=app.kubernetes.io/name=ingress-nginx-internal
$ curl http://tttest-chart-ingress-nginx-internal-controller.default.svc.cluster.local
```
curlするドメインは内部イングレスコントローラのLoadbalancerIPがついてるサービス名＋default.svc.cluster.localをつかってるが、ingress.yamlのhostに定義したドメインでいいはず。

#### 更新時のコマンド
更新するとき、イングレスコントローラのPodが作り直される系の更新の場合（IngressClass使うようにする更新だとそうなる）、消してから追加する感じにしたほうが余計なものが残らなくていいと思います。（要メンテだけども）
helmでもkubectlでも消せるけどkubectlで消すとhelm diffで確認ができないので急いでるとか検証とかじゃないければhelmチャート内のマニフェストを消す感じになる。

```
#diff
## helm plugin install https://github.com/databus23/helm-diff --version e186caafe744378a6059f9b70084b49daf196ede ##初回のみ
## リリース済みと未リリースの比較
helm diff upgrade tttest-chart tttest-chart -C 3 --allow-unreleased --values env/${environment}.yaml
## revisionの比較
helm diff revision tttest-chart 8 10

#install/upgrade
helm list
helm upgrade --install tttest-chart tttest-chart --values=env/${environment}.yaml
```
#### 非推奨APIが無いかを確認する
使っているhelmチャートのディレクトリで[deplicationGuide](https://kubernetes.io/docs/reference/using-api/deprecation-guide/)に基づいて以下を実施

```bash:find＋grep参考
$ find ./ -type f |xargs grep admissionregistration.k8s.io/v1beta
$ find ./ -type f |xargs grep apiextensions.k8s.io/v1beta
$ find ./ -type f |xargs grep authorization.k8s.io/v1beta1
$ find ./ -type f |xargs grep extensions/v1beta1
$ find ./ -type f |xargs grep networking.k8s.io/v1beta1
$ find ./ -type f |xargs grep rbac.authorization.k8s.io/v1beta1
```

### AKS1.19,ingress-nginx3.33.0のケース

<details><summary>古い情報はたたんでおきます</summary>
<div>
#### 成功した設定(singleのIngress Controllerの場合)

:warning: ベースチャートにしてるingress-nginxのチャートのappVersion: が1.8以上でないとIngressClassリソースを使って複数のコントローラを制御することができなさそうであることがわかった。（IngressClassが一つなら大丈夫。）
https://www.nginx.co.jp/blog/announcing-nginx-ingress-controller-for-kubernetes-release-1-8-0/


> NGINX IngressとHelmチャートにおける複数のIngress Controllerのサポート
これまでのリリースでは、NGINX Ingress Controllerの複数のインスタンスを
同一クラスター上に共存させることが可能でしたが、標準のKubernetes Ingressリソースで
kubernetes.io/ingress.classアノテーションを使用して対象のNGINX Ingress Controller
デプロイメントを指定した場合に限られていました。
リリース1.8では、VirtualServer/VirtualServerRouteリソースにingressClassNameフィールド
を追加して、これらのリソースでも同じことができるようにしました。また、複数のNGINX Ingress
 Controllerデプロイメントをサポートするように、Helmチャートをアップデートしました。



```ingressclass.yaml
---
apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
  name: nginx
# annotations:
# ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: nginx.org/ingress-controller
```

```ingress.yaml
{{- if eq .Values.env "test" }}
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  labels:
    app: ingress
  annotations:
    #kubernetes.io/ingress.class: nginx    ##★コメントイン
    nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.publicWhitelist }}
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    nginx.ingress.kubernetes.io/custom-http-errors: 502,503,504,405
    nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
    nginx.ingress.kubernetes.io/server-snippet: |
      error_page 405 = @custom_custom-default-backend-test-ingress-error_405;
    nginx.ingress.kubernetes.io/default-backend: test-ingress-error
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  ingressClassName: nginx   ##★IngressClassリソースのname指定追加
  rules:
    - http:
#～略～
```

```動確
$ kubectl get ingress
NAME      CLASS          HOSTS   ADDRESS   PORTS   AGE
ingress   nginx          *                 80      3d8h
$ kubectl get ingressclass
NAME           CONTROLLER                     PARAMETERS   AGE
nginx          nginx.org/ingress-controller   <none>       76s
$ kubectl get service
NAME                                         TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)                      AGE
kubernetes                                   ClusterIP      1xx.xx.16.1     <none>           443/TCP                      4d7h
test-chart-nginx-ingress-controller          LoadBalancer   1xx.xx.17.253   20.xxx.xxx.xxx   80:30125/TCP,443:31458/TCP   3d8h
test-chart-nginx-ingress-default-backend     ClusterIP      1xx.xx.16.47    <none>           80/TCP                       3d8h
test-ingress-error                           ClusterIP      1xx.xx.17.160   <none>           80/TCP                       3d8h
test-static                                  ClusterIP      1xx.xx.18.149   <none>           80/TCP                       3d8h
```
ブラウザからEXTERNAL-IPのサイトが見えるのを確認。特に問題なさそう。
IngressClassリソースを使ってるingressコントローラの数分複数書く場合はチャートのバージョンほかspec.controller等に注意。

#### IngressController/IngressClassを複数動かすために必要なこと

- 簡単な前提
  - AKSのVersion1.19を利用（PublicIPのタグのあたりなど）
  - publicIngressコントローラ1つ, internalIngressコントローラ１つを必要とする環境
  - ingressのリポジトリから落としてきたソースアーカイブを解凍し複製してnameに手を入れて利用している
  　（オプション指定でhelmコマンドだけで入れることも可能そうでMS公式だとそちらの手順しか出てこなかったがそれだと依存順序定めて自動で入れる動きじゃなくてパイプラインでコマンド書く感じになると思われる）
  - helmのバージョンは3.1のままでよさそう
  - charts/ディレクトリに置かれたベースチャートのアプリは本体チャートのアプリより先にインストールされる
  - chartsディレクトリを使わずにingressコントローラを準備する公式手順はこちら
　https://docs.microsoft.com/ja-jp/azure/aks/ingress-basic

- nginx-ingressの1.25.0 -> ingress-nginxの3.33.0　にチャートのバージョンを上げる（潔く作業時の最新）
　（なぜかバージョン上げるとチャートの名前が微妙に変わる関係でコントローラ作り直しになる。名前戻してもいいけどどうせAPIバージョンやIngressClassに変わる関係で作り直しなのは変わらないのでそのままにしている）
  - Chart.lockを削除
  　これを削除しないとhelm dependencyコマンドが打てない。rubyのGemfile.lockと同じ働きで複数人プロジェクトで各個人のローカル環境とパイプラインの環境の依存パッケージのバージョンを合わせる目的で使われるものと思われ、ビルド時に自動生成されるファイルなので、バージョン変えたいときは消すか戻すときのために退避するもののようです。つまり要らないファイル。
　ベース側じゃなくて本体チャート側のを消します。

```
mv test-chart/Chart.lock /tmp/
```

  - helmリポジトリを登録しなおしてアップデートする

  ```
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
  ```
  - dependenciesをChart.yamlに書いてビルドしてcharts配下にパッケージを落としてくる  
  　※requirement.yamlにdependenciesを書いてるのはhelmのバージョンが2かChart.yamlのapiVersionがv1の場合のようです。   
　※dependencies別にかかなくてもpullで取ってきて配置したほうが簡単かもしれない。  

  ```
  vi test-chart/Chart.yaml
  --add↓-
  dependencies:
- name: ingress-nginx
  version: 3.33.0
  repository: https://kubernetes.github.io/ingress-ngin
  --add↑--
  helm dependency build ./test-chart
  helm dependency update ./test-chart
  ```
buildするとChart.lockが作られ、updateするとcharts/下にChart.yamlのdependenciesに書いたバージョンのtgzアーカイブがダウンロードされる

  - ダウンロードしたアーカイブを解凍して、旧バージョンとアーカイブはリポジトリの外にでも退避する（そうしないとinstall/upgrade時に余計なingressリソースが増える）

  ~~admission-webhooks/validating-wehook.yamlの中身を全コメントアウトする
  　　（admissionコントローラがwebhookによる更新を受け付ける体でhostの設定が別に要りそうであるのと不正な設定のIngressがデプロイされないようにバリデーション設定が存在するらしい。しかしそれがあることでデプロイができない。今までなかったものであるので今後の課題として全コメントアウトにするとデプロイできる。）~~　→別のクラスタで再現できずコメントもせずに普通にアドミッションコントローラが動いている。前回はチャートとマニフェストの状態の微妙だったのかも
　　　

  - chartsの下に解凍したディレクトリ丸ごとコピーして別名で保存してその名前にChart.yamlのnameを修正する

  ```
cd test-chart/charts
tar xzvf ingress-nginx-3.33.0.tgz
mv ingress-nginx-3.33.0.tgz /usr/local/src/
mv nginx-ingress /usr/local/src/
mv nginx-ingress-internal /usr/local/src/
sed -i 's/^/#/g' ingress-nginx/admission-webhooks/validating-webhook.yaml
cat ingress-nginx/admission-webhooks/validating-webhook.yaml
cp -rp ingress-nginx ingress-nginx-internal
vi ingress-nginx-internal/Chart.yaml
--modify↓-
name: ingress-nginx
  ↓
name: ingress-nginx-internal
--modify↑-
  ```
一応念のためディレクトリの差分をみてみる

```
$ diff -crN test-chart/charts/ingress-nginx{,-internal}
diff -crN test-chart/charts/ingress-nginx/Chart.yaml test-chart/charts/ingress-nginx-internal/Chart.yaml
*** test-chart/charts/ingress-nginx/Chart.yaml        2021-06-07 01:23:21.000000000 +0900
--- test-chart/charts/ingress-nginx-internal/Chart.yaml       2021-06-15 18:51:46.833348700 +0900
***************
*** 12,18 ****
  kubeVersion: '>=1.16.0-0'
  maintainers:
  - name: ChiefAlexander
! name: ingress-nginx
  sources:
  - https://github.com/kubernetes/ingress-nginx
  type: application
--- 12,18 ----
  kubeVersion: '>=1.16.0-0'
  maintainers:
  - name: ChiefAlexander
! name: ingress-nginx-internal
  sources:
  - https://github.com/kubernetes/ingress-nginx
  type: application
```
  - ベースチャートではなく本体チャート側のvalues.yamlにベースチャートのvalues.yamlに書かれてる上書きできる上書きしたい値を書く   
　　　特に２つめ以降のingressClassの名前指定（これをingress/ingressClassリソースで定義する  
　　　あとなぜか無効にされてるdefaultBackendを有効化(enabled:true)にするのと、  
　　　Internal側のもともと設定されてるannotationなど。  
　　　　service.beta.kubernetes.io/azure-load-balancer-internal: "true"  

```
vi values.yaml
ingress-nginx-internal:
  controller:
    ingressClass: "nginx-internal"
    config:
      server-tokens: "false"
    service:
      annotations:
        service.beta.kubernetes.io/azure-load-balancer-internal: "true"
  defaultBackend:
    enabled: "true"

ingress-nginx:
  controller:
    ingressClass: "nginx"
    config:
      server-tokens: "false"
  defaultBackend:
    enabled: "true"
```

- ingressリソースのapiVersionを最新にする
  - apiVersionを修正する（**ここを変えることでIngressリソースを作り直すことになる。または別名で作成する必要がある。AKSでIngressControllerに紐づいているPublicIPを保持することは可能で同じPublicIPを保持するためにはPublicIPについているタグ「service :～」を削除する必要がある。ただしリソースを消して作り直して新しいほうに既存のPublicIPを紐づける動きになり、断時間があるためメンテに入れる必要がある**）
  - pathTypeを書く、serviceの書き方を修正する
  - ingressClassNameはvalus.yamlで上書きしたものとデフォルトのingressClass名で定義

```
{{- if eq .Values.env "tmp" }}
---
#apiVersion: extensions/v1beta1
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  labels:
    app: ingress
  annotations:
    #kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.publicWhitelist }}
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    nginx.ingress.kubernetes.io/custom-http-errors: 502,503,504,405
    #nginx.ingress.kubernetes.io/enable-rewrite-log: "true"
    #nginx.ingress.kubernetes.io/configuration-snippet: |
    #  recursive_error_pages on;
    nginx.ingress.kubernetes.io/server-snippet: |
      error_page 405 = @custom_custom-default-backend-test-ingress-error_405;
    nginx.ingress.kubernetes.io/default-backend: test-ingress-error
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  ingressClassName: nginx
  rules:
    - http:
        {{ include "test.public-ingress" . | indent 8 }}
{{- end }}
---
##apiVersion: extensions/v1beta1
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-internal
  labels:
    app: ingress-internal
  annotations:
    #kubernetes.io/ingress.class: nginx-internal
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.publicWhitelist }}
    nginx.ingress.kubernetes.io/default-backend: test-ingress-error
spec:
  ingressClassName: nginx-internal
  rules:
    - http:
        paths:
          - path: /(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: test-static
                port:
                  number: 80
```

- ingressClassをただしく設定する
  - apiVersionを新しいのにする
  - valus.yamlで上書きした、またはデフォルトのingressClass名でリソースを定義する

```
---
apiVersion: networking.k8s.io/v1
#apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
  name: nginx
# annotations:
# ingressclass.kubernetes.io/is-default-class: "true"
  labels:
    app.kubernetes.io/managed-by: Helm
spec:
  #controller: nginx.org/ingress-controller
  controller: k8s.io/ingress-nginx
---
apiVersion: networking.k8s.io/v1
#apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
  name: nginx-internal
# annotations:
# ingressclass.kubernetes.io/is-default-class: "true"
  labels:
    app.kubernetes.io/managed-by: Helm
spec:
  #controller: nginx.org/ingress-controller
  controller: k8s.io/ingress-nginx
```
- publicIPについているserviceタグを消す

タグがついたままだとIngressコントローラが消されるまたは作り替えられる際にPublicIPもろとも消されてしまうのでタグを外します。

Azure Portal より [対象の AKS クラスター] - [プロパティ] と遷移し、[インフラストラクチャ リソース グループ (MC_***)] を開く
Ingress Controller 作成時に作られた [パブリック IP アドレス] を開き、[タグ(変更)] より Ingress Controller のタグ (service : ***) を削除する

- networkpolicyを修正する

ingressコントローラのPodについているラベルを用いてnginxのingressコントローラのPodとupstreamのPodとの通信を許可していたが、
実はUpgradeしたことによりラベルの名前が異なるものに変わった。
ラベルを修正しないとingressからアプリのコンテナへの通信ができなくなってサービス断になるところだった。

```
$ kubectl get pods test-chart-ingress-nginx-internal-controller-774f98b778-tnmdx -o yaml|head -20
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2021-07-20T05:50:43Z"
  generateName: test-chart-ingress-nginx-internal-controller-774f98b778-
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: test-chart
    app.kubernetes.io/name: ingress-nginx-internal
    pod-template-hash: 774f98b778
  managedFields:
  - apiVersion: v1
```
なのでnginxのルールは以下のようになる。

```
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: nginx-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      nginx: allowed
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx
    - podSelector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx-internal
```
※元のラベルは「app: nginx-ingress」等でした。

- helmコマンドは無かったら入れる（helm3は~/.kube/configあたりのクレデンシャルを見てK8sクラスタにつなぎに行っていてkubectl config current-contextでみえた現在つないでるクラスタの操作をすることになる）

```
wget https://get.helm.sh/helm-v3.2.4-linux-amd64.tar.gz && tar -zxvf helm-v3.2.4-linux-amd64.tar.gz && sudo mv linux-amd64/helm /usr/local/bin/helm
## plugin入れることで現在の未デプロイとインストール済みチャートを比較
helm plugin install https://github.com/databus23/helm-diff --version e186caafe744378a6059f9b70084b49daf196ede 
```

- ipを保持したい場合一旦既存のingress関連のリソースを消す（PublicIPのタグを消すのも忘れない）

※既存リソースを消さないままIngressClassを作り替えるオペレーションをするとたぶんもういっこPublicIPができる
※移動したりコメントに入れたものはあとで元に戻す
※消して戻すまでの間はリソースがないので計画メンテにしないとサービスが止まる。
（許容できなければだいぶ手間はかかるがブルーグリーンupgradeにするとよさそう）
chartsの下のディレクトリ毎移動しつつvalues.yamlの関連部分をコメントに入れてhelm upgradeでingressコントローラをアンインストールする
chartsの下のディレクトリ毎移動

```
$ mv test-chart/charts/ingress-nginx ./ingress-nginx_20210721test
$ mv test-chart/charts/ingress-nginx-internal ./ingress-nginx-internal_20210721test
```
values.yamlのingress-nginxの依存chartの上書きしてる変数の部分をコメントイン

```
$ sed -i 's/^/#/g' test-chart/values.yaml 
$ cat test-chart/values.yaml 
```
ingress/ingressClassリソースをコメントイン

```
$ sed -i 's/^/#/g' test-chart/templates/ingress.yaml 
$ sed -i 's/^/#/g' test-chart/templates/ingress-class.yaml 
$ cat test-chart/templates/ingress.yaml 
$ cat test-chart/templates/ingress-class.yaml 
```
chartsの下のdependenciesの部分をコメントイン

```
$ vi test-chart/Chart.yaml
$ sed -i '4,7s/^/#/g' test-chart/Chart.yaml 
$ cat test-chart/Chart.yaml
```
ingress.yamlに取り込んでるhelperのルーティングブロックの部分をコメントイン

```
$ sed -i '1,12s/^/#/g' test-chart/templates/_helpers.tpl

```
消えるリソースをdiffで確認

```
$ helm diff upgrade test-chart test-chart -C 3 --allow-unreleased --values env/tmp.yaml|grep 'default,'
```

- helmでインストールする（コメントに入れたリソースを消してる）

```
$ helm upgrade --install test-chart test-chart --values=env/tmp.yaml
```

- helmで差分を確認する

```
helm diff upgrade test-chart test-chart -C 3 --allow-unreleased --values env/tmp.yaml
```
- helmでdeployしたリソースが存在することを確認する

```
kubectl get all
kubectl get ingress
kubectl get ingressClass
kubectl get configmap
```
- リソースを戻す（バージョン上がってる状態で戻す）

chartsの下に移動していたディレクトリをもどす。

```
$ mv ./ingress-nginx_20210721test test-chart/charts/ingress-nginx
$ mv ./ingress-nginx-internal_20210721test test-chart/charts/ingress-nginx-internal
```
values.yamlのingress-nginxの依存chartの上書きしてる変数の部分をコメントはずす

```
$ sed -i 's/^#//g' test-chart/values.yaml 
$ cat test-chart/values.yaml 
```
ingress/ingressClassリソースをコメントはずす

```
$ sed -i 's/^#//g' test-chart/templates/ingress.yaml 
$ sed -i 's/^#//g' test-chart/templates/ingress-class.yaml 
$ cat test-chart/templates/ingress.yaml 
$ cat test-chart/templates/ingress-class.yaml 
```
chartsの下のdependenciesの部分をコメントはずす

```
$ vi test-chart/Chart.yaml
$ sed -i '4,7s/^#//g' test-chart/Chart.yaml 
$ cat test-chart/Chart.yaml
```
ingress.yamlに取り込んでるhelperのルーティングブロックの部分をコメント外す

```
$ sed -i '1,12s/^#//g' test-chart/templates/_helpers.tpl
$ cat test-chart/templates/_helpers.tpl
```
増えるリソースをdiffで確認

```
$ helm diff upgrade test-chart test-chart -C 3 --allow-unreleased --values env/tmp.yaml|grep 'default,'
```

- helmでインストールする（追加）

```
$ helm upgrade --install test-chart test-chart --values=env/tmp.yaml
```
- helmでdeployしたリソースを確認する

```
kubectl get all
kubectl get ingress
kubectl get ingressClass
kubectl get configmap
```
もとのPublicIPが新しく作られたingress-nginxのServiceに紐づいてるのを確認する。

- 変なエラーでてないかログやdescribeとかを確認する

```
kubectl get po
kubectl logs -f <ingress-controller-pod-name>
kubectl describe po <ingress-controller-pod-name>
```
あとサイトの表示も確認する。
</div>
</details>



##### エラーまとめ

  - IngressClassリソースのコントローラの指定の文字列を修正（kubectl logsでingressコントローラのpodのログを眺めると出ていた）

```
E0617 10:53:50.872817       6 main.go:134] Invalid IngressClass (Spec.Controller) value "nginx.org/ingress-controller". Should be "k8s.io/ingress-nginx"
F0617 10:53:50.872955       6 main.go:135] IngressClass with name nginx is not valid for ingress-nginx (invalid Spec.Controller)
```
   - pathType間違ってると出るエラー

 ```
 $ helm upgrade --install test-chart test-chart --values=env/tmp.yaml
 Error: UPGRADE FAILED: failed to create resource: Ingress.extensions "ingress-internal" is invalid: spec.rules[0].http.paths[0].pathType: Required value: pathType must be specified
 ```

- ingressリソースが見当たらない→if分岐でenvを限定してたことに気づかずにenv変えたせいで無かった

```
$ kubectl get ingress -A
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
No resources found
```

- よくわからないパースエラー（予期しないEOF）だがgit checkoutして書き直したらなおった

```
$ helm upgrade --install test-chart test-chart --values=env/tmp.yaml
Error: UPGRADE FAILED: parse error at (test-chart/templates/ingress.yaml:72): unexpected EOF
```

- IPアドレス間違えてnodePoolがあるリソースグループ内に生成されたPublicIPリソースではなくVMSSのIPを付けてた時に出たエラー

```
$ kubectl describe service test-chart-nginx-ingress-controller

Events:
  Type     Reason                  Age                  From                Message
  ----     ------                  ----                 ----                -------
  Normal   EnsuringLoadBalancer    34s (x6 over 3m10s)  service-controller  Ensuring load balancer
  Warning  SyncLoadBalancerFailed  34s (x6 over 3m10s)  service-controller  Error syncing load balancer: failed to ensure load balancer: user supplied IP Address 20.194.xxx.xxx was not found in resource group mc_ryoji-test_rc-test-je-cluster2-aks_japaneast
```

- ingressClassの名前が重複しているため更新できないエラー（ingress作り直すとPublicIPを保持できない疑惑を確認中）

```
Error: UPGRADE FAILED: cannot patch "nginx" with kind IngressClass: IngressClass.networking.k8s.io "nginx" is invalid: spec.controller: Invalid value: "k8s.io/ingress-nginx": field is immutable
```

- ingressClassの命名規則が間違ってるエラー

```
Error: UPGRADE FAILED: failed to create resource: Ingress.extensions "ingress" is invalid: spec.ingressClassName: Invalid value: "nginxPublic": a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
```

あと、helperにある引用されないブロックがそのままだとhelm upgradeでapiVersionがないみたいな内容のエラーが出る

##### 参考サイト
k8sアプリデバッグ方法
https://qiita.com/tkusumi/items/a62c209972bd0d4913fc

ingress公式マニュアル
https://kubernetes.io/ja/docs/concepts/services-networking/ingress/

nginx公式側のhelmインストール手順（参考にはしたがつかっているのとは違うリポジトリ）
https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-helm/
https://github.com/nginxinc/kubernetes-ingress/tree/master/deployments/helm-chart#readme
nginx公式のingress-controllerのリリースノート
https://docs.nginx.com/nginx-ingress-controller/releases/
複数のingress-controllerをサポートしたとかかれている1.8のリリースノート
https://www.nginx.co.jp/blog/announcing-nginx-ingress-controller-for-kubernetes-release-1-8-0/

AKS公式のingress/nginxをhelmインストールしている手順
https://docs.microsoft.com/ja-jp/azure/aks/ingress-basic

ingress-nginx公式のhelmインストール手順
https://kubernetes.github.io/ingress-nginx/deploy/#using-helm
https://artifacthub.io/packages/helm/ingress-nginx/ingress-nginx
公式のChart.yaml
https://github.com/kubernetes/ingress-nginx/blob/master/charts/ingress-nginx/Chart.yaml

ジョナサンという野生のAKSに詳しい人が書いた割と真に迫っている詳しい記事
https://jonathan18186.medium.com/azure-kubernetes-service-aks-with-different-ingress-options-part1-835a9d09a057

複数のnginxingressコントローラを頑張って再現しようとしているが決定的な情報が欠けたissue
https://github.com/kubernetes/ingress-nginx/issues/5996

複数のIngressClassとかいってアノテーションでしかない残念な記事
https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-access-kubernetes-services/

ValidatingWebhookConfigurationについて理解が進んだ記事
https://qiita.com/gashirar/items/8dc7bd4886e82f7ffd73
https://github.com/kubernetes/ingress-nginx/issues/5401#issuecomment-662424306
https://sysdig.jp/blog/kubernetes-admission-controllers-2/

Helmのリリース
https://github.com/helm/helm/releases

helm dependency
https://helm.sh/docs/helm/helm_dependency/

requirement.yamlはhelm2かChartのバージョンが低い
https://www.skyarch.net/blog/?p=16621

だいぶ詳細なHelm入門とチャートのつくり方など
https://knowledge.sakura.ad.jp/23603/
https://qiita.com/thinksphere/items/5f3e918015cf4e63a0bc
https://qiita.com/HaraShun/items/cd7735d82b87770292e4

Helmチャート開発ベストプラクティス
https://jfrog.com/ja/blog/helm-charts-best-practices/
https://helm.sh/docs/chart_best_practices/

helmのバージョンを上げる場合にmanaged-byの推奨ラベルが必要そう
https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/common-labels/

helmで分岐やループを定義する関連情報
https://golang.org/pkg/text/template/#hdr-Functions
https://stackoverflow.com/questions/49789867/can-we-use-or-operator-in-helm-yaml-files

ingress-nginxの強化ガイドで目的別に逆引きで設定が載ってる
https://kubernetes.github.io/ingress-nginx/deploy/hardening-guide/

苦しんでる人の記録
https://stackoverflow.com/questions/65165216/kubernetes-ingress-nginx-ingressclass-with-name-nginx-is-not-valid-for-ingress
https://stackoverflow.com/questions/69363123/not-found-404-nginx-unable-to-connect-through-ingress-ip-on-minikube
https://stackoverflow.com/questions/52021925/kubernetes-ingress-non-root-path-404-not-found

イングレスコントローラの一般的な内容
https://cstoku.dev/posts/2018/k8sdojo-22/

イングレスのhostの定義でADDRESSの中身がでる
https://www.webdevqa.jp.net/ja/kubernetes/%E7%A9%BA%E3%81%AE%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9kubernetes%E3%82%A4%E3%83%B3%E3%82%B0%E3%83%AC%E3%82%B9/806207987/

pathtypeの件
https://kubernetes.io/ja/docs/concepts/services-networking/ingress/#%E3%83%91%E3%82%B9%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%97

マルチイングレスクラス関連
https://kubernetes.github.io/ingress-nginx/#how-to-easily-install-multiple-instances-of-the-ingress-nginx-controller-in-the-same-cluster
https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/

コマンドライン引数一覧
https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/