Edited at

KubernetesのService Accountについて調べてみた

More than 1 year has passed since last update.


概要

以下のライブラリを使おうと思ったらPod内ならService Account使えば良いと書いてあって、いまいち理解してなかったので調べてみました。

https://github.com/kubernetes/client-go

大体ドキュメントに書いてある通りなのですが、何となく雰囲気掴んでおけばドキュメント読むにも役立つんじゃないかと思うので軽くまとめておきます。

端折ってるので詳しく知りたい方はドキュメントをどうぞ。


参考


バージョン


  • Kubernetes


    • 1.10



多分バージョン変わったら役に立たなくなる気がしたので、調べたときのバージョンを明記しておきます。


User accounts vs service accounts

Kubernetesではuser accountとservice accountは明確に区別されるようです


  • User accountsは人のためのもの、Service accountsはPod内で動くプロセスのためのもの

  • User accountsは全てのNamespaceを通して固有である必要があるが、Service accountsはNamespace内で固有であれば良い

そのため、testという名前で2つService accountを作ろうとするとエラーになります。

$ kubectl create serviceaccount test

serviceaccount "test" created
$ kubectl create serviceaccount test
Error from server (AlreadyExists): serviceaccounts "test" already exists

しかし、異なるNamespaceなら作成可能です。

% kubectl create serviceaccount test -n name1

serviceaccount "test" created


Service account automation

Service accountsは3つのコンポーネントで成り立っています。


  • A Service account admission controller

  • A Token controller

  • A Service account controller


Service Account Controller

これは単に全てのNamespaceに default というServiceAccountを作成するためのコントローラのようです。

name1というNamespaceを作った直後にServiceAccountを確認すると、既にdefaultが存在することがわかります。

$ kubectl create namespace name1

namespace "name1" created
$ kubectl get serviceaccounts -n name1
NAME SECRETS AGE
default 1 24s

この default というServiceAccountはNamespaceとしての default と関連があると思いこんでいたのですが、単に一番最初にあるデフォルトのServiceAccountである、というぐらいの意味でNamespaceとは関係ないようです。

何でもかんでも default って付けるのどうなんですかね...


Token Controller

これはServiceAccountを監視して、新しくServiceAccountが作成されたらAPIアクセスするための Secret を作成してくれる存在のようです。この Secret にAPIアクセスのためのtokenなどが入っている感じですね。

ServiceAccountが削除されたら Secret を消してくれます。

試しにServiceAccountを作ってみると、直後に Secret が作成されていることが分かります。 test-token-wt6nr というやつですね。

$ kubectl create serviceaccount test

serviceaccount "test" created
$ kubectl get secret
NAME TYPE DATA AGE
default-token-lg725 kubernetes.io/service-account-token 3 7d
test-token-wt6nr kubernetes.io/service-account-token 3 4s

中を見てみるとtokenとca.crtが入っています。

$ kubectl describe secret test-token-wt6nr

Name: test-token-wt6nr
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name=test
kubernetes.io/service-account.uid=XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX

Type: kubernetes.io/service-account-token

Data
====
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
ca.crt: 1042 bytes

Service Account Controllerは他にもServiceAccountに紐づく Secret をユーザが自分で作った場合に、ServiceAccountの存在確認やtokenの追加もやってくれるみたいです。

追加で Secret 欲しい場合ですかね。


secret.json

{

"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "mysecretname",
"annotations": {
"kubernetes.io/service-account.name": "myserviceaccount"
}
},
"type": "kubernetes.io/service-account-token"
}

こんな感じでJSON書いて kubectl create -f secret.json すればいいみたいです。


Service Account Admission Controller

そして一番知りたかった肝心のコントローラーです。

まずそもそもですが、PodはServiceAccountと紐づけて起動されます。それを知っておかないと混乱するかと思うので最初に補足しました。流れとしては以下です。


  1. Pod起動時にServiceAccountが指定されていればそれを使い、指定されていない場合は default と紐付けられる

  2. Podと紐付いたServiceAccountが存在するか確認し、存在しなければ弾く

  3. Podが ImagePullSecrets の設定を持っていなければ、 ServiceAccountImagePullSecrets が追加される

  4. APIアクセスするためのtokenを入れるための volume が追加される

  5. Podの各コンテナに volumeSource を足す。pathは /var/run/secrets/kubernetes.io/serviceaccount

簡単に言えば、ServiceAccountが持っているAPIアクセスするためのtokenが、しれっとコンテナにマウントされます!

見たほうが早いと思うので、適当にnginxのPodをデプロイします。

$ kubectl run nginx --image=nginx

deployment.apps "nginx" created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-8586cf59-2ksl7 0/1 ContainerCreating 0 4s

この状態でコンテナに入ってみます。そして上のpathを見に行ってみると、確かにtokenなどがマウントされています。

% kubectl exec -it nginx-8586cf59-2ksl7 bash

root@nginx-8586cf59-2ksl7:/# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token

このtokenの中身は単なるJWTなのでデコードしてPayloadを見ると以下のようになっています。

{

"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "default",
"kubernetes.io/serviceaccount/secret.name": "default-token-lg725",
"kubernetes.io/serviceaccount/service-account.name": "default",
"kubernetes.io/serviceaccount/service-account.uid": "68b1d424-6af0-11e8-84f0-067120183a92",
"sub": "system:serviceaccount:default:default"
}

secret.nameが default-token-lg725 になっていますね。service-account.nameも default になっています。

確かにServiceAccountの default が持っている Secret が勝手にマウントされています。

なるほどな〜という感じ。

でも自分YAMLに何も書いてないのに...と思ってPodの定義を見ると以下のようになっています(一部省略)。

$ kubectl get pods nginx-8586cf59-2ksl7 -o yaml

apiVersion: v1
kind: Pod
metadata:
...
namespace: default
...
spec:
containers:
- image: nginx
...
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-lg725
readOnly: true
serviceAccount: default
serviceAccountName: default
...
volumes:
- name: default-token-lg725
secret:
defaultMode: 420
secretName: default-token-lg725
...

確かにマウントされてるし ServiceAccount: default になっている...!!

ということで勘の鋭い方はもう分かっていると思いますが、異なるServiceAccountを使いたければYAMLに書いてapplyすれば良いだけですね。

以上から分かったように、PodにはServiceAccountのtokenやca.crtが自動でマウントされるため、Pod内で動かすプログラムは /var/run/secrets/kubernetes.io/serviceaccount 以下を見に行くことで自分で ~/.kube/config 書いたりしなくてもAPIを叩くことが出来ます。

実際に利用可能なAPIはServiceAccountに紐づくRole次第ですが、それはまた別の話なので省略します。


まとめ

ServiceAccountについてまとめました。

仕組みとしてはAWSでEC2にIAM Roleをアタッチする場合に近い感じですね。

k8sのオブジェクトとして全て表現される世界観で実現されており面白いなーと思いました。

k8s勉強始めたばかりで意味わからずだったので、誰かの役に立てば幸いです。

※ Service accountsだったりService accountだったりServiceAccountだったり混ざってますが、ドキュメントに合わせてます。リソース名を指すときはServiceAccountって書いて一般的なアカウントの説明の場合はService accountsなのかなーと思ってますが、微妙なところもあり不明です。