tl;dr - Adding a rebuilt node with a different IP under an already-used node name did not sit well with Calico in my setup. I could see the problem (mismatched IP address information in Node
resources), but I couldn’t fix it – ultimately I gave the new node a new never-before-used name (i.e.. worker-< n+1 >
instead of worker-2
).
Recently during an expansion and some shifting of my k8s cluster, I found out (the hard way, of course) that if you add a new node that has the same name (let’s say worker-1
or worker-1.your-k8s-cluster
), but a different IP address from one that previously existed, It’s possible that Calico may get a bit confused and fail to set up networking between other nodes and that node. As the CRDs that are created by Calicio can persist in a cluster, I’m fairly certain that was the problem I ran into.
In general, ifyou’re going to add a new node that has the same name but a different IP address as an old node, you’re going to want to check and see if Calico hasn’t become misconfigured when you do.
In general, none of the workloads scheduled on the new node will have properly working network access via Calico.
A specific example is Pod
s scheduled on the new node not being able to successfully query DNS (internal or external) – they won’t be able to reach the CoreDNS (kubedns
) instance as no inter-node traffic will be working work.
Throughout this diagnosis we’re going to assume that my Node
(the Kubernetes Node resource) is called node-2.eu-central-1
. This isn’t the Node
this issue happened on, but it will serve for demonstrative purposes.
calico-node
DaemonSet output for the new nodeTo make sure that you’ve fixed the issue, you’re going to want to check the logs in the calico-node
for the new node. Check the kube-system
namespace for pods owned by the calico-node
DaemonSet:
$ k get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-78659cc5bf-mn58n 1/1 Running 6 10d
calico-node-4qs6d 1/1 Running 7 10d
calico-node-vbc9k 1/1 Running 0 10d
... more pods ...
The logs in there should look much better and contain no errors
Here is what the logs for calico-node
look like on a node (I can’t remember if this was the node or another node) that is having this issue (I’ve added some spaces for emphasis):
2021-09-20 14:29:38.733 [INFO][80] felix/conntrack.go 90: Removing conntrack flows ip=10.244.128.10
2021-09-20 14:29:38.740 [INFO][80] felix/status_combiner.go 81: Endpoint up for at least one IP version id=proto.WorkloadEndpointID{OrchestratorId:"k8s", WorkloadId:"email/haraka-4nzzc", EndpointId:"eth0"} ipVersion=0x4 status="up"
2021-09-20 14:29:38.740 [INFO][80] felix/status_combiner.go 98: Reporting combined status. id=proto.WorkloadEndpointID{OrchestratorId:"k8s", WorkloadId:"email/haraka-4nzzc", EndpointId:"eth0"} status="up"
2021-09-20 14:29:38.937 [INFO][80] felix/int_dataplane.go 1430: Received *proto.WireguardEndpointUpdate update from calculation graph msg=hostname:"node-4.eu-central-1" public_key:"<redacted>" interface_ipv4_addr:"10.244.128.1"
2021-09-20 14:29:39.046 [INFO][80] felix/wireguard.go 570: Public key out of sync or updated ourPublicKey=<redacted>
2021-09-20 14:29:39.067 [INFO][78] tunnel-ip-allocator/node.go 88: Error updating Node resource error=Operation cannot be fulfilled on nodes "node-4.eu-central-1": the object has been modified; please apply your changes to the latest version and try again
2021-09-20 14:29:39.067 [INFO][78] tunnel-ip-allocator/allocateip.go 563: Error updating node node-4.eu-central-1: %!s(<nil>). Retrying. type="ipipTunnelAddress"
2021-09-20 14:29:39.244 [WARNING][80] felix/vxlan_mgr.go 354: Failed configure VXLAN tunnel device, retrying... error=Unable to find parent interface with address YYY.YYY.YYY.YYY
2021-09-20 14:29:39.276 [INFO][80] felix/int_dataplane.go 1430: Received *proto.WireguardEndpointUpdate update from calculation graph msg=hostname:"node-4.eu-central-1" public_key:"<redacted>" interface_ipv4_addr:"10.244.128.1"
2021-09-20 14:29:39.301 [INFO][80] felix/int_dataplane.go 1430: Received *proto.WireguardEndpointUpdate update from calculation graph msg=hostname:"node-4.eu-central-1" public_key:"<redacted>" interface_ipv4_addr:"10.244.128.1"
2021-09-20 14:29:39.363 [INFO][80] felix/wireguard.go 570: Public key out of sync or updated ourPublicKey=<redacted>
It’s a surprisingly small indicator, but that WARNING
is the smoking gun – not being able to find the node with the OLD IP address (YYY.YYY.YYY.YYY
) means the VXLAN tunnel couldn’t be built, and cluster networking didn’t quite work for the given node.
kubectl
To futher confirm if you’re seeing the specific issue I was, you’re going to want to cross-reference the Node information maintained by two systems – Kubernetes itself and Calico. Check out the Kubernetes native Node
object:
$ kubectl get node <the new node> -o yaml
What you want to confirm is that status.addresses[0]
(the type
is likely InternalIP
). Here’s what that looks like:
apiVersion: v1
kind: Node
metadata:
annotations:
# ... a bunch of annotations ...
projectcalico.org/IPv4Address: XXX.XXX.XXX.XXX # redacted
projectcalico.org/IPv4VXLANTunnelAddr: 10.244.192.64
projectcalico.org/IPv4WireguardInterfaceAddr: 10.244.192.65
projectcalico.org/WireguardPublicKey: <redacted>
creationTimestamp: "2021-10-24T14:33:08Z"
labels:
# ... a bunch of labels ...
kubernetes.io/arch: amd64
kubernetes.io/hostname: node-2.eu-central-1
kubernetes.io/os: linux
name: node-2.eu-central-1
resourceVersion: "141344346"
uid: 84a2ccb4-3cee-4758-8da0-5db6601cc5c4
spec:
podCIDR: 10.244.6.0/24
podCIDRs:
- 10.244.6.0/24
status:
addresses:
- address: XXX.XXX.XXX.XXX # here's the IP address (should match the calico annotation above)!
type: InternalIP
- address: node-2.eu-central-1 # here's your node's name
type: Hostname
# ... lots more information
Note that a few things – you’ve got your node status, what calico thinks your node’s IPv4 Address is, where it’s doing VXLAN tunneling, etc.
calicoctl
On the Calico side, we’re going to take a look at Calico’s Node
resource and see what it says for the node in question. Download calicoctl
(I prefer to use it as a kubectl plugin) and use it to check your node to confirm the information:
$ calicoctl get node <your node name> -o yaml
If you have the problem I’m describing in this post, you’ll see that the information output by calicoctl
outputs are the old node’s information:
apiVersion: projectcalico.org/v3 # <----- this is a *calico* Node resource
kind: Node
metadata:
annotations:
projectcalico.org/kube-labels: '{.... JSON object holding node labels ...}' # redacted
creationTimestamp: "2021-10-24T14:33:08Z"
labels:
# ... labels for the node ...
kubernetes.io/hostname: node-2.eu-central-1
kubernetes.io/os: linux
name: node-2.eu-central-1
resourceVersion: "141344346"
uid: 84a2ccb4-3cee-4758-8da0-5db6601cc5c4
spec:
addresses:
- address: XXX.XXX.XXX.XXX/32
- address: XXX.XXX.XXX.XXX
bgp:
ipv4Address: XXX.XXX.XXX.XXX/32
ipv4VXLANTunnelAddr: 10.244.192.64
orchRefs:
- nodeName: node-2.eu-central-1
orchestrator: k8s
wireguard:
interfaceIPv4Address: 10.244.192.65
status:
podCIDRs:
- 10.244.6.0/24
wireguardPublicKey: <redacted> # yeah it's the public key but anyway
Drastically less information here, but much easier to parse/understand! This resoruce shows us what Calico thinks are the addresses this node can be accessed at, the address it uses for BGP, wireguard interface IP address, etc.
Here’s the crux of the diagnosis – I was finding that the Calico-created-and-managed Node
Resource was differing from the Kubernetes-created-and-managed Node
resource. They were not agreeing on IP address and this meant that commmunication between other nodes and this node just wasn’t working.
Here are some things I tried:
--node-ip
kubelet worker arg)
/etc/hosts
All of those things sound like great ideas, and I tried them, but in the end I just couldn’t get Kubernetes and Calico to agree on the IP for the stale-named new node.
Ultimately, none of the things I tried worked, and I simply rebuilt & renamed the node and re-added it to the cluster.
Just a reminder – the fix that worked for me was changing the node name. It’s possible that I was just barely off with some of the other remediations, but I didn’t want to dig too far deeper into Calico’s code to find exactly what was causing the wrong values to be propagated, when the situation is fairly rare and the remediation is so easy.
To make sure that you’ve fixed the issue, you’re going to want to:
calico-node
for the new node – you should have no warnings or errors (obviously, especially failures to configure VXLAN tunnels)$ kubectl run -it --rm \
test \
--image=alpine \
--overrides='{"spec":{"nodeSelector":{"kubernetes.io/hostname":"<node name>"}}}'
This outline has been sitting around for a while but I’m glad I was able to write it up finally – whenever I add new nodes to a cluster or rebuild nodes I always give them a new ID at this point. It’s not quite the same as completely treating them as cattle (I do still write down the names and the mappings are fairly static/hand-updated), but it at least helps me avoid this issue.
Thanks for reading, hopefully this helps someone out there. If you’ve found that you were able to get past this problem and correctly configure Calico (or know where I went wrong in trying to reconfigure it), please drop me a line!