为了账号安全,请及时绑定邮箱和手机立即绑定

拆解Ultron:深度解析

Ultron旨在通过一系列定制服务和HTTP处理器来解决多云工作负载分配的挑战。该应用是用Go语言编写的,采取结构化的方式来处理缓存、证书管理和与外部API交互等任务。在这篇文章中,我们将通过分析main.go入口点、核心服务以及内部处理器(负责突变和验证Pod)来探讨Ultron的架构,并通过关键代码示例来展示其设计和功能。

奥创概念架构

Ultron的核心是main.go文件

乌隆特(Ultron)的[main.go](https://github.com/be-heroes/ultron/blob/main/main.go)文件作为应用的入口点,负责服务初始化、设置HTTP服务器和确保安全通信。首先值得注意的是,使用Uber的Zap日志库实现的结构化日志系统,用于关键性能场景的日志记录。

这是Ultron设置日志记录器的方法。

    logger, _ := zap.NewProduction()  
    defer logger.Sync()  

    sugar := logger.Sugar()  
    sugar.Info("正在初始化Ultron")

在启用日志记录后,Ultron 加载其配置,初始化 Redis 客户端并初始化核心服务模块。以下代码片段展示了 Ultron 如何把这些组件连接起来:

    config, err := ultron.LoadConfig()
    if err != nil {
        sugar.Fatalf("加载配置时出错: %v", err)
    }

    ctx := context.Background()
    // 从配置初始化Redis客户端
    redisClient := ultron.InitializeRedisClientFromConfig(ctx, config, sugar)
    mapperInstance := mapper.NewMapper()
    algorithmInstance := algorithm.NewAlgorithm()
    cacheService := services.NewCacheService(nil, redisClient)
    certificateService := services.NewCertificateService()
    // 计算服务使用算法实例、缓存服务和映射实例
    computeService := services.NewComputeService(algorithmInstance, cacheService, mapperInstance)

在通过InitializeRedisClientFromConfig()初始化 Redis 客户端之后,将会实例化[IMapper][IAlgorithm]这两个对象,来作为核心服务(比如缓存服务[CacheService]和证书管理服务[CertificateService])的依赖。这些服务分别处理缓存和 TLS 证书管理。例如,缓存服务[CacheService](如其代码在[此处]所示)和证书管理服务[CertificateService]处理 TLS 证书管理。

设置好所需的服务后,奥创开始配置其用于 Pod 变体、验证和健康检查的 HTTP 路由配置,健康检查,。

(Note: There is a minor grammatical issue with the trailing comma after "健康检查". The corrected version should omit this trailing comma for proper sentence structure.)

Final corrected translation:
设置好所需的服务后,奥创开始配置其用于 Pod 变体、验证和健康检查的 HTTP 路由配置。

    mux := http.NewServeMux()  // 创建一个新的ServeMux实例
    mux.HandleFunc("/mutate", mutationHandler.MutatePodSpec)  // 将/mutate路径与mutationHandler.MutatePodSpec函数关联
    mux.HandleFunc("/validate", validationHandler.ValidatePodSpec)  // 将/validate路径与validationHandler.ValidatePodSpec函数关联
    mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })  // 将/healthz路径设置为健康检查,返回状态码表示健康检查成功
    // 返回状态码表示健康检查成功
    // PodSpec: 容器组规格

这三条关键步骤使Ultron能够在pod规格信息被提供给Kubernetes集群中的节点之前进行拦截和增强。

核心服务

乌特隆中的服务,位于[pkg/services](https://github.com/be-heroes/ultron/tree/main/pkg/services)目录,是应用程序的实际工作主力所在。每个服务都承担着一组特定的责任,从与外部系统(如Redis)的交互,到执行复杂的内部任务,比如生成证书或缓存。

Redis 缓存:cache_service.go 文件

[CacheService](https://github.com/be-heroes/ultron/blob/main/pkg/services/cache_service.go) 管理与 Redis 服务器的交互,抽象处理缓存结果和检索存储数据的过程。此服务对于提高 Ultron 的性能至关重要,通过避免冗余计算,减少外部 API 调用,并加快对常用数据的访问,从而让我们能在“Ultron 查询”的 5 秒内快速完成任务。

下面是一个包含服务接口的内容:

    type ICacheService interface {  
     AddCacheItem(key string, value interface{}, d time.Duration) error  
     GetCacheItem(key string) (interface{}, error)  
     GetAllComputeConfigurations() ([]ultron.ComputeConfiguration, error)  
     GetEphemeralComputeConfigurations() ([]ultron.ComputeConfiguration, error)  
     GetDurableComputeConfigurations() ([]ultron.ComputeConfiguration, error)  
     GetWeightedNodes() ([]ultron.WeightedNode, error)  
     GetWeightedInterruptRates() ([]ultron.WeightedInterruptRate, error)  
     GetWeightedLatencyRates() ([]ultron.WeightedLatencyRate, error)  
    }

在代码中,ICacheService 提供了从 Redis 设置和获取缓存数据的方法。该服务使用一个 Redis 客户端初始化,便于注入其他系统组件。

有关证书处理 certificate_service.go

Ultron的[证书服务](https://github.com/be-heroes/ultron/blob/main/pkg/services/certificate_service.go)负责生成TLS证书,这是实现安全通信的重要功能。在main.go文件中,我们看到该服务被用来生成自签名证书。

下面来深入了解一下它是如何工作的:

    func (cs *CertificateService) GenerateSelfSignedCert(organization string, commonName string, dnsNames []string, ipAddresses []net.IP) (tls.Certificate, error) {  
        if organization == "" || commonName == "" {  
            return tls.Certificate{}, fmt.Errorf("组织名和通用名都必须提供")  
        }  

        priv, err := rsa.GenerateKey(rand.Reader, 2048)  
        if err != nil {  
            return tls.Certificate{}, err  
        }  

        serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)  
        serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)  
        if err != nil {  
            return tls.Certificate{}, err  
        }  

        certTemplate := x509.Certificate{  
            SerialNumber: serialNumber,  
            Subject: pkix.Name{  
                Organization: []string{organization},  
                CommonName:   commonName,  
            },  
            NotBefore: time.Now(),  
            NotAfter:  time.Now().Add(365 * 24 * time.Hour),  

            KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,  
            ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},  
            BasicConstraintsValid: true,  
            DNSNames:              dnsNames,  
            IPAddresses:           ipAddresses,  
        }  

        certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &priv.PublicKey, priv)  
        if err != nil {  
            return tls.Certificate{}, err  
        }  

        certPEM := pem.EncodeToMemory(&pem.Block{Type: "BlockTypeCertificate", Bytes: certDERBytes})  
        keyPEM := pem.EncodeToMemory(&pem.Block{Type: "BlockTypeRsaPrivateKey", Bytes: x509.MarshalPKCS1PrivateKey(priv)})  
        tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)  
        if err != nil {  
            return tls.Certificate{}, err  
        }  

        return tlsCert, nil  
    }

该服务封装了证书生成和管理的复杂流程,提供了创建和导出证书的方法。通过抽象这些功能,Ultron使安全相关操作变得易于管理和扩展,并避免了在开发和测试环境中依赖外部证书颁发机构。

Ultron的脑部代码: compute_service.go

[ComputeService](https://github.com/be-heroes/ultron/blob/a23d3c8487a05907c12325c0caea58c26da23425/pkg/services/compute_service.go#L27) 是 Ultron 最重要的服务之一,负责处理 pod 变体和校验的核心逻辑。该服务与其他组件(如算法和缓存等)集成,执行计算操作,以修改或校验 Kubernetes pod 规格。

这里看看它的结构:

    // 匹配Pod规格函数
    func (cs *ComputeService) MatchPodSpec(pod *corev1.Pod) (*ultron.WeightedNode, error) {  
        // 将Pod映射为加权Pod对象
        wPod, err := cs.mapper.MapPodToWeightedPod(pod)  
        if err != nil {  
            return nil, err  
        }  

        // 根据加权Pod匹配加权节点
        wNode, err := cs.MatchWeightedPodToWeightedNode(&wPod)  
        if err != nil {  
            return nil, err  
        }  

        // 如果wNode为空,则根据加权Pod匹配计算配置
        if wNode == nil {  
            computeConfiguration, err := cs.MatchWeightedPodToComputeConfiguration(&wPod)  
            if err != nil {  
                return nil, err  
            }  

            // 如果计算配置不为空,则初始化wNode
            if computeConfiguration != nil {  
                var instanceType string  

                // 根据计算类型设置实例类型
                if computeConfiguration.ComputeType == ultron.ComputeTypeDurable {  
                    instanceType = ultron.DefaultDurableInstanceType  
                } else {  
                    instanceType = ultron.DefaultEphemeralInstanceType  
                }  

                // 初始化加权节点对象
                wNode = &ultron.WeightedNode{  
                    Selector:         map[string]string{ultron.LabelInstanceType: instanceType, ultron.AnnotationManaged: "true"},  
                    AvailableCPU:     float64(*computeConfiguration.VCpu),  
                    TotalCPU:         float64(*computeConfiguration.VCpu),  
                    AvailableMemory:  float64(*computeConfiguration.RamGb),  
                    TotalMemory:      float64(*computeConfiguration.RamGb),  
                    AvailableStorage: float64(*computeConfiguration.VolumeGb),  
                    DiskType:         wPod.RequestedDiskType,  
                    NetworkType:      wPod.RequestedNetworkType,  
                    Price:            float64(*computeConfiguration.Cost.PricePerUnit),  
                    InstanceType:     instanceType,  
                }  

                // 获取加权节点的中断率
                interuptionRate, err := cs.GetInteruptionRateForWeightedNode(wNode)  
                if err != nil {  
                    return nil, err  
                }  

                // 设置加权节点的中断率
                wNode.InterruptionRate = *interuptionRate  

                // 获取加权节点的延迟率
                latencyRate, err := cs.GetLatencyRateForWeightedNode(wNode)  
                if err != nil {  
                    return nil, err  
                }  

                // 设置加权节点的延迟率
                wNode.LatencyRate = *latencyRate  
            }  
        }  

        // 返回加权节点对象和错误
        return wNode, nil  
    }
HTTP 处理程序

Ultron 内部的处理程序负责管理与 Kubernetes 基础设施集成的核心变异和验证操作。这些处理程序负责修改 pod 规范,或者确保在部署到节点前满足特定要求。详见 [internal/handlers](https://github.com/be-heroes/ultron/tree/main/internal/handlers)

pod 的变异处理:mutation_handler.go

[MutationHandler](https://github.com/be-heroes/ultron/blob/a23d3c8487a05907c12325c0caea58c26da23425/internal/handlers/mutation_handler.go#L20) 会接收 Kubernetes pod 规格并根据预定义的规则对其进行修改。这可能包括注入环境变量、添加 sidecar 容器,或其他自动应用的修改。

func (mh *MutationHandler) HandleAdmissionReview(request *admissionv1.AdmissionRequest) (*admissionv1.AdmissionResponse, error) {  
 if request.Kind.Kind != "Pod" {  
  return &admissionv1.AdmissionResponse{  
   Allowed: true,  
  }, nil  
 }  

 var pod corev1.Pod  
 if err := json.Unmarshal(request.Object.Raw, &pod); err != nil {  
  return &admissionv1.AdmissionResponse{  
   Allowed: true,  
  }, err  
 }  

 wNode, err := mh.computeService.MatchPodSpec(&pod)  
 if err != nil {  
  return nil, err  
 }  

 if wNode == nil {  
  return &admissionv1.AdmissionResponse{  
   Allowed: true,  
  }, nil  
 }  

 pod.Spec.NodeSelector = wNode.Selector  
 patchBytes, err := json.Marshal([]map[string]interface{}{  
  {  
   "op":    "add",  
   "path":  "/spec/nodeSelector",  
   "value": pod.Spec.NodeSelector,  
  },  
 })  
 if err != nil {  
  return &admissionv1.AdmissionResponse{  
   Allowed: true,  
  }, err  
 }  

 return &admissionv1.AdmissionResponse{  
  Allowed:   true,  
  Patch:     patchBytes,  
  PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),  
 }, nil  
}

处理程序通过 [MatchPodSpec](https://github.com/be-heroes/ultron/blob/a23d3c8487a05907c12325c0caea58c26da23425/pkg/services/compute_service.go#L41) 操作利用 ComputeService 来修改 pod 规范。这种清晰的隔离确保了业务逻辑(在服务里)与 HTTP 处理代码分离,从而使系统更加模块化和易于维护。

验证 Pod:validation_handler.go

同样,[ValidationHandler](https://github.com/be-heroes/ultron/blob/main/internal/handlers/validation_handler.go) 确保 Kubernetes Pod 规格在被集群接受之前符合某些验证规则。这对于防止无效或不安全的配置被部署到集群中至关重要。

    func (vh *ValidationHandler) HandleAdmissionReview(request *admissionv1.AdmissionRequest) (*admissionv1.AdmissionResponse, error) {  
     // 如果请求的类型不是Pod,则返回允许的响应
     if request.Kind.Kind != "Pod" {  
      return &admissionv1.AdmissionResponse{  
       Allowed: true,  
      }, nil  
     }  

     // 定义一个Pod对象
     var pod corev1.Pod  
     // 解析请求中的Pod对象,如果解析失败则返回错误
     if err := json.Unmarshal(request.Object.Raw, &pod); err != nil {  
      return &admissionv1.AdmissionResponse{  
       Allowed: true,  
      }, err  
     }  

     // 根据Pod对象匹配节点
     wNode, err := vh.computeService.MatchPodSpec(&pod)  
     if err != nil {  
      return nil, err  
     }  

     // 如果节点为空且redisClient不为空,则发布Pod观察事件到redisClient
     if wNode == nil && vh.redisClient != nil {  
      vh.redisClient.Publish(context.Background(), ultron.TopicPodObserve, pod)  
     } else if vh.redisClient != nil {  
      // 否则,发布节点观察事件到redisClient
      vh.redisClient.Publish(context.Background(), ultron.TopicNodeObserve, wNode)  
     }  

     // 返回允许的响应
     return &admissionv1.AdmissionResponse{  
      Allowed: true,  
     }, nil  
    }

ComputeService 用于验证 pod 规格。若发现任何验证错误,返回一个 400 Bad Request 错误给客户端。这样可以确保只有有效的 pod 才能进入 Kubernetes 集群,减少了配置错误的可能性。一旦验证通过,会向 Redis 服务器发送一条消息,以便其他组件开始监控集群内的资源,并反馈指标给 Ultron 处理,以判断优化是否达到预期效果,或者是否有进一步改进的可能通过重新调度工作负载。

摘要

Ultron 是一个开源应用程序,利用 Go 语言的优势来构建一个多云 Kubernetes 环境中工作负载部署的可扩展和安全的系统。服务和处理器之间职责的清晰划分、依赖注入的使用以及对可测试性的重视,使这个代码库易于理解和扩展。

乌隆特(Ultron)还有改进的空间,例如增加更细致的错误处理,对外部服务实现重试功能以及增强服务初始化时的日志记录。不过,乌隆特仍然是一个很好的例子,展示了如何构建一个与Kubernetes紧密结合的应用,提供了有关如何在大规模环境中管理Pod变异、验证和供应的宝贵经验。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消