把整個技術(shù)平臺送上云的開源項目

今天介紹的開源項目是用 Go 語言寫的一站式云原生 PaaS 平臺——Erda,專為企業(yè)提供 DevOps、微服務(wù)治理、多云管理以及快數(shù)據(jù)管理等平臺級服務(wù)。
GitHub 地址→https://github.com/erda-project/erda
Erda 借助云原生的優(yōu)勢為企業(yè)打造一個完整的技術(shù)平臺,助力企業(yè)數(shù)字化轉(zhuǎn)型。并且已通過大量的企業(yè)成功交付案例,積累了大量經(jīng)驗。放心地把服務(wù)交給 Erda 打理吧!

進化歷程
Erda 的演進歷程:

Erda 很早就開始探索云原生 DevOps 實踐,最初的平臺只擁抱了 Docker 實現(xiàn)了基于 Docker 的手動部署平臺,整個設(shè)計理念是面向資源的。(2016年:容器化)
而后隨著容器編排技術(shù)的興起,選型了 DC/OS(mesos+marathon)作為容器服務(wù)技術(shù)底座。選擇 mesos 也是因為它的出現(xiàn)要遠(yuǎn)早于 Docker、K8s、Swarm 等,有不少大規(guī)模的落地案例,穩(wěn)定性得到了很好的驗證,并且還有很強的擴展能力,在 mesos 之上除了有 marathon、metronome 等服務(wù)、任務(wù)的調(diào)度框架,還支持了不少大數(shù)據(jù)的框架,比如 Hadoop、Spark 等,這與我們需要發(fā)展大數(shù)據(jù)平臺不謀而合。我們的設(shè)計理念也調(diào)整到以應(yīng)用為中心,去面向開發(fā)者。(2017年:DC/OS)
前期交付的都用一個集群將平臺完整的私有化輸出到客戶環(huán)境,但隨著客戶越來越多資源成本變成了一個大問題。Erda 開始考慮將平臺能力 SaaS 化,提供企業(yè)租戶,并支持多集群管理。客戶側(cè)的集群不需要再部署一個完整的 Erda 平臺,只需開放口子可以給平臺訪問即可。在零產(chǎn)品能力損失的情況下,節(jié)省了大量的資源,并且客戶不需要再關(guān)心平臺的維護問題。(2018年:多集群架構(gòu))
Kubernetes 逐漸成為了業(yè)界的事實標(biāo)準(zhǔn),Erda 提前預(yù)知了這個趨勢。 在支持多集群架構(gòu)時就將容器編排層進行了封裝抽象,并引入了插件的機制便于未來的擴展。所以后面很平滑地將平臺全部切至了 K8s。(2019年:支持 Kubernetes)

Erda 用多集群管理解決的痛點
如今數(shù)字化轉(zhuǎn)型已經(jīng)成為現(xiàn)階段企業(yè)發(fā)展的主流趨勢,該趨勢也促使著云市場的快速增長。我們已經(jīng)處于云時代,而面向企業(yè)又會出現(xiàn)混合云、多云的架構(gòu)。
混合云:私有云和公有云相互搭配在一起使用。該場景下企業(yè)主要結(jié)合經(jīng)濟效益或者安全因素進行考量,來獲取云計算的各方優(yōu)勢,取長補短。公有云可以獲得更多彈性伸縮的能力,私有云可以用于存放企業(yè)數(shù)據(jù)。或者就是企業(yè)在嘗試遷移上云的一個中間態(tài)等等。 多云:包含兩個及以上公有云服務(wù)提供商。這類場景,客戶更多是不希望被單個云服務(wù)提供商鎖定,把雞蛋放在一個菜籃子。或者是因為地理位置的原因需要選擇其他的服務(wù)商。
企業(yè)總可能出于各種原因進行多云、混合云的決策。而 Erda 面向企業(yè)進行交付時,必然也少不了會面對這些場景。Erda 最終都通過多集群管理的方式輸出解決方案,為不同的環(huán)境搭建不同的集群,由平臺側(cè)統(tǒng)一管理,上層業(yè)務(wù)可以按需選擇不同的集群進行應(yīng)用的生命周期管理。
除了這些,一些其他的場景也可通過多集群管理的方式解決,比如:
開發(fā)/測試/預(yù)發(fā)/生產(chǎn)環(huán)境隔離,把這些環(huán)境部署到不同的集群中; 業(yè)務(wù)與數(shù)據(jù)的環(huán)境隔離,分離到不同的集群中; 提升可擴展性,突破單一集群的節(jié)點上線; 等等
綜上所述,針對多集群管理,Erda 除了壓縮交付的資源訴求,還輸出了大量的解決方案,來滿足客戶的不同場景。

跨集群管理經(jīng)驗分享
上面主要介紹了 Erda 多集群管理的一個背景,及 ToB 交付時如何滿足企業(yè)的各種場景需求。回到多集群管理會面臨的一個大問題,就是如何跨集群進行訪問、管理?
經(jīng)歷的階段
Erda 在 DC/OS 時代,使用了最簡單的 basic auth 的方式,直連集群進行管控。

隨著客戶量越來越大,并且平臺支持異構(gòu)調(diào)度之后,管理難度和安全問題凸顯。Erda 實現(xiàn)了一套 netportal 的網(wǎng)絡(luò)管控鏈路,中心有一個 netportal 組件,邊緣則需要在集群端的 nginx configuration 中加一段配置。中心訪問用戶集群時會統(tǒng)一通過 netportal 進行代理,將流量轉(zhuǎn)發(fā)到對端集群的 nginx 上。netportal 和 nginx 之間采用了 https 雙向認(rèn)證的方式進行建連,并且所有的證書都由中心統(tǒng)一簽發(fā),提升了通道的安全性。

隨著產(chǎn)品戰(zhàn)略的提升,開始建設(shè) Erda Cloud,加強 Erda 產(chǎn)品的商業(yè)化能力,讓用戶可以自助地在平臺上完成一系列操作,這個就對 netportal 鏈路產(chǎn)生了巨大挑戰(zhàn)。netportal 有不少依賴,比如集群端的 nginx 版本,nginx 需要增加額外的配置,對應(yīng)的證書需要由中心統(tǒng)一簽發(fā)等等,這些都無法讓用戶完成自助操作。而此時 Erda 的容器服務(wù)默認(rèn)換成了 Kubernetes,針對 Kubernetes 訪問方式進行了擴展,引入了社區(qū)常見的訪問方式:KubeConfig、ServiceAccount(Token)。

KubeConfig、ServiceAccount 的方式,會要求客戶將 Kubernetes 集群的 ApiServer 暴露在公網(wǎng)上,由平臺進行直連。如果沒有外加安全措施,這種直連會存在一定的安全隱患。還碰到了有客戶無法提供公網(wǎng)入口 IP,而上面所有的方式都是需要有公網(wǎng)入口才能實現(xiàn)。還好客戶還不是全封閉的,可以訪問公網(wǎng),于是 Erda 借鑒了 Rancher 的做法,實現(xiàn)了 dialer 的通道管理,可以不必讓 Erda 去連接用戶的集群,而是在用戶的集群安裝 cluster agent 來連接 Erda,讓用戶集群主動建立一條連接,這條連接就變成了 Erda 去管控用戶集群的隧道(Tunnel)。

核心組件
Cluster Agent:部署在用戶業(yè)務(wù)集群的組件,會主動去連接 Erda 的 Cluster Dialer,同時會將一些集群信息做上報,例如集群內(nèi) kube-apiserver 的地址、訪問 token 等。 Cluster Dialer:接收 Cluster Agent 的連接,同時將集群信息注冊到 Cluster Manager,跟 Cluster Agent 之間的連接通過 Tunnel Session 維護起來。
Erda 控制平面內(nèi)的其他組件比如 K8s Manager,可以通過請求 Cluster Dialer,找到要管控集群的 Tunnel Session,再通過這個 Tunnel 去訪問對應(yīng)集群中的 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 服務(wù),可以通過如下方式構(gòu)造 client:
client = &http.Client{
Transport: &http.Transport{
DialContext: clusterdialer.DialContext(cluster.Name),
},
}
如果使用 K8s client-go 也可以借助 Dial Function 來構(gòu)造對應(yīng)的 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
},
}
甚至如果要訪問用戶業(yè)務(wù)集群的 MySQL,實現(xiàn) MySQL 控制臺的功能,也可以實現(xiàn):
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()
...
結(jié)束語
一站式企業(yè)數(shù)字化平臺 Erda,目前已服務(wù)過 50+ 大中型頭部企業(yè)并完成交付,行業(yè)覆蓋了零售、地產(chǎn)、園區(qū)、金融、建筑等領(lǐng)域。
沉淀了不少解決方案,未來也仍有一些需要改進的地方,比如支撐企業(yè)完成各種容災(zāi)方案:異地多活、兩地三中心等,集群隧道高可用、安全等問題。
同時 Erda 擁有一個非常開放的社區(qū):
GitHub 地址→https://github.com/erda-project/erda
歡迎廣大的開源愛好者和用戶加入共同建設(shè),如果你有任何問題,歡迎進群交流!添加下面小助手的微信,即可進群。
??小助手的微信??
在混合云的時代下,以應(yīng)用為中心的多集群架構(gòu),也是發(fā)展的必然趨勢。未來企業(yè)的云可以運行在任何地方,交給 Erda 來幫你管理。讓我們一起擁抱云原生,一起關(guān)注應(yīng)用本身的價值!
GitHub 地址:https://github.com/erda-project/erda 官網(wǎng):https://www.erda.cloud/
