概要
以下のライブラリを使おうと思ったらPod内ならService Account使えば良いと書いてあって、いまいち理解してなかったので調べてみました。
https://github.com/kubernetes/client-go
大体ドキュメントに書いてある通りなのですが、何となく雰囲気掴んでおけばドキュメント読むにも役立つんじゃないかと思うので軽くまとめておきます。
端折ってるので詳しく知りたい方はドキュメントをどうぞ。
参考
- https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
バージョン
- 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
欲しい場合ですかね。
{
"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と紐づけて起動されます。それを知っておかないと混乱するかと思うので最初に補足しました。流れとしては以下です。
- Pod起動時にServiceAccountが指定されていればそれを使い、指定されていない場合は
default
と紐付けられる - Podと紐付いたServiceAccountが存在するか確認し、存在しなければ弾く
- Podが
ImagePullSecrets
の設定を持っていなければ、ServiceAccount
のImagePullSecrets
が追加される - APIアクセスするためのtokenを入れるための
volume
が追加される - 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なのかなーと思ってますが、微妙なところもあり不明です。