偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Kubernetes 驚天地泣鬼神之大Bug

云計(jì)算
自從三月份生產(chǎn)、非生產(chǎn)全面上線 Kubernetes 后,本以為部署的問題可以舒一口氣了,但是斷斷續(xù)續(xù)在生產(chǎn)、非生產(chǎn)環(huán)境發(fā)現(xiàn)一個(gè)詭異的問題,這禮拜又調(diào)試了三天,在快要放棄的時(shí)候居然找到原因了,此缺陷影響目前(2018-5-23)為止所有 Kubernetes 版本(見后面更新,夸大了)。

自從三月份生產(chǎn)、非生產(chǎn)全面上線 Kubernetes 后,本以為部署的問題可以舒一口氣了,但是斷斷續(xù)續(xù)在生產(chǎn)、非生產(chǎn)環(huán)境發(fā)現(xiàn)一個(gè)詭異的問題,這禮拜又調(diào)試了三天,在快要放棄的時(shí)候居然找到原因了,此缺陷影響目前(2018-5-23)為止所有 Kubernetes 版本(見后面更新,夸大了),包括 GitHub 當(dāng)前 master 分支,后果是某種情況觸發(fā)下,Kubernetes service 不能提供服務(wù),影響非常惡劣。

問題的現(xiàn)象是,在某種情況下,一個(gè)或者多個(gè) Kubernetes service 對應(yīng)的 Kubernetes endpoints 消失幾分鐘至幾十分鐘,然后重新出現(xiàn),然后又消失,用 "kubectl get ep --all-namespaces" 不斷查詢,可以觀察到一些 AGE 在分鐘級別的 endpoints 要么忽然消失,要么 AGE 突然變小從一分鐘左右起步。Endpoints 的不穩(wěn)定,必然導(dǎo)致對應(yīng)的 Kubernetes service 無法穩(wěn)定提供服務(wù)。有人在 Github 上報(bào)告了 issue,但是一直沒得到開發(fā)人員注意和解決。

首先解釋下 Kubernetes 大體上的實(shí)現(xiàn)機(jī)制,有助于理解下面的破案過程。

Kubernetes 的實(shí)現(xiàn)原理跟配置管理工具 CFengine、Ansible、Salt 等非常類似:configuration convergence——不斷的比較期望的配置和實(shí)際的配置,修訂實(shí)際配置以收斂到期望配置。

Kubernetes 的關(guān)鍵系統(tǒng)服務(wù)有 api-server、scheduler、controller-manager 三個(gè)。api-server 一方面作為 Kubernetes 系統(tǒng)的訪問入口點(diǎn),一方面作為背后 etcd 存儲的代理服務(wù)器,Kubernetes 里的所有資源對象,包括 Service、Deployment、ReplicaSet、DaemonSet、Pod、Endpoint、ConfigMap、Secret 等等,都是通過 api-server 檢查格式后,以 protobuf 格式序列化并存入 etcd。這就是一個(gè)寫入“期望配置”的過程。

Controller-manager 服務(wù)里包含了很多 controller 實(shí)例,對應(yīng)各種資源類型:

 

001.jpg
v1.12.0-alpha.0/cmd/kube-controller-manager/app/controllermanager.go#L317

這些 controller 做的事情就是收斂:它通過 api-server 的 watch API 去間接的 watch etcd,以收取 etcd 里數(shù)據(jù)的 changelog,對改變(包括ADD、DELETE、UPDATE)的資源(也就是期望的“配置“),逐個(gè)與通過 kubelet + Docker 收集到的信息(實(shí)際“配置”)做對比并修正差異。

比如 etcd 里增加了一個(gè) Pod 資源,那么 controller 就要請求 scheduler 調(diào)度,然后請求 kubelet 創(chuàng)建 Pod,如果etcd里刪除了一個(gè) Service 資源,那么就要?jiǎng)h除其對應(yīng)的 endpoint 資源,而這個(gè) endpoint 刪除操作會(huì)被 kube-proxy 監(jiān)聽到而觸發(fā)實(shí)際的 iptables 命令。

注意上面 controller 通過 watch 獲取 changelog 只是一個(gè)實(shí)現(xiàn)上的優(yōu)化,它沒有周期性的拿出所有的資源對象然后逐個(gè)判斷,在集群規(guī)模很大時(shí),這樣的全量收斂是很慢的,后果就是集群的調(diào)度、自我修復(fù)進(jìn)行的非常慢。

有了大框架的理解后,endpoints 被誤刪的地方是很容易找到的:

 

002.jpg
v1.12.0-alpha.0/pkg/controller/endpoint/endpoints_controller.go#L396

然后問題來了,什么情況下那個(gè) Services(namespace).Get(name) 會(huì)返回錯(cuò)誤呢?通過注釋,可以看到在 service 刪除時(shí)會(huì)走到 397 行里去,把無用的 endpoints 刪掉,因?yàn)?endpoint 是給 service 服務(wù)的,service 不存在時(shí),endpoint 沒有存在的意義。

然后問題來了,通過 "kubectl get svc" 可以看到出問題期間,服務(wù)資源一直都在,并沒有重建,也沒有剛剛部署,甚至很多服務(wù)資源都是創(chuàng)建了幾個(gè)月,為啥它對應(yīng)的 endpoints 會(huì)被誤刪然后重建呢?這個(gè)問題困擾了我兩個(gè)月,花了很長時(shí)間和很多腦細(xì)胞,今天在快放棄時(shí)突然有了重大發(fā)現(xiàn)。

由于我司搭建的 Kubernetes 集群開啟了 X509 認(rèn)證以及 RBAC 權(quán)限控制,為了便于審查,我開啟了 kube-apiserver 的審計(jì)日志,在出問題時(shí),審計(jì)日志中有個(gè)特別的模式:

 

003.jpg
用 jq 命令摘取的審計(jì)日志片段

審計(jì)日志中,在每個(gè) endpoint delete & create 發(fā)生前,都有一個(gè) "/api/v1/services...watch=true" 的 watch 調(diào)用,上面講到,controller-manager 要不斷的去爬 etcd 里面資源的 changelog,這里很奇怪的問題是,這個(gè)調(diào)用的 "resourceVersion=xxx" 的版本值始終不變,難道不應(yīng)該不斷從 changelog 末尾繼續(xù)爬取因?yàn)?resourceVersion 不斷遞增么?

通過一番艱苦卓絕的連猜帶蒙,終于找到“爬取changelog”對應(yīng)的代碼:

 

004.jpg
v1.12.0-alpha.0/staging/src/k8s.io/client-go/tools/cache/reflector.go#L394

上面的代碼對 resourceVersion 的處理邏輯是: 遍歷 event 列表,取當(dāng)前 event 的 "resourceVersion" 字段作為新的要爬取的“起始resourceVersion",所以很顯然遍歷結(jié)束后,"起始resourceVersion" 也就是***一條 event 的 "resourceVersion"。

好了,我們來看看 event 列表漲啥樣,把上面 api-server 的請求照搬就可以看到了:

 

  1. kubectl proxy 
  2. curl -s localhost:8001/api/v1/services?resourceVersion=xxxx&timeoutSeconds=yyy&watch=t 

 

005.jpg
/api/v1/services 的輸出,僅示意,注意跟上面的審計(jì)日志不是同一個(gè)時(shí)間段的

可以很明顯看到,坑爹,resourceVersion 不是遞增的!

這個(gè)非遞增的問題其實(shí)很容易想明白,resourceVersion 并不是 event 的序列號,它是 Kubernetes 資源自身的版本號,如果把 etcd 看作 Subversion,兩者都有一個(gè)全局遞增的版本號,把 Kubernetes 資源看作 Subversion 里保存的文件。 在 svn revision=100 時(shí)存入一個(gè)文件 A,那么A的版本是 100,然后不斷提交其它文件的修改到 svn,然后在某個(gè)版本刪掉 A,此時(shí)用 svn log 看到的相關(guān)文件的“自身***一次修改版本”并不是遞增的,***一條的“自身***一次修改版本”是 100。

所以真相大白了,reflector.go 那段遍歷 etcd "changelog" 的代碼,誤以為 event 序列的 resourceVersion 是遞增的了,雖然絕大部分時(shí)候是這樣的。由于這段代碼是通用的,所有資源都受這個(gè)影響,所以是非常嚴(yán)重的 bug,之前看到很多人報(bào)告(我自己也遇到)controller-manager 報(bào)錯(cuò) "reflector.go:341] k8s.io/kubernetes/pkg/controller/bootstrap/bootstrapsigner.go:152: watch of *v1.ConfigMap ended with: too old resource version: " 很可能也是這個(gè)導(dǎo)致的,因?yàn)樗д`從很老的 resourceVersion 開始爬取 etcd "changelog",但 etcd 已經(jīng)把太老的 changelog 給 “compact” 掉了。

***總結(jié)下怎么觸發(fā)以及繞過這個(gè) bug:

觸發(fā)

陸續(xù)創(chuàng)建、刪除、創(chuàng)建 Kubernetes service 對象,然后"kubectl delete svc xxx"刪掉創(chuàng)建時(shí)間靠前的 service,也就是往 service event list 末尾插入了一條 resourceVersion 比較小的記錄,這將使得 controller-manager 去從已經(jīng)爬過的 service event list 位置重新爬取重放,然后就重放了 service 的 ADDED、DELETED event,于是 controller-manager 內(nèi)存里緩存的 service 對象被刪除,導(dǎo)致 EndpointController 刪除了“不存在的”service 對應(yīng)的 endpoints。

繞過

用 docker restart 重啟 kube-controller-manager 容器,強(qiáng)迫其從 event list ***位置開始爬取

創(chuàng)建一個(gè) service,其 resourceVersion 會(huì)是 etcd 的***全局版本號,這個(gè) ADDED event 會(huì)出現(xiàn)在 event list 末尾,從而 controller-manager 從這個(gè)***的 resourceVersion 開始爬取

"kubectl edit" 修改某個(gè) service,加點(diǎn)無意義的 label 之類的,保存,這樣其 resourceVersion 也會(huì)更新,之后跟上一個(gè) workaround 原理一樣了。

錯(cuò)誤的繞過方式

  • "kubectl delete pod/xxx":不影響 service event list
  • "kubectl delete deploy/xxxx":不影響 service event list

Kubernetes 里凡是創(chuàng)建后基本不變的資源,比如 service、configmap、secrets 都會(huì)受這個(gè)影響,它們的版本號都很久,刪一個(gè)后都會(huì)觸發(fā)這個(gè) bug,比如 configmap,可能就會(huì)被重復(fù)的更新,映射到容器里的 config file 也就不斷更新,對于不支持 config hot reload 的服務(wù)沒有影響,對于支持 config hot reload 的服務(wù),會(huì)頻繁的重新加載配置文件重新初始化,可能導(dǎo)致意想不到的問題。

Bug 的修復(fù)很簡單,在遍歷 event list 選擇 resourceVersion 時(shí),總是取 max(event.resourceVersion, currentResourceVersion) 即可,我提了個(gè) pull request 給 Kubernetes 官方看看,希望我這個(gè)粗暴的修正不會(huì)帶來新的問題。

責(zé)任編輯:未麗燕 來源: 知乎專欄
相關(guān)推薦

2012-12-28 11:03:02

2024-07-03 12:01:36

2025-06-10 04:11:00

2017-07-18 10:05:58

2009-11-24 09:44:59

WIN7chrome谷歌

2016-01-11 10:10:53

2019-01-22 10:10:07

2023-05-10 09:42:39

代碼開源

2023-11-17 22:56:47

ChatGPTAI

2023-03-31 11:36:52

阿里中臺架構(gòu)

2019-07-29 10:46:14

HTTPHTTP 3HTTP 2

2021-09-13 15:54:01

程序員技能開發(fā)者

2013-11-26 15:44:25

Android設(shè)計(jì)模式

2013-12-13 09:31:43

2010-05-21 17:19:15

2012-04-26 22:41:13

2012-07-26 15:15:03

微軟Office2013蘋果

2019-10-17 10:12:02

Go語言Java函數(shù)

2015-08-27 16:55:09

2012-07-19 13:50:11

Power7+IBM
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號