Kubernetes 1.27: SIG-CLI (kubectl) の変更内容

ここでは、Kubernetes 1.27 の CHANGELOG から SIG-CLI (kubectl) の取り組みについてまとめています。:memo: は筆者によるコメントです。

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

**新たに追加された主なコマンドとフラグ**

- `kubectl auth whoami` コマンド: 自分自身の属性を確認する
- `kubectl debug --file/-f` フラグ: デバッグするリソースの指定

**変更があった主なコマンドとフラグ**

- `kubectl explain --output` フラグ: スキーマを表示する形式 (plaintext, plaintext-openapiv2) (デフォルト "plaintext")

**廃止予定のコマンドとフラグ**

なし

**削除されたコマンドとフラグ**

- `kubectl alpha auth whoami`: コマンド: 自分自身の属性を確認する
- `kubectl kustomize --reorder` フラグ: 出力する直前にリソースを並び替える

そのほか、細かい変更は、 https://github.com/superbrothers/kubectl-docs/compare/v1.26.0...v1.27.0 で参照できます。

## 所感

今回は特に SIG-CLI に関連する変更の少ないバージョンでした。

- 自分自身の属性が確認できる `kubectl auth whoami` コマンドが昇格しましたが、このコマンドが使用する API がベータでデフォルトで無効のためまだ使用できません
- `kubectl debug` コマンドにデバック目的に応じたプロファイルを指定する機能が追加されました。よさそうです

大きな機能追加には ApplySet 機能があります。`kubectl apply` コマンドの `--prune` の機能を刷新するものでアルファで追加されました。相当昔に prune をどうにかして使えないかと悪戦苦闘した日を思い出しました。この機能、使い物にならなかったので ApplySet には期待したいところです。GitOps ツールは通常 prune に相当する機能を独自で実装しているので、直接使って便利ってシーンはあまりないかもしれませんが。使用してみた感じをページ最後に書きましたのでみてみてください。

## What's New (Major Themes)（主要な変更）

SIG-CLI に関連する情報はありません。

## Urgent Upgrade Notes（必ず一読してからアップグレードしなければならない事項）

SIG-CLI に関連する情報はありません。

## Changes by Kind（種類別の変更）

### Deprecation（廃止）

SIG-CLI に関連する情報はありません。

### API Change（API の変更）

- `SelfSubjectReview` がベータに昇格しました ([#116274](https://github.com/kubernetes/kubernetes/pull/116274), [nabokihms](https://github.com/nabokihms)) [SIG API Machinery, Auth, CLI and Testing] [sig/api-machinery,sig/auth,sig/cli,sig/testing]
    - :memo: `SelfSubjectReview` のベータ昇格に合わせて `kubectl alpha auth whoami` コマンドが `kubectl auth whoami` に昇格しています
    - :memo: SelfSubjectReviews API がデフォルトで有効になっていないため、使用するには明示的に API を有効にする必要があります。これはおそらく v1.24 から Beta API はデフォルト無効に変更されたためだと思われます。
        - [Beta APIs Are Off by Default · Issue \#3136 · kubernetes/enhancements](https://github.com/kubernetes/enhancements/issues/3136)

### Feature（機能）

- `kubectl debug` に "general", "baseline", "restricted" のデバッグプロファイルが追加しました ([#114280](https://github.com/kubernetes/kubernetes/pull/114280), [sding3](https://github.com/sding3)) [SIG CLI] [sig/cli]
    - :memo: `kubectl debug` をロンチしてからユーザから生成される Pod やコンテナでより設定を柔軟にできるようにしたいというフィードバックがあり、それを受けて追加された機能。事前に定義されたプロファイルを選択させるという方法を取ることで、有効にしたい機能を細かくフラグで指定させる煩雑な作業を避けられるようにしたとのこと。個々のプロファイルはデバッグ目的より使い分けることになっている。
        - `general`: 合理的なデフォルトのセット
        - `baseline`: Pod Security Standard の baseline と互換
        - `restricted`: Pod Security Standard の restricted と互換
        - `legacy`: 1.22 での挙動との後方互換
    - :memo: それそれのプロファイルで有効になる機能は、デバッグ対象が Node なのか、Pod Copy なのか Ephemeral Container なのかで異なり、正確なところは実装をみるしかないので分かりにくい気がする
        - https://github.com/kubernetes/kubernetes/blob/v1.27.0/staging/src/k8s.io/kubectl/pkg/cmd/debug/profiles.go#L126-L148
- `kubectl debug` に `netadmin` デバッグプロファイルを追加しました ([#115712](https://github.com/kubernetes/kubernetes/pull/115712), [wedaly](https://github.com/wedaly)) [SIG CLI] [sig/cli]
    - :memo: これはネットワークデバッグのためのプロファイルで、`NET_ADMIN` Capability と Node が対象の場合は privileged, host namespace の使用が設定される
- `kubectl explain` に古い openapiv2 の `explain` 実装を使う `--output plaintext-openapiv2` 引数が追加しました ([#115480](https://github.com/kubernetes/kubernetes/pull/115480), [alexzielenski](https://github.com/alexzielenski)) [sig/node,sig/auth,sig/cli,sig/architecture,sig/cloud-provider]
    - :memo: `--output` フラグのデフォルト値は `plain` で OpenAPIv3 を使います
- ベータへの昇格のために kubectl `--subresource` に e2e テストを追加しました ([#116590](https://github.com/kubernetes/kubernetes/pull/116590), [MadhavJivrajani](https://github.com/MadhavJivrajani)) [sig/cli,sig/testing]
- debug コマンドに明示的な名前の代わりに pod または node のファイルを渡せる新しい `-f` フラグを追加しました ([#111453](https://github.com/kubernetes/kubernetes/pull/111453), [ardaguclu](https://github.com/ardaguclu)) [sig/cli,sig/testing]
    - :memo: `-f/--file` にマニフェストファイルを渡して `kubectl debug` コマンドを実行できるようになったという意味です
- kubectl `--subresource` フラグがベータに変更されました ([#116595](https://github.com/kubernetes/kubernetes/pull/116595), [MadhavJivrajani](https://github.com/MadhavJivrajani)) [sig/cli]
- Kubectl は値があれば pods, containers, ephemeral containers で `SeccompProfile` を表示するようになりました ([#113284](https://github.com/kubernetes/kubernetes/pull/113284), [williamyeh](https://github.com/williamyeh)) [sig/cli,sig/security]
    - :memo: 表示するというのは `kubectl describe` コマンドでです  
        ```
        $ kubectl describe po nginx-655d4774bb-k7fnw | grep SeccompProfile
        SeccompProfile:   RuntimeDefault
        ```
- Kubectl: default container annotation の e2e test を追加しました ([#115046](https://github.com/kubernetes/kubernetes/pull/115046), [pacoxu](https://github.com/pacoxu)) [sig/cli,sig/testing,sig/architecture]
- `whoami` kubectl コマンドを昇格 ([#116510](https://github.com/kubernetes/kubernetes/pull/116510), [nabokihms](https://github.com/nabokihms)) [sig/auth,sig/cli]
    - :memo: `kubectl alpha auth whoami` コマンドは削除されました
- Kubernetes v1.5 から `kubectl apply` にはアルファステージの `--prune` フラグがあり、以前適用されたオブジェクトが渡されたマニフェストから削除されている場合、そのオブジェクトを削除することをサポートしています。この機能は設計に元から存在するパフォーマンスと正確性の問題によりアルファにとどまり続けています。この PR は `ApplySets` という新しい標準によって実現された、第二の独立した pruning をアルファで公開します。`ApplySet` は server-side オブジェクト (デフォルトでは Secret, Configmaps も可) で、kubectl が `apply` 操作全体でセットメンバーシップを正確かつ効率的に追跡するための使用できます。`ApplySet` で使用される形式は、低レベルの仕様として [KEP 3659](https://github.com/kubernetes/enhancements/issues/3659) で規定されています。エコシステム内の他のツールも相互運用性を向上させるためにこの仕様に基づいて構築できます。`ApplySet` に基づくアルファの pruning を試すには `KUBECTl_APPLYSET=true` と `--prune --applyset=secret-name` というフラグを `kubectl apply` につけてください。 ([#116205](https://github.com/kubernetes/kubernetes/pull/116205), [justinsb](https://github.com/justinsb)) [sig/cli]
    - :memo: ApplySet 機能についてはページ最後にまとめています
-  kubectl explain はサーバが公開する OpenAPIV3 の情報を使うように変更されました  ([#116390](https://github.com/kubernetes/kubernetes/pull/116390), [alexzielenski](https://github.com/alexzielenski)) [SIG API Machinery, CLI and Testing] [sig/api-machinery,sig/cli,sig/testing]
- https://github.com/kubernetes-sigs/kustomize/pull/4954: gh: をホストとして使用するためのサポートを削除。これの使用方法と根拠を見出せず、カスタムの gitconfig の短縮構文を対象にしていたと考えられます 
- `kubectl kustomize` を kustomize v5.0.1 にアップグレードします。これは kustomize のメジャーリリースのため、いくつかの後方互換性のない変更が含まれますが、そのほとんどは稀なユースケース、副作用のあるバグ修正、またはすでにこれまでのリリースで非推奨とされているものです。 ([#116598](https://github.com/kubernetes/kubernetes/pull/116598), [natasha41575](https://github.com/natasha41575)) [SIG CLI] [sig/cli]
    - :memo: Kustomize v5 の変更については Kubernetes Meetup Tokyo 56 で発表があったので詳細はそちらを参照: [\[k8sjp \#56\] Kustomize v5 を含む最新機能とテクニックの紹介 \- Speaker Deck](https://speakerdeck.com/koba1t/k8sjp-number-56-kustomize-v5-wohan-muzui-xin-ji-neng-totekunitukunoshao-jie)
    - :memo: 後方互換性のない変更として次があげられている
        - https://github.com/kubernetes-sigs/kustomize/pull/4911: Drop support for a very old, legacy style of patches. patches used to be allowed to be used as an alias for patchesStrategicMerge in kustomize v3. You now have to use patchesStrategicMerge explicitly, or update to the new syntax supported by patches. See examples in the PR description of https://github.com/kubernetes-sigs/kustomize/pull/4911.
        - https://github.com/kubernetes-sigs/kustomize/issues/4731: Remove a potential build-time side-effect in ConfigMapGenerator and SecretGenerator, which loaded values from the local environment under some circumstances, breaking kustomize build's side-effect-free promise. While this behavior was never intended, we deprecated it and are announcing it as a breaking change since it existed for a long time. See also the Eschewed Features documentation.
        - https://github.com/kubernetes-sigs/kustomize/pull/4985: If you previously included .git in an AWS or Azure URL, we will no longer automatically remove that suffix. You may need to add an extra / to replace the .git for the URL to properly resolve.
        - https://github.com/kubernetes-sigs/kustomize/pull/4954: Drop support for using gh: as a host (e.g. gh:kubernetes-sigs/kustomize). We were unable to find any usage of or basis for this and believe it may have been targeting a custom gitconfig shorthand syntax.
- [alpha: kubectl apply --prune --applyset] 特定のカスタムリソース (CR) を "ApplySet" の親オブジェクトとして使用できるようになりました。特定の CR でこれを有効にするには、それを定義する CustomResourceDefinition (CRD) に `applyset.kubernetes.io/is-parent-type: true` ラベルを適用します。([#116353](https://github.com/kubernetes/kubernetes/pull/116353), [KnVerey](https://github.com/KnVerey)) [sig/cli]
    - :memo: GitOps ツールの Flux は独自の CR のステータスに管理するリソースのリストを持っているので、そこからの移行が想定された変更です
    - :memo: ApplySet 機能についてはページ最後にまとめています
- `kubectl` はデフォルトで `HorizontalPodAutoscaler` `v2` を使うようになりました ([#114886](https://github.com/kubernetes/kubernetes/pull/114886), [a7i](https://github.com/a7i)) [sig/cli]

### Documentation（ドキュメント）

- この変更は下記 CLI コマンドに影響する: `kubectl create rolebinding -h` ([#107124](https://github.com/kubernetes/kubernetes/pull/107124), [ptux](https://github.com/ptux)) [SIG CLI] [sig/cli]
    - :memo: `kubectl create rolebinding` のヘルプメッセージが改善されただけ

### Bug or Regression（バグまたはリグレッション）

- `dry-run` を付けた際に `kubectl scale` コマンドに (dry run)、(server dry run) のサフィックスを追加しました ([#114252](https://github.com/kubernetes/kubernetes/pull/114252), [ardaguclu](https://github.com/ardaguclu)) [sig/cli,sig/testing]
- 続く `kubectl rollout restart` コマンドが1秒以内に実行された場合の `kubectl rollout restart` のエラーメッセージを変更しました ([#113040](https://github.com/kubernetes/kubernetes/pull/113040), [ardaguclu](https://github.com/ardaguclu)) [sig/cli]
    - :memo: `kubectl rollout restart` コマンドは、`kubectl.kubernetes.io/restartedAt` annotation に RFC3339 形式で実行時間を値としてパッチすることでロールアウトをトリガする仕組みとなっているが、RFC3339 は秒単位までしか扱っていないため、1秒以内に連続して `kubectl rollout restart` コマンドを実行しても二度目の実行は同じ値でパッチすることになり、「変更なし」として無視されてしまう。人間は通常1秒以内に連続して実行することがないので問題がないが、自動化のためのスクリプトでは起きる可能性がある。この修正ではそれが修正されたわけではなく、エラーメッセージに次の文章を追加してトリガされなかったことに気づけるようにするというもの。
        - `if restart has already been triggered within the past second, please wait before attempting to trigger another`
- `kubectl exec` に渡されたファイルに複数のリソースが含まれる場合、エラーメッセージを `cannot exec into multiple objects at a time` に変更しました ([#114249](https://github.com/kubernetes/kubernetes/pull/114249), [ardaguclu](https://github.com/ardaguclu)) [sig/cli,sig/testing]
- kubectl: `kubectl diff` の pruning 時にリソースをフィルタするためのラベルセレクタの使用を有効にしました ([#114863](https://github.com/kubernetes/kubernetes/pull/114863), [danlenar](https://github.com/danlenar)) [sig/cli,sig/testing]
    - :memo: `kubectl diff --prune` を実行するのに `-l` (ラベルセレクタ) を指定していてとしても Prune の処理でラベルセレクタが渡っておらず無視されてしまっていたのを修正したというもの
- deployments を describe したときに `OldReplicaSets` はまだ利用可能な replicasets だけでなく deployment が制御する全てを常に表示するようになりました ([#113083](https://github.com/kubernetes/kubernetes/pull/113083), [llorllale](https://github.com/llorllale)) [SIG CLI] [sig/cli]

### Other (Cleanup or Flake)（そのほか）

SIG-CLI に関連する情報はありません。

## アルファ: ApplySet 機能

`kubectl apply` コマンドを使った Kubernetes リソースの宣言的な管理で問題になるのは不要になったリソースの削除です。必要なくなったリソースをマニフェストファイルから削除して `kubectl apply` で適用してもクラスタに作成されたリソースは削除されません。この問題に対して Kubernetes v1.5 で `--prune` フラグが追加されました。これは以前適用されたリソースがマニフェストから削除されている場合にそれをクラスタから自動的に削除するという機能でしたが、一部のリソースしかサポートされていなかったり、予測できない挙動から意図しないリソースが削除されてしまう可能性があるといった問題がありました。この機能についてより安全で優れた実装が `ApplySet` です。ArgoCD や Flux といった GitOps ツールらがすでに同等機能を独自に持っていますが、それらツールからも利用しやすく移行できることが目標にもなっています。ApplySet 機能は v1.27 時点でアルファレベルで使用には明示的に環境変数を設定する必要があります。基本的にアルファレベルの機能を本番で使用することは避けましょう。

ApplySet 機能は標準化されたラベルとアノテーションによってリソースグループを構成するという方式です。これによりグループに属するリソースを正確に識別できます。またラベル等のビルトイン機能のみを使用しており追加で CRD をインストールする必要はありません。

ここでは1つの Deployment と1つの Service を適用してみます。

```
$ kubectl version
WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short.  Use --output=yaml|json to get the full version.
Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.1", GitCommit:"4c9411232e10168d7b050c49a1b59f6df9d7ea4b", GitTreeState:"clean", BuildDate:"2023-04-14T13:21:19Z", GoVersion:"go1.20.3", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v5.0.1
Server Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.1", GitCommit:"4c9411232e10168d7b050c49a1b59f6df9d7ea4b", GitTreeState:"clean", BuildDate:"2023-04-19T20:53:25Z", GoVersion:"go1.20.3", Compiler:"gc", Platform:"linux/amd64"}

$ KUBECTL_APPLYSET=true kubectl apply -f nginx.yaml --prune --applyset=applyset-nginx -n default
deployment.apps/nginx created
service/nginx created
```

作成されたオブジェクトを確認してみます。

```
$ kubectl get secret,deploy,service --show-labels
NAME                    TYPE     DATA   AGE   LABELS
secret/applyset-nginx   Opaque   0      26s   applyset.kubernetes.io/id=applyset-KJRZa9n3K5KqyLy4h8wiB2NM8RkbsMbZgRrYCrkBV_E-v1

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE   LABELS
deployment.apps/nginx   1/1     1            1           26s   app=nginx,applyset.kubernetes.io/part-of=applyset-KJRZa9n3K5KqyLy4h8wiB2NM8RkbsMbZgRrYCrkBV_E-v1

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE   LABELS
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    34m   component=apiserver,provider=kubernetes
service/nginx        ClusterIP   10.96.113.111   <none>        8080/TCP   26s   app=nginx,applyset.kubernetes.io/part-of=applyset-KJRZa9n3K5KqyLy4h8wiB2NM8RkbsMbZgRrYCrkBV_E-v1
```

親リソースの `applyset-nginx` Secret には `applyset.kubernetes.io/id=applyset-KJRZa9n3K5KqyLy4h8wiB2NM8RkbsMbZgRrYCrkBV_E-v1` ラベルが付与されています。Deployment と Service のリソースには `applyset.kubernetes.io/part-of=applyset-KJRZa9n3K5KqyLy4h8wiB2NM8RkbsMbZgRrYCrkBV_E-v1` ラベルが付与されています。`applyset.kubernetes.io/part-of` ラベルという名前から今回適用した Deployment と Service が親リソースに紐付いてそうなことが分かります。ちなみに ID は `base64(sha256(<name>.<namespace>.<kind>.<group>))` の形式です。

マニフェストファイルから Service リソースを削除してから再度適用してみて正しく削除されるかどうかを確認してみます。

```
$ cp nginx.yaml{,.bak}
$ vim nginx.yaml
$ diff -u nginx.yaml.bak nginx.yaml
--- nginx.yaml.bak      2023-05-10 14:32:43.799310894 +0900
+++ nginx.yaml  2023-05-10 14:32:50.051398622 +0900
@@ -17,17 +17,3 @@
       containers:
       - image: nginxinc/nginx-unprivileged
         name: nginx-unprivileged
----
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    app: nginx
-  name: nginx
-spec:
-  ports:
-  - port: 8080
-    protocol: TCP
-    targetPort: 8080
-  selector:
-    app: nginx

$ KUBECTL_APPLYSET=true kubectl apply -f nginx.yaml --prune --applyset=applyset-nginx -n default
deployment.apps/nginx unchanged
service/nginx pruned
$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   41m
```

`service/nginx pruned` のメッセージにより期待どおり削除されたことが分かります。

以上の通り便利そうな機能ではありますが、課題もあります。
まず、複数の namespace に跨るリソースを ApplySet で管理することが推奨されていないことです。基本的には親リソースが作成される namespace に閉じてリソースをグループとして管理するということになっています。複数の namespace を跨いだりクラスタレベルのリソースを管理することについても仕様には含まれているので、できることはできるようです（v1.26 時点で実装されているのかは確認していません。少なくとも `kubectl apply` コマンドを使ってはできなさそうです）。サードパーティが提供するマニフェストが CRD を含んでいたり、Role リソースを kube-system namespace に作成するようになっていたりするので、現実的には namespace 内に閉じることは難しいように思います。
また、`kubectl apply` 実行時に明示的に親リソースを指定する必要があります。どれが親リソースなのかを調べていちいち指定しないといけないのは面倒です。ただこれは使い勝手を見直すことで改善できそうなのと、GitOps ツールが内部的に使用するということでいえば課題にならなさそうです。本番ではなんらかの GitOps ツールを使うことになると思うので、`kubectl apply` コマンドでの ApplySet 機能のサポートが十分でなかったとしても問題なさそうです。

ApplySet 機能の詳細は [ApplySet : \`kubectl apply \-\-prune\` redesign and graduation strategy · Issue \#3659 · kubernetes/enhancements](https://github.com/kubernetes/enhancements/issues/3659) で確認できます。