當前位置:首頁 » 服務存儲 » k8s集群存儲起不來
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

k8s集群存儲起不來

發布時間: 2023-05-07 17:26:38

Ⅰ 記一次k8s集群節點鏡像存儲容量報警問題

自從我們的kubernetes集群部署到生產環境後,將流量從原有的伺服器上切過來之後,部分節點出現掛載目錄容量爆滿的情況。

運維的同事報給我們之後,我們首先想到的是節點鏡像過多,於是我們提供一個命令用於清理當前節點上無用的、報錯的、鏡像和docker資源文件

docker system prune  命令可以用於清理磁碟,刪除關閉的容器、無用的數據卷和網路,以及dangling鏡像(即無tag的鏡像)

docker system prune -a 命令清理得更加徹底,可以將沒有容器使用Docker鏡像都刪掉。

待運維執行之後,目錄存儲資源釋放了一些,我們本以為這就告一李畝友段落了。然而,事與願違,沒過多久,再次容量報警。。。

我們開始重視起來,開始檢視節點上工作的容器,發現在日誌爆炸的節點上運行了定時任務,開發人員將定時任務的日誌輸出到控制台,於是我們回到節點docker的工作目錄,通過 -sh * 方式查看每個文件夾大小,發現docker目錄下containers目錄佔用空間巨大,進去看原來是每個運行的容器存放日誌的目錄,我們找出佔用空間最大的日誌目錄,發現容器日誌特別的大

我們可使用如下命令查看各個日誌的文件大小

ls -lh $(find /var/lib/docker/containers/ -name *-json.log)

那我們如何清理日誌呢,如果docker容器正在運行,那麼使哪槐用rm -rf 方式刪除日誌後,通過df -h會發現磁碟空間並沒有釋放

原因:在Linux或者Unix系統中,通過rm或者文件管理器刪除文件將會從文件系統的目錄結構上解除鏈接(unlink).然而如果文件是被打開的(有一個進程正在使用),那麼進程將仍然可以讀取該文件,磁碟空間也一直被佔用

我們通過 cat /dev/null > *-json.log 來清理相應的日誌,然後重啟

systemctl daemon-reload

systemctl restart docker

然而,我思考,不能每次滿的時候找運維清理日誌啊,這多麻煩,難道docker沒有相應的機制應付輸出到控制台的日誌嗎?答案是:當然不會

在新版的docker中我們可以通過設置 vim /etc/docker/daemon.json 來限制docker的日誌量

"log-driver":"json-file","log-opts":{ "max-size" :"200m","max-file":"5"}

顧名思義max-size就是每個日誌文件大小,max-file是最多生成的文件數,如上我設置成功後,每個容器運行的日誌最多有五份每份200M大小,這樣就基本限制了容器的日誌大小。

然後你覺得結束了嗎??並不!!

容器日誌我們是限制完了,本以為高枕無憂,不用擔心出現日誌爆滿的情況了,但是事與願違,過幾天硬碟容量又滿了。。。

我們究其原因,發現在docker的運行目錄下overlay這個文件夾里存放著所有的容器掛載目錄,也就是容器的系統文件在這里放著,在容器中跑著的服務產生日誌很可能並不是輸出到控制台,而是保存到本地,容器內的日誌文件也是會佔用磁碟空間的,這就讓我們犯愁了,這個不好限制開發團隊不存日誌或者規定團隊存放目錄啊,對於一個成熟的容器平台來說,海納百川那是必須的~

於是我們打起了kubelet的主意

在 k8s中文社區中有詳細的限制方法  那具體做法呢,其實就是為節點加上驅逐策略,當cpu或者內存或者硬碟空間不滿足要求時,自動驅逐一些消耗資源大的容器,保證節點穩定性。

裡面主要是有以下幾個關鍵驅逐信號

上面的每個信號都支持整數值或者百分比。百分比的分母部分就是各個信號的總量。kubelet 支持兩種文件系統分區。

nodefs:保存 kubelet 的卷和守護進程日誌等。

imagefs:在容器運行時,用於保存鏡像以及可寫入層。

imagefs 是可選的。Kubelet 能夠利用 cAdvisor 自動發現這些文耐殲件系統。Kubelet 不關注其他的文件系統。所有其他類型的配置,例如保存在獨立文件系統的卷和日誌,都不被支持。

因為磁碟壓力已經被驅逐策略接管,因此未來將會停止對現有 垃圾收集 方式的支持。

具體的內容大家可以詳細去看看社區里的介紹,我這里就不再贅述了,我這邊獻上我的驅逐方案~

執行vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

在裡面插入

Environment="KUBELET_OTHER_ARGS=

--eviction-hard=memory.available<2Gi,nodefs.available<5Gi,imagefs.available<5Gi 

--eviction-minimum-reclaim=memory.available=500Mi,nodefs.available=5Gi,imagefs.available=5Gi 

--node-status-update-frequency=10s 

--eviction-pressure-transition-period=30s"

解讀:內存小於2G驅逐,root目錄磁碟空間小於5G驅逐,鏡像目錄磁碟空間小於5G驅逐,節點檢測為每10秒一次,在跳出壓力狀態之前要等待的時間為30秒。

在某些場景下,驅逐 Pod 可能只回收了很少的資源。這就導致了 kubelet 反復觸發驅逐閾值。另外回收資源例如磁碟資源,是需要消耗時間的。

要緩和這種狀況,Kubelet 能夠對每種資源定義 minimum-reclaim。kubelet 一旦發現了資源壓力,就會試著回收至少 minimum-reclaim 的資源,使得資源消耗量回到期望范圍。

也就是說當內存觸發驅逐時,kubelet至少要讓內存有2.5G,當root和鏡像磁碟空間發生驅逐時,kubelet至少要讓磁碟有10G的空間。

那驅逐的規則是什麼呢,對什麼樣的容器做驅逐呢?這個我們下回分解哈。

那總的來說,若要解決節點鏡像存儲報警,我們可以從三個方面入手

1.容器:通過docker限制容器日誌大小

2.k8s:通過kubelet來驅逐過大的容器

3.跟開發人員溝通,精簡容器,不讓內存泄漏,不隨意使用資源(很難啦~~~)

                                                                                                                                    祝各位新春快樂~

Ⅱ k8s-踩坑篇2-伺服器重啟後重啟集群

昨天不知道說明原因,測試環境的物理顫兆機掛了,安裝k8s的3台虛枯洞嘩擬機正好全在這台物理機上面,現在要把他們全部啟動起來,安裝的時候好像沒有相關的步驟,今天研究一下手動重啟。

報錯:The connection to the server 10.100.1.236:6443 was refused

很明顯apiserver沒有起來,但是apiserver安裝的時候是以容器的方式安裝的

顯示一個容器也沒起來,完全不知道咋整,搜索k8s重啟,看了好幾篇文章,有的文章居然是kubeadm init,這txx還有什麼好說的呢。不過民間的高手也是很多的,如下:

靜態pod可以直接被kubelet啟動,那很有可能是kubelet沒有正確啟動,嘗試如下:每台機器沒行上都要操作

然後用 docker ps 查看,可以看到master節點上的很多k8s容器已經啟動起來了,但是worker node上的容器依然沒有啟動,用 kubectl get nodes ,看到node的狀態還是notReady,那就很有可能是防火牆的問題了,直接關閉防火牆,看到worker node上的容器也起來了。

等待所有的calico pod啟動完畢,node狀態就變成ready了。

但是之前啟動的 nignx pod 都不存在了,原因可能是:etcd的啟動方式也是容器化的,重啟後etcd內的數據被初始化了。

---本來懷疑是 systemctl daemon-reload 命令造成的,但是,今天這台伺服器又重啟了,我又試了一遍,不執行 systemctl daemon-reload 命令是無法重啟k8s的。

---但是今天重啟k8s,完成之後,昨天新建的2個pod仍然是存在的,那很有可能是我昨天不熟悉流程參雜了誤操作,但是現在也想不起來了,就暫時告一段落了,後面遇到問題再說吧。

Ⅲ 9. K8s存儲

Kubernetes的Volume是Pod的一部分,Volume不是單獨的對象,不能獨立創建,只能在Pod中定義。
Pod中的所有容器都可以訪問Volume,但必須要掛載,且可以掛載到容器中任何目錄。
實際中使用容器存儲如下圖所示,將容器的內容掛載到Volume中,通過Volume兩個容器間實現了存儲共享。

Volume的生命周期與掛載它的Pod相同,但是Volume裡面的文件可能在Volume消失後仍然存在,這取決於Volume的類型。

Kubernetes的Volume有非常多的類型:

這個Volume掛載後就是一個空目錄,應用程序可以在裡面讀寫文件,emptyDir Volume的生命周期與Pod相同,Pod刪除後Volume的數據也同時刪除掉。
emptyDir的一些用途:

配置示例

emptyDir也可以設置存儲介質為內存

HostPath存儲的內容與節點相關,如果Pod被調度到其他Node,HostPath無法提供跨Node的數據。
配置示例

ConfigMap存儲的是鍵值對,在Volume中應用時鍵值對表示的是 文件名 和 文件內容 ,代表將ConfigMap的每條數據填入Volume。ConfigMap的配置數據在 data 欄位下定義。

ConfigMap文件示例

Volume中引用ConfigMap

與ConfigMap類似,在data欄位中存儲key-value鍵值對形式,不過存儲的value不是明文,是Base64編碼的加密值。
在Volume中引用後,文件中的值是Base64解碼後的值,而非加密值。

Kubernetes抽象了PV(PersistentVolume)和PVC(PersistentVolumeClaim)來解耦網路存儲種類多樣的問題,從而讓使用者不用關心具體的基礎設施,當需要存儲資源的時候,只要像CPU和內存一樣,聲明要多少即可。
PV是集群級別的資源,並不屬於某個命名空間(在 default 命名空間下),而PVC是命名空間級別的資源,PV可以與任何命名空間的PVC資源綁定。

Kubernetes管理員設置好網路存儲的類型,提供對應的PV描述符配置到Kubernetes,使用者需要存儲的時候只需要創建PVC,然後在Pod中使用Volume關聯PVC,即可讓Pod使用到存儲資源,它們之間的關系如下圖所示。

PV 持久卷是用 插件 的形式來實現的,目前支持以下插件:

CSI
Container Storage Interface,容器存儲介面,基於CSI這套介面,可以開發定製出CSI插件,從而支持特定的存儲,達到解耦的目的。

PVC創建示例:

使用StorageClass可以自動創建PV,StorageClass包含 provisioner 、 parameters 和 reclaimPolicy 欄位, 這些欄位會在 StorageClass 需要動態分配 PersistentVolume 時會使用到。在聲明PVC時加上StorageClassName,就可以自動創建PV,並自動創建底層的存儲資源。

provisioner: PV配置器,用來決定使用哪個卷插件制備 PV。 該欄位必須指定。
reclaimPolicy: 回收策略,可以是 Delete 或者 Retain ,默認是 Delete

使用StorageClassName創建PVC示例:

Ⅳ 深入剖析k8s中的存儲

本文是《深入剖析k8s》學習筆記的第四篇,主要對k8s中的存儲數據卷進行分析和學喚臘習。

容器中的存儲是不穩定的,一旦容器重啟或者銷毀,這些存儲就都會丟失。所以真實使用場景下,都會以數據卷掛載的方式將外部存儲應用到容器中,帶來的好處就是:

在k8s中,如果要在容器中使用數據卷,需要像如下一個pod的yaml示例一樣進行聲明定義:

其中pod的定義中,聲明使用了自定義名稱為 my-volume 的數據卷,並且類型為emptyDir;k8s的volume支持多種數據類型,可以通過命令 kubectl explain pod.spec.volumes 來查看所有支持的volume類型,其中常用的類型及意義比較如下:

從工程分工角度上來說,存儲的定義和聲明應該由運維人員完成,開發人員只要使用即可。因此,為了避免將存儲細節過度地暴露給開發人員,k8s才引進了Persistent Volume Claim(PVC)和Persistent Volume(PV)這兩個API對象,同時也降低了開發人員使用存儲卷的門檻。如此,開發人員只需要如下兩步就能解決存儲卷的使用問題:

那麼開發人員聲明的PVC到底使用的是什麼存儲卷呢,這個和廳就由運維人員負責維護就行了,如下是一個PV的定義,開發人員了解即可:

為什麼這個PV可以和上面的PVC綁定呢?只要符合如下的條件,k8s就會自動將它們綁定,綁定後,在PVC的配置文件中就能看到使用的數據卷就是該PV了。

總的來說,PVC和PV的關系就像是介面和實現的關系,PVC是介面定義,聲明要使用什麼,至於怎麼實現,就是PV去完成的事情。如此解耦,使得開發和運維都能高效地搞定存儲。

假設開發人員在創建好帶有PVC的pod之後,且pod已經運行了,才發現運維還沒有來得及創建對應的PVC或者PVC創建有問題,致使pod存儲卷使用有問題該怎麼辦?只要運維及時創建對應的PV,k8s中的volume controller會循環遍歷沒有成功綁定PV的PVC,幫它們尋找喚鏈隱合適的PV進行綁定。

一個k8s集群往往有很多開發團隊在使用,開發會部署很多pod,如果這些pod都需要存儲卷,運維人員就需要天天創建pv來滿足開發人員pvc綁定的需求了,太浪費人力,所以這種重復工作就被k8s中的storageClass取代了。原先手動創建PV的方式被稱為static provisioning,使用storageClass自動創建PV的方式被稱為dynamic provisioning,storageClass其實就是PV的一個模板,其定義大致分為兩個部分:

在開發人員創建PVC的時候,只要指定使用的storageClassName是如上定義和創建的my-storageclass,那麼k8s就會根據該storageClass自動創建一個PV與該PVC綁定。

Ⅳ 解決k8s集群中Redis Cluster故障

k8s集群中的一個node節點故障,將這個node節點下線後上面的pod遷移到其他節點,但是大量pod都產生報錯。經排查,是由於redis集群故障導致。但是查看resdis pod,都是running狀態,如下圖

由於這些pod是組成集群使用,既然pod是正常的,應用又報redis鏈接的錯誤,所以問題肯定出在Redis Cluster上,查看Redis Cluster狀態:

這個示意圖我只畫出三個node,簡單表達一下意思即可。三個node上各運行了一個master和一個slave節點。由於node3節點故障已經移除集群,這個節點上之前運行的其他無狀態pod遷移到其他節點可以正常運行,但是master2和slave2在node3上有持久化數據,雖然在node4上重建了,但是由於缺失數據,原來的集群狀態被破壞了,所以重新部署也無法恢復,由於是master2和slave2的數據都丟失了,集群無法重建。通過開發了解到,redis上都是緩存數據,丟失影響不大,於是刪除本地持久化數據,重新部署redis node,再手動創建集群。

三個節點都添加完成,並且沒有報錯。進入一個master節點查看集群狀態:

集群狀態終於恢復正常。重建後的Redis Cluster集群架構示意圖如下

總結:對於有狀態的應用,redis、mysql等,容器化時一定要考慮周全,避免主從節點運行在一個節點上。對於redis應用,如果讀寫I/O不是特別高,還是建議直接使用主從復制架構,故障恢復簡單且迅速。