2026/2/8 9:17:02
网站建设
项目流程
做app网站的公司哪家好,盐城市城乡建设局门户网站,做响应式网站所用的代码,唐山cms模板建站一、核心目标与核心概念澄清1. 核心目标实现 K8s 中 type: LoadBalancer 类型的 Service 按需对接自定义的多套云平台#xff08;如 “一云”“二云”#xff09;#xff0c;不同 Service 可通过注解指定对接的云平台及负载策略#xff0c;且仅 LoadBalancer 类型 Service …一、核心目标与核心概念澄清1. 核心目标实现 K8s 中type: LoadBalancer类型的 Service 按需对接自定义的多套云平台如 “一云”“二云”不同 Service 可通过注解指定对接的云平台及负载策略且仅LoadBalancer类型 Service 触发自定义逻辑不影响其他类型 Service。2. 关键概念修正易混淆点易混淆表述精准定义“修改配置让所有 Service 走负载”K8s 仅对type: LoadBalancer的 Service 调用云提供商插件CCM逻辑ClusterIP/NodePort/ExternalName类型 Service 完全走原生逻辑无需额外配置隔离“Service 通过污点走自定义云”污点Taint用于节点调度与 Service 对接云平台无关Service 通过annotations传递参数如云平台名称、负载策略由自定义 CCM 解析后调用对应云接口“kube-controller-manager 配置影响所有 Service”--cloud-provider参数仅作用于需要云基础设施交互的场景仅LoadBalancer类型 Service其他 Service 不受影响二、全流程开发步骤1. 前期准备技术栈Go 1.19CCM 开发、K8s 1.24 集群、私有镜像仓库如 Harbor、已实现多云负载均衡接口REST/gRPC含创建 / 删除 / 更新 / 查询 LB 能力。核心依赖k8s.io/apimachinery、k8s.io/client-go、k8s.io/cloud-provider等 K8s 核心库。2. 自定义 CCM 插件开发核心环节CCMCloud Controller Manager是 K8s 对接云厂商的核心组件需基于 K8s 标准接口开发实现 “解析 Service 注解 → 区分云平台 → 调用对应云接口 → 回填 LB IP” 的逻辑。2.1 工程结构标准化plaintextmy-cloud-controller-manager/ ├── cmd/ │ └── manager/ │ └── main.go # CCM 入口注册云提供商并启动 ├── pkg/ │ ├── cloudprovider/ # 云提供商核心实现 │ │ ├── mycloud/ │ │ │ ├── client.go # 多云接口客户端对接一云/二云 │ │ │ ├── loadbalancer.go # LoadBalancer 接口实现核心逻辑 │ │ │ └── provider.go # 注册自定义云提供商 │ └── utils/ # 工具函数 │ ├── node.go # 提取节点 IP │ └── service.go # 解析 Service 注解 ├── deploy/ # 部署配置 │ ├── rbac.yaml # CCM 权限配置 │ └── ccm-deployment.yaml # CCM 部署文件 ├── Dockerfile # 镜像构建文件 ├── go.mod # Go 依赖管理 └── go.sum2.2 核心代码实现1多云接口客户端封装pkg/cloudprovider/mycloud/client.go封装对接 “一云”“二云” 的接口调用逻辑支持传递注解参数如 LB 规格、带宽go运行package mycloud import ( bytes encoding/json fmt net/http time k8s.io/klog/v2 ) // 定义 LB 创建响应结构与云接口一致 type LBCreateResponse struct { LBId string json:lbId IP string json:ip Err string json:err } // 多云客户端结构体 type MyLBClient struct { cloud1BaseURL string // 一云接口地址 cloud2BaseURL string // 二云接口地址 httpClient *http.Client } // 初始化客户端 func NewMyLBClient(cloud1URL, cloud2URL string) *MyLBClient { return MyLBClient{ cloud1BaseURL: cloud1URL, cloud2BaseURL: cloud2URL, httpClient: http.Client{Timeout: 30 * time.Second}, } } // 调用一云创建 LB 接口 func (c *MyLBClient) CreateCloud1LB(serviceName string, nodeIPs []string, port, nodePort int, spec, bandwidth string) (*LBCreateResponse, error) { reqBody : map[string]interface{}{ name: serviceName, nodes: nodeIPs, port: port, nodePort: nodePort, spec: spec, bandwidth: bandwidth, } return c.callLBAPI(c.cloud1BaseURL/api/v1/lb/create, POST, reqBody) } // 调用二云创建 LB 接口 func (c *MyLBClient) CreateCloud2LB(serviceName string, nodeIPs []string, port, nodePort int, spec, bandwidth string) (*LBCreateResponse, error) { reqBody : map[string]interface{}{ name: serviceName, nodes: nodeIPs, port: port, nodePort: nodePort, spec: spec, bandwidth: bandwidth, } return c.callLBAPI(c.cloud2BaseURL/api/v1/lb/create, POST, reqBody) } // 通用接口调用方法 func (c *MyLBClient) callLBAPI(url, method string, reqBody interface{}) (*LBCreateResponse, error) { reqBytes, err : json.Marshal(reqBody) if err ! nil { return nil, err } req, err : http.NewRequest(method, url, bytes.NewBuffer(reqBytes)) if err ! nil { return nil, err } req.Header.Set(Content-Type, application/json) resp, err : c.httpClient.Do(req) if err ! nil { return nil, err } defer resp.Body.Close() var lbResp LBCreateResponse if err : json.NewDecoder(resp.Body).Decode(lbResp); err ! nil { return nil, err } if lbResp.Err ! { return nil, fmt.Errorf(lbResp.Err) } return lbResp, nil } // 补充删除/更新/查询接口略2LoadBalancer 接口实现pkg/cloudprovider/mycloud/loadbalancer.go实现 K8s 标准LoadBalancer接口核心逻辑解析注解 → 区分云平台 → 调用对应接口 → 回填 IPgo运行package mycloud import ( context fmt github.com/your-name/my-cloud-controller-manager/pkg/utils k8s.io/api/core/v1 k8s.io/cloud-provider k8s.io/cloud-provider/pkg/framework k8s.io/klog/v2 ) type MyLoadBalancer struct { lbClient *MyLBClient kubeClient framework.ControllerClient } func NewMyLoadBalancer(lbClient *MyLBClient, kubeClient framework.ControllerClient) *MyLoadBalancer { return MyLoadBalancer{lbClient: lbClient, kubeClient: kubeClient} } // 核心方法创建 LoadBalancer func (m *MyLoadBalancer) CreateLoadBalancer( ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node, ) (*v1.LoadBalancerStatus, error) { // 1. 校验仅带指定注解的 LoadBalancer Service 触发自定义逻辑 enable, ok : service.Annotations[mycloud.com/enable] if !ok || enable ! true { return nil, fmt.Errorf(my-cloud not enabled for service %s, service.Name) } // 2. 解析注解参数云平台/规格/带宽 provider : service.Annotations[mycloud.com/provider] // cloud1/cloud2 lbSpec : service.Annotations[mycloud.com/lb-spec] lbBandwidth : service.Annotations[mycloud.com/lb-bandwidth] if provider { return nil, fmt.Errorf(missing mycloud.com/provider annotation) } if lbSpec { lbSpec standard } if lbBandwidth { lbBandwidth 5M } // 3. 提取节点 IP 和端口 nodeIPs : utils.ExtractNodeIPs(nodes) if len(nodeIPs) 0 { return nil, fmt.Errorf(no node IPs extracted) } port : service.Spec.Ports[0].Port nodePort : service.Spec.Ports[0].NodePort if nodePort 0 { return nil, fmt.Errorf(nodePort not allocated for service %s, service.Name) } // 4. 分支逻辑调用对应云平台接口 var lbResp *LBCreateResponse var err error switch provider { case cloud1: lbResp, err m.lbClient.CreateCloud1LB(service.Name, nodeIPs, int(port), int(nodePort), lbSpec, lbBandwidth) case cloud2: lbResp, err m.lbClient.CreateCloud2LB(service.Name, nodeIPs, int(port), int(nodePort), lbSpec, lbBandwidth) default: return nil, fmt.Errorf(unsupported provider: %s, provider) } if err ! nil { return nil, err } // 5. 记录 LB ID 到 Service 注解方便后续删除/更新 utils.SetLBIdAnnotation(service, lbResp.LBId) _ m.kubeClient.Update(ctx, service) // 忽略更新失败仅日志告警 // 6. 回填 LB IP 到 Service 的 EXTERNAL-IP return v1.LoadBalancerStatus{ Ingress: []v1.LoadBalancerIngress{{IP: lbResp.IP}}, }, nil } // 实现删除/更新/查询等接口略 func (m *MyLoadBalancer) DeleteLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) error { // 解析 LB ID → 调用对应云平台删除接口 → 删除注解 return nil } func (m *MyLoadBalancer) UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error { // 解析 LB ID → 调用对应云平台更新接口 return nil }3注册自定义云提供商pkg/cloudprovider/mycloud/provider.gogo运行package mycloud import ( io k8s.io/cloud-provider k8s.io/client-go/kubernetes k8s.io/client-go/rest k8s.io/klog/v2 ) type MyCloudProvider struct { loadBalancer *MyLoadBalancer initialized bool } // 初始化云提供商 func NewMyCloudProvider(config io.Reader) (cloudprovider.Interface, error) { // 1. 加载 K8s 集群内配置 kubeConfig, err : rest.InClusterConfig() if err ! nil { return nil, err } kubeClient, err : kubernetes.NewForConfig(kubeConfig) if err ! nil { return nil, err } // 2. 初始化多云客户端替换为实际接口地址 lbClient : NewMyLBClient(http://cloud1-api:8080, http://cloud2-api:8080) // 3. 初始化 LoadBalancer 实现 loadBalancer : NewMyLoadBalancer(lbClient, framework.NewControllerClient(kubeClient, nil)) return MyCloudProvider{ loadBalancer: loadBalancer, initialized: true, }, nil } // 实现 cloudprovider.Interface 接口仅暴露 LoadBalancer 能力 func (m *MyCloudProvider) Initialize(_ cloudprovider.ControllerClientBuilder, _ -chan struct{}) {} func (m *MyCloudProvider) LoadBalancer() (cloudprovider.LoadBalancer, bool) { return m.loadBalancer, m.initialized } func (m *MyCloudProvider) Instances() (cloudprovider.Instances, bool) { return nil, false } func (m *MyCloudProvider) Zones() (cloudprovider.Zones, bool) { return nil, false } func (m *MyCloudProvider) ProviderName() string { return my-cloud } func (m *MyCloudProvider) HasClusterID() bool { return true } // 注册云提供商init 函数自动执行 func init() { cloudprovider.RegisterCloudProvider(my-cloud, NewMyCloudProvider) klog.Info(my-cloud provider registered) }4CCM 入口文件cmd/manager/main.gogo运行package main import ( flag os _ github.com/your-name/my-cloud-controller-manager/pkg/cloudprovider/mycloud k8s.io/cloud-provider/cmd/cloud-controller-manager/app k8s.io/cloud-provider/cmd/cloud-controller-manager/app/options k8s.io/klog/v2 ) func main() { opts : options.NewCloudControllerManagerOptions() cmd : app.NewCloudControllerManagerCommand(opts) // 默认参数配置 flag.Set(cloud-provider, my-cloud) // 自定义云提供商名称 flag.Set(leader-elect, false) // 单实例无需选主生产环境建议开启 flag.Set(controllers, service) // 仅启用 Service 控制器 flag.Set(v, 4) // 日志级别 if err : cmd.Execute(); err ! nil { klog.Fatalf(run CCM failed: %v, err) os.Exit(1) } }2.3 工具函数实现pkg/utils/node.go提取节点内网 / 公网 IPservice.go解析 / 设置 Service 注解如 LB ID。3. 镜像构建与部署3.1 构建镜像dockerfile# 编译阶段 FROM golang:1.20-alpine AS builder WORKDIR /workspace COPY . . ENV GOPROXYhttps://goproxy.cn,direct RUN CGO_ENABLED0 GOOSlinux GOARCHamd64 go build -o my-cloud-controller-manager ./cmd/manager # 运行阶段 FROM alpine:3.18 COPY --frombuilder /workspace/my-cloud-controller-manager /usr/bin/ RUN apk add --no-cache ca-certificates ENTRYPOINT [/usr/bin/my-cloud-controller-manager]构建并推送镜像bash运行docker build -t harbor.your-domain.com/my-ccm:v1 . docker push harbor.your-domain.com/my-ccm:v13.2 部署 CCM 到 K8s 集群1RBAC 权限配置deploy/rbac.yamlyamlapiVersion: v1 kind: ServiceAccount metadata: name: my-cloud-controller-manager namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: my-cloud-controller-manager-role rules: - apiGroups: [] resources: [services, nodes, events] verbs: [get, list, watch, update, patch, create] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-cloud-controller-manager-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: my-cloud-controller-manager-role subjects: - kind: ServiceAccount name: my-cloud-controller-manager namespace: kube-system2CCM 部署文件deploy/ccm-deployment.yamlyamlapiVersion: apps/v1 kind: Deployment metadata: name: my-cloud-controller-manager namespace: kube-system spec: replicas: 1 selector: matchLabels: app: my-ccm template: metadata: labels: app: my-ccm spec: serviceAccountName: my-cloud-controller-manager tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule containers: - name: my-ccm image: harbor.your-domain.com/my-ccm:v1 imagePullPolicy: Always command: - /usr/bin/my-cloud-controller-manager - --cloud-providermy-cloud - --leader-electfalse - --controllersservice - --v4 resources: limits: cpu: 100m memory: 128Mi requests: cpu: 50m memory: 64Mi3执行部署bash运行kubectl apply -f deploy/rbac.yaml kubectl apply -f deploy/ccm-deployment.yaml # 验证 CCM 运行状态 kubectl get pods -n kube-system -l appmy-ccm kubectl logs -n kube-system my-ccm-pod-name # 查看是否输出 my-cloud provider registered4. K8s 集群配置修改 kube-controller-manager 静态 Pod 配置/etc/kubernetes/manifests/kube-controller-manager.yaml添加参数指定自定义云提供商yamlspec: containers: - name: kube-controller-manager command: - kube-controller-manager # 新增/修改以下参数 - --cloud-providermy-cloud - --external-cloud-volume-pluginmy-cloud # 保留原有其他参数...修改后 kube-controller-manager 会自动重启配置生效。5. 测试自定义 LoadBalancer Service5.1 部署测试应用yaml# nginx-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo spec: replicas: 2 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: nginx image: nginx:alpine ports: - containerPort: 805.2 创建自定义 LoadBalancer Serviceyaml# nginx-lb-cloud1.yaml对接一云 apiVersion: v1 kind: Service metadata: name: nginx-lb-cloud1 annotations: mycloud.com/enable: true mycloud.com/provider: cloud1 mycloud.com/lb-spec: standard mycloud.com/lb-bandwidth: 10M spec: selector: app: nginx-demo ports: - port: 80 targetPort: 80 type: LoadBalancer # nginx-lb-cloud2.yaml对接二云 apiVersion: v1 kind: Service metadata: name: nginx-lb-cloud2 annotations: mycloud.com/enable: true mycloud.com/provider: cloud2 mycloud.com/lb-spec: high-performance mycloud.com/lb-bandwidth: 20M spec: selector: app: nginx-demo ports: - port: 80 targetPort: 80 type: LoadBalancer # 普通 ClusterIP Service不受影响 apiVersion: v1 kind: Service metadata: name: nginx-clusterip spec: selector: app: nginx-demo ports: - port: 80 targetPort: 80 type: ClusterIP5.3 验证结果bash运行# 创建 Service kubectl apply -f nginx-deploy.yaml kubectl apply -f nginx-lb-cloud1.yaml kubectl apply -f nginx-lb-cloud2.yaml kubectl apply -f nginx-clusterip.yaml # 查看 Service 状态 watch kubectl get svc # 预期结果 # - nginx-lb-cloud1/nginx-lb-cloud2 的 EXTERNAL-IP 会变为对应云平台返回的 LB IP # - nginx-clusterip 为 ClusterIP 类型EXTERNAL-IP 为空完全不受影响。 # 访问测试 curl http://nginx-lb-cloud1-EXTERNAL-IP # 触发一云负载策略 curl http://nginx-lb-cloud2-EXTERNAL-IP # 触发二云负载策略三、关键控制逻辑与容错1. 仅 LoadBalancer 类型 Service 触发自定义逻辑K8s 仅对type: LoadBalancer的 Service 调用 CCM 的 LoadBalancer 接口ClusterIP/NodePort等类型完全走原生逻辑无需额外隔离。2. 精细化控制仅指定注解的 LB Service 生效通过mycloud.com/enable: true注解开关仅显式声明的 LB Service 触发自定义逻辑其他 LB Service 会返回错误EXTERNAL-IP 处于 pending 状态可按需兼容原生云厂商逻辑。3. 容错处理接口调用超时 / 失败CCM 会重试并记录日志确保 LB 创建 / 删除逻辑幂等注解解析失败返回明确错误避免无效调用Service 注解更新失败仅日志告警不影响核心 LB 逻辑。四、核心总结开发核心基于 K8s 标准 CCM 框架开发插件实现 LoadBalancer 接口通过解析 Service 注解区分多云平台调用对应接口生成 LB IP部署核心CCM 部署到 kube-system 命名空间配置 RBAC 权限修改 kube-controller-manager 指定自定义云提供商控制核心K8s 天然区分 Service 类型仅type: LoadBalancer触发 CCM 逻辑通过注解开关可进一步精细化控制隔离核心自定义 CCM 仅实现 LoadBalancer 接口其他接口返回未实现确保集群其他功能如节点管理、存储不受影响。