Featured image of post Kubernetes Podが生きている間にNamespaceとかServiceAccountを消すとどうなるの?

Kubernetes Podが生きている間にNamespaceとかServiceAccountを消すとどうなるの?

気になったからやってみた

まとめ

あんまりやる機会はないだろうけど、
Podが生きている間にそれに紐づくNamespaceServiceAccountを削除したときの挙動は次のようになる。

  • Podが生きている間にmetadata.namespaceNamespaceを削除した場合
    • Podが削除される
  • Podが生きている間にspec.serviceAccountNameServiceAccountを削除した場合
    • Podは削除されない
    • 自動マウントされたServiceAccount Tokenはそのまま残る
      • ただしServiceAccountが削除された時点でKubenretes APIの認証が通らなくなる

環境

もくじ

Namespace消す

とあるNamespacePodが生きているときに、kubectl delete namespaceしたらどうなるんだろう…?

# じゅんび
$ kubectl create namespace space-1
$ kubectl -n space-1 create serviceaccount user-1 
$ kubectl -n space-1 run nginx --image=nginx --overrides='{ "spec": { "serviceAccountName" : "user-1" } }'

# Namespace: space-1でspec.serviceAccountName: user-1のPodが動いている状態
$ kubectl -n space-1 get pod nginx
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          15s
$ kubectl -n space-1 get pod nginx -o jsonpath="{.spec.serviceAccountName}"
user-1

# Podが動いてるNamespace消す
$ kubectl delete namespace space-1
namespace "space-1" deleted

# どこにもPodが残ってない
$ kubectl get pod -A | grep -c nginx
0

Namespaceを削除すると、動いているPodがあっても一緒に削除されるらしい。

ServiceAccount消す

今度はPodspec.serviceAccountNameに指定されたServiceAccountPodが生きている間に消してみる。

# さっきと同じ状態
$ kubectl -n space-1 get pod nginx
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          15s
$ kubectl -n space-1 get pod nginx -o jsonpath="{.spec.serviceAccountName}"
user-1
$ kubectl -n space-1 get serviceaccount user-1
NAME     SECRETS   AGE
user-1   1         75s

# Podに紐付いてるServiceAccount消す
$ kubectl -n space-1 delete serviceaccount user-1
serviceaccount "user-1" deleted

# Podは生きてる上にserviceAccountNameもそのまま
$ kubectl -n space-1 get pod nginx
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          2m46s
$ kubectl -n space-1 get pod nginx -o jsonpath="{.spec.serviceAccountName}"
user-1

# PodにマウントされているServiceAccount Tokenは削除したServiceAccountのものが残っている
$ kubectl -n space-1 get pod nginx -o jsonpath="{.spec.volumes}"
[{"name":"kube-api-access-fdd7d","projected":{"defaultMode":420,"sources":[{"serviceAccountToken":{"expirationSeconds":3607,"path":"token"}},{"configMap":{"items":[{"key":"ca.crt","path":"ca.crt"}],"name":"kube-root-ca.crt"}},{"downwardAPI":{"items":[{"fieldRef":{"apiVersion":"v1","fieldPath":"metadata.namespace"},"path":"namespace"}]}}]}}]
$ kubectl -n space-1 exec nginx -it -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | jq -R 'split(".") | .[1] | @base64d | fromjson | ."kubernetes.io"'
{
  "namespace": "space-1",
  "pod": {
    "name": "nginx",
    "uid": "c1595cec-699d-41dd-bb51-6f648b23e832"
  },
  "serviceaccount": {
    "name": "user-1",
    "uid": "e1d20d3d-5f9f-4ab0-b8e0-d021a42d189c"
  },
  "warnafter": 1637856366
}

なるほど…?

Podspec.serviceAccountNameに指定されたServiceAccountが削除されてもPodは消されることはなく、
volumeとしてコンテナにマウントされたServiceAccount Tokenもそのままの状態で残るらしい。

じゃあそのTokenを使ったKubernetes APIの認証/認可はどうなるんだろう?

# またまたさっきと同じ状態
$ kubectl -n space-1 get pod nginx
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          15s
$ kubectl -n space-1 get pod nginx -o jsonpath="{.spec.serviceAccountName}"
user-1
$ kubectl -n space-1 get serviceaccount user-1
NAME     SECRETS   AGE
user-1   1         75s

# user-1にspace-1のPod操作権限を付与
$ kubectl -n space-1 create role pod-owner --verb="*" --resource="pods"
$ kubectl -n space-1 create rolebinding pod-owner-bind --role=pod-owner --serviceaccount=space-1:user-1

# この状態でServiceAccount Tokenを用いてKubernetes APIをたたくと成功(200 OK)する
$ kubectl -n space-1 exec nginx -it -- bash -c 'curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -I -X GET https://kubernetes.default.svc/api/v1/namespaces/space-1/pods'
HTTP/2 200
...

# ServiceAccountを削除
$ kubectl -n space-1 delete serviceaccount user-1

# Podに残ったServiceAccount Tokenを用いてKubernetes APIをたたくと失敗(401 Unauthorized)する
$ kubectl -n space-1 exec nginx -it -- bash -c 'curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -I -X GET https://kubernetes.default.svc/api/v1/namespaces/space-1/pods'
HTTP/2 401
...

# 同名のServiceAccountを再作成
$ kubectl -n space-1 create serviceaccount user-1

# しかし結果は変わらず
$ kubectl -n space-1 exec nginx -it -- bash -c 'curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -I -X GET https://kubernetes.default.svc/api/v1/namespaces/space-1/pods'
HTTP/2 401
...

はえ〜

ServiceAccountが削除された時点でTokenでの認証ができなくなるみたい。
後から同名のServiceAccountを作ってもたぶんTokenuidが新しいServiceAccountと合わないから認証ができないんじゃないかな…?

ちなみにkubectl edit podspec.serviceAccountNameをいじろうとすると

Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)`

って怒られちゃうので、
Podが生きてる間にServiceAccountを消してしまった場合はそのTokenでのAPI認証は諦めてPodを作り直すのがよさそう。
(というかPodが生きているうちはあまり消さないほうがいい)

おわり

こんなことは普段仕事でKubernetesを運用しているときは絶対やらないとは思うけど、すこし気になったので試してみた。

NamespaceServiceAccountPodを巻き添えにして削除されるものだと思いこんでいたので、
ServiceAccountが消えてもPodとマウントされたTokenが残るのは勉強になった。

おまけ

こたつでごろごろするそとちゃん