Closed Skaronator closed 3 weeks ago
I have the feeling that the controlPlaneLoadBalancing
implementation is broken. My above issue is still valid. This was done using 3 CP + 3 Worker nodes + 1 HA Proxy VM as Loadbalancer for kube-api.
I now turned off the HA Proxy VM and enabled controlPlaneLoadBalancing
. Everything looked fine until I rebooted CP1. kubectl stopped working for like 2 seconds and then keeps working again, this is expected since keepalived does it thingi.
But now CP1 spams No agent available forever.
While writing this text:
ssh 172.20.4.56 'cat /var/log/syslog | grep "No agent" | wc -l'
525182
ssh 172.20.4.56 'cat /var/log/syslog | grep "No agent" | wc -l'
526645
ssh 172.20.4.56 'cat /var/log/syslog | grep "No agent" | wc -l'
528153
ssh 172.20.4.56 'cat /var/log/syslog | grep "No agent" | wc -l'
597661
ssh 172.20.4.56 'cat /var/log/syslog | grep "No agent" | wc -l'
624413
ssh 172.20.4.56 'cat /var/log/syslog | grep "No agent" | wc -l'
676964
After removing controlPlaneLoadBalancing
and restarting the HAProxy VM everything stabilized again.
Here is our k0sctl.yaml. kubernetes-controller.redacted.de
points to 172.20.4.54. The HAProxy VM uses the same IP.
apiVersion: k0sctl.k0sproject.io/v1beta1
kind: Cluster
metadata:
name: main-cluster
spec:
k0s:
version: 1.30.0+k0s.0
config:
apiVersion: k0sctl.k0sproject.io/v1beta1
kind: Cluster
metadata:
name: main-cluster
spec:
api:
externalAddress: kubernetes-controller.redacted.de
sans:
- kubernetes-controller.redacted.de
extraArgs:
oidc-issuer-url: https://dex.redacted.de
oidc-client-id: kubernetes
oidc-username-claim: preferred_username
oidc-username-prefix: "oidc:"
oidc-groups-claim: groups
oidc-groups-prefix: "oidc:"
network:
podCIDR: 10.244.0.0/16
serviceCIDR: 10.96.0.0/12
controlPlaneLoadBalancing:
enabled: true
type: Keepalived
keepalived:
vrrpInstances:
- virtualIPs: ["172.20.4.54/26"]
authPass: kubemain
virtualRouterID: 72 # just a unique number on the network
telemetry:
enabled: false
hosts:
- ssh:
address: 172.20.4.56
port: 22
user: root
keyPath: ../on-premise-infrastructure/ansible_deploy_key
role: controller
installFlags: &controllerInstallFlags
- --enable-metrics-scraper
- ssh:
address: 172.20.4.57
port: 22
user: root
keyPath: ../on-premise-infrastructure/ansible_deploy_key
role: controller
installFlags: *controllerInstallFlags
- ssh:
address: 172.20.4.58
port: 22
user: root
keyPath: ../on-premise-infrastructure/ansible_deploy_key
role: controller
installFlags: *controllerInstallFlags
- ssh:
address: 172.20.4.59
port: 22
user: root
keyPath: ../on-premise-infrastructure/ansible_deploy_key
role: worker
- ssh:
address: 172.20.4.60
port: 22
user: root
keyPath: ../on-premise-infrastructure/ansible_deploy_key
role: worker
- ssh:
address: 172.20.4.61
port: 22
user: root
keyPath: ../on-premise-infrastructure/ansible_deploy_key
role: worker
The error message comes from konnectivity-server when kube-apiserver attempts to connect to a node, but the node's konnectivity-agent has not yet connected to the controller's konnectivity-server. This is quite normal when a controller is restarted. It takes some time for all worker node agents to reconnect. The number of log lines depends on how often kube-apiserver tries to connect to the non-connected nodes, and is somewhat out of k0s' control, both because k0s doesn't usually initiate node connections through kube-apiserver itself, and because konnectivity reports this as an error each time it happens.
If the problem persists, it means that workers are unable to connect to one or more controllers. Typically, this indicates some sort of network problem. The konnectivity agents rely on the load balancer to eventually provide connections to all controllers. The LB address is used to brute-force open connections until the agent has the desired number of connections to different controller nodes.
From your experiments with CPLB, I'm surprised that the k0s controllers did even start with the configuration you presented. Currently, api.externalAddress
and network.controlPlaneLoadBalancing.enabled: true
are mutually exclusive and should result in an error during startup.
I had a quick look at the logs when bootstrapping a 3/3 cluster locally, and I only got 3 such log statements per controller :thinking:
Our old cluster (over 2 years old) with the latest k0s version had the same behavior. Something is very wrong, but I don't know what.
We just deployed a fresh cluster on 6 fresh Ubuntu 22.04 virtual machines and after rebooting a machine the same issue happened. The main difference: it looks like the fresh cluster fixed itself while the old cluster continue to spam these lines.
The konnectivity Pods on the worker nodes are all running and healthy. No sign of errors there.
Check that the konnectivity-agents are actually using the load balancer:
kubectl -n kube-system get po -l k8s-app=konnectivity-agent --field-selector=status.phase=Running -o=go-template='
{{- range .items -}}
{{- $host := "" -}}
{{- $port := "" -}}
{{- range (index .spec.containers 0).args -}}
{{- $prefix := (printf "%.20s" .) }}
{{- if eq $prefix "--proxy-server-host=" -}}
{{- $host = (slice . 20) -}}
{{- else }}
{{- if eq $prefix "--proxy-server-port=" -}}
{{- $port = (slice . 20) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- printf "%s on %s connects to %s:%s\n" .metadata.name .spec.nodeName $host $port -}}
{{- end -}}
'
This should yield something like
konnectivity-agent-xxxxx on worker-a connects to kubernetes-controller.redacted.de:8132
konnectivity-agent-yyyyy on worker-b connects to kubernetes-controller.redacted.de:8132
konnectivity-agent-zzzzz on worker-c connects to kubernetes-controller.redacted.de:8132
Check if the load balancer is able to provide connections to all three controllers on port 8132. This might be depending on the load-balancing strategy. Round-robin and random should work. Least-connections or similar may be problematic. Try to restart the konnectivity-agent pods and have a look at to how many servers they connect to. This script could be helpful (extracted from the ostests pre-flight checks):
#!/usr/bin/env sh
set -eu
expectedControllers="${1-3}"
expectedWorkers="${2-3}"
countSeenControllers() {
kubectl -n kube-system logs "$1" \
| grep -F '"Start serving"' \
| sed -E 's/.+serverID="([0-9a-f]+)".+/\1/g' \
| sort -u \
| wc -l
}
testKonnectivityPods() {
pods="$(kubectl -n kube-system get po -l k8s-app=konnectivity-agent --field-selector=status.phase=Running -oname)"
seenPods=0
for pod in $pods; do
seenControllers="$(countSeenControllers "$pod")"
echo Pod: "$pod", seen controllers: "$seenControllers" >&2
[ "$seenControllers" -ne "$expectedControllers" ] || seenPods=$((seenPods + 1))
done
echo Seen pods with expected amount of controller connections: $seenPods >&2
[ $seenPods -eq "$expectedWorkers" ]
}
testKonnectivityPods
Thanks, Tom, for the thorough response!
So this issue contains two separate issues: The flooding from konnectivity and the controlPlaneLoadBalancing not working as expected.
The first issue with the flooding is part of the kube-api server, which is outside k0s control.
We use 3 control planes, 3 worker nodes and a single VM using HAProxy. Our HAProxy uses the recommended config from the docs. So I don't think there is anything wrong with our setup. Could you enable the metrics-server on your test cluster? I saw a couple of metrics-server errors as well between the many konnectivity errors. Maybe that's the culprit of the spam.
Using your first command reveals that everything looks as expected. The second command didn't work because the konnectivity pods were already running for too long, but I checked Grafana Loki logs and everything looks fine there as well.
The second issue is quite interesting. I wasn't aware that api.externalAddress
and network.controlPlaneLoadBalancing.enabled: true
are mutually exclusive. The k0s control plane just started successfully with the above posted k0sctl.yaml.
Everything seemed to work just fine, even kubectl can reach the control plane using the virtual IP. After restarting the control plane 1, everything slowly broke down. The VM/Control Plane came back but spammed the log with the konnectivity agent log mentioned above. In our old cluster, we even broke our etcd by repeated reboots to possibly resolve the konnectivity agent issue.
I'll need to retest this setup without defining an api.externalAddress
but this will take a couple of days. We're still a bit busy recovering our primary cluster.
Lastly, error handling and documentation.
As a non-native English speaker, the documentation is written a bit confusing with the double negation:
If VirtualServers are used, the cluster configuration mustn't specify a non-empty spec.api.externalAddress. If only VRRPInstances are specified, a non-empty spec.api.externalAddress may be specified.
Here is a better rewording by ChatGPT:
If you are using VirtualServers, you must leave spec.api.externalAddress empty. If you are using only VRRPInstances, you are allowed to specify a value for spec.api.externalAddress.
It would also be great to have a full example. I never really used keepalived on my own, so I'm still not sure if I should use only VRRPInstances or VRRPInstances + VirtualServers. I'm also not sure if the IP in VRRPInstances and VirtualServers should be the same. Even the "Full example using" at the bottom of the page just uses an "External ip address" placeholder.
This is also not the first time that I deployed an invalid configuration. When you try to google the k0s docs, you often land on an older version of the documentation. (This was more painful back then, today you have a giant reminder on top, which is fantastic.)
I used fields in the config that no longer existed and k0sctl
just applied then without any warning/error beforehand. There should be a better way of validating the configuration. Instead of just yolo it, there should be an error before even touching/shutting down a worker/controlplane node.
Just noticed that I also defined spec.api.externalAddress
and VRRPInstances
at the same time in my homelab for over two weeks and everything works fine so far (which it shouldn't):
https://github.com/Skaronator/homelab/blob/4762154fda1a7e125b717aecc6773c4154976916/k0sctl.yaml
Edit: Just did an upgrade from 1.30.0 to 1.30.1 and it broke my homelab cluster as well. It seems like migrating from a broken to a correct config completely broke it. I just have a single node but I don't use --single
because I plan to add more nodes in the near future.
Looking at the k0s.yaml
on my Node, I can see it defining the etcd storage with the VRRPInstances
IP as peers but there are no peers in my cluster resulting in (I assume) etcd not starting up due to missing peers?
storage:
etcd:
peerAddress: 192.168.0.30
Edit2: Maybe it is not a good idea that peerAddress points to itself when there is just a single node?
Here the logs, they are in reverse (newest on the bottom)
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="respawning in 5s" component=etcd
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=warning msg="Failed to wait for process" component=etcd error="exit status 1"
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"fatal\",\"ts\":\"2024-05-28T21:55:19.078026+0200\",\"caller\":\"etcdmain/etcd.go:204\",\"msg\":\"discovery failed\",\"error\":\"listen tcp 192.168.0.30:2380: bind: cannot assign requested address\",\"stacktrace\":\"go.etcd.io/etcd/server/v3/etcdmain.startEtcdOrProxyV2\\n\\t/etcd/server/etcdmain/etcd.go:204\\ngo.etcd.io/etcd/server/v3/etcdmain.Main\\n\\t/etcd/se>
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"info\",\"ts\":\"2024-05-28T21:55:19.078013+0200\",\"caller\":\"embed/etcd.go:377\",\"msg\":\"closed etcd server\",\"name\":\"node1\",\"data-dir\":\"/var/lib/k0s/etcd\",\"advertise-peer-urls\":[\"https://192.168.0.30:2380\"],\"advertise-client-urls\":[\"https://127.0.0.1:2379\"]}" component=etcd stream=stderr
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"info\",\"ts\":\"2024-05-28T21:55:19.078004+0200\",\"caller\":\"embed/etcd.go:375\",\"msg\":\"closing etcd server\",\"name\":\"node1\",\"data-dir\":\"/var/lib/k0s/etcd\",\"advertise-peer-urls\":[\"https://192.168.0.30:2380\"],\"advertise-client-urls\":[\"https://127.0.0.1:2379\"]}" component=etcd stream=stderr
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"error\",\"ts\":\"2024-05-28T21:55:19.077916+0200\",\"caller\":\"embed/etcd.go:536\",\"msg\":\"creating peer listener failed\",\"error\":\"listen tcp 192.168.0.30:2380: bind: cannot assign requested address\",\"stacktrace\":\"go.etcd.io/etcd/server/v3/embed.configurePeerListeners\\n\\t/etcd/server/embed/etcd.go:536\\ngo.etcd.io/etcd/server/v3/embed.StartEtcd\\n\>
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"info\",\"ts\":\"2024-05-28T21:55:19.077548+0200\",\"caller\":\"embed/etcd.go:494\",\"msg\":\"starting with peer TLS\",\"tls-info\":\"cert = /var/lib/k0s/pki/etcd/peer.crt, key = /var/lib/k0s/pki/etcd/peer.key, client-cert=, client-key=, trusted-ca = /var/lib/k0s/pki/etcd/ca.crt, client-cert-auth = true, crl-file = \",\"cipher-suites\":[\"TLS_ECDHE_ECDSA_WITH_AE>
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"info\",\"ts\":\"2024-05-28T21:55:19.077519+0200\",\"caller\":\"embed/etcd.go:127\",\"msg\":\"configuring peer listeners\",\"listen-peer-urls\":[\"https://192.168.0.30:2380\"]}" component=etcd stream=stderr
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"warn\",\"ts\":\"2024-05-28T21:55:19.077507+0200\",\"caller\":\"embed/config.go:679\",\"msg\":\"Running http and grpc server on single port. This is not recommended for production.\"}" component=etcd stream=stderr
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"info\",\"ts\":\"2024-05-28T21:55:19.077486+0200\",\"caller\":\"etcdmain/etcd.go:116\",\"msg\":\"server has been already initialized\",\"data-dir\":\"/var/lib/k0s/etcd\",\"dir-type\":\"member\"}" component=etcd stream=stderr
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"info\",\"ts\":\"2024-05-28T21:55:19.077416+0200\",\"caller\":\"etcdmain/etcd.go:73\",\"msg\":\"Running: \",\"args\":[\"/var/lib/k0s/bin/etcd\",\"--client-cert-auth=true\",\"--peer-trusted-ca-file=/var/lib/k0s/pki/etcd/ca.crt\",\"--peer-key-file=/var/lib/k0s/pki/etcd/peer.key\",\"--cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_25>
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="{\"level\":\"warn\",\"ts\":\"2024-05-28T21:55:19.077228+0200\",\"caller\":\"embed/config.go:679\",\"msg\":\"Running http and grpc server on single port. This is not recommended for production.\"}" component=etcd stream=stderr
May 28 21:55:19 node1 k0s[479277]: time="2024-05-28 21:55:19" level=info msg="Restarted (22)" component=etcd
HI @Skaronator there are a few issues going on.
As for the last issue we suspect the peerAddress is being filled automatically by k0sctl, we believe we need to manually exclude the IP address. Can you please provide the full k0s.yaml file for each node? We suspect the issue is
So using my homelab cluster for now since its simpler to run/debug stuff.
My k0sctl.yaml looks like this:
apiVersion: k0sctl.k0sproject.io/v1beta1
kind: Cluster
metadata:
name: tardis
spec:
k0s:
version: v1.30.1+k0s.0
config:
spec:
api:
sans:
- 192.168.0.30
network:
podCIDR: 10.244.0.0/16
serviceCIDR: 10.96.0.0/12
controlPlaneLoadBalancing:
enabled: true
type: Keepalived
keepalived:
vrrpInstances:
- virtualIPs: ["192.168.0.30/23"]
authPass: homelab
telemetry:
enabled: false
hosts:
- ssh:
address: 192.168.0.5
user: root
keyPath: ~/.ssh/id_rsa
role: controller+worker
noTaints: true
And results in this config on my one and only host:
# cat /etc/k0s/k0s.yaml
# generated-by-k0sctl 2024-06-12T21:43:50+02:00
apiVersion: k0s.k0sproject.io/v1beta1
kind: ClusterConfig
spec:
api:
address: 192.168.0.30
sans:
- 192.168.0.30
- 192.168.0.5
- 127.0.0.1
network:
controlPlaneLoadBalancing:
enabled: true
keepalived:
vrrpInstances:
- authPass: homelab
virtualIPs:
- 192.168.0.30/23
type: Keepalived
podCIDR: 10.244.0.0/16
serviceCIDR: 10.96.0.0/12
storage:
etcd:
peerAddress: 192.168.0.30
telemetry:
enabled: false
As you can see, the storage.etcd.peerAddress
points to the CPLB. The VIP is not reachable since keepalived is running in the same systemd job which just crashed because etcd cannot reach the peers?
I know CPLB is not really useful with a single node, but I think this is the exact reason why our prod cluster just crashed. It rotated (maybe too quickly) all control plane nodes resulting in not available etcd peers, resulting in crashing the systemd service and just shooting itself in the foot.
@Skaronator Right, so k0s now picks up the CPLB address as etcd peering mistakenly. This happens because in your case the CPLB/keepalived interface happens to be found before the real interfaces when k0s is doing defaulting for the config.
In k0sctl yaml, you can add spec.hosts[*].privateInterface
to specify which interface should be picked up for etcd peering.
There's a fix in flight for this in #4582 which will make k0s itself to ignore CPLB address from defaulting.
Alright. This fixed it, and the real fix is already included in the next k0s release. I'm going to close this, since the konnectivity agent logs are outside k0s control.
Thanks guys!
Before creating an issue, make sure you've checked the following:
Platform
Version
v1.30.0+k0s.0
Sysinfo
`k0s sysinfo`
What happened?
I have a 3 control plane and 3 worker node cluster. I just installed it freshly and I rebooted a control plane to verify that HA correctly works.
When doing so, the following log one is spammed when the control plane freshly starts:
Look at the timestamp, it's like 1000 lines per second.
In fact, when counting them, it's 733 per second!
Looking across the 3 control planes:
Its over a million log lines and this cluster exists for like 5 minutes and I restarted control plane 1 and 2 once.
Steps to reproduce
Expected behavior
Only create 1 log line per second at best.
Actual behavior
Creates 700 log lines per second.
Screenshots and logs
No response
Additional context
Our old cluster (over 2 years old) with the latest k0s version had the same behavior. Something is very wrong, but I don't know what.
We just deployed a fresh cluster on 6 fresh Ubuntu 22.04 virtual machines and after rebooting a machine the same issue happened. The main difference: it looks like the fresh cluster fixed itself while the old cluster continue to spam these lines.
The konnectivity Pods on the worker nodes are all running and healthy. No sign of errors there.