把整个技术平台送上云的开源项目

200次阅读  |  发布于3年以前

今天介绍的开源项目是用 Go 语言写的一站式云原生 PaaS 平台——Erda,专为企业提供 DevOps、微服务治理、多云管理以及快数据管理等平台级服务。

GitHub 地址→https://github.com/erda-project/erda

Erda 借助云原生的优势为企业打造一个完整的技术平台,助力企业数字化转型。并且已通过大量的企业成功交付案例,积累了大量经验。放心地把服务交给 Erda 打理吧!

进化历程

Erda 的演进历程:

Erda 很早就开始探索云原生 DevOps 实践,最初的平台只拥抱了 Docker 实现了基于 Docker 的手动部署平台,整个设计理念是面向资源的。(2016年:容器化)

而后随着容器编排技术的兴起,选型了 DC/OS(mesos+marathon)作为容器服务技术底座。选择 mesos 也是因为它的出现要远早于 Docker、K8s、Swarm 等,有不少大规模的落地案例,稳定性得到了很好的验证,并且还有很强的扩展能力,在 mesos 之上除了有 marathon、metronome 等服务、任务的调度框架,还支持了不少大数据的框架,比如 Hadoop、Spark 等,这与我们需要发展大数据平台不谋而合。我们的设计理念也调整到以应用为中心,去面向开发者。(2017年:DC/OS)

前期交付的都用一个集群将平台完整的私有化输出到客户环境,但随着客户越来越多资源成本变成了一个大问题。Erda 开始考虑将平台能力 SaaS 化,提供企业租户,并支持多集群管理。客户侧的集群不需要再部署一个完整的 Erda 平台,只需开放口子可以给平台访问即可。在零产品能力损失的情况下,节省了大量的资源,并且客户不需要再关心平台的维护问题。(2018年:多集群架构)

Kubernetes 逐渐成为了业界的事实标准,Erda 提前预知了这个趋势。 在支持多集群架构时就将容器编排层进行了封装抽象,并引入了插件的机制便于未来的扩展。所以后面很平滑地将平台全部切至了 K8s。(2019年:支持 Kubernetes)

参考官网:https://www.erda.cloud/

Erda 用多集群管理解决的痛点

如今数字化转型已经成为现阶段企业发展的主流趋势,该趋势也促使着云市场的快速增长。我们已经处于云时代,而面向企业又会出现混合云、多云的架构。

企业总可能出于各种原因进行多云、混合云的决策。而 Erda 面向企业进行交付时,必然也少不了会面对这些场景。Erda 最终都通过多集群管理的方式输出解决方案,为不同的环境搭建不同的集群,由平台侧统一管理,上层业务可以按需选择不同的集群进行应用的生命周期管理。

除了这些,一些其他的场景也可通过多集群管理的方式解决,比如:

综上所述,针对多集群管理,Erda 除了压缩交付的资源诉求,还输出了大量的解决方案,来满足客户的不同场景。

跨集群管理经验分享

上面主要介绍了 Erda 多集群管理的一个背景,及 ToB 交付时如何满足企业的各种场景需求。回到多集群管理会面临的一个大问题,就是如何跨集群进行访问、管理?

经历的阶段

Erda 在 DC/OS 时代,使用了最简单的 basic auth 的方式,直连集群进行管控。

随着客户量越来越大,并且平台支持异构调度之后,管理难度和安全问题凸显。Erda 实现了一套 netportal 的网络管控链路,中心有一个 netportal 组件,边缘则需要在集群端的 nginx configuration 中加一段配置。中心访问用户集群时会统一通过 netportal 进行代理,将流量转发到对端集群的 nginx 上。netportal 和 nginx 之间采用了 https 双向认证的方式进行建连,并且所有的证书都由中心统一签发,提升了通道的安全性。

随着产品战略的提升,开始建设 Erda Cloud,加强 Erda 产品的商业化能力,让用户可以自助地在平台上完成一系列操作,这个就对 netportal 链路产生了巨大挑战。netportal 有不少依赖,比如集群端的 nginx 版本,nginx 需要增加额外的配置,对应的证书需要由中心统一签发等等,这些都无法让用户完成自助操作。而此时 Erda 的容器服务默认换成了 Kubernetes,针对 Kubernetes 访问方式进行了扩展,引入了社区常见的访问方式:KubeConfig、ServiceAccount(Token)。

KubeConfig、ServiceAccount 的方式,会要求客户将 Kubernetes 集群的 ApiServer 暴露在公网上,由平台进行直连。如果没有外加安全措施,这种直连会存在一定的安全隐患。还碰到了有客户无法提供公网入口 IP,而上面所有的方式都是需要有公网入口才能实现。还好客户还不是全封闭的,可以访问公网,于是 Erda 借鉴了 Rancher 的做法,实现了 dialer 的通道管理,可以不必让 Erda 去连接用户的集群,而是在用户的集群安装 cluster agent 来连接 Erda,让用户集群主动建立一条连接,这条连接就变成了 Erda 去管控用户集群的隧道(Tunnel)。

核心组件

Erda 控制平面内的其他组件比如 K8s Manager,可以通过请求 Cluster Dialer,找到要管控集群的 Tunnel Session,再通过这个 Tunnel 去访问对应集群中的 kube-apiserver。

Dialer 的使用

在 Erda 开源项目的 pkg/clusterdialer/dialer.go 模块:

https://github.com/erda-project/erda/blob/master/pkg/clusterdialer/dialer.go

提供了一个 cluster dialer lib,主要提供了以下三个 Dial Function:

type DialContextFunc func(ctx context.Context, network, address string) (net.Conn, error)
type DialContextProtoFunc func(ctx context.Context, address string) (net.Conn, error)

func DialContext(clusterKey string) DialContextFunc {
 return func(ctx context.Context, network, addr string) (net.Conn, error) {
  logrus.Debugf("use cluster dialer, key:%s", clusterKey)
  return getClusterDialer(ctx, clusterKey)(ctx, network, addr)
 }
}

func DialContextProto(clusterKey, proto string) DialContextProtoFunc {
 return func(ctx context.Context, addr string) (net.Conn, error) {
  logrus.Debugf("use cluster dialer, key:%s", clusterKey)
  return getClusterDialer(ctx, clusterKey)(ctx, proto, addr)
 }
}

func DialContextTCP(clusterKey string) DialContextProtoFunc {
 return DialContextProto(clusterKey, "tcp")
}

如果使用 http client 要访问用户集群的 HTTP 服务,可以通过如下方式构造 client:

client = &http.Client{
    Transport: &http.Transport{
        DialContext: clusterdialer.DialContext(cluster.Name),
    },
}

如果使用 K8s client-go 也可以借助 Dial Function 来构造对应的 rest.Config。如下:

rc := &rest.Config{
     Host:        host,
     BearerToken: cluster.ServiceAccountToken,
     TLSClientConfig: rest.TLSClientConfig{
         CAData:     append(caBytes, suffix...),
         NextProtos: []string{"http/1.1"},
     },
     Timeout:     45 * time.Second,
     RateLimiter: ratelimit.None,
     UserAgent:   rest.DefaultKubernetesUserAgent() + " cluster " + cluster.Name,
     WrapTransport: func(rt http.RoundTripper) http.RoundTripper {
         if ht, ok := rt.(*http.Transport); ok {
             ht.DialContext = clusterdialer.DialContext(cluster.Name)
         }
         return rt
     },
}

甚至如果要访问用户业务集群的 MySQL,实现 MySQL 控制台的功能,也可以实现:

import (
    "database/sql"
    ...
    "github.com/go-sql-driver/mysql"
    ...
)

...

mysql.RegisterDialContext("tcp", clusterdialer.DialContextTCP(cluster.Name))
db, _ := sql.Open("mysql", "root@tcp(127.0.0.1:3306)/")
defer db.Close()
...

结束语

一站式企业数字化平台 Erda,目前已服务过 50+ 大中型头部企业并完成交付,行业覆盖了零售、地产、园区、金融、建筑等领域。

沉淀了不少解决方案,未来也仍有一些需要改进的地方,比如支撑企业完成各种容灾方案:异地多活、两地三中心等,集群隧道高可用、安全等问题。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8