戰(zhàn):從YAML編寫(xiě)到部署排查全解析)
做運(yùn)維和開(kāi)發(fā)這些年Kubernetes以下簡(jiǎn)稱(chēng) k8s已經(jīng)成了一個(gè)繞不開(kāi)的話(huà)題。接觸 k8s 的人大多會(huì)有同樣的感受命令記了一堆文檔翻了不少但真正動(dòng)手時(shí)還是發(fā)懵。尤其是剛?cè)腴T(mén)時(shí)看到滿(mǎn)屏的 YAML 文件心里多少會(huì)犯嘀咕——這些東西到底是什么、每個(gè)字段有什么用、為什么寫(xiě)完之后集群沒(méi)反應(yīng)。我自己就是從這個(gè)階段過(guò)來(lái)的踩過(guò)的坑比寫(xiě)過(guò)的配置文件還多。今天這篇內(nèi)容就把“k8s 資源文件”這件事從頭到尾捋一遍用一個(gè)最小可用的實(shí)踐路徑講清楚它是怎么組織、怎么寫(xiě)、怎么用的以及我實(shí)際操作中遇到過(guò)的問(wèn)題和排查思路。這篇文章適合正在學(xué) k8s 基礎(chǔ)、準(zhǔn)備把服務(wù)往集群里部署的開(kāi)發(fā)者也適合那些能用 kubectl 跑命令但對(duì) YAML 文件還停留在“能復(fù)制、不能改”階段的運(yùn)維同學(xué)。讀完你至少能獨(dú)立寫(xiě)出一份簡(jiǎn)單的 DeploymentService 資源配置并且知道報(bào)錯(cuò)之后應(yīng)該從哪里入手排查。我不會(huì)一股腦堆術(shù)語(yǔ)盡量用實(shí)際場(chǎng)景把概念講透因?yàn)楣饪炊拍铍x能上手干活還差得很遠(yuǎn)差的那部分就是今天要補(bǔ)齊的。1. 資源文件到底是什么為什么要從它入手1.1 資源文件與 kubectl 命令行的關(guān)系很多人入門(mén)時(shí)的第一個(gè)動(dòng)作是敲 kubectl run、kubectl create這沒(méi)有錯(cuò)但只靠命令行的方式管理集群很快會(huì)碰壁。舉個(gè)例子你手動(dòng)創(chuàng)建了一個(gè) Pod運(yùn)行得好好的但是節(jié)點(diǎn)一重啟這個(gè) Pod 可能就沒(méi)了因?yàn)闆](méi)有任何機(jī)制替你把它拉起來(lái)。如果你想要“服務(wù)掛了自動(dòng)恢復(fù)”的效果就得寫(xiě)一個(gè) Deployment 資源讓控制器替你維護(hù)副本數(shù)和生命周期。這時(shí)候你會(huì)發(fā)現(xiàn)命令行雖然能創(chuàng)建資源卻很難完整表達(dá)這些復(fù)雜的聲明意圖。資源文件也叫資源清單、Manifest本質(zhì)上是一份用 YAML 或 JSON 格式寫(xiě)成的聲明式配置。它的核心思想是“告訴集群我想要的最終狀態(tài)是什么”而不是“告訴集群每一步去做什么”。這兩者的差別是理解 k8s 一切實(shí)踐的分水嶺。kubectl apply 是資源文件最常用的交互入口我會(huì)在后面實(shí)際操作部分詳細(xì)介紹它的用法。1.2 聲明式 API 的核心邏輯聲明式 API 可以這樣理解你去餐廳吃飯不是告訴廚師“先切菜、再熱鍋、然后放油”而是直接說(shuō)“我要一盤(pán)番茄炒蛋”。之后怎么切、用什么火候、放多少鹽由廚房自己決定廚師只保證端上來(lái)的菜符合你的要求。k8s 里也是這樣你寫(xiě)好一份 YAML聲明“我要 3 個(gè)副本、鏡像版本是 v1.2.0、容器端口是 8080”剩下的調(diào)度、拉起、健康檢查、滾動(dòng)更新這些動(dòng)作全部由集群內(nèi)部的各種控制器完成。這種模式的好處非常明顯文件即配置、配置即代碼。同一份資源文件放到測(cè)試環(huán)境和生產(chǎn)環(huán)境只要鏡像地址、副本數(shù)這些參數(shù)改一改跑出來(lái)的行為基本一致。版本管理、代碼評(píng)審、回滾這些成熟的開(kāi)發(fā)流程也都可以直接套用到集群配置上。從學(xué)習(xí)和工程化兩個(gè)角度看資源文件都是 k8s 入門(mén)階段必須先啃下的一塊硬骨頭。2. 資源文件的基礎(chǔ)結(jié)構(gòu)與必備字段2.1 apiVersion、kind、metadata、spec 四件套隨便打開(kāi)一份 k8s 資源文件大概率會(huì)看到四個(gè)頂層字段apiVersion、kind、metadata、spec。這四個(gè)字段是所有資源文件的骨架明白了它們剩下的細(xì)節(jié)都是在往里填肉。apiVersion 表示資源所屬的 API 版本。不同版本的 k8s 對(duì)同一資源可能提供不同的 API 組比如 Deployment 常見(jiàn)的寫(xiě)法是 apps/v1Pod 是 v1Job 是 batch/v1。版本選錯(cuò)了kubectl 會(huì)直接報(bào)錯(cuò)告訴你找不到對(duì)應(yīng)資源。kind 表示資源類(lèi)型比如 Pod、Deployment、Service、ConfigMap、Secret。這兩個(gè)字段決定了“這是一個(gè)什么對(duì)象、走哪個(gè) API 路徑去處理它”。metadata 是元數(shù)據(jù)最重要的兩個(gè)子字段是 name 和 labels。name 是資源在命名空間內(nèi)的唯一標(biāo)識(shí)labels 則是給資源打標(biāo)簽用來(lái)做篩選和關(guān)聯(lián)。spec 是具體期望狀態(tài)的描述不同類(lèi)型的資源spec 長(zhǎng)得完全不一樣這也是資源文件里內(nèi)容最豐富、最容易寫(xiě)錯(cuò)的部分。2.2 命名規(guī)范與 YAML 語(yǔ)法要點(diǎn)寫(xiě)資源文件還要注意 YAML 本身的語(yǔ)法。YAML 對(duì)縮進(jìn)敏感一個(gè)空格錯(cuò)了文件可能根本解析不了。不同字段之間層級(jí)關(guān)系必須清楚同級(jí)的 key 縮進(jìn)要一致??s進(jìn)推薦用兩個(gè)空格不要用 Tab。字段名區(qū)分大小寫(xiě)不能隨意改變大小寫(xiě)形式。注釋用 #用于說(shuō)明某些關(guān)鍵字段的含義方便團(tuán)隊(duì)協(xié)作。name 字段在 k8s 里有自己的命名規(guī)則一般要求小寫(xiě)字母、數(shù)字、中劃線(xiàn)或點(diǎn)并且在創(chuàng)建后不可變。metadata.name 一旦寫(xiě)入資源文件再 apply改名字實(shí)際上會(huì)創(chuàng)建一個(gè)新資源而不是原地修改。這個(gè)坑我踩過(guò)。給 labels 命名時(shí)也盡量帶上應(yīng)用名和層級(jí)信息比如 app: nginx、tier: frontend這樣后面查日志、做網(wǎng)絡(luò)策略、寫(xiě) Service selector 都會(huì)省很多事。3. 最常用的五類(lèi)資源文件示例與解析3.1 Pod 資源文件一切資源的最小運(yùn)行單元Pod 是 k8s 里最小的調(diào)度單位也是容器的運(yùn)行載體。一個(gè) Pod 里可以有一個(gè)或多個(gè)容器這些容器共享網(wǎng)絡(luò)棧和存儲(chǔ)卷。學(xué)習(xí)資源文件從 Pod 開(kāi)始是最直觀的。apiVersion: v1 kind: Pod metadata: name: nginx-pod labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80這份文件創(chuàng)建后集群會(huì)調(diào)度一個(gè) Pod里面跑一個(gè)來(lái)自 nginx:1.25 鏡像的容器端口 80 映射到容器內(nèi)。需要提醒的是只寫(xiě)一個(gè)裸 Pod 意味著它沒(méi)有自愈能力節(jié)點(diǎn)宕機(jī)、容器異常退出Pod 不會(huì)自動(dòng)重新創(chuàng)建。這也是為什么實(shí)際工作中很少直接創(chuàng)建裸 Pod而是用 Deployment 這類(lèi)控制器來(lái)管理 Pod 的生命周期。3.2 Deployment 資源文件工業(yè)級(jí)副本管理Deployment 是日常部署中最常用的資源類(lèi)型。它負(fù)責(zé)管理 ReplicaSet再由 ReplicaSet 保證指定數(shù)量的 Pod 副本存在。這部分內(nèi)容適合放一個(gè)帶副本數(shù)的 Deployment 示例來(lái)做詳細(xì)說(shuō)明。apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80這里值得講清楚的是 template 與 selector 的關(guān)系。selector 告訴 Deployment 哪些 Pod 歸它管而 template 是創(chuàng)建 Pod 的模板。template 的 labels 必須包含 selector.matchLabels 中的標(biāo)簽否則會(huì)創(chuàng)建失敗或者出現(xiàn)“selector does not match template labels”之類(lèi)的報(bào)錯(cuò)。簡(jiǎn)而言之副本要幾個(gè)鏡像是什么版本更新策略是什么都寫(xiě)在 spec 里。創(chuàng)建之后如果只想改鏡像版本可以直接修改 spec.template.spec.containers[0].image再由控制器完成滾動(dòng)更新。3.3 Service 資源文件穩(wěn)定的訪問(wèn)入口Deployment 保證 Pod 的數(shù)量和健康但 Pod 的 IP 是漂移的每次重建都可能變化。想要給外部或集群內(nèi)其他服務(wù)提供一個(gè)固定的入口就要引入 Service。apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - port: 80 targetPort: 80 type: ClusterIPService 通過(guò) selector 里的標(biāo)簽關(guān)聯(lián)到對(duì)應(yīng)的 Pod當(dāng) Pod 的 IP 發(fā)生變化時(shí)Service 會(huì)自動(dòng)更新它維護(hù)的 Endpoints 列表。port 是 Service 對(duì)外暴露的端口targetPort 是容器內(nèi)應(yīng)用監(jiān)聽(tīng)的端口兩者可以不同。type 有 ClusterIP、NodePort、LoadBalancer 等幾種ClusterIP 是默認(rèn)值只在集群內(nèi)部可達(dá)想從集群外部訪問(wèn)可以根據(jù)環(huán)境換成 NodePort 或 LoadBalancer。3.4 ConfigMap 與 Secret配置與敏感信息的解耦實(shí)際部署中鏡像往往是同一個(gè)但不同環(huán)境的參數(shù)不一樣。把所有配置硬編碼進(jìn)鏡像會(huì)導(dǎo)致環(huán)境之間互相污染每次改配置都要重新構(gòu)建鏡像。ConfigMap 和 Secret 就是為了解決這個(gè)問(wèn)題存在的。ConfigMap 適合放非敏感的配置內(nèi)容比如日志級(jí)別、開(kāi)關(guān)項(xiàng)、外部服務(wù)地址。Secret 適合放需要加密存儲(chǔ)的敏感信息比如數(shù)據(jù)庫(kù)密碼、訪問(wèn)密鑰。這兩類(lèi)資源都可以通過(guò)環(huán)境變量或掛載文件的方式注入到 Pod 中。ConfigMap 示例apiVersion: v1 kind: ConfigMap metadata: name: app-config data: LOG_LEVEL: info APP_MODE: prod在 Deployment 中引用方式env: - name: LOG_LEVEL valueFrom: configMapKeyRef: name: app-config key: LOG_LEVELSecret 的寫(xiě)法和 ConfigMap 非常像只是 data 里的值要求 base64 編碼。要注意的是Secret 只是對(duì)內(nèi)容做了編碼并不是加密生產(chǎn)環(huán)境還需要配合加密存儲(chǔ)方案和嚴(yán)格的權(quán)限控制。不要真的把生產(chǎn)密碼以明文 base64 形式寫(xiě)進(jìn)倉(cāng)庫(kù)然后以為沒(méi)人看得到。3.5 多資源組合的完整示例一個(gè)完整的應(yīng)用通常由 Deployment 和 Service 配合組成。放在同一個(gè) YAML 文件中可以用 --- 分隔多個(gè)資源文檔也可以用 kubectl apply 指定多個(gè)文件來(lái)一次性生效。apiVersion: apps/v1 kind: Deployment metadata: name: demo-web labels: app: demo-web spec: replicas: 2 selector: matchLabels: app: demo-web template: metadata: labels: app: demo-web spec: containers: - name: demo-web image: nginx:1.25 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: demo-web-svc spec: selector: app: demo-web ports: - port: 80 targetPort: 80這樣一個(gè)組合文件已經(jīng)是一個(gè)能跑通的最小應(yīng)用了。后續(xù)加配置掛載、加健康檢查、加資源限制都可以在這個(gè)基礎(chǔ)上逐步擴(kuò)展。組合文件的好處是復(fù)現(xiàn)方便一個(gè)文件代表一個(gè)完整應(yīng)用的聲明團(tuán)隊(duì)之間交流起來(lái)效率很高。4. 從零到一一個(gè)資源文件創(chuàng)建應(yīng)用的完整流程4.1 編寫(xiě)文件與檢查語(yǔ)法選一個(gè)最小的場(chǎng)景來(lái)演示。假設(shè)我要在集群里部署一個(gè) nginx 服務(wù)叫 nginx-demo副本 2 個(gè)通過(guò) NodePort 暴露端口方便臨時(shí)訪問(wèn)。第一步自然是寫(xiě)文件我一般會(huì)先檢查語(yǔ)法能不能被正確解析。kubectl 提供了 dry-run 和 validate 參數(shù)來(lái)幫忙做靜態(tài)檢查。kubectl create --dry-runclient -f nginx-demo.yaml -o yaml這個(gè)命令會(huì)裝作創(chuàng)建資源但實(shí)際不發(fā)送到集群只做客戶(hù)端側(cè)解析。輸出如果包含完整 YAML 內(nèi)容說(shuō)明文件基本沒(méi)問(wèn)題。如果想做服務(wù)端校驗(yàn)就把 dry-run 換成 server或者用 kubectl apply --dry-runserver。這一步能提前暴露出一批 apiVersion、字段拼寫(xiě)等問(wèn)題省得真正 apply 之后反復(fù)刪改。4.2 應(yīng)用資源與驗(yàn)證狀態(tài)文件檢查無(wú)誤后執(zhí)行kubectl apply -f nginx-demo.yaml正常情況下會(huì)輸出 deployment.apps/nginx-demo created 和 service/nginx-demo-svc created 之類(lèi)的結(jié)果。但這還不算完資源創(chuàng)建成功不等于應(yīng)用就起來(lái)了。緊接著要查看實(shí)際狀態(tài)kubectl get pods -l appdemo-web kubectl get svc這里看的是 Pod 是否 Running 且 ReadyService 是否有可用的 Endpoints。我習(xí)慣用 kubectl get pod -o wide 看一下 Pod 被調(diào)度到了哪個(gè)節(jié)點(diǎn)、IP 是什么。如果 Pod 一直處于 Pending、ContainerCreating、CrashLoopBackOff就需要進(jìn)入排查環(huán)節(jié)。對(duì)于 NodePort 類(lèi)型的 Service集群節(jié)點(diǎn)上會(huì)開(kāi)一個(gè) 30000-32767 范圍內(nèi)的隨機(jī)端口通過(guò)任意節(jié)點(diǎn)的這個(gè)端口就能訪問(wèn)到應(yīng)用。訪問(wèn)不通時(shí)優(yōu)先檢查安全組和防火墻是否放行了這個(gè)端口這類(lèi)問(wèn)題常發(fā)生在云環(huán)境。4.3 更新與回滾應(yīng)用運(yùn)行了一段時(shí)間后要升級(jí)鏡像版本比如從 nginx:1.25 升級(jí)到 nginx:1.26。改文件里的 image 字段然后重新 applykubectl apply -f nginx-demo.yamlDeployment 會(huì)觸發(fā)滾動(dòng)更新默認(rèn)策略是“先啟動(dòng)新 Pod等它 Ready 后再銷(xiāo)毀舊 Pod”這個(gè)過(guò)程從外部看服務(wù)是不中斷的。如果想指定某一次更新也可以用 kubectl set image 命令直接改。升級(jí)出了問(wèn)題怎么辦Deployment 自帶回滾機(jī)制。查歷史版本kubectl rollout history deployment/nginx-demo回滾到指定版本kubectl rollout undo deployment/nginx-demo --to-revision2回滾本身也是提交一次新的更新它會(huì)將資源狀態(tài)恢復(fù)到指定歷史版本所對(duì)應(yīng)的描述。這個(gè)機(jī)制對(duì)日常上線(xiàn)非常有用但前提是每次變更都用 kubectl apply 更新資源文件而不是靠手工 edit 改了一堆過(guò)后又忘了改了什么。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 鏡像拉取失敗Pod 狀態(tài)停留在 ImagePullBackOff 或 ErrImagePull 時(shí)第一個(gè)動(dòng)作是查看 Pod 詳情kubectl describe pod pod-nameEvents 里通常會(huì)直接給出失敗原因。鏡像問(wèn)題常見(jiàn)的有三類(lèi)鏡像名或標(biāo)簽寫(xiě)錯(cuò)、鏡像不存在、私有倉(cāng)庫(kù)需要認(rèn)證。前面兩項(xiàng)改寫(xiě)正確 image 即可私有倉(cāng)庫(kù)需要在資源文件里配置 imagePullSecrets。這件事在沒(méi)有配置好之前describe 的 Events 里會(huì)給出類(lèi)似 unauthorized 或 401 的錯(cuò)誤提示指向非常明顯。5.2 資源一直 Pending 或 CrashLoopBackOffPending 說(shuō)明調(diào)度沒(méi)完成常見(jiàn)原因是節(jié)點(diǎn)資源不足、存在沒(méi)有匹配的污點(diǎn)、或者 PVC 沒(méi)有綁定。describe 同樣會(huì)給出一部分線(xiàn)索比如 0/3 nodes are available。這時(shí)候可以看節(jié)點(diǎn)資源使用情況kubectl top nodes。缺資源就擴(kuò)容或清理污點(diǎn)問(wèn)題則要重新審視調(diào)度策略。CrashLoopBackOff 表示容器啟動(dòng)后反復(fù)崩潰退出。先看日志kubectl logs pod-name --previous--previous 很關(guān)鍵因?yàn)楫?dāng)前容器可能已經(jīng)重啟過(guò)之前的日志里往往藏著真正的原因。配置錯(cuò)誤、啟動(dòng)命令失敗、依賴(lài)的數(shù)據(jù)庫(kù)或接口連不上是幾類(lèi)主要誘因。如果你在 YAML 里配置了 livenessProbe 和 readinessProbe也要考慮探針是否太嚴(yán)格、啟動(dòng)時(shí)間是否不夠?qū)е鲁绦蛎髅髌饋?lái)了卻被 kubelet 判定失敗后殺掉。5.3 服務(wù)訪問(wèn)不通Service 建好了但應(yīng)用訪問(wèn)不到需要逐層排查。先確認(rèn)后端 Pod 有沒(méi)有就緒kubectl get endpoints svc-name如果 Endpoints 列表為空說(shuō)明 selector 的標(biāo)簽沒(méi)匹配到任何 Pod。這是最常見(jiàn)的問(wèn)題Deployment 的 template 里寫(xiě)的標(biāo)簽是 app: nginxService 的 selector 不小心寫(xiě)成了 name: nginx匹配失敗。同樣的端口對(duì)不對(duì)也要驗(yàn)證targetPort 畢竟指的是容器內(nèi)端口不是 Service 端口。用一個(gè)臨時(shí) Pod 進(jìn)去 curl 一下能把問(wèn)題隔離得更干凈。集群內(nèi)流量走通后再測(cè)試 NodePort 或 LoadBalancer 的對(duì)外鏈路。NodePort 的訪問(wèn)方式通常是節(jié)點(diǎn)IP:NodePort不是 Pod IP:Port。我記得第一次調(diào)試時(shí)一直在宿主機(jī)上 curl 容器 IP 的端口當(dāng)然通不了因?yàn)槟侵皇羌簝?nèi)部網(wǎng)絡(luò)。逐個(gè)鏈路驗(yàn)證才能快速定位問(wèn)題出在 Pod、Service 還是節(jié)點(diǎn)路由層面。5.4 檢查項(xiàng)速查表把日常排查步驟整理成一張表適合遇到問(wèn)題時(shí)按圖索驥現(xiàn)象第一步做什么重點(diǎn)關(guān)注資源 apply 報(bào)錯(cuò)kubectl apply --dry-runserverapiVersion、字段拼寫(xiě)Pod Pendingkubectl describe pod節(jié)點(diǎn)資源、污點(diǎn)、PVCPod CrashLoopBackOffkubectl logs --previous啟動(dòng)命令、配置依賴(lài)鏡像拉取失敗kubectl describe podimage 名稱(chēng)、tag、私有倉(cāng)庫(kù)認(rèn)證Service 不通kubectl get endpointsselector 匹配、targetPort外部訪問(wèn)不通檢查 NodePort/防火墻/安全組網(wǎng)絡(luò)策略、端口放行這張表不是標(biāo)準(zhǔn)答案而是我自己的習(xí)慣性排查路徑。實(shí)際過(guò)程中kubectl describe 和 kubectl logs 兩個(gè)命令能解決八成問(wèn)題關(guān)鍵是順著狀態(tài)變化一步步縮小范圍。6. 幾個(gè)實(shí)操經(jīng)驗(yàn)與避坑補(bǔ)充6.1 資源文件一定要納入版本管理我見(jiàn)過(guò)不少同學(xué)手工 kubectl edit 在線(xiàn)改資源集群里改得花團(tuán)錦簇倉(cāng)庫(kù)里的 YAML 還是三個(gè)月前的版本。等哪天集群要重建或者需要快速擴(kuò)容手里沒(méi)有可信的配置就只能對(duì)著終端一遍遍 describe 反推。把資源文件放在 Git 倉(cāng)庫(kù)里每次變更走提交評(píng)審集群狀態(tài)與倉(cāng)庫(kù)內(nèi)容保持一致這是我從混亂走向可控的第一步。工程實(shí)踐上也可以加一套簡(jiǎn)單的自動(dòng)化檢查提交時(shí)用 kubeconform 或者 kubectl dry-run 校驗(yàn)格式減少低級(jí)錯(cuò)誤進(jìn)入集群的機(jī)會(huì)。6.2 命名空間資源要寫(xiě)清楚資源文件里沒(méi)有聲明 namespace 時(shí)kubectl apply 默認(rèn)會(huì)應(yīng)用到當(dāng)前 context 的 namespace很容易出現(xiàn)“以為自己部署到 test 環(huán)境實(shí)際發(fā)到了 default”的烏龍。建議在 metadata 里顯式加 namespace 字段或者用 kubectl apply -n 指定別依賴(lài)默認(rèn)值。尤其在多環(huán)境共用一個(gè)集群的情況下該問(wèn)題相當(dāng)常見(jiàn)且代價(jià)不小。6.3 給容器設(shè)置資源請(qǐng)求與限制資源文件里不寫(xiě) requests 和 limits容器在節(jié)點(diǎn)上就可能“吃滿(mǎn)”所有可用資源影響其他 Pod 運(yùn)行。生產(chǎn)環(huán)境如果不加以約束很容易出現(xiàn)故障雪崩。給容器加資源限制是每個(gè)資源文件都值得做的動(dòng)作resources: requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256Mirequests 是調(diào)度時(shí)的資源預(yù)留值limits 是運(yùn)行時(shí)可用的上限。cpu 的 100m 表示 0.1 個(gè)核心memory 單位可以直接用 Mi、Gi。這類(lèi)值不要拍腦袋先用壓測(cè)拿到應(yīng)用的資源畫(huà)像再留出緩沖。限制設(shè)得過(guò)小容器會(huì)被頻繁殺掉這是很多 CrashLoopBackOff 的隱藏原因。6.4 善用 kubectl explain 查字段寫(xiě) YAML 時(shí)總會(huì)有字段記不準(zhǔn)的情況。與其去搜索引擎翻舊文檔不如直接使用 kubectl explainkubectl explain deployment.spec這個(gè)命令會(huì)展示該字段的類(lèi)型、說(shuō)明、是否必填而且版本一定和當(dāng)前集群匹配。我寫(xiě)文件遇到復(fù)雜的嵌套字段時(shí)幾乎都會(huì)敲一遍它。這個(gè)習(xí)慣會(huì)極大減少“照著網(wǎng)上的寫(xiě)法套用到舊版本”的痛苦。6.5 不要把 Secret 內(nèi)容寫(xiě)進(jìn)鏡像倉(cāng)庫(kù)ConfigMap 和 Secret 的設(shè)計(jì)初衷是讓配置與鏡像解耦。有人為了省事把數(shù)據(jù)庫(kù)連接串直接放進(jìn) Dockerfile 的 ENV或者把 Secret 文件硬編碼進(jìn)應(yīng)用鏡像這基本等于把鑰匙配在鎖旁邊。Secret 的配置引用、權(quán)限管控、輪換機(jī)制雖然需要額外學(xué)習(xí)成本但和安全事故的代價(jià)相比這筆成本非常劃算。哪怕只是做個(gè)人項(xiàng)目也建議從一開(kāi)始就培養(yǎng)正確的配置管理習(xí)慣。7. 總結(jié)形式的個(gè)人體會(huì)就不要了說(shuō)說(shuō)我現(xiàn)在的習(xí)慣寫(xiě)了很多最后說(shuō)點(diǎn)實(shí)在的。我現(xiàn)在拿到一個(gè)部署需求第一反應(yīng)不是敲 kubectl run而是先想清楚它的資源模型是無(wú)狀態(tài)服務(wù)還是有狀態(tài)應(yīng)用需要暴露端口嗎配置里有沒(méi)有敏感信息然后把對(duì)應(yīng)的資源文件一次寫(xiě)好apply 之后盯著狀態(tài)變化確認(rèn)產(chǎn)出。這套流程看似麻煩實(shí)際上比命令行點(diǎn)來(lái)點(diǎn)去更省心因?yàn)樗蓮?fù)現(xiàn)、可評(píng)審、可回滾。還有一個(gè)日常小技巧文件里加注釋。YAML 里的 # 注釋寫(xiě)清楚每個(gè)關(guān)鍵字段為什么這么定比如這個(gè)副本數(shù)為什么是 3、這個(gè)內(nèi)存 limit 為什么是 256Mi。過(guò)半年再回來(lái)看你會(huì)發(fā)現(xiàn)這些注釋比任何文檔都管用因?yàn)樗涗浀氖钱?dāng)時(shí)決策的上下文。k8s 的學(xué)習(xí)曲線(xiàn)確實(shí)陡峭但資源文件就是那條值得先爬上去的主干道。把基礎(chǔ)資源吃透再往后接觸 Helm、Operator、服務(wù)網(wǎng)格這些概念時(shí)會(huì)發(fā)現(xiàn)它們的底層邏輯依然是“聲明期望狀態(tài)、由控制器去調(diào)和”。今天講的這些字段和排查方法夠你支撐起最早期的部署工作了。剩下的場(chǎng)景遇到一個(gè)學(xué)一個(gè)坑踩完了經(jīng)驗(yàn)也就留下了。