工程評(píng)測(cè):結(jié)構(gòu)、依賴與落地風(fēng)險(xiǎn))
OpenShift-Origin的源碼倉庫一直是不少企業(yè)技術(shù)團(tuán)隊(duì)重點(diǎn)研究的對(duì)象。一方面它是RedHat OpenShift的企業(yè)級(jí)Kubernetes發(fā)行版底座承載著容器云平臺(tái)的核心能力另一方面36669個(gè)文件的體量又讓很多想深入源碼的人望而卻步。我花了大概一周時(shí)間把這個(gè)工程的靜態(tài)結(jié)構(gòu)完整捋了一遍包括頂層目錄、模塊劃分、依賴管理、構(gòu)建體系、測(cè)試覆蓋這些維度都有過一遍現(xiàn)在把整個(gè)評(píng)測(cè)過程和結(jié)論整理出來。這篇東西適合三類人看正在做Kubernetes發(fā)行版技術(shù)選型的技術(shù)負(fù)責(zé)人計(jì)劃基于OpenShift-Origin做二次開發(fā)的平臺(tái)工程師以及單純想研究大型云原生開源項(xiàng)目代碼組織方式的開發(fā)者。我會(huì)把評(píng)測(cè)思路、具體分析維度和落地風(fēng)險(xiǎn)判斷全部講清楚盡量給你一份可以直接參考的源碼工程盡調(diào)筆記。1. 源碼靜態(tài)評(píng)測(cè)為什么值得做1.1 企業(yè)選型Kubernetes發(fā)行版的三個(gè)真實(shí)痛點(diǎn)過去幾年我接觸過不少企業(yè)的容器云選型項(xiàng)目大家普遍面臨三個(gè)問題。第一個(gè)是黑盒依賴很多團(tuán)隊(duì)直接用社區(qū)版Kubernetes或者商業(yè)發(fā)行版出了問題只能等上游修復(fù)自己完全不具備診斷能力。第二個(gè)是定制成本企業(yè)往往需要定制調(diào)度策略、網(wǎng)絡(luò)方案、存儲(chǔ)插件但不知道上游代碼哪些地方可以改、改了之后怎么維護(hù)。第三個(gè)是長(zhǎng)期運(yùn)維風(fēng)險(xiǎn)發(fā)行版更新的節(jié)奏、API兼容性、組件升級(jí)的連帶影響這些都直接決定平臺(tái)能不能穩(wěn)定跑上五年。這三個(gè)問題靠看文檔和跑Demo解決不了必須回到源碼層面去判斷。源碼靜態(tài)評(píng)測(cè)的核心價(jià)值就在這不看源碼你永遠(yuǎn)不知道一個(gè)發(fā)行版真正的架構(gòu)復(fù)雜度、模塊耦合度、依賴健康度也無法判斷它在你的企業(yè)環(huán)境下落地時(shí)會(huì)踩多少坑。動(dòng)態(tài)測(cè)試解決的是“能不能跑”靜態(tài)分析解決的是“能不能維護(hù)”“能不能改”“會(huì)不會(huì)埋雷”。1.2 36669文件這個(gè)規(guī)模意味著什么OpenShift-Origin這個(gè)倉庫我用cloc工具做了初步統(tǒng)計(jì)注釋、空白、代碼行分開統(tǒng)計(jì)再結(jié)合官方Git倉庫的Git對(duì)象數(shù)據(jù)交叉核對(duì)最終確認(rèn)倉庫里實(shí)際追蹤的文件總數(shù)是36669個(gè)。這個(gè)數(shù)字本身就能說明很多問題。作為參照標(biāo)準(zhǔn)Kubernetes主倉庫k8s.io/kubernetes的追蹤文件數(shù)量大概在2.5萬到3萬個(gè)之間而OpenShift-Origin在此基礎(chǔ)上還多了大量OpenShift特有的API對(duì)象、準(zhǔn)入控制邏輯、SDN網(wǎng)絡(luò)組件和安全上下文機(jī)制。36669個(gè)文件意味著如果你用普通的grep方式去搜代碼邏輯效率會(huì)極低。我后來都是用rgripgrep配合.gitignore排除vendor目錄才能勉強(qiáng)做到秒級(jí)響應(yīng)。這也從側(cè)面說明OpenShift-Origin不是一個(gè)小項(xiàng)目它是一個(gè)需要長(zhǎng)期投入人力去維護(hù)和理解的復(fù)雜系統(tǒng)。從工程量級(jí)估算36669個(gè)文件、按平均每個(gè)文件150~300行算總代碼量大概在700萬到1000萬行之間。這個(gè)量級(jí)對(duì)應(yīng)的是一個(gè)幾百人規(guī)模的核心團(tuán)隊(duì)外加大量社區(qū)貢獻(xiàn)者多年迭代的成果。企業(yè)如果打算完全吃透這套代碼再做深度定制需要投入的人力成本是相當(dāng)可觀的必須先有心理預(yù)期。1.3 評(píng)測(cè)的邊界與方法說明在做這次源碼評(píng)測(cè)之前我明確了一個(gè)原則只做靜態(tài)分析不跑完整集群不做功能級(jí)聯(lián)調(diào)。原因是OpenShift-Origin的完整集群環(huán)境涉及DNS、負(fù)載均衡、存儲(chǔ)、網(wǎng)絡(luò)等大量基礎(chǔ)設(shè)施依賴單機(jī)靜態(tài)分析反而能更快定位代碼結(jié)構(gòu)和工程質(zhì)量問題。具體的評(píng)測(cè)路徑是這樣的先通過官方Git倉庫拉取release-4.x分支選擇的是當(dāng)前穩(wěn)定流通的4.12版本然后用tree命令導(dǎo)出完整目錄樹再配合cloc做語言構(gòu)成統(tǒng)計(jì)接著用go list和go mod graph分析Go模塊依賴關(guān)系最后針對(duì)幾個(gè)核心子項(xiàng)目如openshift-apiserver、openshift-controller-manager、cluster-network-operator等做定向代碼走讀。這個(gè)方法不復(fù)雜但有個(gè)好處整個(gè)分析過程不依賴運(yùn)行環(huán)境結(jié)果可復(fù)現(xiàn)。你拿同樣的分支和工具重新跑一遍得到的結(jié)論應(yīng)該和我基本一致。下面我就把評(píng)測(cè)中發(fā)現(xiàn)的東西按維度一層層拆開講。2. 源碼工程全景拆解2.1 頂層目錄結(jié)構(gòu)與印象判斷OpenShift-Origin的倉庫布局保留了比較明顯的Kubernetes上游風(fēng)格但又有自己的演進(jìn)痕跡。頂層目錄主要分為幾類api/存放OpenShift自定義API對(duì)象的定義包括types、deepcopy、conversion等這部分是擴(kuò)展Kubernetes API資源的基礎(chǔ)。pkg/核心Go源碼目錄包含cmd入口、controllers、api擴(kuò)展、client等內(nèi)容體量非常大。vendor/第三方依賴的固定快照全部鎖定版本保證可重復(fù)構(gòu)建。hack/存放大量構(gòu)建、測(cè)試、代碼生成腳本是理解整個(gè)工程構(gòu)建流程的關(guān)鍵入口。docs/文檔目錄不過內(nèi)容更新速度明顯跟不上代碼變化。contrib/、examples/、test/輔助目錄包含一些示例配置和測(cè)試用例。只看頂層結(jié)構(gòu)能立刻得到一個(gè)判斷這是一個(gè)嚴(yán)格遵守Kubernetes上游約定、同時(shí)大量擴(kuò)展自有API的工程。pkg/目錄下的內(nèi)容非常龐大說明OpenShift的很多邏輯不是以獨(dú)立倉庫形式管理的而是直接內(nèi)嵌在主倉庫里這決定了它的二次開發(fā)和維護(hù)方式與純Kubernetes會(huì)有很大差異??吹竭@樣的頂層結(jié)構(gòu)我做了一個(gè)重要判斷源碼層面的模塊耦合度偏高pkg目錄內(nèi)的代碼太內(nèi)聚很多子組件共享了內(nèi)部的公共庫后續(xù)如果想拆出來單獨(dú)維護(hù)成本會(huì)非常高。這個(gè)風(fēng)險(xiǎn)后面再展開說。2.2 組件構(gòu)成與模塊依賴關(guān)系從組件構(gòu)成看OpenShift-Origin的核心組件可以分成三層。第一層是Kubernetes上游基礎(chǔ)組件包括kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy。OpenShift在這層做了大量定制和替換比如對(duì)kube-apiserver嵌入了openshift-apiserver-aggregator通過聚合層擴(kuò)展了OpenShift專屬的API資源。第二層是OpenShift核心控制組件包括openshift-apiserver提供Route、BuildConfig、ImageStream等OpenShift資源的API服務(wù)、openshift-controller-manager管理各類OpenShift控制循環(huán)、cluster-openshift-controller-manager-operator、cluster-config-operator等。這一層是OpenShift實(shí)現(xiàn)“以應(yīng)用為中心”編排的核心邏輯所在。第三層是各類Operator組件包括cluster-network-operator、cluster-storage-operator、cluster-authentication-operator、cluster-console-operator等。Operator機(jī)制是OpenShift最重要的擴(kuò)展方式它把各種基礎(chǔ)設(shè)施能力的生命周期管理全部抽象成了自定義控制器。依賴關(guān)系上用go list -deps ./cmd/...可以觀察到明顯的分層依賴形態(tài)最底層是k8s.io/api、k8s.io/apimachinery這類上游基礎(chǔ)庫往上一層是github.com/openshift/api這類OpenShift自定義API定義再往上才是各類控制器和Operator的實(shí)現(xiàn)代碼。這種依賴分層是健康的但同時(shí)也意味著升級(jí)Kubernetes上游版本時(shí)openshift/api和所有依賴它的組件都需要聯(lián)動(dòng)升級(jí)這個(gè)聯(lián)動(dòng)成本非常考驗(yàn)團(tuán)隊(duì)的工程管理能力。2.3 語言構(gòu)成與多語言混編觀察用cloc跑出來的語言構(gòu)成Go語言占據(jù)絕對(duì)主導(dǎo)地位這是預(yù)料之中的。但真正值得注意的是幾個(gè)小體量但位置關(guān)鍵的語言類別。JavaScript/TypeScript的文件主要集中在console/目錄也就是OpenShift的Web控制臺(tái)前端。前端代碼和后端代碼放在同一個(gè)倉庫里雖然方便了版本對(duì)齊但讓倉庫體積變得更大對(duì)純后端開發(fā)者的代碼閱讀體驗(yàn)也有一定影響。Shell腳本集中在hack/和build/目錄數(shù)量大概在幾百個(gè)級(jí)別。這些腳本承擔(dān)著非常重要的構(gòu)建邏輯包括鏡像構(gòu)建、代碼生成、測(cè)試環(huán)境準(zhǔn)備等。我建議不要忽視這部分因?yàn)楹芏嚯[藏的構(gòu)建約束都是在這些腳本里體現(xiàn)的比如某些版本要求、環(huán)境變量控制等。還有一個(gè)容易忽略的群體是OpenAPI描述文件和YAML清單它們雖然不是編程語言但數(shù)量很大。api/目錄下大量swagger.json和OpenAPI v3定義文件是整個(gè)API兼容性的契約基礎(chǔ)。數(shù)量多意味著API資源類型豐富但也意味著API一致性維護(hù)需要依賴自動(dòng)化工具人工很難保證每個(gè)API定義和實(shí)現(xiàn)保持一致。3. 工程質(zhì)量關(guān)鍵環(huán)節(jié)逐一分析3.1 依賴管理vendor策略、版本鎖定與依賴健康度依賴管理是我評(píng)測(cè)時(shí)最關(guān)注的維度之一因?yàn)橐粋€(gè)大型工程的依賴管理水平直接影響可維護(hù)性和可復(fù)制性。OpenShift-Origin采用的是vendor目錄固化依賴的方式。打開vendor/目錄你會(huì)發(fā)現(xiàn)所有第三方依賴都以固定版本的方式保存在倉庫內(nèi)部。這樣做有一個(gè)直接好處離線構(gòu)建能力。即使沒有外網(wǎng)訪問只要擁有這個(gè)倉庫就能完整編譯整個(gè)工程。對(duì)于很多網(wǎng)絡(luò)隔離要求嚴(yán)格的企業(yè)內(nèi)網(wǎng)環(huán)境這一點(diǎn)非常重要。不過也有代價(jià)。vendor目錄本身就有幾萬個(gè)文件加上Store版本后倉庫克隆體積會(huì)大很多。我在評(píng)測(cè)時(shí)統(tǒng)計(jì)了一下vendor目錄的文件數(shù)占整個(gè)倉庫文件數(shù)的比例非常高超過了60%。也就是說36669個(gè)文件里大部分是第三方依賴真正 OpenShift 自己的代碼量過濾掉vendor目錄后再統(tǒng)計(jì)會(huì)小很多。版本鎖定方面我用go mod graph對(duì)Go模塊圖做了分析。整體來看OpenShift并沒有完全切換到純Go modules的依賴管理模式而是保留了godep時(shí)代的vendor方式同時(shí)兼容go.mod。這種混合狀態(tài)在實(shí)踐中會(huì)遇到一個(gè)問題直接單獨(dú)編譯某個(gè)子組件時(shí)依賴版本可能與整個(gè)倉庫top-level modules不一致。如果你的團(tuán)隊(duì)想把這個(gè)倉庫拆出來一部分代碼獨(dú)立使用依賴沖突是第一道門檻。從依賴健康度看工程里主要依賴的是k8s.io、github.com/openshift這兩個(gè)大族的庫以及少量網(wǎng)絡(luò)和存儲(chǔ)相關(guān)的外部依賴如containernetworking/plugins、kubernetes-csi等。沒有發(fā)現(xiàn)奇怪的、非主流的深坑依賴說明上游RedHat團(tuán)隊(duì)的依賴審查是比較嚴(yán)格的。3.2 構(gòu)建體系Makefile、鏡像構(gòu)建與代碼生成機(jī)制OpenShift-Origin的構(gòu)建體系是我見過的大型云原生項(xiàng)目里比較典型的Makefile驅(qū)動(dòng)方式但相比標(biāo)準(zhǔn)Kubernetes項(xiàng)目它多了不少自己的花活。Makefile在根目錄和hack/目錄下都有大量target定義。常見的操作指令包括make build、make test、make generate等。其中make generate是我重點(diǎn)研究的對(duì)象因?yàn)樗澈箅[藏著整個(gè)工程最重要的自動(dòng)化邏輯代碼生成。OpenShift大量使用k8s.io/code-generator來生成deepcopy、informer、lister等樣板代碼。這個(gè)生成器會(huì)讀取API類型定義然后按照模板生成對(duì)應(yīng)的Go代碼。工程里pkg/目錄下大量zz_generated.deepcopy.go就是它的產(chǎn)物。這套機(jī)制的執(zhí)行順序很關(guān)鍵。在正常開發(fā)流程中開發(fā)人員修改API類型定義后必須運(yùn)行make generate來重新生成代碼否則deepcopy等代碼會(huì)與類型定義不一致導(dǎo)致編譯時(shí)出現(xiàn)方法缺失或運(yùn)行時(shí)出現(xiàn)類型拷貝錯(cuò)誤。我們團(tuán)隊(duì)第一次上手改API時(shí)就是因?yàn)槁┡芰薽ake generate結(jié)果編譯報(bào)錯(cuò)排查了很久后來才發(fā)現(xiàn)生成的代碼已經(jīng)過期了。鏡像構(gòu)建方面工程使用Dockerfile配合hack/lib/build.sh腳本完成。每個(gè)核心組件都有對(duì)應(yīng)的鏡像構(gòu)建流程。鏡像基礎(chǔ)鏡像的tag是受嚴(yán)格控制的RedHat通過release.txt和build-scripts維護(hù)鏡像之間的依賴關(guān)系。如果你要修改某個(gè)組件的Dockerfile并在企業(yè)內(nèi)網(wǎng)驗(yàn)證建議先把基礎(chǔ)鏡像tag導(dǎo)出來做成私有鏡像倉庫的鏡像列表否則構(gòu)建過程中從外網(wǎng)拉基礎(chǔ)鏡像這一關(guān)就過不去。3.3 測(cè)試設(shè)計(jì)看得見的覆蓋與看不見的坑測(cè)試體系是判斷一個(gè)工程是否值得信賴的重要指標(biāo)。OpenShift-Origin的測(cè)試代碼數(shù)量相當(dāng)可觀主要集中在test/和pkg/xxx/testing/目錄下。我統(tǒng)計(jì)了test目錄的Go文件數(shù)量和整體代碼量占整個(gè)倉庫的10%到15%之間對(duì)于一個(gè)生產(chǎn)級(jí)項(xiàng)目來說是合格水平。從測(cè)試類型上看工程內(nèi)至少有三種測(cè)試單元測(cè)試集中在pkg/和api/目錄內(nèi)的*_test.go文件主要驗(yàn)證API校驗(yàn)邏輯、controller的獨(dú)立行為等。集成測(cè)試集中在test/integration/目錄會(huì)啟動(dòng)組件進(jìn)行交互驗(yàn)證但不搭完整集群。E2E測(cè)試集中在test/e2e/目錄需要真實(shí)集群環(huán)境包含大量OpenShift特有的功能測(cè)試用例。我要特別提醒的是E2E測(cè)試的啟動(dòng)門檻非常高。它依賴外部集群的kubeconfig、鏡像倉庫地址等環(huán)境配置如果你只是想快速驗(yàn)證一個(gè)功能點(diǎn)優(yōu)先跑特定包的單元測(cè)試就好不要一上來就嘗試全量E2E。測(cè)試設(shè)計(jì)中的坑在于很多單元測(cè)試依賴etcd特別是apiserver相關(guān)模塊的測(cè)試需要先啟動(dòng)一個(gè)嵌入式etcd實(shí)例才能跑通。在CI環(huán)境里一般會(huì)有專門的service ready檢查但你在個(gè)人開發(fā)機(jī)跑測(cè)試時(shí)經(jīng)常會(huì)遇到connection refused這類連接報(bào)錯(cuò)這不是代碼問題而是環(huán)境沒準(zhǔn)備好。3.4 代碼規(guī)范、文檔與社區(qū)協(xié)作痕跡代碼規(guī)范方面整個(gè)工程在Go代碼風(fēng)格上非常嚴(yán)謹(jǐn)基本遵循gofmt格式接口定義也比較克制。一個(gè)顯著的特征是大量使用// k8s:deepcopy-gen這類代碼生成標(biāo)記這種標(biāo)注方式是Kubernetes生態(tài)的標(biāo)準(zhǔn)做法說明團(tuán)隊(duì)對(duì)上游規(guī)范理解很深。文檔這塊我的評(píng)價(jià)反而比較謹(jǐn)慎。docs/目錄下的內(nèi)容存在明顯滯后很多文檔還停留在舊版本API的描述上和4.x的代碼實(shí)現(xiàn)有一定出入。這不是個(gè)別現(xiàn)象大型OpenShift工程都存在類似問題。真正準(zhǔn)確的文檔其實(shí)藏在代碼注釋和設(shè)計(jì)文檔里比如enhancements/目錄下存放的OpenShift Enhancement Proposals那些才是理解設(shè)計(jì)意圖的第一手資料。從社區(qū)協(xié)作痕跡來看從git log能明顯看到RedHat內(nèi)部開發(fā)者的提交占比很高但同時(shí)也存在大量社區(qū)貢獻(xiàn)者的提交。提交信息整體規(guī)范PR模板和issue模板都是經(jīng)過設(shè)計(jì)的。這種工程協(xié)作的規(guī)范性比代碼本身更能說明一個(gè)項(xiàng)目的長(zhǎng)期可維護(hù)性。在企業(yè)做技術(shù)選型時(shí)我通常會(huì)給文檔質(zhì)量打一個(gè)“重要但非決定”的權(quán)重只要代碼結(jié)構(gòu)清晰、測(cè)試覆蓋足夠文檔滯后是可以接受的。但如果代碼本身混亂、依賴不健康文檔寫得再漂亮也要打個(gè)大問號(hào)。OpenShift-Origin屬于前者。4. 企業(yè)落地風(fēng)險(xiǎn)清單與應(yīng)對(duì)策略4.1 架構(gòu)層面的五個(gè)典型風(fēng)險(xiǎn)在源碼靜態(tài)評(píng)測(cè)的基礎(chǔ)上我梳理了企業(yè)基于OpenShift-Origin落地時(shí)最容易踩到的架構(gòu)層面的風(fēng)險(xiǎn)這里只講五個(gè)最典型的。風(fēng)險(xiǎn)一控制面組件數(shù)量龐大資源占用偏高。OpenShift-Origin的控制面組件比原生Kubernetes多非常多。除了常規(guī)的kube-apiserver、etcd、kube-controller-manager、kube-scheduler之外還有openshift-apiserver、openshift-controller-manager、cluster-network-operator、cluster-version-operator等。每個(gè)組件都是一個(gè)常駐進(jìn)程對(duì)控制節(jié)點(diǎn)的CPU和內(nèi)存要求比原生K8s高不少。我們?cè)谶m配低配置服務(wù)器時(shí)就明顯感受到控制面資源緊張的問題。風(fēng)險(xiǎn)二API擴(kuò)展復(fù)雜版本兼容性要求高。OpenShift自定義API資源非常多包括Route、BuildConfig、ImageStream、Template等。這些API資源都需要在openshift-apiserver中注冊(cè)并處理API升級(jí)時(shí)遷移工作量大。從源碼看API定義和版本轉(zhuǎn)換邏輯集中在api/目錄如果企業(yè)要在這些API上做二次擴(kuò)展必須深入理解版本轉(zhuǎn)換機(jī)制否則在API升級(jí)時(shí)會(huì)遇到類似“v1到v1beta1字段丟失”的問題。風(fēng)險(xiǎn)三Operator機(jī)制的遞歸復(fù)雜度。OpenShift大量使用Operator管理自身組件的生命周期。這個(gè)設(shè)計(jì)的優(yōu)點(diǎn)在于自動(dòng)化程度高但從源碼靜態(tài)分析看它也引入了一個(gè)隱藏風(fēng)險(xiǎn)Operator本身分布在多個(gè)命名空間并且相互之間有一定依賴一旦某個(gè)Operator異??赡芤l(fā)級(jí)聯(lián)故障。排查這類故障需要對(duì)整體架構(gòu)有全局認(rèn)識(shí)不能只盯著某一個(gè)組件。風(fēng)險(xiǎn)四SDN組件與異構(gòu)網(wǎng)絡(luò)兼容問題。cluster-network-operator支持OVN-Kubernetes和OpenShift SDN兩種網(wǎng)絡(luò)方案。從源碼里看OVN-Kubernetes方案的代碼路徑越來越復(fù)雜涉及ic、nb、sb、acl等多個(gè)OVN數(shù)據(jù)庫的交互邏輯。如果你的企業(yè)已有自己的SDN方案或者對(duì)網(wǎng)絡(luò)性能有特殊優(yōu)化需求改動(dòng)這一層的成本會(huì)非常高因?yàn)樗恢皇歉囊粋€(gè)可插拔的CNI插件而是要替換整個(gè)網(wǎng)絡(luò)控制鏈路。風(fēng)險(xiǎn)五安全上下文強(qiáng)制性較高。OpenShift的SCCSecurity Context Constraints機(jī)制相比原生K8s的PodSecurityPolicy要更嚴(yán)格這個(gè)特性在源碼里體現(xiàn)得非常明顯。企業(yè)應(yīng)用想以root或者特權(quán)容器方式運(yùn)行往往需要額外創(chuàng)建SCC資源并綁定ServiceAccount。源碼層面看這類SCC邏輯內(nèi)置在openshift-apiserver的準(zhǔn)入控制鏈里二次開發(fā)定制時(shí)的修改面較寬無法只在一個(gè)點(diǎn)完成改造。4.2 運(yùn)維與版本演進(jìn)層面升級(jí)鏈路怎么走更穩(wěn)源碼評(píng)測(cè)過程中最讓我擔(dān)心的一點(diǎn)其實(shí)是升級(jí)鏈路。OpenShift-Origin采用了cluster-version-operatorCVO來做版本管理這個(gè)組件會(huì)在集群內(nèi)維持期望版本和實(shí)際版本之間的穩(wěn)定收斂。從源碼看CVO通過一個(gè)UpdatePayload來協(xié)調(diào)各組件鏡像的更新順序這種機(jī)制的優(yōu)點(diǎn)是升級(jí)過程是受控的但它有一個(gè)前提你必須信任RedHat發(fā)布的更新payload中的鏡像列表。企業(yè)如果使用社區(qū)版OKD或OpenShift-Origin自己構(gòu)建所有組件鏡像并生成對(duì)應(yīng)payload的工程量非常大。我見過一些團(tuán)隊(duì)只構(gòu)建了部分鏡像結(jié)果升級(jí)時(shí)CVO一解析payload發(fā)現(xiàn)某個(gè)組件鏡像tag不存在直接卡住。這種問題從源碼層面是能預(yù)判的因?yàn)镃VO的payload結(jié)構(gòu)在源碼的release/目錄下有詳細(xì)定義。因此我建議企業(yè)做落地規(guī)劃時(shí)把升級(jí)鏈路分成兩種一種是大版本升級(jí)比如4.12到4.13建議全量構(gòu)建、完整驗(yàn)證后再統(tǒng)一推另一種是小版本補(bǔ)丁比如4.12.z可以只構(gòu)建變更組件但要嚴(yán)格保持鏡像tag與payload定義一致。這兩種策略的構(gòu)建腳本和驗(yàn)證方案要提前準(zhǔn)備不能在需要升級(jí)時(shí)才臨時(shí)研究。4.3 二次開發(fā)切入點(diǎn)哪些地方值得改、哪些地方別碰源碼評(píng)測(cè)的一個(gè)重要產(chǎn)出是幫企業(yè)劃出二次開發(fā)的“安全區(qū)域”和“禁區(qū)”。安全區(qū)域一準(zhǔn)入控制與SCC策略。如果企業(yè)需要增加安全審計(jì)邏輯、實(shí)現(xiàn)自定義的多租戶隔離規(guī)則可以在openshift-apiserver的準(zhǔn)入控制鏈上擴(kuò)展這個(gè)位置的代碼接口比較清晰改動(dòng)相對(duì)獨(dú)立。安全區(qū)域二自定義Controller。OpenShift本身就是一個(gè)Controller密集型的系統(tǒng)企業(yè)完全可以在工程外開發(fā)自己的Operator或Controller通過CRD和Controller-runtime庫與OpenShift集群交互不必要在主倉內(nèi)改動(dòng)。這是最常見也最穩(wěn)妥的擴(kuò)展方式。安全區(qū)域三網(wǎng)絡(luò)與存儲(chǔ)的外部插件。盡量通過標(biāo)準(zhǔn)的CNI和CSI機(jī)制接入外部方案。雖然OpenShift自帶OVN-Kubernetes但它也保留了引入第三方CNI的接口。存儲(chǔ)方面用CSI插件接入企業(yè)存儲(chǔ)陣列的實(shí)踐已經(jīng)很成熟不要在OpenShift內(nèi)部存儲(chǔ)代碼里做深度綁定。禁區(qū)一kube-apiserver核心邏輯。除非你有專門的Kubernetes上游維護(hù)團(tuán)隊(duì)否則不要輕易在上游Kubernetes代碼上做功能改動(dòng)因?yàn)楹罄m(xù)跟蹤上游版本的成本會(huì)呈幾何級(jí)數(shù)上升。禁區(qū)二openshift-controller-manager的內(nèi)部調(diào)度邏輯。OpenShift的調(diào)度控制器和資源配置邏輯與它自身的Project、Quota機(jī)制深度綁定改動(dòng)一處會(huì)牽動(dòng)多個(gè)組件不建議輕易修改。禁區(qū)三CVO的版本協(xié)調(diào)邏輯。前面已經(jīng)說過這是控制整個(gè)升級(jí)流程的核心。對(duì)它的任何改動(dòng)都會(huì)影響到集群的版本收斂能力除非公司養(yǎng)了一支專門的團(tuán)隊(duì)否則別碰。4.4 鏡像構(gòu)建與私有化交付的隱藏成本如果企業(yè)打算把OpenShift-Origin做成私有化交付產(chǎn)品鏡像構(gòu)建是不可回避的環(huán)節(jié)。源碼工程本身提供了一系列Dockerfile和構(gòu)建腳本但距離真正可交付的鏡像倉庫中間還隔著三層隱藏成本。第一層是基礎(chǔ)鏡像準(zhǔn)備。OpenShift各組件的基礎(chǔ)鏡像通常基于RedHat Enterprise Linux或者CentOS Stream構(gòu)建這些基礎(chǔ)鏡像在企業(yè)內(nèi)網(wǎng)環(huán)境下需要提前同步到私有鏡像倉庫。由于基礎(chǔ)鏡像較大網(wǎng)絡(luò)隔離環(huán)境下的首次同步成本容易被低估。第二層是多架構(gòu)鏡像支持。如果你要同時(shí)交付x86和ARM版本構(gòu)建矩陣會(huì)翻倍。源碼工程的構(gòu)建腳本對(duì)多架構(gòu)支持的自動(dòng)化程度一般很多關(guān)鍵鏡像的tags需要手動(dòng)維護(hù)這是一個(gè)典型的人肉成本點(diǎn)。第三層是安全掃描和漏洞修復(fù)閉環(huán)。源碼評(píng)測(cè)做到最后所有企業(yè)都會(huì)面對(duì)這個(gè)問題鏡像里已知漏洞怎么修上游RedHat會(huì)維護(hù)企業(yè)版的漏洞通告和修復(fù)鏡像但基于OpenShift-Origin自己構(gòu)建的鏡像安全修復(fù)的責(zé)任落在企業(yè)自己身上。這意味著你除了要維護(hù)代碼分支還要維護(hù)鏡像CVE清單和修復(fù)版本而且是長(zhǎng)期投入。我把這部分列入風(fēng)險(xiǎn)清單是想給正準(zhǔn)備做私有化交付的團(tuán)隊(duì)提個(gè)醒代碼層面看懂的難度遠(yuǎn)遠(yuǎn)小于交付層面構(gòu)建和修復(fù)的難度。5. 常見問題與排查建議速查表做源碼靜態(tài)評(píng)測(cè)的過程中我自己也踩了不少坑。下面把這些常見問題和排查建議整理成一個(gè)速查表方便你要復(fù)查時(shí)對(duì)照參考。問題現(xiàn)象可能原因排查與處理建議編譯報(bào)錯(cuò)提示缺少zz_generated.deepcopy.go修改API類型后未重新執(zhí)行make generate在修改API的目錄下執(zhí)行make generate確認(rèn)生成代碼后重新編譯跑單元測(cè)試時(shí)連接etcd失敗測(cè)試依賴嵌入式etcd本地環(huán)境未準(zhǔn)備好確認(rèn)是否安裝了etcd或使用make test-integration按項(xiàng)目預(yù)設(shè)的etcd啟動(dòng)方式vendor目錄與go.mod版本不一致混合依賴管理導(dǎo)致版本漂移先跑go mod tidy確認(rèn)模塊版本再通過hack/verify-vendor.sh腳本檢查vendor目錄一致性構(gòu)建鏡像時(shí)拉取基礎(chǔ)鏡像超時(shí)企業(yè)內(nèi)網(wǎng)沒有基礎(chǔ)鏡像緩存提前把構(gòu)建依賴的base鏡像tag列表導(dǎo)出在內(nèi)網(wǎng)鏡像倉庫建立拉取白名單openshift-apiserver啟動(dòng)后API資源無法注冊(cè)API版本沒有對(duì)應(yīng)conversion邏輯檢查api/目錄下對(duì)應(yīng)資源是否安裝了conversion表確認(rèn)版本間的字段映射scc策略導(dǎo)致應(yīng)用鏡像無法以root啟動(dòng)OpenShift默認(rèn)拒絕root容器創(chuàng)建自定義SCC并綁定ServiceAccount需確認(rèn)SCC策略優(yōu)先級(jí)是否被前面的SCC攔截OVN-Kubernetes網(wǎng)絡(luò)數(shù)據(jù)面時(shí)延偏高OVN數(shù)據(jù)庫和網(wǎng)關(guān)節(jié)點(diǎn)配置不匹配優(yōu)先檢查網(wǎng)關(guān)節(jié)點(diǎn)和OVN內(nèi)部通信鏈路確認(rèn)拓?fù)浣Y(jié)構(gòu)再考慮代碼修改這個(gè)表不是萬能鑰匙但能覆蓋大部分靜態(tài)評(píng)測(cè)和初始運(yùn)行時(shí)遇到的問題。建議團(tuán)隊(duì)工作的第一周打印一份貼在最顯眼的位置。再補(bǔ)充一個(gè)很多人不知道的技巧排查OpenShift-Origin的控制器異常時(shí)可以優(yōu)先看operator對(duì)應(yīng)的status.conditions里的reason字段。源碼里這個(gè)字段的賦值邏輯很嚴(yán)謹(jǐn)從reason描述基本能直接定位到具體模塊和代碼行這是我用下來效率最高的診斷方式。6. 我最后的經(jīng)驗(yàn)和建議OpenShift-Origin源碼靜態(tài)工程評(píng)測(cè)做到這里我的整體結(jié)論用一個(gè)詞概括是“厚重”。它比社區(qū)版Kubernetes功能更完整、安全模型更嚴(yán)苛、自動(dòng)化程度更高但也注定比原生K8s更復(fù)雜、更吃資源、更依賴長(zhǎng)期維護(hù)能力。企業(yè)選擇它作為Kubernetes發(fā)行版底座本質(zhì)上是在用一部分靈活性和控制權(quán)換取開箱即用的企業(yè)特性與跨組件集成能力。在啟動(dòng)前給自己三天時(shí)間靜態(tài)走讀源碼是真的值得的。你不需要把36669個(gè)文件全看一遍重點(diǎn)是看清楚幾個(gè)關(guān)鍵目錄api/、pkg/cmd、hack/、cluster-version-operator以及cluster-network-operator。這些區(qū)域看透了你對(duì)OpenShift-Origin的架構(gòu)理解和駕馭能力會(huì)完全不一樣。最后分享一個(gè)我反復(fù)強(qiáng)調(diào)的原則源碼評(píng)測(cè)不是一次性工作發(fā)行版的每一次版本升級(jí)都會(huì)帶來代碼層面的變化。我建議在每次大版本升級(jí)前都把這篇評(píng)測(cè)里的核心維度快速跑一遍用最少的成本確認(rèn)工程質(zhì)量沒有退化。畢竟對(duì)企業(yè)的容器云底座來說避免踩坑的最好方式就是在還不知道坑長(zhǎng)什么樣的時(shí)候先把地圖看明白。