etcdに格納されたデータを見る
kubernetesがリソースを作成するにあたり、大きく二つのステップがある。
https://kubernetes.io/ja/docs/concepts/overview/components/
リソースを変更・追加するときはetcdにデータが格納される。 コアコンセプトであるReconciliation Loopでetcdと実際のリソースとの差分を検知しリソースを更新することで実現できる。
この記事では前半のkubectlを実行し、etcdにデータが格納されるまでを確認することにした。 Reconciliation Loopの挙動は別途確認することにする。
今回は以下を確認する。
- etcdにデータが格納されるまで
- etcdのデータの中身
前提
- kubernetes v1.22.3
- minikube v1.24.0
- etcdctl 3.3.12
etcdにデータが格納されるまで
kubectl applyで実行する。
まずはマニフェストを準備する。 最近検証で使ってたので、dnsutilsを使っているが特に意味はない。
apiVersion: v1 kind: Pod metadata: name: dnsutils spec: containers: - name: dnsutils image: k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3 command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always
podを立ち上げる。
# k apply -f dnsutils.yaml # k get po NAME READY STATUS RESTARTS AGE dnsutils 1/1 Running 0 8m32s
余談
kubectlは最終的にapiを実行するので、 クラスタに接続できるようにして、直接apiを実行すると同じことが実現できる。
kubectl proxy Starting to serve on 127.0.0.1:8001 curl -X GET http://127.0.0.1:8001/api/v1/namespaces/default/pods # イメージだけ伝わればいいので、所々省略してる { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion": "13267" }, "items": [ { "metadata": { "name": "dnsutils", "namespace": "default", "uid": "c6c5612f-adbe-43b6-8ff6-9dc63381d6b0", "resourceVersion": "12346", "creationTimestamp": "2022-02-23T05:56:42Z", # 省略 }, "spec": { # 省略 }, "status": { # 省略 } } ] }%
こんな感じで、RESTでkubectlに備わってる機能を実行できる。 無数に存在するapiの中で開発に必要な機能を抜粋し、kubectlで叩けるようになっていることがわかる。
api-serverの挙動も今回はソースコード追ったりはしない。 が、etcdにデータが格納される流れとしては、以下のようになる。
etcdのデータを見てみる
今回はetcdctlを用いて検証したが、curlなどのhttpリクエストでも同じことが実現できる。
その前にetcdについて
- etcdとは、キーバリューストアのデータベースである。
- kubernetesのデータ(状態や構成、メタデータ)を保持している。
- Raftという合意アルゴリズムを用いて、可用性と一貫性を担保している。
Raftの合意アルゴリズムの特徴としては、可用性を高めるために必ずリーダが選出され、フォロワにログをレプリケートする。 例えばリーダが正常に稼働しなくなった場合は次のリーダが選出される(Leader Electionという)。
難しくてあんまり理解できてないが、 高い可用性と一貫性を担保している仕組みという理解に止めておく。
参考
kubernetesの場合は、マスターノードの数だけetcdを用意する「stacked etcd」という構成が一般的。
※ etcdをマスターノードではなく外部で構築し運用する場合は「external etcd」という構成になる。 参考
minikubeに接続しetcdctlで検証
$ minikube ssh $ docker run -it --net host gcr.io/etcd-development/etcd /bin/sh $ etcdctl version etcdctl version: 3.3.12 API version: 3.3
やり方は下記を参考にした。 - qiita.com
まずは、必要な環境変数を設定(今回はminikubeの場合)。
export ETCDCTL_API=3 export ETCDCTL_CACERT=/var/lib/minikube/certs/etcd/ca.crt export ETCDCTL_CERT=/var/lib/minikube/certs/apiserver-etcd-client.crt export ETCDCTL_KEY=/var/lib/minikube/certs/apiserver-etcd-client.key
試しにetcdクラスタの一覧を取得。 今回はマスターノードが1台のため一つだけ表示されている。
/ # etcdctl member list aec36adc501070cc, started, minikube, https://192.168.49.2:2380, https://192.168.49.2:2379
keyvalueストアなので、keyを全て出力してみる。
etcdctl get --keys-only --prefix ""
長すぎるので省略するが、例えばnamespaceの場合。
/registry/namespaces/default /registry/namespaces/kube-node-lease /registry/namespaces/kube-public /registry/namespaces/kube-system
kubectlで試しに取得すると同じ結果が出力されている。
k get namespaces NAME STATUS AGE default Active 9h kube-node-lease Active 9h kube-public Active 9h kube-system Active 9h
適当にkeyを指定して、valueを参照する。
# etcdctl get "/registry/namespaces/default" -w=json { "header":{ "cluster_id":18038207397139142846, "member_id":12593026477526642892, "revision":15809, "raft_term":3 }, "kvs":[ { "key":"Zm9v", "create_revision":203, "mod_revision":203, "version":1, "value":"Zm9v" } ], "count":1 }
さっき作った、podはどんな感じかというと、
etcdctl get --keys-only --prefix "" | grep dnsutils /registry/events/default/dnsutils.16d654aa24560992 /registry/events/default/dnsutils.16d654aa27a5029a /registry/events/default/dnsutils.16d654aa2e24e48b /registry/events/default/dnsutils.16d66d970a929b22 /registry/events/default/dnsutils.16d66d9748aec6fc /registry/events/default/dnsutils.16d66d974aa74a95 /registry/events/default/dnsutils.16d66d97548d2181 /registry/pods/default/dnsutils
eventと、podのリソースが存在する。 適当に見てみると、
etcdctl get "/registry/events/default/dnsutils.16d654aa24560992" -w=json { "header":{ "cluster_id":18038207397139142846, "member_id":12593026477526642892, "revision":15809, "raft_term":3 }, "kvs":[ { "key":"Zm9v", "create_revision":203, "mod_revision":203, "version":1, "value":"Zm9v" } ], "count":1 } etcdctl get "/registry/pods/default/dnsutils" -w=json { "header":{ "cluster_id":18038207397139142846, "member_id":12593026477526642892, "revision":15972, "raft_term":3 }, "kvs":[ { "key":"Zm9v", "create_revision":4742, "mod_revision":15215, "version":14, "value":"Zm9v" } ], "count":1 }
ちゃんとデータが格納されとる。
試しに、新しいkeyを作成してデータを入れてみる。
/ # etcdctl put /test/hoge "hoge" OK / # etcdctl get /test/hoge /test/hoge
さらに、バージョン管理も確認してみる。
/ # etcdctl put /test/piyo piyo OK / # etcdctl put /test/piyo2 piyo2 OK / # etcdctl put /test/piyo3 piyo3 OK / # etcdctl get /test/piyo --prefix /test/piyo piyo /test/piyo2 piyo2 /test/piyo3 piyo3 / # etcdctl get /test/piyo --prefix --sort-by=CREATE /test/piyo piyo /test/piyo2 piyo2 /test/piyo3 piyo3 etcdctl get /test/piyo --rev=0 /test/piyo piyo
まとめ
etcdの中身を確認すると、kubectlやapiがなにをしているのかイメージがつきやすい。
keyはetcdctl put
からもわかるように自由に作成できる。
データ格納後は、各種コントローラーが、apiを経由してetcdの変更差分を検知し、schedulerにより実際の変更を行う流れになる。 この拡張性が、カスタムコントローラを開発しやすくすることにつながってる、と現時点では感じた。
etcdが更新される場合に、コントローラーがどのように差分を検知し、処理を行うのかも気になるので今後検証してみる。