< Back Post-Image

Make metrics-server work out of the box with kubeadm

If you try to install metrics-server into a fresh, up to date, kubeadm cluster, it will fail with this error in the logs:

E0908 15:28:39.751310       1 scraper.go:139] "Failed to scrape node" err="Get \"\": x509: cannot validate certificate for because it doesn't contain any IP SANs" node="scw-sharp-cray"

metrics-server tries to reach the Kubelet API but the TLS connection fails because node’s IP is not part of the certificate SAN.

Why ? Because Kubelet certificates are self-signed, they are not signed by Kubernetes CA. And indeed, InternalIP is not part of the certificate SAN.

metrics-server can be configured to not use the InternalIP but rather the node hostname, or its ExternalIP. The argument --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname allows you to decide which one it should use. But this won’t solve our problem because if you use Hostname as first choice, you end up with a DNS resolution problem because your pod doesn’t know who “scw-sharp-cray” (the name of my node) is. Actually, even if the DNS resolution works, you will still face our first issue, because even the hostname of our node is not a certificate SAN…

Actually, InternalIP is a good choice. And the way to fix this is to signed Kubelet certificate with the APIServer.

From scratch

From scratch, you have to use serverTLSBootstrap: true in your kubeadm configfile.

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true

That’s it.

Running cluster

You basically have two things to do:

  • Edit the kubelet-config ConfigMap in the kube-system namespace. Edit the KubeletConfiguration document to set serverTLSBootstrap: true.
  • On each node, add the serverTLSBootstrap: true field in /var/lib/kubelet/config.yaml and restart the kubelet

Signed certificate

In each case, Kubelet will generate a CSR and submit it to the APIServer. You need to approve the CSR for each Kubelet on your cluster.

$ kubectl get csr
NAME        AGE     SIGNERNAME                                    REQUESTOR                         REQUESTEDDURATION   CONDITION
csr-7xhl8   4m15s   kubernetes.io/kube-apiserver-client-kubelet   system:node:scw-practical-kilby   <none>              Approved,Issued
csr-h24sp   4m4s    kubernetes.io/kubelet-serving                 system:node:scw-practical-kilby   <none>              Pending
$ kubectl certificate approve csr-h24sp
$ kubectl top node
NAME                  CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
scw-sharp-cray        271m         9%     1344Mi          35%

By default, these serving certificate will expire after one year. So, in one year, a new CSR will be generated by Kubelet and you will need to approve it, this is not automagic.

Rubber-stamp is an auto-approver operator for the Kubelet.

This article is part of a new take on articles where we focus on various power user issues we encounter and try to sum up our investigation (basicaly we read Github Issues and stackoverflow for you).

Don’t hesitate to reach us on Github or through our website if you need any help navigating the Kubernetes ecosystem for your projects.