$ kubectl get node
1. 概要
Kubernetes(K8s)を構成するコンポーネントについて説明し、Kubernetesがどのように動作するのか解説します。
2. 参考資料
これらの資料を読むために必要と思われる事柄を説明するのが、このドキュメントの目的です。
3. K8sの基本的な構成
公式ドキュメントには以下のような図が掲載されています。
ノード(Node)と単純に書かれている場合には、Woker Node を指します。K8sシステムを構成する重要なapi-serverを構成するPodが動作するノードは Control Plane Node と呼び、区別することもあります。
このSCCPで利用するKubernetesクラスターでは、Control Planeの機能はWorker Node上で稼動していますので、Control Plane Nodeが独立しているわけではありません。
ノードの役割は次のコマンドで確認できます。
api-serverが動き、システムの中心となるControl-Planeノードは、control-plane(v1.20.x以降)ロールが付いています。
以前はMasterノードと呼ばれていましたが、BLM運動の後、Kubernetesに限らず様々なプロジェクトのドキュメントから、master/slaveなどの用語は変更されています。
NAME STATUS ROLES AGE VERSION
u109ls01 Ready control-plane 2y358d v1.30.4
u109ls02 Ready control-plane 2y358d v1.30.4
u109ls03 Ready <none> 2y358d v1.30.4
u109ls04 Ready <none> 2y358d v1.30.4
3.1. ノード(Worker Node)の構成
Kubernetesのシステム上でコンテナを動作させるために、各ノードでは Container Runtime Interface (CRI)に準拠したDocker, containerd, CRI-O等のコンテナ・エンジンが動作していますが、その他にKubernetes固有のコンポーネントとして、kubelet と kube-proxy が動作しています。
-
仮想化ネットワーク
-
CRIに準拠したコンテナ・エンジン (Docker, containerd, CRI-O等)
-
kubelet プロセス
-
kube-proxy プロセス
このコンテナ・エンジンとkubelet, kube-proxyは必ず全てのノードで動作していて、後で説明するAPI-server(kube-apiserver)と協調し、ユーザーのPodなどを稼動させます。
3.2. 仮想化ネットワーク
公式ガイドの図には明記されていませんが、Kubernetesの中心となるのはネットワークの仮想化です。
初期の設計については、GitHubの中にドキュメントが残されています。
この文書では基本的な機能として4つが挙げられています。
-
(Pod内部の)コンテナ間通信
-
Pod間通信
-
ServiceとPod間通信
-
外部と内部を繋ぐ通信
ゼミ室10の環境では、仮想化ネットワークを構築するために、Calicoを利用しています。
CalicoはLayer-3レベルで動作しBGPを利用して、各ノードに割り当てた/32なIPアドレスの所在を広報します。Flannelなど他のソリューションでは、VxLAN技術を利用してLayer-2での仮想化を提供しますが、パフォーマンス上の懸念があるためLayer-2である必要なければ、Layer-3を利用するCalicoがより良い選択とされています。
KubesprayのCalicoのデフォルト値がipipモードからvxlanモードに変更になりました。詳細は GitHubのissues を確認してください。
3.3. CRIに準拠したコンテナ・エンジン(Runtime)
Kubernetesの公式ドキュメントには、次の3つのCRI runtimesがリストに掲載されています。
-
containerd
-
CRI-O
-
Docker
CRIの説明に掲載されている Protobuf APIに、CRIに準拠したコンテナ・エンジンが実装するサービスが列挙されています。
基本的な役割は、service定義に記述されている以下のような機能を提供することです。
-
Runtime Service: コンテナ・イメージの取得・保存・削除
-
Image Service: コンテナ(pod)の起動・停止・削除
コンテナ・エンジンは定義されたサービスを提供することで、以下のような機能は選択したコンテナ・エンジンによって異なる振舞いとなる可能性があります。
-
コンテナ・イメージをどこからpullするか (Repository名を省略した時に、docker.ioを探すとは限らない)
-
コンテナ・イメージをどこに保存するか (/var/lib/docker/以下とは限らない)
-
独自TSL/SSL CA局ファイルを使用する独自Registryへの接続設定の方法 (ca.keyファイルの配置先は異なる)
以前はDockerを使用していましたがv1.20以降は採用されなくなったため、現在ではcontainerdを使用しています。変更の背景については Don’t Panic: Kubernetes and Docker の公式ブログを参照してください。
3.3.1. kubelet
kubelet は Control-Planeの api-server (kube-apiserver) と連携し、pod(コンテナ)の)活動を管理します。
実際の各ノードでは、次のようなプロセスとして存在しています。
## ps auxww | grep kubelet の出力
root 151957 7.4 0.6 3357840 158592 ? Ssl May31 5958:14 /usr/local/bin/kubelet --logtostderr=true --v=2 --node-ip=192.168.100.51 --hostname-override=u109ls01 --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --config=/etc/kubernetes/kubelet-config.yaml --kubeconfig=/etc/kubernetes/kubelet.conf --pod-infra-container-image=k8s.gcr.io/pause:3.3 --runtime-cgroups=/systemd/system.slice --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin
3.3.2. kube-proxy
kube-proxy は、podの通信(ネットワーク)に関わる制御を行ないます。
各ノードではDockerのコンテナとして実行されています。
## docker ps | grep kube-proxy の出力
39ff1f5995bd 9d368f4517bb "/usr/local/bin/kube…" 5 days ago Up 5 days k8s_kube-proxy_kube-proxy-pg7d4_kube-system_f95cad6f-482b-4c52-91f1-a6759cbe7a0b_2
622fa3ac83bd k8s.gcr.io/pause:3.3 "/pause" 5 days ago Up 5 days k8s_POD_kube-proxy-pg7d4_kube-system_f95cad6f-482b-4c52-91f1-a6759cbe7a0b_2
kube-proxyはpodとして稼動しているので、kubectlからも動作の様子が確認できます。
$ kubectl -n kube-system get pod -l k8s-app=kube-proxy -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS G
ATES
kube-proxy-56j9f 1/1 Running 3 55d 192.168.100.54 u109ls04 <none> <none>
kube-proxy-gt7gg 1/1 Running 2 55d 192.168.100.52 u109ls02 <none> <none>
kube-proxy-hlkn8 1/1 Running 2 55d 192.168.100.53 u109ls03 <none> <none>
kube-proxy-pg7d4 1/1 Running 2 55d 192.168.100.51 u109ls01 <none> <none>
通信において、Load-Balancerが利用するIPアドレスは、各ノードのMACアドレスと関連付けられます。
その他のClusterIPなどのK8s内部で利用するIPアドレスは、ローカルMACアドレス(x[26ae]:xx:xx:xx:xx:xx)と
関連づけられ、いずれかのノードに割り当てられます。
## 外部から観察できるK8s内部ネットワークの様子
$ arp -n
Address HWtype HWaddress Flags Mask Iface
192.168.100.160 ether 00:1b:21:bc:0c:3a C ens1
192.168.100.52 ether 00:1b:21:bc:0c:89 C ens1
192.168.100.53 ether 00:1b:21:bc:0c:3a C ens1
192.168.100.54 ether 00:1b:21:bc:0c:3b C ens1
...
## 内部から観察できるK8s内部ネットワークの様子
$ arp -n
Address HWtype HWaddress Flags Mask Iface
192.168.100.52 ether 00:1b:21:bc:0c:89 C enp1s0
192.168.100.53 ether 00:1b:21:bc:0c:3a C enp1s0
192.168.100.54 ether 00:1b:21:bc:0c:3b C enp1s0
10.233.113.131 ether 1a:73:20:a4:cd:b3 C cali1223486b3a4
10.233.113.140 ether 72:e9:69:66:14:dc C cali79c5fc4a9e9
...
3.4. K8sシステム・コンポーネント (Control Plane)
Kubernetesのシステムの本体はControl Planeとして稼動しています。
-
api-server
-
kube-scheduler
-
etcd
-
kube-controller-manager (Controller Manager)
-
(オプション) Cloud Controller Manager
この内、etcd を除くコンポーネントは、Control Plane Node上のpodとしてコンテナ・エンジンの管理下で動作しています。
podとして稼動しているプロセスは、kubectlを利用して、どのサーバーで実行されているか確認することができます。
$ kubectl -n kube-system get pod
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
calico-kube-controllers-8b5ff5d58-rr4jp 1/1 Running 1 6d1h 192.168.100.54 u109ls04 <none> <none>
calico-node-2cm5r 1/1 Running 8 171d 192.168.100.53 u109ls03 <none> <none>
calico-node-5x5pr 1/1 Running 10 171d 192.168.100.51 u109ls01 <none> <none>
calico-node-7v65s 1/1 Running 10 171d 192.168.100.52 u109ls02 <none> <none>
calico-node-l7hqn 1/1 Running 7 171d 192.168.100.54 u109ls04 <none> <none>
coredns-85967d65-7g7fb 1/1 Running 0 6d1h 10.233.112.58 u109ls03 <none> <none>
coredns-85967d65-hbtjj 1/1 Running 3 55d 10.233.105.203 u109ls04 <none> <none>
dns-autoscaler-5b7b5c9b6f-44jh8 1/1 Running 0 6d1h 10.233.105.9 u109ls04 <none> <none>
kube-apiserver-u109ls01 1/1 Running 20 110d 192.168.100.51 u109ls01 <none> <none>
kube-apiserver-u109ls02 1/1 Running 17 109d 192.168.100.52 u109ls02 <none> <none>
kube-controller-manager-u109ls01 1/1 Running 8 171d 192.168.100.51 u109ls01 <none> <none>
kube-controller-manager-u109ls02 1/1 Running 7 171d 192.168.100.52 u109ls02 <none> <none>
kube-proxy-56j9f 1/1 Running 3 55d 192.168.100.54 u109ls04 <none> <none>
kube-proxy-gt7gg 1/1 Running 2 55d 192.168.100.52 u109ls02 <none> <none>
kube-proxy-hlkn8 1/1 Running 2 55d 192.168.100.53 u109ls03 <none> <none>
kube-proxy-pg7d4 1/1 Running 2 55d 192.168.100.51 u109ls01 <none> <none>
kube-scheduler-u109ls01 1/1 Running 8 171d 192.168.100.51 u109ls01 <none> <none>
kube-scheduler-u109ls02 1/1 Running 7 171d 192.168.100.52 u109ls02 <none> <none>
metrics-server-7c5f68c54d-zrtgl 2/2 Running 1 6d1h 10.233.105.248 u109ls04 <none> <none>
nodelocaldns-9pz2w 1/1 Running 7 171d 192.168.100.54 u109ls04 <none> <none>
nodelocaldns-bzhwn 1/1 Running 11 171d 192.168.100.51 u109ls01 <none> <none>
nodelocaldns-nsgk7 1/1 Running 12 171d 192.168.100.53 u109ls03 <none> <none>
nodelocaldns-z44sj 1/1 Running 12 171d 192.168.100.52 u109ls02 <none> <none>
直接OSの管理下で稼動している etcd, kubelet はここには出てきません。
3.4.1. api-server
Kubernetes APIを提供し、各Nodeで動作するkubeletや、クライアントのkbuectlと通信をするサーバー・コンポーネントです。
受信した内容は、etcd に保存しています。
我々が直接通信する相手は api-server となります。この機能が失なわれると
3.4.2. kube-scheduler
Podオブジェクトが登録されると、そのPodをどのNodeで動作をするのかを決定するコンポーネントです。
3.4.3. etcd
etcd はオープンソースで開発されている、Key-Value型のNoSQL分散データベースです。 Kubernetes以外のプロジェクトでも多く利用されています。
etcdはpodではなく、Ubuntuではsystemdの管理下にあって、OSのサーバープロセスとして動作しています。
etcdctlというコマンドを利用して、etcdに保存されているデータがどのようなものか確認することができます。 サーバー上で実行する必要はありますが、namespace: metallb-system の情報は次のように確認することができます。
## 192.168.100.51-54上で実行
$ sudo etcdctl --endpoints https://192.168.100.51:2379 --cacert=/etc/ssl/etcd/ssl/ca.pem --cert=/etc/ssl/etcd/ssl/member-u109ls01.pem --key=/etc/ssl/etcd/ssl/member-u109ls01-key.pem get /registry/namespaces/metallb-system
/registry/namespaces/metallb-system
k8s
v1 Namespace
metallb-system"*$50ded0f3-600b-4433-a4ac-0adc17a50f192ZB
appmetallbb
0kubectl.kubernetes.io/last-applied-configurationx{"apiVersion":"v1","kind":"Namespace","metadata":{"annotatio
ns":{},"labels":{"app":"metallb"},"name":"metallb-system"}}
z
kubectl-client-side-applyUpdatevFieldsV1:
{"f:metadata":{"f:annotations":{".":{},"f:kubectl.kubernetes.io/last-applied-configuration":{}},"f:labels":{".
":{},"f:app":{}}},"f:status":{"f:phase":{}}}
kubernetes
Active"
3.4.4. kube-controller-manager
Controllerという仕組みで動作する kube-controller-manager というpodが動作しています。
Controllerオブジェクト自体は独自に作成することもできますが、このコンポーネントはKubernetesを稼動させるために必要なContorllerオブジェクトを複数実装しています。
公式ガイドの説明では、次の4つが挙げられていますが、Deployment や StatefulSet オブジェクトなどのコントロールも行ないます。
-
Node Controller - NodeのUp/Downを検出・通知するController
-
Replication Controller - Podの数を適切に保つためのController
-
EndPoint Controller - PodとServiceを結びつけるController
-
ServiceAccount/Token Controller - Namespaceが新規作成された際のデフォルトアカウント・APIアクセストークンを作成するController
最後のServiceAccount/Tokenは意識することはありませんが、Secretオブジェクトとして必要な情報が保存されています。
$ kubectl -n $(id -un) get secret
出力された先頭にある default-token-xxxxx が、ServiceAccount/Token Controllerが作成したSecretオブジェクトです。
NAME TYPE DATA AGE
default-token-4pmsl kubernetes.io/service-account-token 3 109d
objectstore Opaque 4 109d
ssh-auhorized-keys Opaque 1 13d
ssh-host-keys Opaque 3 13d
$ kubectl -n $(id -un) get secret default-token-4pmsl -o yaml
次のように、token: … と kubernetes.io/service-account.name: default と関連する情報が登録されていることが分かります。
apiVersion: v1
data:
ca.crt: ....
namespace: eWFzdS1hYmU=
token: ....
kind: Secret
metadata:
annotations:
kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: a37638b6-917e-41d0-b14b-7ba4eac7889c
creationTimestamp: "2021-04-07T02:53:50Z"
....
kube-controller-managerのpodは次のような操作で確認することができます。
$ kubectl -n kube-system get pod -l component=kube-controller-manager -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED N
ODE READINESS GATES
kube-controller-manager-u109ls01 1/1 Running 8 170d 192.168.100.51 u109ls01 <none> <none>
kube-controller-manager-u109ls02 1/1 Running 7 170d 192.168.100.52 u109ls02 <none> <none>
3.5. この他のk8sコンポーネント
この他にDNSサーバーやCalicoの動作に必要なネットワーク・コントローラーなどが稼動しています。
4. K8sのカスタマイズ
Kubernetesシステムの中で、api-server は クライアントである kubelet や、Controllerオブジェクトを実装する kube-controller-manager と通信する中心的なシステムでした。 この api-server と通信することができれば、動作をカスタマイズすることができます。
Kubernetesは拡張のための柔軟な仕組みを持っていますが、ここでは次の仕組みについて説明していきます。
-
Controller
-
Custom Resource Definition (CRD)
CRDもControllerも個別の仕組みですが、通常は両方を利用して、システムを拡張します。
4.1. カスタマイズの事例
例えば、次のようにシステムの導入を簡単にするために、独自Operatorという仕組みを導入しているプロジェクトがあります。
-
RabbitMQ Cluster Operator for Kubernetes (Source Code: GitHub rabbitmq/cluster-operator)
-
Apache Solr Operator (Source Code: GitHub apache/solr-operator)
GitHubを確認すると分かりますが、これらのProjectが利用するプログラミング言語は Go です。 KubernetesではAPIと通信するためのライブラリとして、*client-go を提供していて、その他のライブラリもGo言語をベースにしています。
Kubernetesに限らずシステム管理系のアプリケーションを作成するのであれば、スクリプト系言語だけでなく、C言語、Go言語は一通り学習することを目指してください。C言語は多くのオープンソースソフトウェアが利用しているので、コードを読める事、必要な修正を加えること、コンパイルできることなど、ができると役に立ちます。
Go言語は今回のようにカスタマイズをするために必要になる場合が今後は増えると思われます。その他にも、C言語やPerlのように作者によって作法が違う、OSが違ってコンパイルできない、本番環境にコピーしたら必要な共有ライブラリがなくて動かない、などの不具合は起こりにくくなります。そのため、今後はGo言語で作成されるアプリケーションが増えると思われますし、少し複雑なユーティリティプログラミングに向いています。
client-goではkubectlのような外部コマンドとして、api-serverと通信するサンプルなどが提供されています。
実際に独自Operatorを作成するには、client-go だけでは大変なので支援するツールとして、以下のようなフレームワークが提供されています。
-
code-generator (aka Kubernetes Way) (GitHub kubernetes/code-genrator)
-
KubeBuilder (GitHub kubernetes-sig/kubebuilder) (SIGs - Special Interest Groups)
-
Operator SDK (hosted by RedHat Inc.)
これらのいずれかを選択して独自Operatorを、CRD と Controller を利用して構築していきます。
4.2. Controller (コントローラー)
コントローラー・オブジェクトは、一つの Resource (リソース) を管理し、api-server と通信するプログラムです。
既に登録されているリソースには、対応するコントローラーが登録されていて、kube-controller-manager は Deployment, ReplicaSet などの基本的なオブジェクトを管理します。
最終的にはDockerコンテナとなりますが、以下のリンク先のようにRabbitMQ OperatorなどのDockerfileを確認すると、コンパイル(Build)された実行形式のファイルをコピーし、実行するだけのシンプルなDockerコンテナを作成しています。
## managerコマンドをコピーし、実行するだけのDockerfile本体の抜粋
FROM scratch
ARG GIT_COMMIT
LABEL GitCommit=$GIT_COMMIT
WORKDIR /
COPY --from=builder /workspace/manager .
COPY --from=etc-builder /etc/passwd /etc/group /etc/
COPY --from=etc-builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
USER 1000:1000
ENTRYPOINT ["/manager"]
4.2.1. コントローラーの基本的な動作
Controllerオブジェクトは、api-server と通信を行なうだけのコマンドですが、システムの状態を常に監視し、状態が変化した際にすばやく動作するための仕組みを備えています。
この仕組みはKubernetesとは別に各コントローラーが実装するもので、この共通の仕組みの作成をサポートするために各種のフレームワークが存在します。 またフレームワークは、api-server の負荷を下げるために、in-memory-cacheを利用した差分更新を提供するなど様々な工夫を備えています。
Reconciliation Loop と呼ばれる、その動きについては参考資料に挙げている ゼロから始めるKubernetes Controller を確認してください。
この動作を行なうために、1つのリソースと協調して動作するコントローラーは1つに限定されることになります。
4.3. Custom Resource Definition (CRD)
Kubernetesにはkubectlを通して任意のCRDが登録可能です。 サンプルとして、RabbitMQのOperatorがどのようなCRDを登録しているか確認します。
定義は長いので、lessなどにパイプで繋ぐか、ファイルに落してEditorでみるか、Webブラウザなどから確認しましょう。。
$ curl -L "https://github.com/rabbitmq/cluster-operator/releases/latest/download/cluster-operator.yml" | less
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.6.0
labels:
app.kubernetes.io/component: rabbitmq-operator
app.kubernetes.io/name: rabbitmq-cluster-operator
app.kubernetes.io/part-of: rabbitmq
name: rabbitmqclusters.rabbitmq.com
ここまでで、KubeBuilderを利用して、controller-gen コマンドが生成していることが分かります。(controller-gen.kubebuilder.io/version: v0.6.0)
ここから先には、リソース定義の内容が続きます。
spec:
group: rabbitmq.com
names:
categories:
- all
kind: RabbitmqCluster
listKind: RabbitmqClusterList
plural: rabbitmqclusters
shortNames:
- rmq
singular: rabbitmqcluster
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.conditions[?(@.type == 'AllReplicasReady')].status
name: AllReplicasReady
type: string
- jsonPath: .status.conditions[?(@.type == 'ReconcileSuccess')].status
name: ReconcileSuccess
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1beta1
schema:
openAPIV3Schema:
....
ここで、kind: RabbitmqCluster の部分で、新しいRabbitmqClusterリソースの定義であることが分かります。
ツールが生成したファイルを読み込むのはなかなか大変ですが、--- に注目すると、このファイルは次の定義を含んでいます。
-
Namespace
-
CustomResourceDefinition
-
ServiceAccount
-
Role
-
ClusterRole
-
RoleBinding
-
ClusterRoleBinding
-
Deployment
このファイルを kubectl apply -f で適用しているので、結果を確認します。
$ kubectl -n rabbitmq-system get all
このコマンドの実行結果は次のようになります。
NAME READY STATUS RESTARTS AGE
pod/rabbitmq-cluster-operator-5b4b795998-sfxmm 1/1 Running 0 9m32s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/rabbitmq-cluster-operator 1/1 1 1 9m32s
NAME DESIRED CURRENT READY AGE
replicaset.apps/rabbitmq-cluster-operator-5b4b795998 1 1 1 9m32s
Error from server (Forbidden): rabbitmqclusters.rabbitmq.com is forbidden: User "yasu-abe@u-aizu.ac.jp" cannot list
resource "rabbitmqclusters" in API group "rabbitmq.com" in the namespace "rabbitmq-system"
エラーがでていますが、管理者権限でも表示される内容に変化はないため、いまは無視します。
登録したCRDに対応したRabbitmqClusterオブジェクトを作成するために、次のようなYAMLファイルを受け付けてくれるようになります。
apiVersion: rabbitmq.com/v1beta1
kind: RabbitmqCluster
metadata:
name: definition
spec:
replicas: 3
persistence:
storageClassName: rook-ceph-block
storage: 20Gi
service:
type: LoadBalancer
annotations:
metallb.universe.tf/address-pool: rabbitmq-pool
このファイルを kubectl -n rabbitmq-system apply -f した後に、管理者権限で状態を確認すると次のような表示になります。
$ kubectl -n rabbitmq-system get all
NAME READY STATUS RESTARTS AGE
pod/definition-server-0 0/1 Init:0/1 0 30s
pod/definition-server-1 0/1 Init:0/1 0 30s
pod/definition-server-2 0/1 Init:0/1 0 29s
pod/rabbitmq-cluster-operator-5b4b795998-sfxmm 1/1 Running 0 29m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/definition LoadBalancer 10.233.53.4 <pending> 5672:32533/TCP,15672:30224/TCP,15692:32648/TCP 31s
service/definition-nodes ClusterIP None <none> 4369/TCP,25672/TCP 32s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/rabbitmq-cluster-operator 1/1 1 1 29m
NAME DESIRED CURRENT READY AGE
replicaset.apps/rabbitmq-cluster-operator-5b4b795998 1 1 1 29m
NAME READY AGE
statefulset.apps/definition-server 0/3 30s
NAME ALLREPLICASREADY RECONCILESUCCESS AGE
rabbitmqcluster.rabbitmq.com/definition False Unknown 32s
これは、登録した RabbitMQCluster オブジェクトを受けて、Operatorが必要な StatefulSet や Service オブジェクトを登録した様子を表わしています。
実際にどのように動作しているかの確認は、GitHub上のコードを確認するのが一番の近道です。
以上