Pod Ready++(PodReadinessGate)

Kubernetes v1.12.0 で新たな Kubernetes を拡張するための機能が追加されました！ :tada: 
このエントリでは新たに追加された Pod Ready++(PodReadinessGate) についてまとめます。

# Pod Ready++ とは

Pod Ready++ は Pod の Readinessの条件に拡張を加える事ができる機能です。v1.11 で密かに Alpha として追加され、v1.12 で Beta に昇格しました。これにより独自の Readiness 条件を追加することができます。

# 目的

Kubernetes は Pod Readiness が true になるとトラフィックを受け付ける準備をします。Pod Readiness は kubelet の Readiness Probe によって判定されます。Pod Readiness が true になるとその Pod は Service の Endpoints に追加され、kube-proxy が iptables などの更新をして トラフィックが流れるようになります。しかし、Deployments(ReplicaSet) などはこのトラフィックが流れる状態になったことを確認してから次のPodのアップデートなどを実施しません。そのため、iptableの更新処理などに時間がかかると 一瞬 Service から Pod 接続できなくなる問題が発生する可能性があります。

具体的には極端な例ですが次のようなケースで瞬断が発生します。

Pod が一つの Deployment があったとして、このDeploymentを更新して、Pod v1 を Pod v2に更新する例を考えます。
Deployment を v2 に更新すると以下の図のように各コンポーネントが遷移したとします。(以下の図は左から右に時系列順で各コンポーネントが行う処理や状態を記載しています。)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100565/4842bd89-1446-74be-7df6-06b6b6d36f42.png)

このように遷移すると P 点でPod v2 の Readiness が true となり、Pod v2へトラフィックを流すための更新の処理(endpoint-controllerの更新とkube-proxyのiptables更新)が行われます。問題となるのがこのとき同時に deployment controller は Pod v1 の ReplicaSet の Replicasを減らし Pod 削除を開始してしまうことです。そのため、図のようにiptablesの更新が遅れ、Podの終了処理が先に行われるとリクエストがすでに終了している Pod v1 へ流れてしまうことになります。

*おそらくこのdeployment controllerが行うReplicaSet v1のReplicasを減らす処理をPod v2 がServiceに接続された後から実行するようにしたいのだと思いますが、今回の提案内容だけでは実現できないように思えます。Proposal に記載されていた Motivation の内容を上記のように解釈しましたがもしかしたら誤っているのかもしれません。*

# 追加された内容

## ReadinessGate

PodSpec に ReadinessGates という Spec が追加されました。

```
type ReadinessGate struct {
	conditionType string	
}
```

ReadinessGate は ConditionType を持っており、これを指定すると該当の Pod の PodConditons に ReadinessGateで指定した Type の Condition が True にならない場合は Readiness を false にします。
つまり Readiness の判定は以下のようになります。

```
Pod is ready == containers are ready AND conditions in ReadinessGates are True
```

例えば以下のPodの場合は www.example.com/feature-1 の ConditionType が False なため Pod は not ready になります。

```
Kind: Pod 
… 
spec: 
  readinessGates:
  - conditionType: www.example.com/feature-1
  - conditionType: www.example.com/feature-2
… 
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: 2018-01-01T00:00:00Z
    status: "False"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: 2018-01-01T00:00:00Z
    status: "False"
    type: www.example.com/feature-1
  - lastProbeTime: null
    lastTransitionTime: 2018-01-01T00:00:00Z
    status: "True"
    type: www.example.com/feature-2
  containerStatuses:
  - containerID: docker://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ready : true
… 
```

また、ContainerReady という Status も追加されました。これは従来の Ready 条件を満たした場合 True になります。

## Custom Pod Condition

Custom Pod Condition は 独自のステータスを PATCH によって追加することです。注意することとして `kubectl patch` はこれに対応していません。実施する場合は curl などを使って直接 PATCH リクエストを送ってください。

# 実例

実際に利用してみます。Kubernetes v1.12.1 のクラスタを用意して、次の Pod に 以下のように readinessGates を設定します。

```nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  readinessGates:
  - conditionType: www.example.com/feature-1
  - conditionType: www.example.com/feature-2
  containers:
  - image: nginx
    name: nginx
```

このマニフェストで Pod と Service を作成します。

```
# nginx pod 作成
$ kubectl apply -f nginx.yaml
# わかりやすくするために Service も作成
$ kubectl expose pod nginx --port 80
```

PodとEndpointの状態を確認します。

```
$ kubectl get pod nginx -o yaml
apiVersion: v1
kind: Pod
metadata:
  ...
spec:
  ...
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:35Z
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:35Z
    message: corresponding condition of pod readiness gate "www.example.com/feature-1"
      does not exist., corresponding condition of pod readiness gate "www.example.com/feature-2"
      does not exist.
    reason: ReadinessGatesNotReady
    status: "False"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:45Z
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:35Z
    status: "True"
    type: PodScheduled
  ...

$ kubectl get endpoints -o yaml nginx
apiVersion: v1
kind: Endpoints
metadata:
  ...
subsets:
- notReadyAddresses:
  - ip: 10.26.10.56
    nodeName: shmurata-worker-44ca8be0-xh7gq-demo-knlwj-caas
    targetRef:
      kind: Pod
      name: nginx
      namespace: default
      resourceVersion: "12384928"
      uid: 0a8b0e0f-d111-11e8-8d93-fa163e1a5c07
  ports:
  - port: 80
    protocol: TCP
```

コンテナは普通に立ち上がっていますが、追加された ReadinessGates が true にならないため Endpoints に追加されていないことがわかります。ContainersReady という従来の Ready は true になっています。

ReadinessGates の Conditions を true に変更してみます。

```
$ kubectl proxy
Starting to serve on 127.0.0.1:8001

# www.example.com/feature-1 を true にする
$ curl http://localhost:8001/api/v1/namespaces/default/pods/nginx/status -X PATCH -H "Content-Type: application/json-patch+json" -d '[{"op": "add", "path": "/status/conditions/-", "value": {"type": "www.example.com/feature-1", "status": "True", "lastTransitionTime": "2018-10-16T06:59:45Z", "lastProbeTime": null}}]'

# まだ readiness は true にならない
$ kubectl get endpoints nginx
NAME    ENDPOINTS   AGE
nginx               66m

# www.example.com/feature-2 も true にする
$ curl http://localhost:8001/api/v1/namespaces/default/pods/nginx/status -X PATCH -H "Content-Type: application/json-patch+json" -d '[{"op": "add", "path": "/status/conditions/-", "value": {"type": "www.example.com/feature-2", "status": "True", "lastTransitionTime": "2018-10-16T06:59:45Z", "lastProbeTime": null}}]'

# endpoints に追加される
$ kubectl get endpoints nginx
NAME    ENDPOINTS        AGE
nginx   10.26.10.56:80   68m

# Pod を確認する
$ kubectl get pod nginx -o yaml
apiVersion: v1
kind: Pod
metadata:
  ...
spec:
  ...
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:45Z
    status: "True"
    type: www.example.com/feature-1
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:45Z
    status: "True"
    type: www.example.com/feature-2
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:35Z
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T08:09:38Z
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:45Z
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: 2018-10-16T06:59:35Z
    status: "True"
    type: PodScheduled
  containerStatuses:
    ...
```

Ready になりました。

# まとめ

この記事では PodReady++(ReadinessGates) の機能を紹介しました。ReadinessGatesを設定することで Pod の Ready の条件を拡張できます。ただ、今回の目的としてあげていた問題の解決にはこの機能だけでは出来ない気がしているため、今後に期待したいと思います。
おそらく標準のワークロードオブジェクトの問題を修正するなら Endpoints オブジェクトに加える条件を ContainersReady (従来のReady)にする等の対応が必要になるのではないかと思いますが、調査した限りではそれらのロードマップは見当たりませんでした。

そのため、今回の機能単体だといまいちユースケースが不明ですが、先日公開された https://www.publickey1.jp/blog/18/google_cloud_platformvm.html や Ingress Controller など利用するようになるのかもしれません。

# 参考

- https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md
- https://github.com/kubernetes/features/issues/580