阿里云CCM源碼分析

阿里云Cloud-Provider

系開源項(xiàng)目铐炫,見github

主要提供Service的LoadBalance功能

源碼解析

源碼相對比較簡單逛裤,就是一個(gè)簡單的自定義控制器笛求,默認(rèn)包括了這幾個(gè)控制器:

  • 節(jié)點(diǎn)控制器:負(fù)責(zé)維護(hù)節(jié)點(diǎn)信息

  • 路由控制器:負(fù)責(zé)添加POD網(wǎng)段到VPC路由表

  • Service控制器:負(fù)責(zé)申請/釋放LoadBalance類型Service的負(fù)載均衡器

  • NLB控制器:類似CLB碍庵,不過控制的是網(wǎng)絡(luò)負(fù)載均衡瞬沦,對應(yīng)阿里云文檔

節(jié)點(diǎn)控制器

當(dāng)使用云廠商的cloud-provider時(shí)醒颖,Kubelet 必須配置啟動參數(shù)--cloud-provider=external

設(shè)置后kubelet會給節(jié)點(diǎn)添加污點(diǎn)朱躺,參考kubelet代碼

if kl.externalCloudProvider {
        taint := v1.Taint{
            Key:    cloudproviderapi.TaintExternalCloudProvider,
            Value:  "true",
            Effect: v1.TaintEffectNoSchedule,
        }

        nodeTaints = append(nodeTaints, taint)
    }

而這里的節(jié)點(diǎn)控制器就是為了從云廠商獲取節(jié)點(diǎn)IP刁赖、狀態(tài)等信息,然后去除這個(gè)污點(diǎn)长搀,使節(jié)點(diǎn)可用

func (m *ReconcileNode) syncCloudNode(node *corev1.Node) error {
        var cloudTaint *v1.Taint
    for _, taint := range taints {
        if taint.Key == api.TaintExternalCloudProvider {
            cloudTaint = &taint
        }
    }
    if cloudTaint == nil {
        klog.V(5).Infof("node %s is registered without cloud taint. return ok", node.Name)
        return nil
    }


    err := m.doAddCloudNode(node)
    
    return nil
}

這里可用看到如果污點(diǎn)已經(jīng)沒了宇弛,則節(jié)點(diǎn)控制器就不再處理這個(gè)節(jié)點(diǎn)了;否則就需要進(jìn)行初始化了

  • 首先查找這個(gè)節(jié)點(diǎn)對應(yīng)的ECS
func findCloudECS(ins prvd.IInstance, node *v1.Node) (*prvd.NodeAttribute, error) {
    if node.Spec.ProviderID != "" {
        return findCloudECSById(ins, node)
    } else {
        return findCloudECSByIp(ins, node)
    }
}

其中providerId的格式是cn-hangzhou.i-v98dklsmnxkkgiiil7:即REGION.NODEID

如果指定了providerId源请,則會查詢r(jià)egion下是否存在這個(gè)nodeid對應(yīng)的ECS

如果沒有指定枪芒,則會查詢VPC下這個(gè)節(jié)點(diǎn)的內(nèi)網(wǎng)IP對應(yīng)的ECS

var internalIP []string
    for _, addr := range node.Status.Addresses {
        if addr.Type == v1.NodeInternalIP {
            internalIP = append(internalIP, addr.Address)
        }
    }
  • 找到ECS后,就能更新節(jié)點(diǎn)信息了谁尸,主要是更新節(jié)點(diǎn)的標(biāo)簽信息和污點(diǎn)信息
func setFields(node *v1.Node, ins *prvd.NodeAttribute, cfgRoute bool) {

    var modifiers []nodeModifier
    if ins.InstanceType != "" {
        modify := func(n *v1.Node) {
            n.Labels["beta.kubernetes.io/instance-type"] = ins.InstanceType
            n.Labels["node.kubernetes.io/instance-type"] = ins.InstanceType
        }
        modifiers = append(modifiers, modify)
    }

    if ins.Zone != "" {
        modify := func(n *v1.Node) {
            n.Labels["failure-domain.beta.kubernetes.io/zone"] = ins.Zone
            n.Labels["topology.kubernetes.io/zone"] = ins.Zone
        }
        modifiers = append(modifiers, modify)
    }

    if ins.Region != "" {
        modify := func(n *v1.Node) {
            n.Labels["failure-domain.beta.kubernetes.io/region"] = ins.Region
            n.Labels["topology.kubernetes.io/region"] = ins.Region
        }
        modifiers = append(modifiers, modify)
    }

    if node.Spec.ProviderID == "" && ins.InstanceID != "" {
        prvdId := fmt.Sprintf("%s.%s", ins.Region, ins.InstanceID)
        modify := func(n *v1.Node) {
            n.Spec.ProviderID = prvdId
        }
        modifiers = append(modifiers, modify)
    }

    modifiers = append(modifiers, removeCloudTaints)

    for _, modify := range modifiers {
        modify(node)
    }
}

主要包括這些標(biāo)簽舅踪,可以看到主要就是ECS對應(yīng)的信息

beta.kubernetes.io/instance-type
node.kubernetes.io/instance-type
failure-domain.beta.kubernetes.io/zone
topology.kubernetes.io/zone
failure-domain.beta.kubernetes.io/region
topology.kubernetes.io/region

然后移除污點(diǎn)、并且設(shè)置node.Spec.ProviderIDREGION.NODEID

  • 最后再更新節(jié)點(diǎn)的status字段良蛮,主要是設(shè)置status.address字段
func findAddress(instance *ecs.Instance) []v1.NodeAddress {
    var addrs []v1.NodeAddress

    if len(instance.PublicIpAddress.IpAddress) > 0 {
        for _, ipaddr := range instance.PublicIpAddress.IpAddress {
            addrs = append(addrs, v1.NodeAddress{Type: v1.NodeExternalIP, Address: ipaddr})
        }
    }

    if instance.EipAddress.IpAddress != "" {
        addrs = append(addrs, v1.NodeAddress{Type: v1.NodeExternalIP, Address: instance.EipAddress.IpAddress})
    }

    if len(instance.InnerIpAddress.IpAddress) > 0 {
        for _, ipaddr := range instance.InnerIpAddress.IpAddress {
            addrs = append(addrs, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ipaddr})
        }
    }

    if len(instance.VpcAttributes.PrivateIpAddress.IpAddress) > 0 {
        for _, ipaddr := range instance.VpcAttributes.PrivateIpAddress.IpAddress {
            addrs = append(addrs, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ipaddr})
        }
    }

    return addrs
}

這里會統(tǒng)計(jì)ECS上的公網(wǎng)抽碌、內(nèi)網(wǎng)IP;其中公網(wǎng)IP作為status.address中的ExternalIP决瞳,內(nèi)網(wǎng)IP作為status.address中的InternalIP

然后再拼接kubelet默認(rèn)為節(jié)點(diǎn)設(shè)置的hostname货徙,作為status.address中的Hostname

func setHostnameAddress(node *v1.Node, addrs []v1.NodeAddress) []v1.NodeAddress {
    // Check if a hostname address exists in the cloud provided addresses
    hostnameExists := false
    for i := range addrs {
        if addrs[i].Type == v1.NodeHostName {
            hostnameExists = true
        }
    }
    // If hostname was not present in cloud provided addresses, use the hostname
    // from the existing node (populated by kubelet)
    if !hostnameExists {
        for _, addr := range node.Status.Addresses {
            if addr.Type == v1.NodeHostName {
                addrs = append(addrs, addr)
            }
        }
    }
    return addrs
}

拼接完成后左权,使用patch接口將信息更新到節(jié)點(diǎn)上

diff := func(copy runtime.Object) (client.Object, error) {
            nins := copy.(*corev1.Node)
            nins.Status.Addresses = cloudNode.Addresses
            return nins, nil
        }
        err := helper.PatchM(m.client, node, diff, helper.PatchStatus)

路由控制器

路由控制器主要是為了解決POD跨主訪問的問題

通過cloud-provider的配置參數(shù)啟動,默認(rèn)就是會配置路由信息

fs.BoolVar(&cfg.ConfigureCloudRoutes, flagConfigureCloudRoutes, true, "Should CIDRs allocated by allocate-node-cidrs be configured on the cloud provider.")

配置的是節(jié)點(diǎn)的POD CIDR

func getIPv4RouteForNode(node *v1.Node) (*net.IPNet, string, error) {
    var (
        ipv4CIDR    *net.IPNet
        ipv4CIDRStr string
        err         error
    )
    for _, podCidr := range append(node.Spec.PodCIDRs, node.Spec.PodCIDR) {
        if podCidr != "" {
            _, ipv4CIDR, err = net.ParseCIDR(podCidr)
            
            ipv4CIDRStr = ipv4CIDR.String()
            if len(ipv4CIDR.Mask) == net.IPv4len {
                ipv4CIDRStr = ipv4CIDR.String()
                break
            }
        }
    }
    return ipv4CIDR, ipv4CIDRStr, nil
}

然后獲取VPC路由表痴颊,vpc id可以直接通過阿里云的metadata服務(wù)器獲取赏迟,參考terway系列文章中的metadata獲取

func getRouteTables(ctx context.Context, providerIns prvd.Provider) ([]string, error) {
    vpcId, err := providerIns.VpcID()
    
    tables, err := providerIns.ListRouteTables(ctx, vpcId)
    
    if len(tables) > 1 {
        return nil, fmt.Errorf("multiple route tables found by vpc id[%s], length(tables)=%d", ctrlCfg.CloudCFG.Global.VpcID, len(tables))
    }
    if len(tables) == 0 {
        return nil, fmt.Errorf("no route tables found by vpc id[%s]", ctrlCfg.CloudCFG.Global.VpcID)
    }
    return tables, nil
}

最后就是添加POD CIDR到路由表上

func (r *VPCProvider) CreateRoute(
    ctx context.Context, table string, provideID string, destinationCIDR string,
) (*model.Route, error) {
    createRouteEntryRequest := vpc.CreateCreateRouteEntryRequest()
    createRouteEntryRequest.RouteTableId = table
    createRouteEntryRequest.DestinationCidrBlock = destinationCIDR
    createRouteEntryRequest.NextHopType = model.RouteNextHopTypeInstance
    _, instance, err := util.NodeFromProviderID(provideID)
    if err != nil {
        return nil, fmt.Errorf("invalid provide id: %v, err: %v", provideID, err)
    }
    createRouteEntryRequest.NextHopId = instance
    _, err = r.auth.VPC.CreateRouteEntry(createRouteEntryRequest)
    if err != nil {
        return nil, fmt.Errorf("error create route entry for %s, %s, error: %v", provideID, destinationCIDR, err)
    }
    return &model.Route{
        Name:            fmt.Sprintf("%s-%s", provideID, destinationCIDR),
        DestinationCIDR: destinationCIDR,
        ProviderId:      provideID,
    }, nil
}

可以看到這里的路由信息:目標(biāo)網(wǎng)段是POD CIDR的,設(shè)置下一跳為當(dāng)前ECS的nodeid蠢棱,即所有到POD CIDR的數(shù)據(jù)包下一跳都是當(dāng)前ECS锌杀,這樣就能打通POD跨主訪問的網(wǎng)絡(luò)了,都是通過VPC路由來實(shí)現(xiàn)的

Service控制器

Service是主要控制對象泻仙,而且支持的注解很多糕再,參考阿里云CLB文檔

下面主要分兩個(gè)主要流程分析下

創(chuàng)建CLB

這個(gè)場景對應(yīng)的是在創(chuàng)建Service時(shí)指定type: LoadBalancer

  • 首先給Service添加一個(gè)特殊的Finalizer,方便刪除CLB的時(shí)候只處理有這個(gè)Finalizer的Service
if err := m.finalizerManager.AddFinalizers(req.Ctx, req.Service, helper.ServiceFinalizer); err != nil {
        
    }
  • 然后解析Service的注解饰豺,獲取LoadBalance的基本信息亿鲜,如公網(wǎng)還是內(nèi)網(wǎng)、收費(fèi)方式冤吨、帶寬等信息
func (mgr *LoadBalancerManager) BuildLocalModel(reqCtx *svcCtx.RequestContext, mdl *model.LoadBalancer) error {
    mdl.LoadBalancerAttribute.AddressType = model.AddressType(reqCtx.Anno.Get(annotation.AddressType))
    mdl.LoadBalancerAttribute.InternetChargeType = model.InternetChargeType(reqCtx.Anno.Get(annotation.ChargeType))
    mdl.LoadBalancerAttribute.InstanceChargeType = model.InstanceChargeType(reqCtx.Anno.Get(annotation.InstanceChargeType))
    mdl.LoadBalancerAttribute.LoadBalancerSpec = model.LoadBalancerSpecType(reqCtx.Anno.Get(annotation.Spec))
    bandwidth := reqCtx.Anno.Get(annotation.Bandwidth)
    if bandwidth != "" {
        i, err := strconv.Atoi(bandwidth)
        mdl.LoadBalancerAttribute.Bandwidth = i
    }
    mdl.LoadBalancerAttribute.LoadBalancerSpec = model.LoadBalancerSpecType(reqCtx.Anno.Get(annotation.Spec))

    if reqCtx.Anno.Get(annotation.LoadBalancerId) != "" {
        mdl.LoadBalancerAttribute.LoadBalancerId = reqCtx.Anno.Get(annotation.LoadBalancerId)
        mdl.LoadBalancerAttribute.IsUserManaged = true
    }
    mdl.LoadBalancerAttribute.LoadBalancerName = reqCtx.Anno.Get(annotation.LoadBalancerName)
    mdl.LoadBalancerAttribute.VSwitchId = reqCtx.Anno.Get(annotation.VswitchId)
    mdl.LoadBalancerAttribute.MasterZoneId = reqCtx.Anno.Get(annotation.MasterZoneID)
    mdl.LoadBalancerAttribute.SlaveZoneId = reqCtx.Anno.Get(annotation.SlaveZoneID)
    mdl.LoadBalancerAttribute.ResourceGroupId = reqCtx.Anno.Get(annotation.ResourceGroupId)
    mdl.LoadBalancerAttribute.AddressIPVersion = model.AddressIPVersionType(reqCtx.Anno.Get(annotation.IPVersion))
    mdl.LoadBalancerAttribute.DeleteProtection = model.FlagType(reqCtx.Anno.Get(annotation.DeleteProtection))
    mdl.LoadBalancerAttribute.ModificationProtectionStatus =
        model.ModificationProtectionType(reqCtx.Anno.Get(annotation.ModificationProtection))
    mdl.LoadBalancerAttribute.Tags = reqCtx.Anno.GetLoadBalancerAdditionalTags()
    mdl.LoadBalancerAttribute.Address = reqCtx.Anno.Get(annotation.IP)
    return nil
}
  • 然后構(gòu)建后端負(fù)載均衡池蒿柳,主要是解析Service對應(yīng)的Endpoints,主要是解析Endpoints中所有的容器IP和容器端口
func setBackendsFromEndpoints(candidates *backend.EndpointWithENI, vgroup model.VServerGroup) []model.BackendAttribute {
    var backends []model.BackendAttribute

    if len(candidates.Endpoints.Subsets) == 0 {
        return nil
    }
    for _, ep := range candidates.Endpoints.Subsets {
        var backendPort int
        if vgroup.ServicePort.TargetPort.Type == intstr.Int {
            backendPort = vgroup.ServicePort.TargetPort.IntValue()
        } else {
            for _, p := range ep.Ports {
                if p.Name == vgroup.ServicePort.Name {
                    backendPort = int(p.Port)
                    break
                }
            }
            if backendPort == 0 {
                klog.Warningf("%s cannot find port according port name: %s", vgroup.VGroupName, vgroup.ServicePort.Name)
            }
        }

        for _, addr := range ep.Addresses {
            backends = append(backends, model.BackendAttribute{
                NodeName: addr.NodeName,
                ServerIp: addr.IP,
                // set backend port to targetPort by default
                // if backend type is ecs, update backend port to nodePort
                Port:        backendPort,
                Description: vgroup.VGroupName,
            })
        }
    }
    return backends
}

構(gòu)建后端負(fù)載均衡池的時(shí)候會分三種類型處理
1)如果Service指定了externalTrafficPolicy: Local

我們知道這種類型只會將POD所在的那些節(jié)點(diǎn)加入到負(fù)載均衡池里面漩蟆,因此只需要將Endpoints中的那些POD加入到負(fù)載均衡池中即可

這里的ServerId對應(yīng)的就是ECS的nodeid垒探,然后置空ServerIp,通過NodePort方式訪問

for _, backend := range initBackends {
        backend.ServerId = id
        backend.ServerIp = ""
        backend.Type = model.ECSBackendType
        // for ECS backend type, port should be set to NodePort
        backend.Port = int(vgroup.ServicePort.NodePort)
        ecsBackends = append(ecsBackends, backend)
    }

最后在設(shè)置后端負(fù)載均衡池中的各個(gè)后端服務(wù)器的權(quán)重怠李,節(jié)點(diǎn)上存在的POD個(gè)數(shù)就是當(dāng)前節(jié)點(diǎn)的權(quán)重

func podNumberAlgorithm(mode helper.TrafficPolicy, backends []model.BackendAttribute) []model.BackendAttribute {
    
    // LocalTrafficPolicy
    ecsPods := make(map[string]int)
    for _, b := range backends {
        ecsPods[b.ServerId] += 1
    }
    for i := range backends {
        backends[i].Weight = ecsPods[backends[i].ServerId]
    }
    return backends
}

2)如果Service指定了externalTrafficPolicy: Cluster

我們知道這種類型會將所有節(jié)點(diǎn)都加入到負(fù)載均衡池里面圾叼,因此這種模式會將所有節(jié)點(diǎn)都加入到負(fù)載均衡池里

func (mgr *VGroupManager) buildClusterBackends(reqCtx *svcCtx.RequestContext, candidates *backend.EndpointWithENI, vgroup model.VServerGroup) ([]model.BackendAttribute, error) {

    // 1. add ecs backends. add all cluster nodes.
    for _, node := range candidates.Nodes {

        ecsBackends = append(
            ecsBackends,
            model.BackendAttribute{
                ServerId:    id,
                Weight:      DefaultServerWeight,
                Port:        int(vgroup.ServicePort.NodePort),
                Type:        model.ECSBackendType,
                Description: vgroup.VGroupName,
            },
        )
    }

    return setWeightBackends(helper.ClusterTrafficPolicy, backends, vgroup.VGroupWeight), nil
}

然后各個(gè)后端服務(wù)器的權(quán)重都是一樣的

func podNumberAlgorithm(mode helper.TrafficPolicy, backends []model.BackendAttribute) []model.BackendAttribute {
    if mode == helper.ClusterTrafficPolicy {
        for i := range backends {
            backends[i].Weight = 100
        }
        return backends
    }
}

3)還一種是直接使用EIP,通過注解service.beta.kubernetes.io/backend-type: eni

這種對應(yīng)的是Terway網(wǎng)絡(luò)插件直接分配EIP給POD捺癞,然后直接將POD加入到負(fù)載均衡池中

func updateENIBackends(reqCtx *svcCtx.RequestContext, mgr *VGroupManager, backends []model.BackendAttribute, ipVersion model.AddressIPVersionType) (
    []model.BackendAttribute, error) {
    vpcId, err := mgr.cloud.VpcID()

    vpcCIDRs, err := mgr.cloud.DescribeVpcCIDRBlock(context.TODO(), vpcId, ipVersion)

    var ips []string
    for _, b := range backends {
        ips = append(ips, b.ServerIp)
    }
    result, err := mgr.cloud.DescribeNetworkInterfaces(vpcId, ips, ipVersion)

    var skipIPs []string
    for i := range backends {
        eniid, ok := result[backends[i].ServerIp]
        // for ENI backend type, port should be set to targetPort (default value), no need to update
        backends[i].ServerId = eniid
        backends[i].Type = model.ENIBackendType
    }

    return backends, nil
}

然后各個(gè)后端服務(wù)器的權(quán)重也都是一樣的

func podNumberAlgorithm(mode helper.TrafficPolicy, backends []model.BackendAttribute) []model.BackendAttribute {
    if mode == helper.ENITrafficPolicy{
        for i := range backends {
            backends[i].Weight = 100
        }
        return backends
    }
}
  • 最后是構(gòu)建Load Balance的監(jiān)聽器夷蚊,也是通過解析Service的注解,支持的注解同樣參考阿里云CLB文檔
func (mgr *ListenerManager) BuildLocalModel(reqCtx *svcCtx.RequestContext, mdl *model.LoadBalancer) error {
    for _, port := range reqCtx.Service.Spec.Ports {
        listener, err := mgr.buildListenerFromServicePort(reqCtx, port)
        mdl.Listeners = append(mdl.Listeners, listener)
    }
    return nil
}

至此髓介,LoadBalance從Service上解析完成了惕鼓,可以看到主要包括三個(gè)方面

  • 基本信息,從注解里解析

  • 負(fù)載均衡池唐础,從Endpoints中解析

  • 監(jiān)聽器箱歧,從注解里解析

LoadBalance解析完成后,然后就要看是否需要創(chuàng)建這個(gè)LoadBalance了一膨,怎么看呢呀邢?

首先需要確定這個(gè)Service是否已經(jīng)關(guān)聯(lián)了已有的LoadBalance,主要是看是否通過注解設(shè)置了service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id

如果指定了這個(gè)注解豹绪,則會先去SLB查詢這個(gè)LoadBalance

func (mgr *LoadBalancerManager) Find(reqCtx *svcCtx.RequestContext, mdl *model.LoadBalancer) error {
    // 1. set load balancer id
    if reqCtx.Anno.Get(annotation.LoadBalancerId) != "" {
        mdl.LoadBalancerAttribute.LoadBalancerId = reqCtx.Anno.Get(annotation.LoadBalancerId)
    }

    // 2. set default loadbalancer name
    // it's safe to set loadbalancer name which will be overwritten in FindLoadBalancer func
    mdl.LoadBalancerAttribute.LoadBalancerName = reqCtx.Anno.GetDefaultLoadBalancerName()

    // 3. set default loadbalancer tag
    // filter tags using logic operator OR, so only TAGKEY tag can be added
    mdl.LoadBalancerAttribute.Tags = []tag.Tag{
        {
            Key:   "kubernetes.do.not.delete",
            Value: reqCtx.Anno.GetDefaultLoadBalancerName(),
        },
    }
    return mgr.cloud.FindLoadBalancer(reqCtx.Ctx, mdl)
}

如果沒有指定LoadBalance价淌,那么就會根據(jù)上面解析出來的LoadBalance信息自動創(chuàng)建一個(gè)

if remote.LoadBalancerAttribute.LoadBalancerId == "" {
        if err := m.slbMgr.Create(reqCtx, local); err != nil {
            
        }
    }

自動創(chuàng)建的LoadBalance名稱和Service的UID有關(guān)

func (n *AnnotationRequest) GetDefaultLoadBalancerName() string {
    //GCE requires that the name of a load balancer starts with a lower case letter.
    ret := "a" + string(n.Service.UID)
    ret = strings.Replace(ret, "-", "", -1)
    //AWS requires that the name of a load balancer is shorter than 32 bytes.
    if len(ret) > 32 {
        ret = ret[:32]
    }
    return ret
}

最終,不管是自動創(chuàng)建的LoadBalance還是使用已有的LoadBalance,到現(xiàn)在是已經(jīng)有了一個(gè)可用的LoadBalance了

但是還需要給這個(gè)LoadBalance設(shè)置負(fù)載均衡池和監(jiān)聽器

而負(fù)載均衡池和監(jiān)聽器就來自上面解析出來的信息了

需要注意的是输钩,如果使用的是已有的LoadBalance豺型,那么會對比已有LoadBalance中的負(fù)載均衡池及監(jiān)聽器 和 上面解析出來的是負(fù)載均衡池及監(jiān)聽器仲智,并且以解析出來的 負(fù)載均衡池及監(jiān)聽器 為準(zhǔn)來更新已有的LoadBalance

比如對負(fù)載均衡池的更新买乃,會以local(解析出來的)中的負(fù)載均衡池為基準(zhǔn),在remote(SLB中的LoadBalancer)中找對應(yīng)的后端服務(wù)器钓辆,如果找到了剪验,就以local的為準(zhǔn)更新remote中的后端服務(wù)器信息;如果沒有找到前联,就先創(chuàng)建這個(gè)后端服務(wù)器功戚,然后加入到remote中

func (m *ModelApplier) applyVGroups(reqCtx *svcCtx.RequestContext, local *model.LoadBalancer, remote *model.LoadBalancer) error {
    var errs []error
    for i := range local.VServerGroups {
        found := false
        var old model.VServerGroup
        for _, rv := range remote.VServerGroups {
            // for reuse vgroup case, find by vgroup id first
            if local.VServerGroups[i].VGroupId != "" &&
                local.VServerGroups[i].VGroupId == rv.VGroupId {
                found = true
                old = rv
                break
            }
            // find by vgroup name
            if local.VServerGroups[i].VGroupId == "" &&
                local.VServerGroups[i].VGroupName == rv.VGroupName {
                found = true
                local.VServerGroups[i].VGroupId = rv.VGroupId
                old = rv
                break
            }
        }

        // update
        if found {
            if err := m.vGroupMgr.UpdateVServerGroup(reqCtx, local.VServerGroups[i], old); err != nil {
                
            }
        }

        // create
        if !found {
            
            err := m.vGroupMgr.CreateVServerGroup(reqCtx, &local.VServerGroups[i], remote.LoadBalancerAttribute.LoadBalancerId)
            
            if err := m.vGroupMgr.BatchAddVServerGroupBackendServers(reqCtx, local.VServerGroups[i],
                local.VServerGroups[i].Backends); err != nil {
            }
            remote.VServerGroups = append(remote.VServerGroups, local.VServerGroups[i])
        }
    }

}

LoadBalance設(shè)置完成后,會添加Service的標(biāo)簽信息似嗤,其中HASH是根據(jù)Service中的所有字段求出來的

service.beta.kubernetes.io/hash
service.k8s.alibaba/loadbalancer-id

然后再把LoadBalance的地址更新到Service的status里

if len(newStatus.Ingress) == 0 {
        newStatus.Ingress = append(newStatus.Ingress,
            v1.LoadBalancerIngress{
                IP: lb.LoadBalancerAttribute.Address,
            })
    }
刪除CLB

這個(gè)主要對應(yīng)的就是刪除Service或者修改Service的類型

func NeedDeleteLoadBalancer(svc *v1.Service) bool {
    return svc.DeletionTimestamp != nil || svc.Spec.Type != v1.ServiceTypeLoadBalancer
}

這時(shí)候就只會處理那些帶有Finalizer的啸臀,而且是自動創(chuàng)建的LoadBalancer,將其自動刪除

if helper.NeedDeleteLoadBalancer(reqCtx.Service) {
        if !local.LoadBalancerAttribute.IsUserManaged {
            err := m.slbMgr.Delete(reqCtx, remote)
            
            remote.LoadBalancerAttribute.LoadBalancerId = ""
            remote.LoadBalancerAttribute.Address = ""
            return nil
        }
    }

然后再移除Service的標(biāo)簽烁落、status乘粒、Finalizer信息

NLB控制器

最后一個(gè)默認(rèn)會啟用的控制器,對應(yīng)的是阿里云的NLB負(fù)載均衡伤塌,代碼流程和CLB幾乎是一模一樣

只是由于NLB的自身產(chǎn)品實(shí)現(xiàn)和CLB的不同灯萍,所以最終的效果才不同

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市每聪,隨后出現(xiàn)的幾起案子旦棉,更是在濱河造成了極大的恐慌,老刑警劉巖药薯,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绑洛,死亡現(xiàn)場離奇詭異,居然都是意外死亡童本,警方通過查閱死者的電腦和手機(jī)真屯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巾陕,“玉大人讨跟,你說我怎么就攤上這事”擅海” “怎么了晾匠?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梯刚。 經(jīng)常有香客問我凉馆,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任澜共,我火速辦了婚禮向叉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嗦董。我一直安慰自己母谎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布京革。 她就那樣靜靜地躺著奇唤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匹摇。 梳的紋絲不亂的頭發(fā)上咬扇,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音廊勃,去河邊找鬼懈贺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坡垫,可吹牛的內(nèi)容都是我干的梭灿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼葛虐,長吁一口氣:“原來是場噩夢啊……” “哼胎源!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屿脐,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤涕蚤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后的诵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體万栅,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年西疤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烦粒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡代赁,死狀恐怖扰她,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芭碍,我是刑警寧澤徒役,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站窖壕,受9級特大地震影響忧勿,放射性物質(zhì)發(fā)生泄漏杉女。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一鸳吸、第九天 我趴在偏房一處隱蔽的房頂上張望熏挎。 院中可真熱鬧,春花似錦晌砾、人聲如沸坎拐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽廉白。三九已至,卻和暖如春乖寒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背院溺。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工楣嘁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珍逸。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓逐虚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谆膳。 傳聞我的和親對象是個(gè)殘疾皇子叭爱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容