云原生應用發(fā)布組件 Triton 開源之旅

Triton 概述
伴隨著云原生技術在越來越多的企業(yè)落地,如今的 Kubernetes 和容器已經(jīng)完全進入主流市場,成為云計算的新界面,幫助企業(yè)充分享受云原生的優(yōu)勢,加速應用迭代創(chuàng)新效率,降低開發(fā)運維成本。但在向著云原生架構轉型的過程中,也有許多問題需要被解決。比如原生 Kubernetes 的復雜性、容器化應用的生命周期管理,以及向以容器為基礎設施遷移的過程中可能出現(xiàn)的服務穩(wěn)定性挑戰(zhàn)等等。
開源云原生應用發(fā)布組件 Triton 的出現(xiàn),就是為了解決企業(yè)應用在容器化過程中安全落地生產(chǎn)的問題。Triton 以 OpenKruise[1] 作為容器應用自動化引擎,實現(xiàn)應用負載的擴展增強能力,為原有持續(xù)交付系統(tǒng)帶來全面升級,不僅解決了應用生命周期管理的問題,包括開發(fā)、部署、運維等,同時打通微服務治理,可以幫助研發(fā)提升持續(xù)交付的效率。
有關 Trion 設計方案、實現(xiàn)原理的詳細介紹可以參考這篇文章。本文將從源碼級安裝、debug、demo 應用發(fā)布演示三個方面介紹 Triton 的核心特性以及 Triton 的快速上手使用、開發(fā),最后介紹 Triton 的 Roadmap。由于時間關系,一鍵安裝、Helm 安裝的方式正在開發(fā)中,會在正式版本 release 中提供。
核心能力
本次帶來的 v0.1.0 開源版本在代碼上進行了重構,暫時去掉了對 terway 網(wǎng)絡方案、微服務架構的依賴,抽象出應用模型的概念,更具備普適性,核心特性如下:
全托管于 k8s 集群,便于組件安裝、維護和升級;
支持使用 API 和 kubectl 插件(規(guī)劃中)完成應用創(chuàng)建、部署、升級,并支持單批發(fā)布、分批發(fā)布和金絲雀發(fā)布;
提供從創(chuàng)建到運行的應用全生命周期管理服務,包括應用的發(fā)布、啟動、停止、擴容、縮容和刪除等服務,可以輕松管理上千個應用實例的交付問題;
Triton 提供了大量 API 來簡化部署等操作,如Next、Cancel、Pause、Resume、Scale、Gets、Restart 等,輕松對接公司內部的 PaaS 系統(tǒng);
基于 OpenKruise 擴展,OpenKruise 是 Kubernetes 的一個標準擴展,它可以配合原生 Kubernetes 使用,并為管理應用容器、sidecar、鏡像分發(fā)等方面提供更加強大和高效的能力。
操作指南
在開始之前,檢查一下當前環(huán)境是否滿足以下前提條件:
確保環(huán)境能夠與 kube-apiserver 連通;
確保
OpenKruise已經(jīng)在當前操作 k8s 集群安裝,若未安裝可以參考文檔[2];確保有 Golang 開發(fā)環(huán)境,F(xiàn)ork & git clone 代碼后,執(zhí)行
make install安裝 CRDDeployFlow;操作 API 的過程中需要
grpcurl這個工具,參考 grpcurl 文檔[3]進行安裝;創(chuàng)建 DeployFlow 來發(fā)布 Nginx Demo Application
運行 DeployFlow controller
進入到代碼根目錄下執(zhí)行
make run創(chuàng)建 DeployFlow 準備發(fā)布應用
kubectl apply -f https://github.com/triton-io/triton/raw/main/docs/tutorial/v1/nginx-deployflow.yaml
這個操作會創(chuàng)建出一個 DeployFlow 資源和本應用對應的 Service,可以查看該 yaml 文件了解詳細的 DeployFlow 定義。
apiVersion: apps.triton.io/v1alpha1
kind: DeployFlow
metadata:
labels:
app: "12122"
app.kubernetes.io/instance: 12122-sample-10010
app.kubernetes.io/name: deploy-demo-hello
group: "10010"
managed-by: triton-io
name: 12122-sample-10010-df
namespace: default
spec:
action: create
application:
appID: 12122
appName: deploy-demo-hello
groupID: 10010
instanceName: 12122-sample-10010
replicas: 3
selector:
matchLabels:
app: "12122"
app.kubernetes.io/instance: 12122-sample-10010
app.kubernetes.io/name: deploy-demo-hello
group: "10010"
managed-by: triton-io
template:
metadata: {}
spec:
containers:
- image: nginx:latest
name: 12122-sample-10010-container
ports:
- containerPort: 80
protocol: TCP
resources: {}
updateStrategy:
batchSize: 1
batchIntervalSeconds: 10
canary: 1 # the number of canary batch
mode: auto # the mode is auto after canary batch可以看到我們本次發(fā)布的應用名字是
12122-sample-10010,副本數(shù)量是 3,批次大小是 1,有一個金絲雀批次,批次大小是 1,發(fā)布的模式是 auto,意味著本次發(fā)布只會在金絲雀批次和普通批次之間暫停,后續(xù)兩個批次會以batchIntervalSeconds為時間間隔自動觸發(fā)。檢查 DeployFlow 狀態(tài)

可以看到我們創(chuàng)建出一個名為
12122-sample-10010-df的 DeployFlow 資源,通過展示的字段了解到本次發(fā)布分為 3 個批次,當前批次的大小是 1,已升級和已完成的副本數(shù)量都是 0。啟動幾十秒后,檢查 DeployFlow 的 status 字段:
kubectl get df 12122-sample-10010-df -o yamlstatus:
availableReplicas: 0
batches: 3
conditions:
- batch: 1
batchSize: 1
canary: true
failedReplicas: 0
finishedAt: null
phase: Smoked
pods:
- ip: 172.31.230.23
name: 12122-sample-10010-2mwkt
phase: ContainersReady
port: 80
pullInStatus: ""
pulledInAt: null
startedAt: "2021-09-13T12:49:04Z"
failedReplicas: 0
finished: false
finishedAt: null
finishedBatches: 0
finishedReplicas: 0
paused: false
phase: BatchStarted
pods:
- 12122-sample-10010-2mwkt
replicas: 1
replicasToProcess: 3
startedAt: "2021-09-13T12:49:04Z"
updateRevision: 12122-sample-10010-6ddf9b7cf4
updatedAt: "2021-09-13T12:49:21Z"
updatedReadyReplicas: 0
updatedReplicas: 1可以看到目前在啟動的是
canary批次,該批次已經(jīng)處于smoked階段,該批次中的 pod 是12122-sample-10010-2mwkt,同時也能看到當前批次中 pod 的拉入狀態(tài)、拉入時間等信息。將應用拉入流量
在此之前我們可以先檢查一下 Service 的狀態(tài):
kubectl describe svc sample-12122-svc -o yaml
從顯示的結果來看,pod 12122-sample-10010-2mwkt 并沒有出現(xiàn)在 Service 的 Endpoints 中,意味著當前應用沒有正式接入流量:
Name: sample-12122-svc
Namespace: default
Labels: app=12122
app.kubernetes.io/instance=12122-sample-10010
app.kubernetes.io/name=deploy-demo-hello
group=10010
managed-by=triton-io
Annotations: <none>
Selector: app.kubernetes.io/instance=12122-sample-10010,app.kubernetes.io/name=deploy-demo-hello,app=12122,group=10010,managed-by=triton-io
Type: ClusterIP
IP Families: <none>
IP: 10.22.6.154
IPs: <none>
Port: web 80/TCP
TargetPort: 80/TCP
Endpoints:
Session Affinity: None
Events: <none>
接下來我們執(zhí)行拉入操作(Bake),對應 pod 的狀態(tài)會從 ContainerReady 變?yōu)?Ready,從而被掛載到對應 Service 的 Endpoints 上開始正式接入流量:
grpcurl --plaintext -d '{"deploy":{"name":"12122-sample-10010-df","namespace":"default"}}' localhost:8099 deployflow.DeployFlow/Next
再次檢查 DeployFlow,Service,CloneSet 的狀態(tài)后,發(fā)現(xiàn) Pod 已被掛載到 Endpoints,DeployFlow 的 UPDATED_READY_REPLICAS 字段變?yōu)?1,金絲雀批次進入 baking 階段,如果此時應用正常工作,我們再次執(zhí)行上面的 Next 操作,將 DeployFlow 置為 baked 階段,表示本批次點火成功,應用流量正常。
Rollout 操作
金絲雀批次到達 baked 階段后,執(zhí)行 Next 操作就會進入后面的普通批次發(fā)布,由于我們應用的副本數(shù)量設置為 3,去掉金絲雀批次中的一個副本后,還剩 2 個,而 batchSize 的大小為 1,所有剩余的普通批次會分兩個批次發(fā)布,兩個批次之間會間隔 10s 觸發(fā)。
grpcurl --plaintext -d '{"deploy":{"name":"12122-sample-10010-df","namespace":"default"}}' localhost:8099 deployflow.DeployFlow/Next


最后應用發(fā)布完成,檢查 DeployFlow 的狀態(tài)為 Success:

再次查看 Service 的 Endpoints 可以看到本次發(fā)布的 3 個副本都已經(jīng)掛載上去。
再次回顧整個發(fā)布流程,可以總結為下面的 DeployFlow 狀態(tài)流轉圖:

暫停/繼續(xù) DeployFlow
在部署過程中,如果要暫停 DeployFlow,可以執(zhí)行 Pause 操作:
grpcurl --plaintext -d '{"deploy":{"name":"12122-sample-10010-df","namespace":"default"}}' localhost:8099 deployflow.DeployFlow/Pause
等到應用可以繼續(xù)發(fā)布了,執(zhí)行 Resume 操作:
grpcurl --plaintext -d '{"deploy":{"name":"12122-sample-10010-df","namespace":"default"}}' localhost:8099 deployflow.DeployFlow/Resume
取消本次發(fā)布
如果在發(fā)布過程中,遇到啟動失敗,或者拉入失敗的情況,要取消本次發(fā)布,可執(zhí)行 Cancel 操作:
grpcurl --plaintext -d '{"deploy":{"name":"12122-sample-10010-df","namespace":"default"}}' localhost:8099 deployflow.DeployFlow/Cancel
啟動一個擴縮容 DeployFlow
同樣可以使用 auto 或 mannual 模式劃分多個批次來執(zhí)行擴縮容操作。當一個 CloneSet 縮容時,有時用戶傾向于刪除特定的 Pod,可以使用 podsToDelete 字段實現(xiàn)指定 Pod 縮容:
kubectl get pod | grep 12122-sample
12122-sample-10010-2mwkt 1/1 Running 0 29m
12122-sample-10010-hgdp6 1/1 Running 0 9m55s
12122-sample-10010-zh98f 1/1 Running 0 10m
我們在縮容的時候指定被縮掉的 Pod 為 12122-sample-10010-zh98f:
grpcurl --plaintext -d '{"instance":{"name":"12122-sample-10010","namespace":"default"},"replicas":2,"strategy":{"podsToDelete":["12122-sample-10010-zh98f"],"batchSize":"1","batches":"1","batchIntervalSeconds":10}}' \
localhost:8099 application.Application/Scale
{
"deployName": "12122-sample-10010-kvn6b"
}
? kubectl get pod | grep 12122-sample
12122-sample-10010-2mwkt 1/1 Running 0 29m
12122-sample-10010-zh98f 1/1 Running 0 11m
CloneSet 被縮容為 2 個副本,被縮容的 Pod 正是指定的那個。該功能的實現(xiàn)得益于 OpenKruise 中增強型無狀態(tài) workload CloneSet 提供的能力,具體的功能描述可以參考 OpenKruise 文檔。
在操作過程中,Triton 也提供了 Get 方法實時獲取當前 DeployFlow 的 Pod 信息:
grpcurl --plaintext -d '{"deploy":{"name":"12122-sample-10010-df","namespace":"default"}}' localhost:8099 deployflow.DeployFlow/Get
{
"deploy": {
"namespace": "default",
"name": "12122-sample-10010-df",
"appID": 12122,
"groupID": 10010,
"appName": "deploy-demo-hello",
"instanceName": "12122-sample-10010",
"replicas": 3,
"action": "create",
"availableReplicas": 3,
"updatedReplicas": 3,
"updatedReadyReplicas": 3,
"updateRevision": "6ddf9b7cf4",
"conditions": [
{
"batch": 1,
"batchSize": 1,
"canary": true,
"phase": "Baked",
"pods": [
{
"name": "12122-sample-10010-2mwkt",
"ip": "172.31.230.23",
"port": 80,
"phase": "Ready",
"pullInStatus": "PullInSucceeded"
}
],
"startedAt": "2021-09-13T12:49:04Z",
"finishedAt": "2021-09-13T13:07:43Z"
},
{
"batch": 2,
"batchSize": 1,
"phase": "Baked",
"pods": [
{
"name": "12122-sample-10010-zh98f",
"ip": "172.31.226.94",
"port": 80,
"phase": "Ready",
"pullInStatus": "PullInSucceeded"
}
],
"startedAt": "2021-09-13T13:07:46Z",
"finishedAt": "2021-09-13T13:08:03Z"
},
{
"batch": 3,
"batchSize": 1,
"phase": "Baked",
"pods": [
{
"name": "12122-sample-10010-hgdp6",
"ip": "172.31.227.215",
"port": 80,
"phase": "Ready",
"pullInStatus": "PullInSucceeded"
}
],
"startedAt": "2021-09-13T13:08:15Z",
"finishedAt": "2021-09-13T13:08:45Z"
}
],
"phase": "Success",
"finished": true,
"batches": 3,
"batchSize": 1,
"finishedBatches": 3,
"finishedReplicas": 3,
"startedAt": "2021-09-13T12:49:04Z",
"finishedAt": "2021-09-13T13:08:45Z",
"mode": "auto",
"batchIntervalSeconds": 10,
"canary": 1,
"updatedAt": "2021-09-13T13:08:45Z"
}
}
TODOS
上面演示的就是 Triton 提供的核心能力。對于基礎團隊來說,Triton 不僅僅是一個開源項目,它也是一個真實的比較接地氣的云原生持續(xù)交付項目。通過開源,我們希望 Triton 能豐富云原生社區(qū)的持續(xù)交付工具體系,為更多開發(fā)者和企業(yè)搭建云原生化的 PaaS 平臺助力,提供一種現(xiàn)代的、高效的的技術方案。
開源只是邁出的一小步,未來我們會進一步推動 Triton 不斷走向完善,包括但不限于以下幾點:
支持自定義注冊中心,可以看到目前 Triton 采用的是 k8s 原生的 Service 作為應用的注冊中心,但據(jù)我們所了解,很多企業(yè)都使用自定義的注冊中心,比如 spring cloud 的 Nacos 等; 提供 helm 一鍵安裝方式; 完善 REST & GRPC API 以及相應文檔; 結合用戶需求,持續(xù)迭代。項目開源后,我們也會根據(jù)開發(fā)者需求開展迭代。
歡迎大家向 Triton 提交 issue 和 PR 共建 Triton 社區(qū)。我們誠心期待更多的開發(fā)者加入,也期待 Triton 能夠助力越來越多的企業(yè)快速構建云原生持續(xù)交付平臺。如果有企業(yè)或者用戶感興趣,我們可以提供專項技術支持和交流,歡迎入群咨詢。
相關鏈接
項目地址:https://github.com/triton-io/triton
交流群
參考資料
OpenKruise: https://openkruise.io/zh-cn/docs/what_is_openkruise.html
[2]OpenKruise 安裝文檔: https://openkruise.io/en-us/docs/installation.html
[3]grpcurl 安裝文檔: https://github.com/fullstorydev/grpcurl
