Kubernetes 1.16: SIG-Network の変更内容

[Kubernetes 1.16 の Changelog](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.16.md#v1160) の [SIG-Network](https://github.com/kubernetes/community/tree/master/sig-network) に関連する内容をまとめました。

## Notable Features

### Beta
- `Network`: Service LoadBalaner の finalizer に対する保護機能がベータとなり、デフォルトで有効になりました。この機能によって、紐付けられたロードバランサのリソースが削除されるまで、Service リソースは削除されないようになります。 ([#81691](https://github.com/kubernetes/kubernetes/pull/81691), [@MrHohn](https://github.com/MrHohn))

### Misc

- `Network`:  EndpointSlice の feature gate を有効にした際に、kube-proxy も EndpointSlice  を見るようになりました  ([#81430](https://github.com/kubernetes/kubernetes/pull/81430), [@robscott](https://github.com/robscott))
    - :memo: [Endpoint Slice](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) は v1.16 でアルファとして導入された、 Endpoint リソースの代替となるものです。詳細は後述しています。
- `Network`: controller manager で IPV6 のデュアルスタックを設定できるようになしました。`--cluster-cidr="<cidr1>,<cidr2>"`で設定可能です。
	- CIDR の指定の最初の2つのみが使われます(アルファではソフトリミットで、今後取り除かれる可能性があります)
	- `--cidr-allocator-type`. としてはデフォルトの "RangeAllocator" のみが許可されます。CloudAllocator は IPV6 デュアルスタックとは互換性がありません
     ([#73977](https://github.com/kubernetes/kubernetes/pull/73977), [@khenidak](https://github.com/khenidak))

    - :memo: [IPv4/IPv6 dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/) という公式ドキュメントが追加されています。`--cluster-cidr=10.244.0.0/16,fc00::/24` のように、IPV4, IPV6 それぞれの cidr を指定するようです。


## Other notable changes

- IPVS のグレースフルシャットダウンにて、UDP の Real サーバーが削除されない文字列比較のバグを修正。([#78999](https://github.com/kubernetes/kubernetes/pull/78999), [@andrewsykim](https://github.com/andrewsykim))
    - :memo: IPVS で UDP の場合はグレースフルシャットダウンをしないというロジックで、大文字、小文字の `UDP`, `udp` を case-sensitive に扱っていたことにより正しく動いていなかったバグのようです。case-insensitive に修正されました。
- `kube-proxy --cleanup will` はクリーンアップの成功時に正しい終了コードを返すようになりました ([#78775](https://github.com/kubernetes/kubernetes/pull/78775), [@johscheuer](https://github.com/johscheuer))
    - :memo: 今までは一律 `klog.Fatal()` で終了していて、終了コードが 255 となっていたようです
- IPVS proxier で対応する Service オブジェクトが削除されたにも関わらず、Virtual サーバーがクリーンアップされないバグが修正されました。([#80942](https://github.com/kubernetes/kubernetes/pull/80942), [@gongguan](https://github.com/gongguan))
- kube-proxy はノードが定義されるまでのある程度の間を待つようになりました  ([#77167](https://github.com/kubernetes/kubernetes/pull/77167), [@paulsubrata55](https://github.com/paulsubrata55))
    - :memo: kube-proxy の起動時に Node オブジェクトから IP アドレスを取得する関係で、kubelet が Node オブジェクトを登録するまで待つ必要がありました
- グレースフルシャットダウンのログレベルを `v=5` に増やしました ([#80100](https://github.com/kubernetes/kubernetes/pull/80100), [@andrewsykim](https://github.com/andrewsykim))
    - :memo: 元々は `v=2` だったため大量にログが出る問題があったようです
- IPVS モードで nodePort を利用するService が大量にあった場合の、kube-proxy の CPU 使用量が削減されました ([#79444](https://github.com/kubernetes/kubernetes/pull/79444), [@cezarsa](https://github.com/cezarsa))
- IPVS が有効な場合、kube-proxy で SCTP の nodeport の Service は Node の Internal IP だけで動作し、その他のノード上の IP で動作しない問題が修正されました ([#81477](https://github.com/kubernetes/kubernetes/pull/81477), [@paulsubrata55](https://github.com/paulsubrata55))
- IPVS モードの kube-proxy で `KUBE-MARK-DROP` の存在がチェーンが保証されるようになりました。このチェーンはデュアルスタックの IPV4, IPV6 の両方で保証されます ([#82214](https://github.com/kubernetes/kubernetes/pull/82214), [@uablrek](https://github.com/uablrek))
    - :memo: IPVS の場合は、このチェーンは kubelet 側で作られる仕様に依存している問題があったようです。IPVS の場合も kube-proxy 側でこのチェーンを作るようになり解消されました。
- `node.kubernetes.io/exclude-balancer` と `node.kubernetes.io/exclude-disruption` ラベルがアルファとして導入されました。これによってクラスタのデプロイにおいて、すべてのクラスタで提供されているわけではいないオプショナルな `node-role` ラベルへの依存を避けることができます ([#80238](https://github.com/kubernetes/kubernetes/pull/80238), [@smarterclayton](https://github.com/smarterclayton))
- targetPort が変更された場合にも Service Controller で処理されるようになりました ([#77712](https://github.com/kubernetes/kubernetes/pull/77712), [@Sn0rt](https://github.com/Sn0rt))


## :memo: EndpointSlice について
[Endpoint Slice](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) は v1.16 でアルファとして導入された、 Endpoint リソースの代替となる機能です。

### Endpoint の課題
今までの Endpoint は 1 オブジェクトで、1 Service の すべての Endpoint の情報(Pod IP や Node)を管理していました。例えば 5000 ノードのような大規模環境で 1 Service に大量の Pod がいるような場合、パフォーマンス上の問題がありました。

- すべての Endpoint の情報 (Pod IP などの情報) が一つの Endpoint オブジェクトに入っている
- etcd レイヤで 1 オブジェクトで最大 1.5 MB という制限がある
    - [10,000 Pod でエラーとなったという issue](https://github.com/kubernetes/kubernetes/issues/73324) が上がっている
- 一つの Endpoint に変更に対して Endpoint オブジェクト全体を書き換えないといけない
- 大規模環境だと rolling-update などでパフォーマンス低下が顕著になる
    - 通信量のオーダーが O(P^2N) となってします (P=Pod数、N=ノード数)

以下は一部省略した Endpoint のリソースです。

```yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: default-http-backend
  namespace: kube-system
subsets:
- addresses:
  # 各 Endpoint の情報がすべて配列として管理されている
  - ip: xxx.xxx.xxx.xxx
    nodeName: node1
    targetRef:
      kind: Pod
      name: default-http-backend-6f89599f78-pjxzt
      namespace: kube-system
  - ip: yyy.yyy.yyy.yyy
    nodeName: node2
    targetRef:
      kind: Pod
      name: default-http-backend-6f89599f78-dx9pc
      namespace: kube-system
  ports:
  - port: 8080
    protocol: TCP
```


### EndpointSlice
EndpointSlice ではデフォルト 100 の単位で、Endpoint の情報を管理することでこの課題を解決します。Endpoint の情報を 1:1 で管理しなかった理由は、そうすると別の問題が発生するからのようです。参考: [EndpointSlice API の KEP](https://github.com/kubernetes/enhancements/blob/master/keps/sig-network/20190603-EndpointSlice-API.md)。

- EndpointSlice が 1 オブジェクト 1 情報を管理する場合
   - 今度は Node (kube-proxy) が watch する量が莫大になる
   - 20,000 Endpoint, 5000 Node だと、20,000 * 5,000 = 100,000,000 の watch となってしまう
   - 100 単位で管理すると、watch の数も 100 分の1 となる
   - バッチで update　する処理もあるので 1 個だとパフォーマンスが下がる
- ほとんどの利用者は 100 以下の Endpoint の Service を使っているので 100 であれば今までと同じ
- 検討の結果 100 がちょうど良いサイズだったようです。[KEP](https://github.com/kubernetes/enhancements/blob/master/keps/sig-network/20190603-EndpointSlice-API.md#estimation) に見積もりが載っています。