kubernetesクラスタを構築していくつかのpodを走らせた後で、kubeletの設定を変更したい場合ってありますよね。Production環境だと多分こういう手順になるかと思います。
- kubectl drainでnodeをクラスタから切り離し
- node上で設定変更してkubelet再起動
- kubectl uncordonでnodeをクラスタに戻す
ただ台数が多くなってくるとちょっとこれは辛い。ということで、動的にkubeletの設定変更を行うDynamic Kubelet Configurationを試してみました。
目次
- 目次
- 参考にさせていただいたサイト
- Dynamic Kubelet Configurationとは
- Dynamic Kubelet Configurationの有効化
- Kubeletの設定をConfigMapに移行する
- nodeからConfigMapを参照するように変更する
- nodeのkubeletの設定をConfigMapで変更する
- 設定をミスった場合
- まとめ
参考にさせていただいたサイト
ほぼほぼ以下の通りにやればOKですが、少しだけ修正してます。
Dynamic Kubelet Configurationとは
Dynamic Kubelet Configurationを使うと、kubeletの起動時の設定をconfigMapで管理することができるようになります。つまりkubeletの設定もkubernetesのリソースとして管理ができるようになります。また、設定内容に不備があった場合の切り戻しも行えるようになります。
例によって、以下のレポジトリでクラスタが作成されているところからスタートします。
実際には上記のレポジトリから少し変更していて、Kubernetesのバージョンはv1.20になっていたりしますが、その点については影響はないと思います。ただjqコマンドとかは入ってないので別途入れる必要があります。(そのうち反映します)
Dynamic Kubelet Configurationの有効化
Dynamic Kubelet Configurationはデフォルトでは無効化されていますので、kubeletの起動オプションを変更して、有効化します。必要なのは以下の2つですが、1つ目はデフォルトで有効になっているため、2つ目だけです。
--feature-gates="DynamicKubeletConfig=true"
--dynamic-config-dir=<path>
worker nodeにログインします。
$ vagrant ssh worker-1
もし追加するなら/etc/default/kubeletあたりが良いかなと思うのですが、上記のサイトに従って/var/lib/kubelet/kubeadm-flags.envに追加しましょう。
$ sudo vi /var/lib/kubelet/kubeadm-flags.env
オプションが並んでいる最後に--dynamic-config-dir=/var/lib/kubelet-dynamic
を追加します。指定したパスにconfigMapの設定が保存されます。
KUBELET_KUBEADM_ARGS="--network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2 --dynamic-config-dir=/var/lib/kubelet-dynamic"
kubeletを再起動します。
$ sudo systemctl restart kubelet $ sudo systemctl status kubelet
プロセスを確認すると、--dynamic-config-dir
のオプションが指定されているのがわかりますね。
$ ps auxw | grep kubelet root 27939 5.6 4.5 1712868 91800 ? Ssl 02:34 0:00 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2 --dynamic-config-dir=/var/lib/kubelet-dynamic --node-ip=10.240.0.31
同様にして他のworker nodeも変更します。
Kubeletの設定をConfigMapに移行する
Dynamic Kubelet Configurationでは、Kubeletの設定をConfigMapに保存します。ということは、最初に現在のKubeletの設定が必要になります。
Kubeletの設定は以下で取得できます。
$ kubectl proxy & $ curl -sSL "http://localhost:8001/api/v1/nodes/worker-1/proxy/configz" | jq '.kubeletconfig|.kind="KubeletConfiguration"|.apiVersion="kubelet.config.k8s.io/v1beta1"' > kubelet_configz_worker
kubectl proxyでapiserverにアクセスできるようにして、apiserverの/api/v1/nodes/
worker nodeの名前/proxy/configz
にアクセスすると、現在のworker nodeの設定が取得できますので、これをファイルに出力しておきます。
ファイルの中身はこんな感じです。ちなみに、どのnodeを選んでも同じでした。
{ "enableServer": true, "staticPodPath": "/etc/kubernetes/manifests", "syncFrequency": "1m0s", "fileCheckFrequency": "20s", "httpCheckFrequency": "20s", "address": "0.0.0.0", "port": 10250, "tlsCertFile": "/var/lib/kubelet/pki/kubelet.crt", "tlsPrivateKeyFile": "/var/lib/kubelet/pki/kubelet.key", "rotateCertificates": true, "authentication": { "x509": { "clientCAFile": "/etc/kubernetes/pki/ca.crt" }, "webhook": { "enabled": true, "cacheTTL": "2m0s" }, "anonymous": { "enabled": false } }, "authorization": { "mode": "Webhook", "webhook": { "cacheAuthorizedTTL": "5m0s", "cacheUnauthorizedTTL": "30s" } }, "registryPullQPS": 5, "registryBurst": 10, "eventRecordQPS": 5, "eventBurst": 10, "enableDebuggingHandlers": true, "healthzPort": 10248, "healthzBindAddress": "127.0.0.1", "oomScoreAdj": -999, "clusterDomain": "cluster.local", "clusterDNS": [ "10.96.0.10" ], "streamingConnectionIdleTimeout": "4h0m0s", "nodeStatusUpdateFrequency": "10s", "nodeStatusReportFrequency": "5m0s", "nodeLeaseDurationSeconds": 40, "imageMinimumGCAge": "2m0s", "imageGCHighThresholdPercent": 85, "imageGCLowThresholdPercent": 80, "volumeStatsAggPeriod": "1m0s", "cgroupsPerQOS": true, "cgroupDriver": "systemd", "cpuManagerPolicy": "none", "cpuManagerReconcilePeriod": "10s", "topologyManagerPolicy": "none", "topologyManagerScope": "container", "runtimeRequestTimeout": "2m0s", "hairpinMode": "promiscuous-bridge", "maxPods": 110, "podPidsLimit": -1, "resolvConf": "/run/systemd/resolve/resolv.conf", "cpuCFSQuota": true, "cpuCFSQuotaPeriod": "100ms", "nodeStatusMaxImages": 50, "maxOpenFiles": 1000000, "contentType": "application/vnd.kubernetes.protobuf", "kubeAPIQPS": 5, "kubeAPIBurst": 10, "serializeImagePulls": true, "evictionHard": { "imagefs.available": "15%", "memory.available": "100Mi", "nodefs.available": "10%", "nodefs.inodesFree": "5%" }, "evictionPressureTransitionPeriod": "5m0s", "enableControllerAttachDetach": true, "makeIPTablesUtilChains": true, "iptablesMasqueradeBit": 14, "iptablesDropBit": 15, "failSwapOn": true, "containerLogMaxSize": "10Mi", "containerLogMaxFiles": 5, "configMapAndSecretChangeDetectionStrategy": "Watch", "enforceNodeAllocatable": [ "pods" ], "volumePluginDir": "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/", "logging": { "format": "text" }, "enableSystemLogHandler": true, "shutdownGracePeriod": "0s", "shutdownGracePeriodCriticalPods": "0s", "kind": "KubeletConfiguration", "apiVersion": "kubelet.config.k8s.io/v1beta1" }
あとはこれをConfigMapとしてcreateしてあげればいいのですが、JSONのままだとちょっと扱いづらいですね。今後のことも考えてYAMLでmanifest化します。JSONからYAMLへの変換はgojsontoyamlあたりを使えば良いと思います。
$ cat kubelet_configz_worker | gojsontoyaml > kubelet_configz_worker.yaml $ kubectl -n kube-system create configmap my-node-config --from-file=kubelet=kubelet_configz_worker.yaml -o yaml --dry-run=client > dynamic-kubelet-config.yaml
出力されたmanifestはこうなりました。
apiVersion: v1 data: kubelet: | address: 0.0.0.0 apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /etc/kubernetes/pki/ca.crt authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s cgroupDriver: systemd cgroupsPerQOS: true clusterDNS: - 10.96.0.10 clusterDomain: cluster.local configMapAndSecretChangeDetectionStrategy: Watch containerLogMaxFiles: 5 containerLogMaxSize: 10Mi contentType: application/vnd.kubernetes.protobuf cpuCFSQuota: true cpuCFSQuotaPeriod: 100ms cpuManagerPolicy: none cpuManagerReconcilePeriod: 10s enableControllerAttachDetach: true enableDebuggingHandlers: true enableServer: true enableSystemLogHandler: true enforceNodeAllocatable: - pods eventBurst: 10 eventRecordQPS: 5 evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% evictionPressureTransitionPeriod: 5m0s failSwapOn: true fileCheckFrequency: 20s hairpinMode: promiscuous-bridge healthzBindAddress: 127.0.0.1 healthzPort: 10248 httpCheckFrequency: 20s imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 imageMinimumGCAge: 2m0s iptablesDropBit: 15 iptablesMasqueradeBit: 14 kind: KubeletConfiguration kubeAPIBurst: 10 kubeAPIQPS: 5 logging: format: text makeIPTablesUtilChains: true maxOpenFiles: 1000000 maxPods: 110 nodeLeaseDurationSeconds: 40 nodeStatusMaxImages: 50 nodeStatusReportFrequency: 5m0s nodeStatusUpdateFrequency: 10s oomScoreAdj: -999 podPidsLimit: -1 port: 10250 registryBurst: 10 registryPullQPS: 5 resolvConf: /run/systemd/resolve/resolv.conf rotateCertificates: true runtimeRequestTimeout: 2m0s serializeImagePulls: true shutdownGracePeriod: 0s shutdownGracePeriodCriticalPods: 0s staticPodPath: /etc/kubernetes/manifests streamingConnectionIdleTimeout: 4h0m0s syncFrequency: 1m0s tlsCertFile: /var/lib/kubelet/pki/kubelet.crt tlsPrivateKeyFile: /var/lib/kubelet/pki/kubelet.key topologyManagerPolicy: none topologyManagerScope: container volumePluginDir: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ volumeStatsAggPeriod: 1m0s kind: ConfigMap metadata: creationTimestamp: null name: my-node-config namespace: kube-system
applyします。
$ kubectl apply -f dynamic-kubelet-config.yaml
configmap/my-node-config created
確認してみましょう。
$ kubectl -n kube-system get configmap my-node-config -o yaml apiVersion: v1 data: kubelet: | address: 0.0.0.0 apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /etc/kubernetes/pki/ca.crt authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s cgroupDriver: systemd cgroupsPerQOS: true clusterDNS: - 10.96.0.10 clusterDomain: cluster.local configMapAndSecretChangeDetectionStrategy: Watch containerLogMaxFiles: 5 containerLogMaxSize: 10Mi contentType: application/vnd.kubernetes.protobuf cpuCFSQuota: true cpuCFSQuotaPeriod: 100ms cpuManagerPolicy: none cpuManagerReconcilePeriod: 10s enableControllerAttachDetach: true enableDebuggingHandlers: true enableServer: true enableSystemLogHandler: true enforceNodeAllocatable: - pods eventBurst: 10 eventRecordQPS: 5 evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% evictionPressureTransitionPeriod: 5m0s failSwapOn: true fileCheckFrequency: 20s hairpinMode: promiscuous-bridge healthzBindAddress: 127.0.0.1 healthzPort: 10248 httpCheckFrequency: 20s imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 imageMinimumGCAge: 2m0s iptablesDropBit: 15 iptablesMasqueradeBit: 14 kind: KubeletConfiguration kubeAPIBurst: 10 kubeAPIQPS: 5 logging: format: text makeIPTablesUtilChains: true maxOpenFiles: 1000000 maxPods: 110 nodeLeaseDurationSeconds: 40 nodeStatusMaxImages: 50 nodeStatusReportFrequency: 5m0s nodeStatusUpdateFrequency: 10s oomScoreAdj: -999 podPidsLimit: -1 port: 10250 registryBurst: 10 registryPullQPS: 5 resolvConf: /run/systemd/resolve/resolv.conf rotateCertificates: true runtimeRequestTimeout: 2m0s serializeImagePulls: true shutdownGracePeriod: 0s shutdownGracePeriodCriticalPods: 0s staticPodPath: /etc/kubernetes/manifests streamingConnectionIdleTimeout: 4h0m0s syncFrequency: 1m0s tlsCertFile: /var/lib/kubelet/pki/kubelet.crt tlsPrivateKeyFile: /var/lib/kubelet/pki/kubelet.key topologyManagerPolicy: none topologyManagerScope: container volumePluginDir: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ volumeStatsAggPeriod: 1m0s kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"kubelet":"address: 0.0.0.0\napiVersion: kubelet.config.k8s.io/v1beta1\nauthentication:\n anonymous:\n enabled: false\n webhook:\n cacheTTL: 2m0s\n enabled: true\n x509:\n clientCAFile: /etc/kubernetes/pki/ca.crt\nauthorization:\n mode: Webhook\n webhook:\n cacheAuthorizedTTL: 5m0s\n cacheUnauthorizedTTL: 30s\ncgroupDriver: systemd\ncgroupsPerQOS: true\nclusterDNS:\n- 10.96.0.10\nclusterDomain: cluster.local\nconfigMapAndSecretChangeDetectionStrategy: Watch\ncontainerLogMaxFiles: 5\ncontainerLogMaxSize: 10Mi\ncontentType: application/vnd.kubernetes.protobuf\ncpuCFSQuota: true\ncpuCFSQuotaPeriod: 100ms\ncpuManagerPolicy: none\ncpuManagerReconcilePeriod: 10s\nenableControllerAttachDetach: true\nenableDebuggingHandlers: true\nenableServer: true\nenableSystemLogHandler: true\nenforceNodeAllocatable:\n- pods\neventBurst: 10\neventRecordQPS: 5\nevictionHard:\n imagefs.available: 15%\n memory.available: 100Mi\n nodefs.available: 10%\n nodefs.inodesFree: 5%\nevictionPressureTransitionPeriod: 5m0s\nfailSwapOn: true\nfileCheckFrequency: 20s\nhairpinMode: promiscuous-bridge\nhealthzBindAddress: 127.0.0.1\nhealthzPort: 10248\nhttpCheckFrequency: 20s\nimageGCHighThresholdPercent: 85\nimageGCLowThresholdPercent: 80\nimageMinimumGCAge: 2m0s\niptablesDropBit: 15\niptablesMasqueradeBit: 14\nkind: KubeletConfiguration\nkubeAPIBurst: 10\nkubeAPIQPS: 5\nlogging:\n format: text\nmakeIPTablesUtilChains: true\nmaxOpenFiles: 1000000\nmaxPods: 110\nnodeLeaseDurationSeconds: 40\nnodeStatusMaxImages: 50\nnodeStatusReportFrequency: 5m0s\nnodeStatusUpdateFrequency: 10s\noomScoreAdj: -999\npodPidsLimit: -1\nport: 10250\nregistryBurst: 10\nregistryPullQPS: 5\nresolvConf: /run/systemd/resolve/resolv.conf\nrotateCertificates: true\nruntimeRequestTimeout: 2m0s\nserializeImagePulls: true\nshutdownGracePeriod: 0s\nshutdownGracePeriodCriticalPods: 0s\nstaticPodPath: /etc/kubernetes/manifests\nstreamingConnectionIdleTimeout: 4h0m0s\nsyncFrequency: 1m0s\ntlsCertFile: /var/lib/kubelet/pki/kubelet.crt\ntlsPrivateKeyFile: /var/lib/kubelet/pki/kubelet.key\ntopologyManagerPolicy: none\ntopologyManagerScope: container\nvolumePluginDir: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/\nvolumeStatsAggPeriod: 1m0s\n"},"kind":"ConfigMap","metadata":{"annotations":{},"creationTimestamp":null,"name":"my-node-config","namespace":"kube-system"}} creationTimestamp: "2021-02-17T16:00:49Z" managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:data: .: {} f:kubelet: {} f:metadata: f:annotations: .: {} f:kubectl.kubernetes.io/last-applied-configuration: {} manager: kubectl-client-side-apply operation: Update time: "2021-02-17T16:00:49Z" name: my-node-config namespace: kube-system resourceVersion: "2480" uid: 1147e762-8e34-4efc-aa7c-16b3d6126452
nodeからConfigMapを参照するように変更する
Kubeletの設定がConfigMapになりましたので、次はnodeからこれを参照するようにします。nodeのmanifestの.specを参照します。
$ kubectl get node worker-1 -o yaml (snip) spec: podCIDR: 192.168.1.0/24 podCIDRs: - 192.168.1.0/24 (snip)
ここに以下のように.spec. configSource. configMapを追加すればよいです。
spec: podCIDR: 192.168.1.0/24 podCIDRs: - 192.168.1.0/24 configSource: configMap: name: my-node-config namespace: kube-system kubeletConfigKey: kubelet
kubectl editでもよいですが、patchなら一発です。
$ kubectl patch node worker-1 -p '"spec": { "configSource": { "configMap": { "name": "my-node-config", "namespace": "kube-system", "kubeletConfigKey": "kubelet" } } }'
見てみましょう。
$ kubectl get node worker-1 -o yaml (snip) spec: configSource: configMap: kubeletConfigKey: kubelet name: my-node-config namespace: kube-system podCIDR: 192.168.1.0/24 podCIDRs: (snip) status: (snip) config: active: configMap: kubeletConfigKey: kubelet name: my-node-config namespace: kube-system resourceVersion: "2480" uid: 1147e762-8e34-4efc-aa7c-16b3d6126452 assigned: configMap: kubeletConfigKey: kubelet name: my-node-config namespace: kube-system resourceVersion: "2480" uid: 1147e762-8e34-4efc-aa7c-16b3d6126452 (snip)
ConfigMapを参照する設定が追加されていると同時に、configのステータスとしてactive, assignedなどが表示されていますね。
さらにnodeのイベントを見てみます。
$ kubectl describe node worker-1
ConfigMapを参照するように設定したと同時にkubeletが自動的に再起動されているのがわかりますね。
(snip) Events: Type Reason Age From Message ---- ------ ---- ---- ------- ... Normal KubeletConfigChanged 3m12s kubelet Kubelet restarting to use /api/v1/namespaces/kube-system/configmaps/my-node-config, UID: 1147e762-8e34-4efc-aa7c-16b3d6126452, ResourceVersion: 2480, KubeletConfigKey: kubelet Normal Starting 2m56s kubelet Starting kubelet. Normal NodeHasSufficientMemory 2m56s kubelet Node worker-1 status is now: NodeHasSufficientMemory Normal NodeHasNoDiskPressure 2m56s kubelet Node worker-1 status is now: NodeHasNoDiskPressure Normal NodeHasSufficientPID 2m56s kubelet Node worker-1 status is now: NodeHasSufficientPID Normal NodeAllocatableEnforced 2m56s kubelet Updated Node Allocatable limit across pods
nodeのkubeletの設定をConfigMapで変更する
では変更してみましょう。元の設定にはevictionHardの設定はありますが、evictionSoftの設定はないので、それを追加してみたいと思います。ちなみにevictionSoftの設定を行う場合はevictionSoftGracePeriodの設定も必要です。
manifestを修正します。
(snip) evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% evictionSoft: imagefs.available: 30% memory.available: 200Mi nodefs.available: 20% nodefs.inodesFree: 10% evictionSoftGracePeriod: memory.available: 1m30s nodefs.available: 1m30s nodefs.inodesFree: 1m30s imagefs.available: 1m30s imagefs.inodesFree: 1m30s evictionPressureTransitionPeriod: 5m0s (snip)
適用します。
$ kubectl apply -f dynamic-kubelet-config.yaml
configmap/my-node-config configured
今回の環境ではworker nodeは2台ありますので、それぞれを見てみましょう。
$ kubectl describe node worker-1 (snip) Normal KubeletConfigChanged 57s kubelet Kubelet restarting to use /api/v1/namespaces/kube-system/configmaps/my-node-config, UID: a58be86a-70fa-4309-8b59-da04b0a1bb5f, ResourceVersion: 5066, KubeletConfigKey: kubelet Normal Starting 42s kubelet Starting kubelet. Normal NodeHasSufficientMemory 42s kubelet Node worker-1 status is now: NodeHasSufficientMemory Normal NodeHasNoDiskPressure 42s kubelet Node worker-1 status is now: NodeHasNoDiskPressure Normal NodeHasSufficientPID 42s kubelet Node worker-1 status is now: NodeHasSufficientPID Normal NodeNotReady 42s kubelet Node worker-1 status is now: NodeNotReady Normal NodeAllocatableEnforced 42s kubelet Updated Node Allocatable limit across pods Normal NodeReady 42s kubelet Node worker-1 status is now: NodeReady
$ kubectl describe node worker-2 (snip) Normal KubeletConfigChanged 18s kubelet Kubelet restarting to use /api/v1/namespaces/kube-system/configmaps/my-node-config, UID: a58be86a-70fa-4309-8b59-da04b0a1bb5f, ResourceVersion: 5172, KubeletConfigKey: kubelet Normal Starting 8s kubelet Starting kubelet. Normal NodeHasSufficientMemory 7s kubelet Node worker-2 status is now: NodeHasSufficientMemory Normal NodeHasNoDiskPressure 7s kubelet Node worker-2 status is now: NodeHasNoDiskPressure Normal NodeHasSufficientPID 7s kubelet Node worker-2 status is now: NodeHasSufficientPID Normal NodeAllocatableEnforced 7s kubelet Updated Node Allocatable limit across pods
2台ともkubeletが再起動しているのがわかりますね。
kubeletの設定が反映されているかも確認してみます。
$ curl -sSL "http://localhost:8001/api/v1/nodes/worker-1/proxy/configz" | jq '.kubeletconfig|.kind="KubeletConfiguration"|.apiVersion="kubelet.config.k8s.io/v1beta1" | .evictionSoft, .evictionSoftGracePeriod' { "imagefs.available": "30%", "memory.available": "200Mi", "nodefs.available": "20%", "nodefs.inodesFree": "10%" } { "imagefs.available": "1m30s", "imagefs.inodesFree": "1m30s", "memory.available": "1m30s", "nodefs.available": "1m30s", "nodefs.inodesFree": "1m30s" }
こちらもOKですね。1回の変更で全てのnodeのkubeletの設定変更ができて、コードとして管理もできるので非常に良いですね!
設定をミスった場合
実はここに来るまでに少し失敗していたので、その点についても書いておきます。
上で書いたとおり、evictionSoftの設定を行う場合はevictionSoftGracePeriodの設定も必要ですが、evictionSoftGracePeriodの設定をせずに反映した場合どうなるでしょうか?
manifestを以下のように修正します。
(snip) evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% evictionSoft: imagefs.available: 30% memory.available: 200Mi nodefs.available: 20% nodefs.inodesFree: 10% evictionPressureTransitionPeriod: 5m0s (snip)
適用します。
$ kubectl apply -f dynamic-kubelet-config.yaml
configmap/my-node-config configured
適用してしばらくすると・・・
$ kubectl get node NAME STATUS ROLES AGE VERSION master Ready control-plane,master 66m v1.20.0 worker-1 NotReady <none> 55m v1.20.0 worker-2 NotReady <none> 44m v1.20.0
workerがNotReadyになってしまいました・・・workerにログインして見てみましょう。
kubeletのプロセスが動いていません。
$ ps auxw | grep kubelet vagrant 879 0.0 0.0 14864 1044 pts/0 S+ 02:15 0:00 grep --color=auto kubelet
起動に失敗しています。
$ sudo systemctl status kubelet ● kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled) Drop-In: /etc/systemd/system/kubelet.service.d └─10-kubeadm.conf Active: activating (auto-restart) (Result: exit-code) since Sun 2021-02-21 02:16:19 JST; 8s ago Docs: https://kubernetes.io/docs/home/ Process: 1272 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=255) Main PID: 1272 (code=exited, status=255) (snip)
ログを見てみると・・・
Feb 21 02:13:10 worker-1 kubelet[30284]: F0221 02:13:10.041844 30284 server.go:269] failed to run Kubelet: failed to create kubelet: grace period must be specified for the soft eviction threshold nodefs.available
soft eviction threashholdを指定する場合はgrace periodの設定が必要、ということですね。
kubeletが動いていないとConfigMapを変更しても反映されません。したがって、手動で復旧する必要があるようです。
一旦、正常に動作していた状態のmanifestを元に戻します。
$ kubectl apply -f dynamic-kubelet-config.yaml
worker nodeにログインして、ローカルに保存されているkubeletの設定を削除して、kubeletを再起動します。
$ sudo rm -rf /var/lib/kubelet-dynamic/* $ sudo systemctl restart kubelet
kubeletのプロセスが上がっていますね。
$ ps auxw | grep kube root 10683 7.1 4.5 1712868 91860 ? Ssl 02:25 0:00 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2 --dynamic-config-dir=/var/lib/kubelet-dynamic --node-ip=10.240.0.31
nodeがReadyに戻りました。
$ kubectl get node NAME STATUS ROLES AGE VERSION master Ready control-plane,master 87m v1.20.0 worker-1 Ready <none> 76m v1.20.0 worker-2 Ready <none> 64m v1.20.0
設定が正しいかどうかまではチェックしてくれるというわけではないということですかね。ちょっと怖いですね。
まとめ
いちいち各worker nodeで設定変更しなくて済むのはとても便利ですし、manifestとしてコード管理できるのもよいです。反面、コンフィグの正常性までチェックしてくれるわけではなさそうなので、商用環境でやる場合には事前に検証してから実施したほうが良いですね。