From bb9ed4b770bc435d00e41a395661b5fe9137c0d4 Mon Sep 17 00:00:00 2001 From: wenqian Date: Mon, 17 Jun 2024 17:18:39 +0800 Subject: [PATCH] feat(pool): ensure secondary ip exists before giving it to Pod (#38) * feat(pool): ensure ip healthy before giving it to pod --- Makefile | 4 ++++ cmd/release-ip/main.go | 52 ++++++++++++++++++++++++++++++++++++++++++ pkg/ipamd/pool.go | 31 ++++++++++++++++++------- pkg/ipamd/uapi.go | 6 +++-- 4 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 cmd/release-ip/main.go diff --git a/Makefile b/Makefile index 9796fd3..76521f5 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,10 @@ vip-controller: $(DOCKER_CMD) push $(VIP_CONTROLLER_IMAGE) @echo "Build done: $(VIP_CONTROLLER_IMAGE)" +.PHONY: release-ip +release-ip: + CGO_ENABLED=0 GOOS="linux" GOARCH="amd64" go build -o ./bin/release-ip ./cmd/release-ip + .PHONY: fmt fmt: @command -v goimports >/dev/null || { echo "ERROR: goimports not installed"; exit 1; } diff --git a/cmd/release-ip/main.go b/cmd/release-ip/main.go new file mode 100644 index 0000000..b4b8c78 --- /dev/null +++ b/cmd/release-ip/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "os" + + "github.com/ucloud/ucloud-sdk-go/ucloud" + "github.com/ucloud/uk8s-cni-vpc/pkg/iputils" + "github.com/ucloud/uk8s-cni-vpc/pkg/uapi" +) + +func releaseIp(ip string) error { + macAddr, err := iputils.GetNodeMacAddress("") + if err != nil { + return fmt.Errorf("failed to get mac addr: %w", err) + } + + uapiClient, err := uapi.NewClient() + if err != nil { + return fmt.Errorf("failed to init uapi client: %w", err) + } + + vpcClient, err := uapiClient.VPCClient() + if err != nil { + return fmt.Errorf("failed to init vpc client: %w", err) + } + + req := vpcClient.NewDeleteSecondaryIpRequest() + req.VPCId = ucloud.String(uapiClient.VPCID()) + req.SubnetId = ucloud.String(uapiClient.SubnetID()) + req.Ip = ucloud.String(ip) + req.Mac = ucloud.String(macAddr) + + _, err = vpcClient.DeleteSecondaryIp(req) + return err +} + +func main() { + if len(os.Args) < 2 { + fmt.Println("Usage: release-ip ") + os.Exit(1) + } + + ip := os.Args[1] + err := releaseIp(ip) + if err != nil { + fmt.Printf("failed to release ip %v: %v\n", ip, err) + os.Exit(1) + } + + fmt.Printf("released ip %v successfully\n", ip) +} diff --git a/pkg/ipamd/pool.go b/pkg/ipamd/pool.go index d8239b7..842dda2 100644 --- a/pkg/ipamd/pool.go +++ b/pkg/ipamd/pool.go @@ -122,12 +122,12 @@ func (s *ipamServer) getPodIp(r *rpc.AddPodNetworkRequest) (*rpc.PodNetwork, err podName := r.GetPodName() podNS := r.GetPodNamespace() sandboxId := r.GetSandboxID() - enable, pod, err := s.podEnableStaticIP(podName, podNS) + enableStatic, pod, err := s.podEnableStaticIP(podName, podNS) if err != nil { return nil, err } var pn *rpc.PodNetwork - if enable { + if enableStatic { pn, err = s.assignStaticPodIP(pod, sandboxId) } else { pn, err = s.assignPodIP() @@ -135,22 +135,37 @@ func (s *ipamServer) getPodIp(r *rpc.AddPodNetworkRequest) (*rpc.PodNetwork, err if err != nil { return nil, err } + + ulog.Infof("Check IP %s status in VPC", pn.VPCIP) + // In some cases, the IP is deleted in VPC but still remain in the pool. If we give it to + // the Pod, the Pod network will be unavailable. + // So this check must be done before we returning IP. If the IP does not exist, returns + // error to make kubelet retries to get another one. + ok, err := s.checkSecondaryIpExist(pn.VPCIP, s.hostMacAddr) + if err != nil { + if !enableStatic { + s.putIpToPool(pn) + } + return nil, fmt.Errorf("check ip %v status in vpc error: %v", pn.VPCIP, err) + } + if !ok { + return nil, fmt.Errorf("ip %v does not exist on current node, we will try to use another one", pn.VPCIP) + } + if !pn.Recycled && pn.VPCIP != "" { // We need to detect IP conflict before using it. // See: https://www.rfc-editor.org/rfc/rfc5227 err = s.checkIPConflict(pn.VPCIP) if err != nil { ulog.Errorf("Detect ip conflict for %s error: %v, we will release it", pn.VPCIP, err) - err = s.uapiDeleteSecondaryIp(pn.VPCIP) - if err != nil { - ulog.Errorf("Release ip %s after conflict error: %v", pn.VPCIP, err) - return nil, err + delErr := s.uapiDeleteSecondaryIp(pn.VPCIP) + if delErr != nil { + ulog.Errorf("Release ip %s after conflict error: %v", pn.VPCIP, delErr) } return nil, err } - } else { - ulog.Infof("IP %s is recycled, no need to detect conflict", pn.VPCIP) } + return pn, nil } diff --git a/pkg/ipamd/uapi.go b/pkg/ipamd/uapi.go index 9938f07..c36d708 100644 --- a/pkg/ipamd/uapi.go +++ b/pkg/ipamd/uapi.go @@ -323,8 +323,10 @@ func (s *ipamServer) checkSecondaryIpExist(ip, mac string) (bool, error) { ulog.Errorf("DescribeSecondaryIp %s error: %v, request id %s", ip, err, resp.GetRequestUUID()) return false, err } - if len(resp.DataSet) > 0 { - return true, nil + for _, data := range resp.DataSet { + if data.Ip == ip { + return true, nil + } } return false, nil }