K8sでServiceAccountをPodから確実に取り外す方法
以前、ServiceAccountを取り外すのと同時に、不要となるServiceAccountリソースを削除しようとしたことがありました。この時はDeploymentからserviceAccountNameフィールドを削除して対応しようとしましたが、ServiceAccountが外れず想定通りにいきませんでした。それを解決するためにServiceAccountの取り外し方について調べたので、今回はそれをまとめました。
ServiceAccountの取り外し方
ServiceAccountをDeploymentやJobsなどのリソースにアタッチするためにはPodテンプレート内のserviceAccountNameフィールドを利用します。しかし、すでにアタッチされているServiceAccountを外したい場合、単にテンプレートからserviceAccountNameフィールドを削除するだけでは外せません。このフィールドを削除しても後方互換性の維持のためにDeploymentやReplicaSetは更新されないためです。
すでにServiceAccountがアタッチされているリソースからServiceAccountを外したい場合は、下記いずれかの対応をする必要があります。
-
serviceAccountNameとserviceAccountフィールドにnullを設定してマニフェストを適用する (基本的にこの対応を推奨) -
serviceAccountNameフィールドにdefaultを設定してマニフェストを適用する -
kubectl editを用いて、DeploymentやJobsの設定から直接serviceAccountNameフィールドを削除する
1つずつ見ていきましょう。
フィールドにnullを設定する方法
これは公式ドキュメントにも記載されている方法になります。Podテンプレート内のserviceAccountNameとserviceAccountフィールドにnullを明示的に設定することで、既存リソースの更新が行われます。通常の設定更新時と同様に、新しい設定のPodが立ち上がり古いPodは削除されます。
The
.spec.serviceAccountfield is a deprecated alias for.spec.serviceAccountName. If you want to remove the fields from a workload resource, set both fields to empty explicitly on the pod template.
Configure Service Accounts for Pods | Kubernetes
この方法の場合、適用後に2つの不要なフィールドがマニフェストに残ります。しかし、ServiceAccountを外した後ならば、フィールドをマニフェストから削除できるので、最終的なマニフェストの見通しは良くなります。
検証
まずは検証用の環境を用意します。ローカル環境にkindを利用してKubernetesクラスタを立ち上げます。今回利用する環境は下記となります。この環境は後続の検証でも利用します。
$ kind version
kind v0.24.0 go1.22.6 darwin/arm64
$ kind create cluster
$ kubectl version
Client Version: v1.31.0
Kustomize Version: v5.4.2
Server Version: v1.31.0
次に、ServiceAccountをアタッチしたDeploymentを作成します。以下のマニフェストをmanifest.yamlとして保存します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
serviceAccountName: my-service-account
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
マニフェストファイルを作成したらkubectl applyコマンドでリソースを作成し、ServiceAccountがアタッチされていることを確認します。
$ kubectl apply -f manifest.yaml
serviceaccount/my-service-account created
deployment.apps/nginx created
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 39s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-5864c46b87-sfcx7 1/1 Running 0 41s
# DeploymentにServiceAccountがアタッチされていることを確認
$ kubectl describe deploy nginx | grep "Service Account"
Service Account: my-service-account
# PodにもServiceAccountがアタッチされていることを確認
$ kubectl describe po nginx-5864c46b87-sfcx7 | grep "Service Account"
Service Account: my-service-account
※1: ここまでの手順は後続の検証でも行います。
ServiceAccountがアタッチされているリソースが作成できたので、次にServiceAccountを外すためにマニフェストを更新します。serviceAccountNameとserviceAccountフィールドにnullを設定します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
serviceAccountName: null # 変更
serviceAccount: null # 追加
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
kubectl applyコマンドで更新したマニフェストを適用し、ServiceAccountが外されていることを確認します。
$ kubectl apply -f manifest.yaml
serviceaccount/my-service-account unchanged
deployment.apps/nginx configured
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-54b9c68f67-7vs2t 1/1 Running 0 22s
# DeploymentからServiceAccountが外されていることを確認
$ kubectl describe deploy nginx | grep "Service Account"
# 何も表示されない
# PodからServiceAccount(my-service-account)が外されていることを確認
# *PodはServiceAccountが無くなることはなく、defaultがアタッチされる。
$ kubectl describe po nginx-54b9c68f67-7vs2t | grep "Service Account"
Service Account: default
これで、ServiceAccountを問題なく取り外すことができました。
フィールドにdefaultを設定する方法
「フィールドにnullを設定する方法」の検証で確認した通り、serviceAccountNameフィールドを指定しない場合、デフォルトでdefaultServiceAccountが適用されます。ServiceAccountは「なし」にはなりません。
すなわち、「すでに適用しているServiceAccountを外す」は「default ServiceAccountを代わりに適用する」と同じです。これを利用して、serviceAccountNameにdefaultを明示的に指定することで、既存のServiceAccountを取り外すことができます。
この方法も「フィールドにnullを設定する方法」と同様に不要なフィールドが一時的に残りますが、のちにそのフィールドをマニフェストから削除することで、最終的なマニフェストの見通しをよくできます。しかし、Kubernetes上のDeploymentやJobsのリソース情報にはdefault ServiceAccountがアタッチされているように表示されます。そのため、ServiceAccountを外したというよりかは、defaultに切り替えたように見えます。(実際のリソースの挙動は外した場合と変わりません)
検証
まず、検証の下準備として、先ほどの検証で行った※1までの手順を行います。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-5864c46b87-sg4fg 1/1 Running 0 18s
$ kubectl describe deploy nginx | grep "Service Account"
Service Account: my-service-account
$ kubectl describe po nginx-5864c46b87-sg4fg | grep "Service Account"
Service Account: my-service-account
ServiceAccountがアタッチされているリソースが作成できたので、次にServiceAccountを外すためにマニフェストを更新します。serviceAccountNameフィールドにdefaultを設定します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
serviceAccountName: default # 変更
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
更新したマニフェストをkubectl applyコマンドで適用し、ServiceAccountが外されていることを確認します。
$ kubectl apply -f manifest.yaml
serviceaccount/my-service-account unchanged
deployment.apps/nginx configured
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-54b9c68f67-7vs2t 1/1 Running 0 22s
# DeploymentからServiceAccount(my-service-account)が外されていることを確認
# ServiceAccount情報が無くなるわけではなく、defaultがアタッチされていることになる。
$ kubectl describe deploy nginx | grep "Service Account"
Service Account: default
# PodからServiceAccount(my-service-account)が外されていることを確認
k describe po nginx-bd49b995-bbncn | grep "Service Account"
Service Account: default
PodからServiceAccountが外されていることが確認できました。しかし、前述した通りDeploymentにはService Account: defaultと表示されフィールド自体は残ってしまいます。
kubectl editを用いて直接削除する方法
すでにデプロイされているDeploymentやJobsから直接ServiceAccountを外す方法もあります。kubectl editコマンドを用いて、リソースの設定からserviceAccountNameとserviceAccountフィールドを直接削除することで外すことができます。
$ kubectl edit deployment <deployment-name>
この場合、マニフェストファイル側からは単にserviceAccountNameフィールドを削除するだけで良くなります。次回以降のマニフェスト適用時には、すでにリソースとマニフェストからServiceAccountが外されているため、変更が発生しません。また、マニフェストに不要なフィールドが残ることもありません。しかし、kubectl editコマンドを用いるため、手動操作が必要な点はデメリットと言えます。
検証
先ほどと同様にまずは※1までの手順を行います。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-5864c46b87-8cwd4 1/1 Running 0 33s
$ kubectl describe deploy nginx | grep "Service Account"
Service Account: my-service-account
$ kubectl describe po nginx-5864c46b87-8cwd4 | grep "Service Account"
Service Account: my-service-account
リソースの準備ができたら、kubectl editコマンドを用いてServiceAccountを外します。(serviceAccountNameとserviceAccountフィールドを削除します)
$ kubectl edit deploy nginx
deployment.apps/nginx edited
削除後にはPodが再生成され、ServiceAccountが外されているはずです。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-54b9c68f67-fxrz7 1/1 Running 0 19s
# DeploymentからServiceAccountが外されていることを確認
$ kubectl describe deploy nginx | grep "Service Account"
# 何も表示されない
# PodからServiceAccount(my-service-account)が外されていることを確認
$ kubectl describe po nginx-54b9c68f67-fxrz7 | grep "Service Account"
Service Account: default
なお、すでにDeploymentやPodからServiceAccountが外されているので、下記のようにマニフェストを更新しても変更が発生しません。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
# serviceAccountName: my-service-account # 削除
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
kubectl applyコマンドで更新後のマニフェストを適用してもPodが再生成されることはありません。
なお、出力結果にdeployment.apps/nginx configuredと表示されていますが、実リソースには変更が発生していません。これはKubernetesが前回適用されたマニフェストを覚えており、そこから差分(serviceAccountNameフィールドが削除された)が発生しているため表示されています。
$ kubectl apply -f manifest.yaml
serviceaccount/my-service-account unchanged
deployment.apps/nginx configured
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-54b9c68f67-fxrz7 1/1 Running 0 1m45s
終わりに
ServiceAccountを確実に取り外すための3つの方法を見てきました。
一般的にはKubernetes上のリソース管理にArgoCDなどのCDツールを利用していることが多いはずです。その場合、kubectl editを用いて直接ServiceAccountを外す方法は避けるべきです。そのため、serviceAccountNameとserviceAccountフィールドにnullを設定する方法が推奨されます。default ServiceAccountを代わりに適用する方法もありますが、リソースの情報からはServiceAccountが適用されているように見えてしまうので、個人的にはあまりお勧めしません。
Discussion