Bootstrap FreeKB - OpenShift - Resolve KubeletDown
OpenShift - Resolve KubeletDown

Updated:   |  OpenShift articles

Let's say something like this is being returned.

KubeletDown
Kubelet has disappeared from Prometheus target discovery
Target disappeared from Prometheus target discovery

 

In the openshift-monitoring namespace, there should be a prometheus rulesfile configmap.

~]$ oc get configmaps --namespace openshift-monitoring
NAME                                             DATA   AGE
prometheus-k8s-rulefiles-0                       46     96d

 

And I was able to see this alert configuration is in a Prometheus rules file configmap.

~]$ oc describe configmap prometheus-custom-rulefiles-0 --namespace openshift-monitoring
  - alert: KubeletDown
    annotations:
      description: Kubelet has disappeared from Prometheus target discovery.
      runbook_url: https://github.com/openshift/runbooks/blob/master/alerts/cluster-monitoring-operator/KubeletDown.md
      summary: Target disappeared from Prometheus target discovery.
    expr: |
      absent(up{job="kubelet", metrics_path="/metrics"} == 1)
    for: 15m
    labels:
      namespace: kube-system
      severity: critical

 

I've saw this twice. Here is the cause for each occurrence of this I saw.

  • Rotated Certificate was not picked up by certain resources (Prometheus-custom and alertmanager-custom)
  • Persistent Volumes for the clusters Prometheus instances reached 100% capacity thus Prometheus was not able to pull in metrics from the cluster nodes to evaluate

 

Persistent Volumes for the clusters Prometheus instances reached 100% capacity

The oc get persistentvolumes command can be used to list the Persistent Volumes. Notice there are two Prometheus Persistent Volumes and two Alert Manager Persistent Volumes.

~]$ oc get persistentvolumes
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                                          STORAGECLASS   REASON   AGE
pvc-53b2f9a5-1cc3-4945-a619-00d82490055a   300Gi      RWO            Delete           Bound    openshift-monitoring/prometheusk8s-db-prometheus-k8s-0         gp3-csi                 254d
pvc-9f311f27-5b80-4afb-8e39-be05b88bc309   10Gi       RWO            Delete           Bound    openshift-monitoring/alertmanagermain-db-alertmanager-main-0   gp3-csi                 254d
pvc-cf6a37aa-c619-4e1b-95e7-1449928401a6   10Gi       RWO            Delete           Bound    openshift-monitoring/alertmanagermain-db-alertmanager-main-1   gp3-csi                 254d
pvc-d2c305c6-847c-4b76-bdf1-e0015ac40b84   300Gi      RWO            Delete           Bound    openshift-monitoring/prometheusk8s-db-prometheus-k8s-1         gp3-csi                 254d

 

The oc get persistentvolumeclaims command can be used to list the Persistent Volume Claims associated with the Persistent Volumes.

~]$ oc get pvc --all-namespaces
NAMESPACE                     NAME                                      STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
openshift-monitoring          alertmanagermain-db-alertmanager-main-0   Bound     pvc-9f311f27-5b80-4afb-8e39-be05b88bc309   10Gi       RWO            gp3-csi        254d
openshift-monitoring          alertmanagermain-db-alertmanager-main-1   Bound     pvc-cf6a37aa-c619-4e1b-95e7-1449928401a6   10Gi       RWO            gp3-csi        254d
openshift-monitoring          prometheusk8s-db-prometheus-k8s-0         Bound     pvc-53b2f9a5-1cc3-4945-a619-00d82490055a   300Gi      RWO            gp3-csi        254d
openshift-monitoring          prometheusk8s-db-prometheus-k8s-1         Bound     pvc-d2c305c6-847c-4b76-bdf1-e0015ac40b84   300Gi      RWO            gp3-csi        254d

 

Notice the Prometheus and Alert Manager Persistent Volume Claims are in the openshift-monitoring namespace. The oc get events command can be used to see if there are any interesting events in the openshift-monitoring namespace.

oc get events --namespace openshift-monitoring --sort-by='.metadata.creationTimestamp'

 

Likewise, you can describe the persistent volume cliams to see if there are any interesting events with the Persistent Volume Claim.

oc describe persistentvolumeclaim prometheusk8s-db-prometheus-k8s-0 --namespace openshift-monitoring

 

Rotated Certificate was not picked up by certain resources

In the openshift-monitoring namespace there should be statefulsets.

~]$ oc get statefulsets --namespace openshift-monitoring
NAME                READY   AGE
alertmanager-main   2/2     2y105d
prometheus-k8s      2/2     2y105d

 

And I was able to see a stateful set was configured to use the configmap.

~]$ oc get statefulset prometheus-k8s --output yaml --namespace openshift-monitoring
spec:
  template:
    spec:
      containers:
        name: prometheus
        volumeMounts:
        - mountPath: /etc/prometheus/rules/prometheus-custom-rulefiles-0
          name: prometheus-custom-rulefiles-0

 

And the stateful set was managed by the cluster-monitoring-operator.

metadata:
  labels:
    app.kubernetes.io/component: prometheus
    app.kubernetes.io/instance: custom
    app.kubernetes.io/managed-by: cluster-monitoring-operator
    app.kubernetes.io/name: prometheus
    app.kubernetes.io/part-of: custom-monitoring
    app.kubernetes.io/version: 2.46.0
    operator.prometheus.io/name: custom
    operator.prometheus.io/shard: "0"

 

The first time I saw this alert I saw a bunch of "certificate has expired" events in the log, like this.

And these events started near the same time I started getting the KubeletDown alerts.

Apr 19 03:37:50.211885 ip-10-29-86-25 kubenswrapper[1507]: E0419 03:37:50.211852    1507 server.go:299] "Unable to authenticate the request due to an error" err="verifying certificate SN=12345678901212345678901263ABCD073300112, SKID=, AKID=67:3A:A2:57:2C:18:52:21:04:BC:36:3B:3A:F4:3A:C8:1B:B3:01:7B failed: x509: certificate has expired or is not yet valid: current time 2024-04-19T03:37:50Z is after 2024-04-19T01:12:29Z"

 

And I noticed new "cert" configmaps were created near the same time the alerts started. In this example, the 564 config map is the config map before the issue started and the 565 config map was created at nearly the same exact time the issue started.

~]$ oc get configmaps --namespace openshift-kube-apiserver | grep cert
kube-apiserver-cert-syncer-kubeconfig-564   1      7d11h
kube-apiserver-cert-syncer-kubeconfig-565   1      4d

 

I used the oc get configmap command with the --output jsonpath option to view the content of the ca-bunder.crt file in the 564 config map (the config map before the issue started). I took the content of the ca-bunder.crt file and placed it in a file named ca-bunder.crt and then used the openssl command to show the certficate details. The certificate in the 564 config map was indeed the certificate that expired.

~]$ oc get configmap kubelet-serving-ca-564 --namespace openshift-kube-apiserver --output jsonpath="{.data.ca-bundle\.crt}" | openssl x509 -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4867864711386769100 (0x438e2129f8de26cc)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=openshift-kube-controller-manager-operator_csr-signer-signer@1709471471
        Validity
            Not Before: Mar 20 01:12:28 2024 GMT
            Not After : Apr 19 01:12:29 2024 GMT <- expired when the issue started
        Subject: CN=kube-csr-signer_@1710897148
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                67:3A:A2:57:2C:18:52:21:04:BC:36:3B:3A:F4:3A:C8:1B:B3:01:7B <- this matches the AKID in the log

 

I created a Must Gather and opened a case with Red Hat.

oc adm must-gather

 

Likewise, the oc get service command can be used to see the kubelet service is in the kube-system namespace.

~]$ oc get service --namespace kube-system
NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                        AGE
kubelet   ClusterIP   None         <none>        10250/TCP,10255/TCP,4194/TCP   2y72d

 

The oc describe service command can be used to see that the kubelet service forwards reqests on the nodes via HTTP, HTTPS and on port 4194 for the cadvisor (Certificate Authority Advisor).

~]$ oc describe service kubelet --namespace kube-system
Name:              kubelet
Namespace:         kube-system
Labels:            app.kubernetes.io/managed-by=prometheus-operator
                   app.kubernetes.io/name=kubelet
                   k8s-app=kubelet
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  RequireDualStack
IP Families:       IPv4,IPv6
IP:                None
IPs:               None
Port:              https-metrics  10250/TCP
TargetPort:        10250/TCP
Endpoints:         10.11.12.13:10250,10.11.12.13:10250,10.29.149.96:10250 + 6 more...
Port:              http-metrics  10255/TCP
TargetPort:        10255/TCP
Endpoints:         10.29.146.118:10255,10.29.148.190:10255,10.29.149.96:10255 + 6 more...
Port:              cadvisor  4194/TCP
TargetPort:        4194/TCP
Endpoints:         10.29.146.118:4194,10.29.148.190:4194,10.29.149.96:4194 + 6 more...
Session Affinity:  None
Events:            <none>

 

You can start one of the nodes that the kubelet service forwards requests onto in debug mode

~]# oc debug node/my-node-5n4fj
Starting pod/my-node-5n4fj-debug ...
sh-4.4#

 

Typically you will first issue the chroot /host command is used to set /host as the root directory because the root file system is mounted to /host in the debug pod.

sh-4.4# chroot /host

 

systemctl can be used to determine if the kubelet service is running.

sh-5.1# systemctl status kubelet
● kubelet.service - Kubernetes Kubelet
     Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; preset: disabled)
    Drop-In: /etc/systemd/system/kubelet.service.d
             └─01-kubens.conf, 10-mco-default-madv.conf, 20-aws-node-name.conf, 20-aws-providerid.conf, 20-logging.conf
     Active: active (running) since Tue 2024-04-23 04:14:54 UTC; 1 day 1h ago

 

The output of the systemctl command may contain events like this.

Jul 27 12:04:58 ip-10-29-108-13 kubenswrapper[2779]: E0727 12:04:58.215777    2779 server.go:336] "Unable to authenticate the request due to an error" err="verifying certificate SN=1870853530484269975151209

 

Here is a one liner that I use to loop through each node and to return the status of the kubelet service.

for node in $(oc get nodes | grep -v ^NAME | awk '{print $1}'); do echo $node; oc debug node/$node -- chroot /host /usr/bin/systemctl status kubelet | grep Active:; done;

 

Which should return something like this. In this sceanrio, I can see that the kubelet has been running since before the certificate expired, meaning the kubelet service was not restarted after the certificate renewed.

infra-node-1
     Active: active (running) since Thu 2024-04-04 16:31:39 UTC; 2 weeks 5 days ago
infra-node-2
     Active: active (running) since Thu 2024-04-04 16:18:48 UTC; 2 weeks 5 days ago
infra-node-3
     Active: active (running) since Thu 2024-04-04 16:26:03 UTC; 2 weeks 5 days ago

master-node-1
     Active: active (running) since Thu 2024-04-04 16:34:01 UTC; 2 weeks 5 days ago
master-node-2
     Active: active (running) since Thu 2024-04-04 16:21:05 UTC; 2 weeks 5 days ago
master-node-3
     Active: active (running) since Thu 2024-04-04 16:27:32 UTC; 2 weeks 5 days ago

worker-node-1
     Active: active (running) since Tue 2024-04-04 16:34:01 UTC; 1 day 1h ago
worker-node-2
     Active: active (running) since Tue 2024-04-04 16:21:05 UTC; 1 day 1h ago
worker-node-3
     Active: active (running) since Tue 2024-04-04 16:27:32 UTC; 1 day 2h ago

 

This one liner can be used to restart the kubelet service in each node, if you want to try restarting the kubelet service to see if this resolves the issue.

for node in $(oc get nodes | grep -v ^NAME | awk '{print $1}'); do echo $node; oc debug node/$node -- chroot /host /usr/bin/systemctl restart kubelet; done;

 

The default directory where for kubelet SSL is /var/lib/kubelet/pki.

sh-5.1# ls -l /var/lib/kubelet/pki
total 32
-rw-------. 1 root root 1183 Feb 29 05:33 kubelet-client-2024-02-29-05-33-41.pem
-rw-------. 1 root root 1183 Mar 15 00:19 kubelet-client-2024-03-15-00-19-13.pem
-rw-------. 1 root root 1183 Mar 30 14:40 kubelet-client-2024-03-30-14-40-24.pem
-rw-------. 1 root root 1183 Apr 13 11:20 kubelet-client-2024-04-13-11-20-57.pem
lrwxrwxrwx. 1 root root   59 Apr 13 11:20 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2024-04-13-11-20-57.pem
-rw-------. 1 root root 1269 Feb 29 05:33 kubelet-server-2024-02-29-05-33-42.pem
-rw-------. 1 root root 1269 Mar 14 12:49 kubelet-server-2024-03-14-12-49-47.pem
-rw-------. 1 root root 1269 Mar 28 21:48 kubelet-server-2024-03-28-21-48-50.pem
-rw-------. 1 root root 1269 Apr 12 22:10 kubelet-server-2024-04-12-22-10-56.pem
lrwxrwxrwx. 1 root root   59 Apr 12 22:10 kubelet-server-current.pem -> /var/lib/kubelet/pki/kubelet-server-2024-04-12-22-10-56.pem

 

The openssl command can be used to display the details of each PEM. I noticed the prior PEM also had the expired certificate.

sh-5.1# openssl x509 -in /var/lib/kubelet/pki/kubelet-client-2024-03-30-14-40-24.pem -text -noout
Certificate:
    Data:
        Issuer: CN = kube-csr-signer_@1712193161
        Validity
            Not Before: Mar 30 14:35:24 2024 GMT
            Not After : Apr 19 01:12:29 2024 GMT <- expired when the issue started
        Subject: O = system:nodes, CN = system:node:ip-10-29-148-190.us-east-2.compute.internal
        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                67:3A:A2:57:2C:18:52:21:04:BC:36:3B:3A:F4:3A:C8:1B:B3:01:7B <- this matches the AKID in the log

 

The which command should return the absolute path to the kubelet CLI.

sh-5.1# which kubelet
/usr/bin/kubelet

 

The kubelet --version command can be used to ensure the kubelet CLI is working as expected and returning stdout.

sh-5.1# kubelet --version
Kubernetes v1.26.13+8f85140

 

The oc adm node-logs command can be used to return the node logs pertaining to kubelet. The logs will be extensive, which is why I redirect the output to a file.

oc adm node-logs --role=master --unit=kubelet >> kubelet_node.log

 

Certificate signed by unknown authority / machine-config degraded


Let’s start a debug node.

oc debug node/my-node

 

And chroot /host.

sh-4.4# chroot /host

 

Let’s use systemctl to look for interesting events with the kubelet systemd service.

systemctl status kubelet | less

 

We I saw this, where the certificate with serial number 123456789123456789123456789123456789123 and AKID 12:34:56:78:90:AB:CD:EF:GH:IJ:KL:MN:OP:QR:ST:UV:WX:YZ:1A:2B failed validation because it was signed by an unknown authority.

Jul 27 13:10:03 ip-10-29-108-13 kubenswrapper[2779]: E0727 13:10:03.686514    2779 server.go:336] "Unable to authenticate the request due to an error" err="verifying certificate SN=123456789123456789123456789123456789123, SKID=, AKID=12:34:56:78:90:AB:CD:EF:GH:IJ:KL:MN:OP:QR:ST:UV:WX:YZ:1A:2B failed: x509: certificate signed by unknown authority"

 

The events in the openshift-monitoring namespace also showed that OpenShiftMonitoringMetricsServerClientCertRequester and OpenShiftMonitoringTelemeterClientCertRequester were rotated.

~]$ oc get events --namespace openshift-monitoring --sort-by='.metadata.creationTimestamp'
LAST SEEN   TYPE      REASON                       OBJECT                                   MESSAGE
78m         Normal    ClientCertificateCreated     deployment/cluster-monitoring-operator   A new client certificate for OpenShiftMonitoringTelemeterClientCertRequester is available
78m         Normal    CSRCreated                   deployment/cluster-monitoring-operator   A csr "system:openshift:openshift-monitoring-8kgvc" is created for OpenShiftMonitoringTelemeterClientCertRequester
78m         Normal    ClientCertificateCreated     deployment/cluster-monitoring-operator   A new client certificate for OpenShiftMonitoringMetricsServerClientCertRequester is available
78m         Normal    CertificateRotationStarted   deployment/cluster-monitoring-operator   The current client certificate for OpenShiftMonitoringMetricsServerClientCertRequester expires in 119h22m27s. Start certificate rotation
78m         Normal    CSRCreated                   deployment/cluster-monitoring-operator   A csr "system:openshift:openshift-monitoring-sgd5k" is created for OpenShiftMonitoringMetricsServerClientCertRequester
78m         Normal    CertificateRotationStarted   deployment/cluster-monitoring-operator   The current client certificate for OpenShiftMonitoringTelemeterClientCertRequester expires in 119h22m27s. Start certificate rotation

 

And the machine-config ClusterOperator was degraded.

~]$ oc get ClusterOperators
NAME                                       VERSION   AVAILABLE   PROGRESSING   DEGRADED   SINCE    MESSAGE
machine-config                             4.20.14   True        False         True       2y44d    Failed to resync 4.20.14 because: bootimage update failed: 3 Degraded MAPI MachineSets | 0 Degraded CAPI MachineSets | 0 CAPI MachineDeployments | Error(s): [error syncing MAPI MachineSet node1: refusing to reconcile machineset node2, hot loop detected. Please opt-out of boot image updates, adjust your machine provisioning workflow to prevent hot loops and opt back in to resume boot image updates, error syncing MAPI MachineSet node3: refusing to reconcile machineset node3, hot loop detected. Please opt-out of boot image updates, adjust your machine provisioning workflow to prevent hot loops and opt back in to resume boot image updates, error syncing MAPI MachineSet node1: refusing to reconcile machineset node1, hot loop detected. Please opt-out of boot image updates, adjust your machine provisioning workflow to prevent hot loops and opt back in to resume boot image updates]

 

In this scenario, the metrics-server-client-certs secret in the openshift-monitoring namespace contains the rotated secret with key ID 12:34:56:78:90:AB:CD:EF:GH:IJ:KL:MN:OP:QR:ST:UV:WX:YZ:1A:2B. Notice the certificate was issued by kube-csr-signer_@1234567890.

~]$ oc get secret metrics-server-client-certs --namespace openshift-monitoring --ooutput jsonpath="{.data.tls\.crt}" | base64 --decode | openssl x509 -text -noout
Certificate:
    Data:
        Issuer: CN = kube-csr-signer_@1234567890
        X509v3 extensions:
            X509v3 Authority Key Identifier:
                keyid:12:34:56:78:90:AB:CD:EF:GH:IJ:KL:MN:OP:QR:ST:UV:WX:YZ:1A:2B

 

The kube-apiserver-client-ca configmap should contain the issue (kube-csr-signer_@1234567890 in this example).

~]$ oc get configmap kube-apiserver-client-ca --namespace openshift-config-managed --output jsonpath='{.data.ca-bundle\.crt}' | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs -noout -text | grep -E "Subject:|Subject Key Identifier" | paste - -
        Subject: CN=kube-csr-signer_@1234567890

 

Ultimately, this was resolved by updating the MachineConfiguration cluster resource to have the following.

spec:
  managedBootImages:
    machineManagers:
    - apiGroup: machine.openshift.io
      resource: machinesets
      selection:
        mode: None

 

And then applying this change to trigger a reconciliation.

oc label machineset machine1 --namespace openshift-machine-api workaround=applied --overwrite 
oc label machineset machine2 --namespace openshift-machine-api workaround=applied --overwrite
oc label machineset machine3 --namespace openshift-machine-api workaround=applied --overwrite

 

Once this was done, the machine-config was no longer degraded, and the KubeletDown alerts stopped.

It is noteworthy that the certificate rotation itself did not cause the Machine Config Operator to become degraded. Instead, two independent events occurred at the same time.

The cluster automatically rotated the monitoring certificates as part of its normal certificate lifecycle. This is expected behavior, and the Authentication and Monitoring components began using the new certificates successfully. At the same time, the Machine Config Operator entered a degraded state due to the Boot Image Management issue. While the MCO was degraded, it was unable to propagate configuration changes to the nodes, including the updated CA bundle required by the kubelet.

As a result, the monitoring components presented certificates signed by the new CA, while the kubelets on the nodes were still using the older CA bundle. Since the updated CA had not yet been propagated, the kubelets rejected the connections with the certificate signed by unknown authority error, which ultimately resulted in the KubeletDown alerts.

Once the Boot Image Management issue was addressed by configuring managedBootImages with mode: None, the MCO recovered and resumed reconciling the pending configuration changes. The updated CA bundle was then propagated to the nodes, allowing the kubelets to trust the new certificates and clearing the alerts.

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter d2945e in the box below so that we can be sure you are a human.