We saw an issue where a Windows CNI add call was killed mid-process (right after it invoked the CreateNetwork HNS call, but before that call returned to CNI), so HNS proceeded (as it was commanded) to create an HNS network for a delegated NIC
Before that CreateNetwork call finished in HNS, CNI also then got a DELETE call for the same pod (since containerd automatically invoked the CNI delete after the CNI add failed)
The CNI DELETE call basically did nothing, since CNI had not yet written anything to state since the ADD call was killed mid-process
After the CNI DELETE call finished, the background HNS createNetwork call completed, so then there was a "leaked" HNS Network (HNS Network exists on the VM, but CNI has no idea about it and did not save it to any state)
Sequence of events:
- CNI ADD call starts
- CNI issues
CreateNetwork request
|
hnsResponse, err = Hnsv2.CreateNetwork(hcnNetwork) |
- HNS begins creating the network for the delegated nic (1-1 mapping)
- CNI ADD call is killed! The last log line we see is "
Creating hcn network", nothing after that (not ADD command completed with error, nothing)
- CNI DELETE call starts
- CNI does not issue delete on HNS Network (because it was not able to save it to any state)
- CNI DELETE call ends (basically did nothing)
- HNS createNetwork call succeeds in the background (but CNI knows nothing about this network, because CNI was killed before it could save the network to any state)
- There is now a drifting HNS Network on the VM, holding the MAC up
- New CNI ADD call comes in for the same MAC
- CNI cannot find the MAC, because it is now tied up to the old, leaked HNS network
One natural thought
Would be to say "Hey, let's store the HNS network that we are about to create, so that in case CNI add call gets killed mid-HNS network create, the CNI Delete will clean it up"
- Wrong, HNS does not have deterministic behavior if we issue an HNS network delete, on a HNS Network that is in progress of being created. It is then a race, as to which call finishes (could be still that HNS network delete call finishes first, and then the HNS network create call finishes last, so we still have a leaked HNS network)
Better solution
- During a CNI Add (for windows, delegated nic), first, do an audit of all the HNS Networks on the VM
- See if any HNS Networks don't belong to any pods (in which case, they have been leaked)
- Delete the network
- Then proceed with CNI ADD call as normal
|
masterIfName := plugin.findMasterInterface(opt) |
We saw an issue where a Windows CNI add call was killed mid-process (right after it invoked the
CreateNetworkHNS call, but before that call returned to CNI), so HNS proceeded (as it was commanded) to create an HNS network for a delegated NICBefore that
CreateNetworkcall finished in HNS, CNI also then got a DELETE call for the same pod (since containerd automatically invoked the CNI delete after the CNI add failed)The CNI DELETE call basically did nothing, since CNI had not yet written anything to state since the ADD call was killed mid-process
After the CNI DELETE call finished, the background HNS createNetwork call completed, so then there was a "leaked" HNS Network (HNS Network exists on the VM, but CNI has no idea about it and did not save it to any state)
Sequence of events:
CreateNetworkrequestazure-container-networking/network/network_windows.go
Line 362 in 20d09ab
Creating hcn network", nothing after that (notADD command completed with error, nothing)One natural thought
Would be to say "Hey, let's store the HNS network that we are about to create, so that in case CNI add call gets killed mid-HNS network create, the CNI Delete will clean it up"
Better solution
azure-container-networking/cni/network/network.go
Line 731 in 20d09ab