Kubernetes 1.22: SIG-Apps の変更内容

## はじめに

このページではKubernetes v1.22 における SIG-Apps に関連する変更内容をまとめています。

- https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.22.md

<!--- https://relnotes.k8s.io/?releaseVersions=1.22.0&sigs=apps-->


SIG-AppsはKubernetesのワークロードの扱いなどの変更を主に扱っているため、他のSIGと関係する変更が多くなっております。

- 直近での過去の変更内容は以下になります。
  - [Kubernetes 1.21: SIG-Apps の変更内容](https://qiita.com/yosshi_/items/ec042a801ef69fa44ef6)
  - [Kubernetes 1.20: SIG-Apps の変更内容](https://qiita.com/yosshi_/items/20994d6446688c5c066a)
  - [Kubernetes 1.19: SIG-Apps の変更内容](https://qiita.com/yosshi_/items/56493481553ded0c3ad1)

<details><summary>SIG / SIG-Apps とは？</summary><div>

- SIGとは？
    - Special Interest Groups の略称
    - 各 SIG には Subproject が与えられていて、 Subproject に対して独立して開発できるようになっています。
    - Kubernetes は巨大なプロジェクトなので、各SIG 毎に担当(Subproject)が割り当てられていて、各SIG は独立して開発をしています。
    - SIG 間を跨って話し合いをする必要が生じた場合は Working Groups が一時的に作られ、その枠組みの中で話し合うことになります。
   
- SIG-Appsとは？
    - Apps Special Interest Group の略称
    - Kubernetes に対して、application を deploy したりすることに関することが対象。具体的には Pod, ReplicaSet, Deployments, DaemonSet, StatefulSet, Jobs, CronJob あたりが開発対象。
    - 詳細について知りたい方は[こちら](https://github.com/kubernetes/community/tree/master/sig-apps)をご参照ください。
</div></details>

SIG-Apps 以外の SIG に関する変更は以下にまとめてありますので、合わせてご参照下さい。

- [Kubernetes 1.22: 変更点まとめ(What's new!)](https://qiita.com/uesyn/items/7493fbbbeb5fa0794bae)

## 注目の変更
今回は、SIG-Apps に関して新規にアルファになったものと、ベータに移行したものについて取り上げます。

### StatefulSet
#### minReadySeconds for StatefulSets
[Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#progress-deadline-seconds) と [Daemonset](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#performing-a-rolling-update) で設定することができた `minReadySeconds` が `StatefulSet` で使用できるようになりました。

詳しくはこちらの[公式ブログ](https://kubernetes.io/blog/2021/08/27/minreadyseconds-statefulsets/)をご確認ください。

- 関連情報

|  KEP  |  [KEP-2599](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/2599-minreadyseconds-for-statefulsets)  |
| ---- | ---- |
|  **Feature stage**  |  **Alpha**  |
|  **Feature Gate**  |  **StatefulSetMinReadySeconds** | 
|  **issue**  |  **[#65098](https://github.com/kubernetes/kubernetes/issues/65098)** | 
|  **PR**  |  **[#101316](https://github.com/kubernetes/kubernetes/pull/101316), [#100842](https://github.com/kubernetes/kubernetes/pull/100842)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#minimum-ready-seconds)** | 


<details><summary>実際の使用イメージ (クリックすると開きます)</summary><div>

- explain で確認すると StatefulSet に `spec.minReadySeconds` が追加されていることが確認できます。

```sh
$ kubectl explain statefulset.spec.minReadySeconds
KIND:     StatefulSet
VERSION:  apps/v1

FIELD:    minReadySeconds <integer>

DESCRIPTION:
     Minimum number of seconds for which a newly created pod should be ready
     without any of its container crashing for it to be considered available.
     Defaults to 0 (pod will be considered available as soon as it is ready)
     This is an alpha field and requires enabling StatefulSetMinReadySeconds
     feature gate.
```

- 以下のように `minReadySeconds` を設定した StatefulSet を用意します。

```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  minReadySeconds: 60 #  デフォルトでは 0 が設定されています
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx
```

- StatefulSet でも Deployment と同じように `minReadySeconds` が設定できるようになってることを確認できます。

```sh
$ kubectl apply -f sts.yaml                                                          
statefulset.apps/web created


$ kubectl get sts,po    
NAME                   READY   AGE
statefulset.apps/web   1/3     7s

NAME        READY   STATUS    RESTARTS   AGE
pod/web-0   1/1     Running   0          7s

$ kubectl get sts -o json | jq -r '.items[] | {"spec.minReadySeconds":.spec.minReadySeconds, "status.availableReplicas":.status.availableReplicas}'
{
  "spec.minReadySeconds": 60,
  "status.availableReplicas": null
}
### minReadySeconds を経過した Pod がないのでこの時点では availableReplicas が null


$ kubectl get sts,po  
NAME                   READY   AGE
statefulset.apps/web   2/3     2m39s

NAME        READY   STATUS    RESTARTS   AGE
pod/web-0   1/1     Running   0          2m39s
pod/web-1   1/1     Running   0          39s

$ kubectl get sts -ojson | jq -r '.items[] | {"spec.minReadySeconds":.spec.minReadySeconds, "status.availableReplicas":.status.availableReplicas}'
{
  "spec.minReadySeconds": 60,
  "status.availableReplicas": 1
}
### minReadySeconds を経過した Pod が増えたので availableReplicas がカウントアップしている
```

他でも使用している minReadySeconds が Statefulset で使えるようになっただけなので、この変更については現在アルファ機能で今後の大きな変更はあまりないのかなと個人的には思っています。

</div></details>

### Job

#### Job tracking with finalizers  
今までは Job Controller がクラスタ内の Pod を数えて Job の実行状況を管理していました。
そのため、以下のような理由で実行中の Pod が削除される場合に Job の実行状況の把握に課題がありました。

1. Node がダウンした時に garbage collector で削除される Pod が出てくる
2. 完了した Pod が閾値を超えると garbage collector によって削除される。詳細は[こちら](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-garbage-collection)
3. 人為的な削除や、他のコントローラー等による外部からの Pod の削除

今回の機能では Job controller が Pod に対して finalizer `batch.kubernetes.io/job-tracking` を用いることでジョブの実行状況を追跡できるようになります。

- 関連情報

|  KEP  |  [KEP-2307](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/2307-job-tracking-without-lingering-pods)  |
| ---- | ---- |
|  **Feature stage**  |  **Alpha**  |
|  **Feature Gate**  |  **JobTrackingWithFinalizers** | 
|  **issue**  |  **[#2307](https://github.com/kubernetes/enhancements/issues/2307)** | 
|  **PR**  |  **[#98817](https://github.com/kubernetes/kubernetes/pull/98817), [#98238](https://github.com/kubernetes/kubernetes/pull/98238)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-tracking-with-finalizers)** | 

<details><summary>実際の使用イメージ (クリックすると開きます)</summary><div>


- 以下のように Job を用意します。

```yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  completions: 3
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
```

- 実行すると以下のようになります。

```diff
$ kubectl apply -f job.yaml                                                                                                                                 
job.batch/pi-with-timeout created

$ kubectl get job,po
NAME                        COMPLETIONS   DURATION   AGE
job.batch/pi-with-timeout   2/3           28s        28s

NAME                           READY   STATUS              RESTARTS   AGE
pod/pi-with-timeout--1-95swl   0/1     Completed           0          28s
pod/pi-with-timeout--1-mtswv   0/1     Completed           0          16s
pod/pi-with-timeout--1-nvw9p   0/1     ContainerCreating   0          2s

$ kubectl get po -o json | jq -r '.items[].metadata| {name:.name,finalizers:.finalizers}'
{
  "name": "pi-with-timeout--1-95swl",
+  "finalizers": null
}
{
  "name": "pi-with-timeout--1-mtswv",
+  "finalizers": null
}
{
  "name": "pi-with-timeout--1-nvw9p",
  "finalizers": [
+    "batch.kubernetes.io/job-tracking"  # 実行中のpodには付与される
  ]
```
上記のように、Job によって作成された Pod には finalizer が付与されるようになっており、コントローラー側が `succeeded` か `failed` を検知すると finalizer　の `batch.kubernetes.io/job-tracking` が削除されます。

Finalizerの仕組みについては詳しく知りたい方は[こちらのブログ](https://zoetrope.github.io/kubebuilder-training/controller-runtime/deletion.html)を参照ください.

- Job 側の確認

```
$ kubectl get job -o json | jq -r '.items[].metadata.annotations,.items[].status'
{
  "batch.kubernetes.io/job-tracking": "",  
  "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"batch/v1\",\"kind\":\"Job\",\"metadata\":{\"annotations\":{},\"name\":\"pi-with-timeout\",\"namespace\":\"default\"},\"spec\":{\"activeDeadlineSeconds\":100,\"backoffLimit\":5,\"completions\":3,\"template\":{\"spec\":{\"containers\":[{\"command\":[\"perl\",\"-Mbignum=bpi\",\"-wle\",\"print bpi(2000)\"],\"image\":\"perl\",\"name\":\"pi\"}],\"restartPolicy\":\"Never\"}}}}\n"
}
{
  "completionTime": "2021-08-12T23:54:04Z",
  "conditions": [
    {
      "lastProbeTime": "2021-08-12T23:54:04Z",
      "lastTransitionTime": "2021-08-12T23:54:04Z",
      "status": "True",
      "type": "Complete"
    }
  ],
  "startTime": "2021-08-12T23:53:33Z",
  "succeeded": 3,
  "uncountedTerminatedPods": {} # 
```

本機能を有効化すると Job のアノテーションに `batch.kubernetes.io/job-tracking` がつくようになり、このアノテーションのある Job が Pod を作成する時に finalizer が付与されるようになります。
このアノテーションは GA 以降に削除される予定で、それ以降は該当のアノテーションがなくても本機能が有効になる予定です。


今回登場した機能と k8s v1.21 でベータ機能になった [TTL Controller](https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/) との組み合わせにより、job で大量の Pod を作成した時に、完了済みの pod が自動で削除されるような状態を作れるようになったかなと思います。

- jobに`.spec.ttlSecondsAfterFinished`設定した際に以下の条件をみたすjobを削除

 1. Job の実行結果が `Complete` or `Failed`
 2. `.spec.ttlSecondsAfterFinished` + `.status.conditions.lastTransitionTime` > now


- TTL Controller による完了した Pod の自動削除の例

```yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100 # Pod 完了後100秒以降に TTL Controller によって検知された場合に削除される
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
```

ただ、`Job tracking with finalizers` はまだアルファ機能のため、今後大きな変更が入る可能性があるので注意してください。


</div></details>

#### Indexed Job  
Job を実行時に `completionMode` に `Indexed` を設定することで、Job を実行する Pod は、 Job 内でユニークな `Completed Indexes` を持つことが出来るようになります。
これにより、 Pod が実行時に自身に割り当てられた `Completed Indexes` を確認して処理を分岐することで、一つの Job の中でそれぞれの Pod が別の処理を容易に実現できるようになります。(今までは、これを実行するために、他の仕組みと併用する必要がありました。)

- 関連情報

|  KEP  |  [KEP-2214](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/2214-indexed-job)  |
| ---- | ---- |
|  **Feature stage**  |  **Beta**  |
|  **Feature Gate**  |  **IndexedJob** | 
|  **issue**  |  **[#2214](https://github.com/kubernetes/enhancements/issues/2214), [#14188](https://github.com/kubernetes/kubernetes/issues/14188)** | 
|  **PR**  |  **[#101292](https://github.com/kubernetes/kubernetes/pull/101292), [#98441](https://github.com/kubernetes/kubernetes/pull/98441), [#98812](https://github.com/kubernetes/kubernetes/pull/98812)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/job/#completion-mode)** | 

<details><summary>実際の使用イメージ (クリックすると開きます)</summary><div>

- 以下のように `completionMode` を設定した Job のマニフェストを用意します。

```yaml 
apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed  # デフォルトでは NonIndexed が設定されています
  template:
    metadata:
      labels:
        job: indexed-job
    spec:
      restartPolicy: Never
      initContainers:
      - name: 'input'
        image: 'docker.io/library/bash'
        command:
        - "bash"
        - "-c"
        - |
          items=(foo bar baz qux xyz)
          echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt  # $JOB_COMPLETION_INDEX で取得        
        volumeMounts:
        - mountPath: /input
          name: input
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        emptyDir: {}
```

- 実際に実行すると以下のようになります。

```
$ kubectl get job,po
NAME                    COMPLETIONS   DURATION   AGE
job.batch/indexed-job   0/5           20s        20s

NAME                      READY   STATUS            RESTARTS   AGE
pod/indexed-job-0-8k4ng   0/1     PodInitializing   0          20s
pod/indexed-job-1-qcbnn   0/1     PodInitializing   0          20s
pod/indexed-job-2-ktr4s   0/1     PodInitializing   0          20s
```

アルファ時点からの変更点として、Pod 名に Indexes 付与されるようになり、分かりやすくなりました。
pod の名：`$(job-name)-$(index)-$(random-string)`

- Job を確認すると以下のようになります。

```diff
$ kubectl describe job                            
e:               indexed-job
Namespace:          default
Selector:           controller-uid=1be7fe59-7529-419e-9d15-c7f81395027f
Labels:             controller-uid=1be7fe59-7529-419e-9d15-c7f81395027f
                    job-name=indexed-job
Annotations:        batch.kubernetes.io/job-tracking: 
Parallelism:        3
Completions:        5
Completion Mode:    Indexed
Start Time:         Fri, 13 Aug 2021 15:51:18 +0900
Completed At:       Fri, 13 Aug 2021 15:51:56 +0900
Duration:           38s
Pods Statuses:      0 Running / 5 Succeeded / 0 Failed
+ Completed Indexes:  0-4  # 0 〜 {Completions - 1} になっています
Pod Template:
```

- Pod 毎の処理結果を確認すると、結果がそれぞれに異なることが分かります。

```
$ kubectl logs indexed-job-2-ktr4s
zyx  # Completed Indexes が 4 だったため

$ kubectl logs indexed-job-4-l5fvz                                          
bar  # Completed Indexes が 2 だったため
```

</div></details>

#### Suspend Jobs
Job を実行時に `suspend` に `true` を設定することで、 Job を一時停止することが出来るようになりました。
今までだと、途中で優先度の高い Job が実行されたとしても既存の実行中の Job は実行し続けていましたが、この機能を使うことにより、限られたクラスタのリソースをより複雑な Job のワークロードの中でうまく使えるようになります。

- 関連情報


|  KEP  |  [KEP-2322](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/2232-suspend-jobs)  |
| ---- | ---- |
|  **Feature stage**  |  **Beta**  |
|  **Feature Gate**  |  **SuspendJob** | 
|  **issue**  |  **[#2232](https://github.com/kubernetes/enhancements/issues/2232)** | 
|  **PR**  |  **[#102022](https://github.com/kubernetes/kubernetes/pull/102022), [#98727](https://github.com/kubernetes/kubernetes/pull/98727)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/job/#suspending-a-job)** | 

<details><summary>実際の使用イメージ (クリックすると開きます)</summary><div>

- 以下のように `suspend` を設定した Job のマニフェストを用意します。

```yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: my-job
spec:
  suspend: true # デフォルトでは false が設定されています
  parallelism: 2
  completions: 10
  template:
    spec:
      containers:
      - name: my-container
        image: busybox
        command: ["sleep", "5"]
      restartPolicy: Never
```

- 実行すると以下のようになり、 `Job suspended` となっていることが分かります。

```diff
$ kubectl get job,po        
NAME               COMPLETIONS   DURATION   AGE
job.batch/my-job   0/10                     9s



$ kubectl describe job 
ame:             my-job
Namespace:        default
Selector:         controller-uid=f3d22fdf-5d62-4f09-86e5-a0d19f4e6509
Labels:           controller-uid=f3d22fdf-5d62-4f09-86e5-a0d19f4e6509
                  job-name=my-job
Annotations:      batch.kubernetes.io/job-tracking: 
Parallelism:      2
Completions:      10
Completion Mode:  NonIndexed
Pods Statuses:    0 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=f3d22fdf-5d62-4f09-86e5-a0d19f4e6509
           job-name=my-job
  Containers:
   my-container:
    Image:      busybox
    Port:       <none>
    Host Port:  <none>
    Command:
      sleep
      5
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason     Age   From            Message
  ----    ------     ----  ----            -------
+  Normal  Suspended  19s   job-controller  Job suspended
```

- `suspend` を `false` に変更します。

```diff
$ kubectl edit job my-job     

spec:
-  suspend: true
+  suspend: false
```

- Job が実行されることが確認できます。

```
$ kubectl get job,po
NAME               COMPLETIONS   DURATION   AGE
job.batch/my-job   1/10          13s        2m8s

NAME                  READY   STATUS              RESTARTS   AGE
pod/my-job--1-4q77z   0/1     Completed           0          14s
pod/my-job--1-mtf5d   0/1     ContainerCreating   0          3s
pod/my-job--1-zcf62   1/1     Running             0          13s
```

手動で `suspend` の値を切り替えることはあまりないと思いますが、 Operator 等で上手く制御することで、複雑な Job のワークロードを Kubernetes 上で実行するための下地の一つが出来たかなという印象があります。
</div></details>

### DaemonSet
#### Allow DaemonSets to surge during update like Deployments
DaemonSet に MaxSurge が導入されました。
従来の通りだと、DaemonSet がローリングアップグレードする際には、既存の Pod が削除された後に新しい Pod が立ち上がり始めるのでダウンタイムが長かったのですが、 MaxSurge を利用すると新しい Pod が Ready になってから古い Pod が削除され始めるのでダウンタイムの低減が期待できます。

- 関連情報

|  KEP  |  [KEP-1591](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/1591-daemonset-surge)  |
| ---- | ---- |
|  **Feature stage**  |  **Beta**  |
|  **Feature Gate**  |  **DaemonSetUpdateSurge** | 
|  **issue**  |  **[#1591](https://github.com/kubernetes/enhancements/issues/1591)** | 
|  **PR**  |  **[#101742](https://github.com/kubernetes/kubernetes/pull/101742), [#96441](https://github.com/kubernetes/kubernetes/pull/96441)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#performing-a-rolling-update)** | 

<details><summary>実際の使用イメージ (クリックすると開きます)</summary><div>

- 以下のように `maxSurge` を設定した DaemonSet を用意します。

```yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ds-maxsurge
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1  # デフォルトでは 0 が設定されています
  selector:
    matchLabels:
      app: ds-maxsurge
  template:
    metadata:
      labels:
        app: ds-maxsurge
    spec:
      containers:
      - name: nginx
        image: nginx:1.18
```

- ついに、DaemonSet に `maxSurge` が設定できるようになっています。

```
$ kubectl apply -f ds.yaml                                            
daemonset.apps/ds-maxsurge created

$ kubectl get ds -ojson | jq -r '.items[].spec.updateStrategy'
{
  "rollingUpdate": {
    "maxSurge": 1,
    "maxUnavailable": 0
  },
  "type": "RollingUpdate"
}
```

- ローリングアップデートすると `maxSurge` が機能してることが確認できます。

```sh
$ kubectl set image DaemonSet/ds-maxsurge nginx=nginx:1.19  
daemonset.apps/ds-maxsurge image updated

$ kubectl get po -o wide                                                 
NAME                READY   STATUS              RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
ds-maxsurge-42nwf   0/1     ContainerCreating   0          5s    <none>       minikube   <none>           <none>
ds-maxsurge-kfdxp   1/1     Running             0          49s   172.17.0.3   minikube   <none>           <none>
```

まだ、同一ノード上で Running の Pod があるにも関わらず、新しい Pod の ContainerCreating になっているところが今回の大きな変更になります。

##### 注意点：hostPort の利用
[KEP-1591](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/1591-daemonset-surge) にも記載がありますが、 `maxSurge` と `hostPort` は併用できません。

- hostPort を利用するマニフェストを作成してデプロイしてみます。

```diff
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ds-maxsurge
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
+      maxSurge: 1
  selector:
    matchLabels:
      app: ds-maxsurge
  template:
    metadata:
      labels:
        app: ds-maxsurge
    spec:
+      hostNetwork: true
      containers:
      - name: nginx
        image: nginx:1.18
        ports:
        - containerPort: 80
+          hostPort: 80
```

- Pod の生成には成功します。

```diff
$ kubectl get ds -o yaml 
          ports:
          - containerPort: 80
+            hostPort: 80
+        hostNetwork: true
    updateStrategy:
      rollingUpdate:
+        maxSurge: 1
        maxUnavailable: 0
      type: RollingUpdate
```

- ローリングアップデートをすると新しい Pod がずっと Pending になります。

```
$ kubectl set image DaemonSet/ds-maxsurge nginx=nginx:1.19  
daemonset.apps/ds-maxsurge image updated

$ kubectl get po
NAME                READY   STATUS    RESTARTS   AGE
ds-maxsurge-jmq2v   1/1     Running   0          28s
ds-maxsurge-nkrfw   0/1     Pending   0          6s
```

- 中を確認すると、port がなくてスケジューラーが割り当てられないことが分かります。

```
$ kubectl describe po ds-maxsurge-khm9w   
    :
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  25s   default-scheduler  0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.
```

</div></details>

### ReplicaSet

#### Random Pod Selection on ReplicaSet Downscale
ReplicaSet をスケールダウンするときに優先的に削除される Pod が、従来では起動時間が短い Pod が優先的に削除されてましたが、この優先度の付け方に少し変更が入りました。これは、起動時間の短い Pod の方がより影響範囲が小さいだろうという仮定の元に実装されていました。

今回の変更では以下のように変更になっています。

1. Pod の起動時間を対数目盛(logarithmic scale) で丸め込んでから起動時間の短い方を比較
1. 同じものがあれが、Pod の UUID で比較する。

これにより、従来の起動時間が短いものを優先するというのをある程度守りながら、 UUID を用いることでランダムに選択されるようになりました。

<details><summary>Pod の起動時間を対数目盛を丸め込むイメージ (クリックすると開きます)</summary><div>

基数 10 で対数目盛を使用した際のイメージになります。

| Duration | Scale |
|----------|-------|
| 5ns      | 0     |
| 23ns     | 1     |
| 71ns     | 1     |
| 1ms      | 6     |
| 8ms      | 6     |
| 50ms     | 7     |
| 2m       | 11    |
| 11m      | 11    |

ある程度起動時間の近しいものだけが、比較対象となり、 UUID を用いて擬似的にランダムで削除される Pod が選択されるイメージが持てるかと思います。

</div></details>

- 関連情報

|  KEP  |  [KEP-2185](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/2185-random-pod-select-on-replicaset-downscale)  |
| ---- | ---- |
|  **Feature stage**  |  **Beta**  |
|  **Feature Gate**  |  **LogarithmicScaleDown** | 
|  **issue**  |  **[#2185](https://github.com/kubernetes/enhancements/issues/2185), [#96748](https://github.com/kubernetes/kubernetes/issues/96748)** | 
|  **PR**  |  **[#101767](https://github.com/kubernetes/kubernetes/pull/101767),[#99212](https://github.com/kubernetes/kubernetes/pull/99212)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#scaling-a-replicaset)** | 

#### ReplicaSet Pod Deletion Cost
Pod に `controller.kubernetes.io/pod-deletion-cost` のアノテーションを付与することで、スケールダウン時に優先的に削除するように制御できるようになりました。
従来だと、起動時間が短いものがより優先的に削除されましたが、例えば preemptible/spot VM などで起動している Pod を優先的に削除したいなど、起動時間と削除の優先度に関係がない場合により柔軟な制御が可能になりました。


- 関連情報

|  KEP  |  [KEP-2255](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/2255-pod-cost)  |
| ---- | ---- |
|  **Feature stage**  |  **Beta**  |
|  **Feature Gate**  |  **PodDeletionCost** | 
|  **issue**  |  **[#2255](https://github.com/kubernetes/enhancements/issues/2255)** | 
|  **PR**  |  **[#101080](https://github.com/kubernetes/kubernetes/pull/101080), [#99163](https://github.com/kubernetes/kubernetes/pull/99163)** | 
|  **参考**  |  **[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#pod-deletion-cost)** | 

<details><summary>実際の使用イメージ (クリックすると開きます)</summary><div>

- 以下のように `controller.kubernetes.io/pod-deletion-cost` のアノテーションを設定した Deployment のマニフェストを用意します。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
      annotations:
　　　　　# 何も設定していない場合は、"0" が設定されているものとしてコスト計算されます
        controller.kubernetes.io/pod-deletion-cost: "0" 
    spec:
      containers:
      - name: nginx
        image: nginx
```

- Deployment によって、 Pod が3つ生成されます。

```
$ kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
sample-deployment-d9489fcc6-2vt9n   1/1     Running   0          5m14s
sample-deployment-d9489fcc6-q69bk   1/1     Running   0          5m14s
sample-deployment-d9489fcc6-twhrr   1/1     Running   0          5m14s
```

- 1つだけ Pod を削除し、 Pod の起動時間が他より短くなるようにします。

```
$ kubectl delete po sample-deployment-d9489fcc6-2vt9n
pod "sample-deployment-d9489fcc6-2vt9n" deleted

$ kubectl get po                                        
NAME                                READY   STATUS    RESTARTS   AGE
sample-deployment-d9489fcc6-7wvvc   1/1     Running   0          15s
sample-deployment-d9489fcc6-q69bk   1/1     Running   0          5m37s
sample-deployment-d9489fcc6-twhrr   1/1     Running   0          5m37s
```

- 起動時間が長い Pod の内の一つを選択して `pod-deletion-cost` の値を変更します。

```diff
$ kubectl edit po sample-deployment-d9489fcc6-q69bk

apiVersion: v1
kind: Pod
metadata:
  annotations:
-    controller.kubernetes.io/pod-deletion-cost: "0"
+    controller.kubernetes.io/pod-deletion-cost: "-10"
```

`pod-deletion-cost` の値は -2147483647 〜 2147483647 の範囲で指定が可能です。


- `pod-deletion-cost` の値を確認すると、1つだけ異なることが分かります。

```
$ kubectl get po -ojson | jq -r '.items[].metadata.annotations'
{
  "controller.kubernetes.io/pod-deletion-cost": "0"
}
{
  "controller.kubernetes.io/pod-deletion-cost": "-10"
}
{
  "controller.kubernetes.io/pod-deletion-cost": "0"
}
```

- この状態で、スケールダウンをして Pod 数を削減します。

```
$ kubectl scale deployment.v1.apps/sample-deployment --replicas=2
deployment.apps/sample-deployment scaled

$ kubectl get po                                                          
NAME                                READY   STATUS    RESTARTS   AGE
sample-deployment-d9489fcc6-7wvvc   1/1     Running   0          79s
sample-deployment-d9489fcc6-twhrr   1/1     Running   0          6m41s
```

従来なら、起動時間の最も短い `sample-deployment-d9489fcc6-7wvvc` が削除されているはずですが残っていることが分かります。

- `pod-deletion-cost` の値を確認します。

```
$ kubectl get po -ojson | jq -r '.items[].metadata.annotations'  
{
  "controller.kubernetes.io/pod-deletion-cost": "0"
}
{
  "controller.kubernetes.io/pod-deletion-cost": "0"
}
```

`pod-deletion-cost` が `-10` を設定した Pod が削除されていることが確認できます。


手動で `pod-deletion-cost` を操作することはあまりやらないと思いますが、 preemptible/spot VM で動作してる Pod に対して自動で `pod-deletion-cost` を付与する仕組みを作ったり、 Pod の起動時間とサービスの影響度の関連性が低い場合において、より柔軟に ReplicaSet をスケールダウンする際の Pod の制御ができるようになったかなと思います。

</div></details>

## v1.22 Release Notes
v1.22 Release Notes の中で SIG-Apps に関するものについて以下に和訳したものを記載します。

> :pencil: がついた文章は、CHANGELOGの公式内容ではなく筆者の補足です。


### :wastebasket: Deprecation(非推奨)

- `kube-proxy` からアルファ機能だった `topologyKeys` のフィールドが削除されました. このフィールドは数サイクル前から非推奨となっていました。エンドポイント毎に対する Topology Aware Hints(alpha) 機能を auto で使用することと `internalTrafficPolicy` フィールド (alpha)　を組み合わせて使用することで置き換えます。 ([#102412](https://github.com/kubernetes/kubernetes/pull/102412), [@andrewsykim](https://github.com/andrewsykim))

  - :pencil: Topology Aware Hints について詳細を知りたい方は公式サイトの[こちら](https://kubernetes.io/docs/concepts/services-networking/topology-aware-hints/)をご参照ください。
  - :pencil: Service Internal Traffic Policy について詳細を知りたい方は公式サイトの[こちら](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/)をご参照ください。

- `PodUnknown` フェイズが非推奨になります。 ([#95286](https://github.com/kubernetes/kubernetes/pull/95286), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev))

### :earth_asia: Changes（変更）

- NodeResourcesFit plugin の新しい score extension は  `NodeResourcesLeastAllocated`, `NodeResourcesMostAllocated`, `RequestedToCapacityRatio` の機能を統合します。v1beta2 では非推奨になりますが、v1beta1 では引き続き統合された3つのプラグインを使用できますが、`NodeResourcesFit` と同時に使用することはできません。 the three plugins can still be used in v1beta1 but not at the same time with the score extension of `NodeResourcesFit`. ([#101822](https://github.com/kubernetes/kubernetes/pull/101822), [@yuzhiquan](https://github.com/yuzhiquan))

  - :pencil: こちらは既存のスケジューラープラグインである `NodeResourcesLeastAllocated`, `NodeResourcesMostAllocated`, `RequestedToCapacityRatio` の3つを `NodeResourcesFit` に統合しようという話になります。詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-scheduling/2458-node-resource-score-strategy) をご確認ください。
  - :pencil: kube-scheduler Configuration (v1beta2) について詳細を知りたい方は公式サイトの[こちら](https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta2/)をご参照ください。

- `service.kubernetes.io/topology-aware-hints` のアノテーションで `Auto` が有効になりました。  ([#100728](https://github.com/kubernetes/kubernetes/pull/100728), [@robscott](https://github.com/robscott))

  - :pencil: 中身を確認すると今まで `auto`(小文字)しか設定できなかったので `Auto`(大文字)も設定できるようにしただけのようです。

    ```diff
    - // corev1.AnnotationTopologyAwareHints key with a value set to "auto".
    + // corev1.AnnotationTopologyAwareHints key with a value set to "Auto" or "auto".
    ```

- PVC の sepc に `DataSourceRef` フィールド(アルファ機能) が追加されました。 これにより `PVCs` と `VolumeSnapshots` 以外のデータソースが指定できるようになります。 ([#103276](https://github.com/kubernetes/kubernetes/pull/103276), [@bswartz](https://github.com/bswartz))

  - :pencil: Volume populators and data sources について詳細を知りたい方は公式サイトの[こちら](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#volume-populators-and-data-sources)をご参照ください。

- StatefulSet API に `PersistentVolumeClaimDeletePoilcy` が追加されました。 ([#99378](https://github.com/kubernetes/kubernetes/pull/99378), [@mattcary](https://github.com/mattcary))

  - :pencil: この PR については revert されているため k8s v1.22 で導入されませんでした。
  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/1847-autoremove-statefulset-pvcs) をご確認ください。  

- Windows で HostProcess containers のアルファサポートを開始します。 ([#99576](https://github.com/kubernetes/kubernetes/pull/99576), [@marosset](https://github.com/marosset)) [SIG API Machinery, Apps, Node, Testing and Windows]

- job controller に job の状態の監視ように3つのメトリクスが追加されました。`IndexedJob` がベータになりました。 ([#101292](https://github.com/kubernetes/kubernetes/pull/101292), [@AliceZhang2016](https://github.com/AliceZhang2016))

  - :pencil: 以下の3つのメトリクスが追加されました。
    - `job_sync_duration_seconds` : The time it took to sync a job
    - `job_sync_total` :  The number of job syncs
    - `job_finished_total` : The number of finished job

- job に `.status.uncountedTerminatedPods` のフィールドが追加されました。 このフィールドは job controller が 完了した pod を job status カウンターに追加する前に追跡するために使用します。job controller で作成された Pod は finalizer に `batch.kubernetes.io/job-tracking` を持ちます。この方法で追跡される Job は `batch.kubernetes.io/job-tracking` のアノテーションを持ちます。この方法は一時的なものです。2リリース後にベータになった以降、このアノテーションは Job に追加されなくなります。 ([#98817](https://github.com/kubernetes/kubernetes/pull/98817), [@alculquicondor](https://github.com/alculquicondor))

  - :pencil: Job tracking with finalizers について詳細を知りたい方は公式サイトの[こちら](https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-tracking-with-finalizers)をご参照ください。

- `PersistentVolumes` と `PersistentVolumeClaims`　にアクセスモード `ReadWriteOncePod` が追加されました。ボリュームへのアクセスが単一ノードの単一 Pod に制限されます。 ([#102028](https://github.com/kubernetes/kubernetes/pull/102028), [@chrishenzie](https://github.com/chrishenzie))

  - :pencil: `ReadWriteOnce(RWO)` は単一ノードからのボリュームへの制限でしたが、`ReadWriteOncePod(RWOP)` ができたことで単一ノードの単一 Pod からの制限となり、より細かく制御できるようになりました。
  - :pencil: `ReadWriteOncePod` を含むアクセスモードについて詳細を知りたい方は公式サイトの[こちら](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#volume-populators-and-data-sources)をご参照ください。

- Because of the implementation logic of `time.Format` in golang, the displayed time zone is not consistent. ([#102366](https://github.com/kubernetes/kubernetes/pull/102366), [@cndoit18](https://github.com/cndoit18))

- Go言語の `time.Format` の実装のため、表示される time zone に一貫性がありません。([#102366](https://github.com/kubernetes/kubernetes/pull/102366), [@cndoit18](https://github.com/cndoit18))

  - :pencil: Changelogの直訳だと意味が分かりにくいので中身をみていきたいと思います。以下のように `UTC` を明示したことにより、異なるノードでも表示される time zone に一貫性が得られるという変更になります。

    ```diff
    -		endpointSlice.Annotations[corev1.EndpointsLastChangeTriggerTime] = triggerTime.Format(time.RFC3339Nano)
    +		endpointSlice.Annotations[corev1.EndpointsLastChangeTriggerTime] = triggerTime.UTC().Format(time.RFC3339Nano)
    ```

- コンテナの  env, command , args で `$` をエスケープする際の記述が修正されました。 ([#101916](https://github.com/kubernetes/kubernetes/pull/101916), [@MartinKanters](https://github.com/MartinKanters)) [SIG Apps]

- `DaemonSet` の `MaxSurge` がデフォルトで有効になりました。([#101742](https://github.com/kubernetes/kubernetes/pull/101742), [@ravisantoshgudimetla](https://github.com/ravisantoshgudimetla))

- スケジューリング中に PVC access mode の `ReadWriteOncePod` が適用されます。 ([#103082](https://github.com/kubernetes/kubernetes/pull/103082), [@chrishenzie](https://github.com/chrishenzie))

  - :pencil: `ReadWriteOncePod` に関する追加の変更になります。 PVC で `ReadWriteOncePod` をもつ Pod をスケジュール際に他に同一の PVC を既に使用している Pod がないか確認するように修正されました。
  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/2485-read-write-once-pod-pv-access-mode#scheduler-enforcement) をご確認ください。  

- Ephemeral containers は Pod とは別の `securityContext` を指定できるようになりました。 この機能を使用するためにはクラスタ管理者は事前に security policy controllers で `EphemeralContainers` がサポートされていることを確認する必要があります。([#99023](https://github.com/kubernetes/kubernetes/pull/99023), [@verb](https://github.com/verb))

  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/277-ephemeral-containers) をご確認ください。  

- `StatefulSets` に `minReadySeconds` の API が追加されました。 ([#100842](https://github.com/kubernetes/kubernetes/pull/100842), [@ravisantoshgudimetla](https://github.com/ravisantoshgudimetla))

  - :pencil: 既に [Deployment]https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#progress-deadline-seconds) と [Daemonset](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#performing-a-rolling-update) で設定できた `minReadySeconds` が `StatefulSet` で使用できるようになりました。
  - :pencil: 詳細を知りたい方は公式サイトの[こちら](hhttps://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#minimum-ready-seconds)をご参照ください。

- horizontal pod autoscaler のために Heapster をサポートする `--horizontal-pod-autoscaler-use-rest-clients` フラグが Kube-controller-manager から削除されました。このフラグについては 1.12 から非推奨となっています。 ([#90368](https://github.com/kubernetes/kubernetes/pull/90368), [@serathius](https://github.com/serathius))

- Kubelet から probe-level termination grace period の feature flag が削除されました。既に作成されている Pod に対してこの変更を適用させたい場合は Pod を再作成してください。 ([#103168](https://github.com/kubernetes/kubernetes/pull/103168), [@raisaat](https://github.com/raisaat)) [SIG Apps and Node]

  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2238-liveness-probe-grace-period) をご確認ください。  

- `StatefulSet` に追加された `PersistentVolumeClaimDeletePoilcy` の API が Revert されました。 ([#103747](https://github.com/kubernetes/kubernetes/pull/103747), [@mattcary](https://github.com/mattcary))

 - :pencil: k8s v1.22 での導入は見送りになりました。

- Suspend Job が beta になりました。 Job controller の`job_sync_total` と `job_sync_duration_seconds` のメトリクスに  `action` ラベルが追加されました。 ([#102022](https://github.com/kubernetes/kubernetes/pull/102022), [@adtac](https://github.com/adtac))

- DaemonSet の `spec.updateStrategy.rollingUpdate.maxUnavailable` フィールドに関する API のドキュメントが修正され、値が切り上げられることが記載されました。([#101296](https://github.com/kubernetes/kubernetes/pull/101296), [@Miciah](https://github.com/Miciah))

- `CertificateSigningRequest.certificates.k8s.io` API はオプションで expirationSeconds フィールドをサポートするようになりクライアントは特定の期間を指定した issued certificate 要求できるようになりました。 Kubernetes controller manager が提供するデフォルトの署名の実装は --cluster-signing-duration のフラグを超えない限り、このフィールドの設定を尊重します。([#99494](https://github.com/kubernetes/kubernetes/pull/99494), [@enj](https://github.com/enj))

- `EndpointSlicen Mirroring controller` は `kubectl` でアップデートされた `EndpointSlices` の `last-applied-configuration` アノテーションをミラーしなくなりました。([#102731](https://github.com/kubernetes/kubernetes/pull/102731), [@sharmarajdaksh](https://github.com/sharmarajdaksh))

- `PodDeletionCost` がベータとなり、デフォルトで使用できるようになりました。 ([#101080](https://github.com/kubernetes/kubernetes/pull/101080), [@ahg-g](https://github.com/ahg-g))

- Pod の spec と container probe の `TerminationGracePeriodSeconds` は負の値を取れません。 `TerminationGracePeriodSeconds` に負の値が設定された場合は削除され `1s` が設定されます。負の値を更新するために Immutable field のバリデーションが緩和されます。将来のリリースで負の値の設定は許可されなくなります。 ([#98866](https://github.com/kubernetes/kubernetes/pull/98866), [@wzshiming](https://github.com/wzshiming))

- `pod/eviction` のサブリソースは `policy/v1beta1` に加えて `policy/v1` を受け入れるようになりました。 ([#100724](https://github.com/kubernetes/kubernetes/pull/100724), [@liggitt](https://github.com/liggitt))

-  `podAffinity`, `NamespaceSelector` , `CrossNamespaceAffinity` の機能はベータとなりデフォルトで使用できるようになりました。 ([#101496](https://github.com/kubernetes/kubernetes/pull/101496), [@ahg-g](https://github.com/ahg-g))

- The `pods/ephemeralcontainers` API now returns and expects a `Pod` object instead of `EphemeralContainers`. This is incompatible with the previous alpha-level API. ([#101034](https://github.com/kubernetes/kubernetes/pull/101034), [@verb](https://github.com/verb)) [SIG Apps, Auth, CLI and Testing]

- `pods/ephemeralcontainers` API は `EphemeralContainers` ではなく `Pod` オブジェクトを返し、期待するようになります。この変更はアルファの時から互換性がありません。 ([#101034](https://github.com/kubernetes/kubernetes/pull/101034), [@verb](https://github.com/verb)) [SIG Apps, Auth, CLI and Testing]

- `v1.Node` と `.status.images[].names` はオプションとなりました。 ([#102159](https://github.com/kubernetes/kubernetes/pull/102159), [@roycaihw](https://github.com/roycaihw))

- status と Pod finalizers を用いて Job の完了を追跡し、Pod からの依存を削除します。  ([#98238](https://github.com/kubernetes/kubernetes/pull/98238), [@alculquicondor](https://github.com/alculquicondor)) [SIG API Machinery, Apps, Auth and Testing]

### :sparkle: FEATURE（機能追加）

- 1000 を超える endpoints は truncate されるようになり Endpoint リソースの `endpoints.kubernetes.io/over-capacity` のアノテーションに `truncated` が設定されます。 ([#103520](https://github.com/kubernetes/kubernetes/pull/103520), [@swetharepakula](https://github.com/swetharepakula)) [SIG Apps and Network]

- `StatefulSets` に `minReadySeconds` が実装されました。 ([#101316](https://github.com/kubernetes/kubernetes/pull/101316), [@ravisantoshgudimetla](https://github.com/ravisantoshgudimetla))

- 新しく `ExpandedDNSConfig` feature gate 使用できるようになります。この機能により Kubernetes で DNS configuration の拡張ができるようになります。 ([#100651](https://github.com/kubernetes/kubernetes/pull/100651), [@gjkim42](https://github.com/gjkim42))

  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/2595-expanded-dns-config) をご確認ください。 

- Cronjobs storage version が `batch/v1` になりました。 ([#102363](https://github.com/kubernetes/kubernetes/pull/102363), [@mengjiao-liu](https://github.com/mengjiao-liu))

- `CronJobControllerV2` フラグが GA となりました。このフラグは 1.23 で削除予定になります。 ([#102529](https://github.com/kubernetes/kubernetes/pull/102529), [@soltysh](https://github.com/soltysh))

- `HugePageStorageMediumSize` 機能が GA となり無条件で有効化されるようになります。 コンテナレベルで複数サイズの huge page resources を利用できるようになります。 ([#99144](https://github.com/kubernetes/kubernetes/pull/99144), [@bart0sh](https://github.com/bart0sh))

- `LogarithmicScaleDown` 機能がベータとなりデフォルトで有効になります。 ([#101767](https://github.com/kubernetes/kubernetes/pull/101767), [@damemi](https://github.com/damemi))

- このリリースで `NamespaceDefaultLabelName` が GA となりました。 全ての Namespace API objects は `metadata.name` フィールドと一致する `kubernetes.io/metadata.name` を持つようになり、label selector を使用して `namespace` を指定できるようになります。 ([#101342](https://github.com/kubernetes/kubernetes/pull/101342), [@rosenhouse](https://github.com/rosenhouse))

  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/2161-apiserver-default-labels) をご確認ください。

- `ServiceInternalTrafficPolicy` 機能がベータとなりデフォルトで有効化されるようになります。これにより、Service の `internalTrafficPolicy` フィールドがデフォルトで有効になります。 ([#103462](https://github.com/kubernetes/kubernetes/pull/103462), [@andrewsykim](https://github.com/andrewsykim))

  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/2086-service-internal-traffic-policy) をご確認ください。

- `ServiceLBNodePortControl` 機能がベータになりデフォルトで有効になります。 ([#100412](https://github.com/kubernetes/kubernetes/pull/100412), [@hanlins](https://github.com/hanlins))

  - :pencil:詳細についてはこちらの [KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/1864-disable-lb-node-ports) をご確認ください。

- job controller removes 完了回数を達成した起動中の Pod を削除するようになります。 ([#99963](https://github.com/kubernetes/kubernetes/pull/99963), [@alculquicondor](https://github.com/alculquicondor))

- kubeconfig で `the kube-scheduler` framework handle が公開されるようになりました。Out-of-tree plugins はこれを使用して CRD informers を簡単にビルドできるようになります。 ([#100644](https://github.com/kubernetes/kubernetes/pull/100644), [@Huang-Wei](https://github.com/Huang-Wei))

### :bug: BUGFIX(バグ修正)

- Disruption controller は unmanaged pod と同期中にエラーを出さなくなります。 ([#103414](https://github.com/kubernetes/kubernetes/pull/103414), [@ravisantoshgudimetla](https://github.com/ravisantoshgudimetla)) [SIG Apps and Testing]

- `endpointslicemirroring` controller が endpoint `NotReadyAddresses` が Ready で一致する `EndpointSlice` をミラーされる際のバグが修正されました。twere mirrored as Ready to the corresponding `EndpointSlice`. ([#102683](https://github.com/kubernetes/kubernetes/pull/102683), [@aojea](https://github.com/aojea))

- Pod が起動もしくは停止中に停止に時間がかかり、Kubelet でいくつかの競合状態になるバグが修正されました。 ([#102344](https://github.com/kubernetes/kubernetes/pull/102344), [@smarterclayton](https://github.com/smarterclayton)) [SIG Apps, Node, Storage and Testing]

- [json patches](https://github.com/evanphx/json-patch) の array と object で null が処理される方法が修正されました。 ([#102467](https://github.com/kubernetes/kubernetes/pull/102467), [@pacoxu](https://github.com/pacoxu))

- CSI migration translation での Azure file inline volume namespace の問題が修正されました。 ([#101235](https://github.com/kubernetes/kubernetes/pull/101235), [@andyzhangx](https://github.com/andyzhangx))

- `kubectl describe` で Job completion mode の表示が修正されました。([#101160](https://github.com/kubernetes/kubernetes/pull/101160), [@alculquicondor](https://github.com/alculquicondor))

- `kube-controller-manager` の起動時に、CSI に移行された `PersistentVolumes` にぶら下がっている `VolumeAttachments` の garbage collection が修正されました。([#102176](https://github.com/kubernetes/kubernetes/pull/102176), [@timebertt](https://github.com/timebertt))

- `batch/v1` で `cronjob.status` に追加された `lastSuccessfulTime` フィールドにコントローラーが追加していなかったバグが修正されました。 ([#102642](https://github.com/kubernetes/kubernetes/pull/102642), [@alaypatel07](https://github.com/alaypatel07))

- CSI migrated volumes 予期しないデタッチに繋がるfalse-positive uncertain volume attachments が修正されました。 ([#101737](https://github.com/kubernetes/kubernetes/pull/101737), [@Jiawei0227](https://github.com/Jiawei0227)) [SIG Apps and Storage]

- `EndpointSlice` IP のバリデーションが `Endpoints` IP のバリデーションと一致するようになりました。 ([#101084](https://github.com/kubernetes/kubernetes/pull/101084), [@robscott](https://github.com/robscott))

## その他：CronJobでの任意のタイムゾーンの指定
k8s v1.22からCronJobの実行時に任意のタイムゾーンを指定できるようになりました。

CronJobはkube-controller-managerプロセスから見たタイムゾーンで実行されますが、このタイムゾーンを確認することができない利用者にとってはCronJobが実行する時刻が保証されないという課題がありました。

また、国内ではk8sのクラスタが`UTC`で作られているが、Jobの実行については`Asia/Tokyo`で指定したいというケースも多くあったのではないかと思います。

今回のそれらの課題に対応する機能が追加されました。

### 実施方法
CronJobの`spec.schedule`に対して`CRON_TZ`にタイムゾーンを設定することで使用することができます。

- 以下のようの`CRON_TZ`を設定したCronJobを作成

```diff
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
+  schedule: "CRON_TZ=Asia/Tokyo 15 18 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
```

- Manifestをapplyして指定時刻まで待つとjobが実行されます。

```
$ kubectl apply -f cronjob.yaml         
cronjob.batch/hello created

$ kubectl get cronjobs,job,po                                                              
NAME                  SCHEDULE                         SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/hello   CRON_TZ=Asia/Tokyo 15 18 * * *   False     0        5m55s           9m38s

NAME                       COMPLETIONS   DURATION   AGE
job.batch/hello-27166155   1/1           1s         5m55s

NAME                          READY   STATUS      RESTARTS   AGE
pod/hello-27166155--1-4tgf2   0/1     Completed   0          5m55s
```

- describeでPodの`Start Time`でも確認できます。

```diff
$ kubectl describe po hello-27166155--1-4tgf2 
Name:         hello-27166155--1-4tgf2
Namespace:    default
Priority:     0
Node:         minikube/192.168.49.2
+ Start Time:   Thu, 26 Aug 2021 18:15:00 +0900    <--- Asia/Tokyo の 18:15 に実施されたことを確認
Labels:       controller-uid=e82c7f37-ce7b-4356-89de-3d43f13a1bb1
              job-name=hello-27166155
Annotations:  <none>
Status:       Succeeded
      :
      :
```

### 導入の経緯

今回の機能追加には[ChangelogのDependencies](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.22.md#dependencies-1)の以下が大きく関係しています。

```
- Added
  - github.com/robfig/cron/v3: v3.0.1
```

具体的には[このPR](https://github.com/kubernetes/kubernetes/commit/4b36a5cbe95d9e305d67cfda2ffa87bb3a0ccd47)以下の部分になります。

```diff
-	github.com/robfig/cron v1.1.0
+	github.com/robfig/cron/v3 v3.0.1
```

v1.22での上記の変更によりGo言語のCron v3で追加された以下の機能がk8sにも取り込まれたため、実現できるようになりました。
そのため、k8s v1.21やそれ以前のバージョンでは使用できません。

Go言語側の詳細は[こちら](https://pkg.go.dev/github.com/robfig/cron/v3#hdr-Time_zones)を確認ください。

| <img width="811" alt="cron_package_-_github_com_robfig_cron_v3_-_pkg_go_dev.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309176/c9513d6e-07c3-85b8-6af5-20de27c1208c.png"> |
|:------------------:|

### コミュニティの反応
元々はCronJobでのタイムゾーンのサポートについては[こちらのKEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/19-Graduate-CronJob-to-Stable#support-timezone-for-cronjobs)で、CronJobに`.spec.timezone`を追加してそこに`Asia/Tokyo`等を指定できるようにすることが検討されていたので、コミュニティ側でも予想外の機能追加になりました。

元々この議題について話していた[issue](https://github.com/kubernetes/kubernetes/issues/47202)での反応は以下。

![CronJobs_should_support_timezones_·_Issue__47202_·_kubernetes_kubernetes.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309176/3b4f7251-50f2-0952-e11e-dca2ded0a628.png)

### 今後

~~`CRON_TZ`でタイムゾーンを指定できる機能については、以下のPRで[公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)への説明とテストが追加されつつあります。~~

- ~~[公式のドキュメントへの説明の追加](https://github.com/kubernetes/website/pull/29455)~~
- ~~[テストの追加](https://github.com/kubernetes/kubernetes/pull/104404)~~

~~ただ、本来は想定していた形の解決方法ではないため、`.spec.timezone`の方が採用され今後のアップデートで使用できなくなる可能性もあります。そのため、利用を検討される際はこちらの[issue](https://github.com/kubernetes/kubernetes/issues/47202)で状況を確認してから採用することをお勧めします。~~

#### 2021年11月18日追記

k8s v1.23からCronJobで`CRON_TZ`をk8sプロジェクトとして、サポートしない旨の警告が出るようになります。k8sのdocsにも同じ内容が反映されました。

![CronJob___Kubernetes.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309176/8b6f75af-c61f-d235-0142-41cae439a40f.png)

ref https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/

上記の関連するissueは以下になります。

- https://github.com/kubernetes/website/pull/30509
- https://github.com/kubernetes/kubernetes/pull/106455

k8s v1.23でも`CRON_TZ`は動作するがk8sプロジェクト推奨しない方向になったようです。
CronJobでタイムゾーンをサポートする件については継続して以下のissueで議論されていくようです。

https://github.com/kubernetes/kubernetes/issues/47202


## 所感
Cronjobのタイムゾーンについては2017年頃から議論がありましたが、まずはCronJobControllerをV2にすることを優先して新規機能の追加は凍結。V2がリリースされたのでやっと新機能の追加が動き出した矢先にこういう形で実装されたのは驚きました:innocent:

CronJobControllerV2 が GA となり、それとともに Job に関する機能が追加されたことによって今までずっと課題とされていた Job 周りの挙動がだいぶ整理されてきたので、その流れの中でタイムゾーンの機能も実装されると思っていたので意外でした。

Job周りの機能は今回でだいぶ増えて落ち着いてきた印象があるので、次回は今回 revert されて延期となった `PersistentVolumeClaimDeletePoilcy` など Stateful 周りの変更が増えてくるのかなと楽しみにしてます。