① 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示例:
② Kubernetes存儲
在 Docker的設計 實現中, 容器中的數據是臨時性的,當容器銷毀或重新啟動時存儲在容器內部的數據將會全部丟失 ,但實際上很多容器化應用是需要持久化保存的數據,這就需要使用Docker數據卷掛載宿主機上的文件或者目錄到容器中以保證數據的持久化存儲。在 Kubernetes中Pod重建如同Docker銷毀一樣,數據就會丟失 ,Kubernetes也通過掛載數據卷方式為Pod數據提供持久化能力,這些數據卷以Pod為最小單位進行存儲,通過共享存儲或分布式存儲在各個Pod之間實現共享。
Kubernetes是由Master節點及Node節點組成的,在Master節點中通過etcd存儲了Kubernetes集群的節點信息、Pod信息、容器信息、配置信息。Node節點主要對外提供容器服務,著重描述Node節點與存儲相關的內容。
Kubernetes以Pod為單位對外提供容器服務,真正的服務是通過Service進行訪問的。 Kubernetes中的服務按類型分成三種:無狀態服務(stateless)、普通有狀態服務、有狀態集群服務 。
無狀態服務:不需要持久化存儲的,即使Pod重建也不會受影響,只要確保服務的可靠性便可,Kubernetes通過ReplicationSet來保證某個服務的實例數量。
普通有狀態服務:這類服務需要保留服務的狀態,通常通過Kubernetes提供的Volume及Persistent Volume、Persistent Volume Claim來保存狀態。
有狀態的集群服務:這類服務除了保存服務狀態的同時還需要提供集群管理的功能,集群管理過程中也涉及臨時數據的保存、集群內數據共享等。
Kubernetes中涉及存儲的主要使用場景 :
1) 容器集群相關配置信息及運行時信息保存,這類信息存儲在etcd中。
2) 服務的基本配置文件及證書文件。
3) 服務的狀態存儲、數據存儲信息。
4) 集群內不同服務交換共享的數據信息。
1) 臨時文件形式 :同一個Pod內不同容器間通過共享內存方式訪問,會創建一個空目錄,交換完信息後會刪除這個空目錄。
2) HostPath方式 :同一個Node內不同的Pod間進行信息共享使用HostPath方式(比如時區timezone)。如果Pod配置了EmptyDir數據卷,則它在Pod的生命周期內都會存在。當Pod被分配到Node上的時候,會在Node上創建EmptyDir數據卷,並掛載到Pod的容器中。
3) PV及PVC: Kubernetes的持久化存儲機制的核心是PV(Persistent Volume)、PVC (Persistent Volume Claim)。PV是Volume插件,關聯到真正的後端存儲系統,PVC是從PV中申請資源,而不需要關心存儲的提供方。PVC和PV的關系就如同Pod和Node一樣,Pod是消費Node提供的資源,PVC是消費PV提供的存儲資源。PVC和PV通過匹配完成綁定關系,PVC可以被Pod里的容器掛載。
4) 網路方式 :不同Node節點之間的數據共享通過網路方式使用,通常採用分布式存儲方式。開源的分布式文件系統比較流行的選擇有GlusterFS和Ceph,還有一些其他的分布式文件系統(NFS)選擇。
5) 自定義插件方式 :Kubernetes提供了豐富的Volume的插件來進行存儲管理。如果存儲管理介面不夠用,用戶可以通過CSI或Flex Volume進行擴展。
存儲管理組件(存儲組件)主要是接收北向API收到的Rest請求,維護持久卷的生命周期管理。如創建、刪除卷, 存儲組件負責與後端存儲軟體交互完成實際的創建、刪除卷等操作;並負責調用Kubernetes原生介面創建對應的PVC和PV 。
存儲後端系統提供數據文件的實際持久化能力,不僅需要實現數據文件的讀寫、副本復制等存儲操作,通常還需具備多節點高可用的能力。
當需要為自己的微服務應用掛載持久卷時,只需要通過存儲組件創建持久卷,存儲組件會在Kubernetes業務集群創建PVC/PV,並到後端存儲系統(如GlusterFS)上創建真正的物理Volume,同時維護好PVC/PV/Volume之間的一一映射對應關系。這樣,用戶部署容器時就可以選擇掛載相應的持久卷,部署後相應的卷就可以掛載到對應的容器。應用掛載持久卷後,進行讀寫時就類似於本地目錄的讀寫操作。
在Pod進行重建或者遷移到其他節點時,Pod可以自動掛回原來對應的持久卷,繼續使用原先的數據。多個Pod可以共享一個持久卷,從而達到容器間文件共享的目的。
摘抄自陸平的《基於Kubernetes的容器雲平台實戰》一書的第11章Kubernetes存儲
③ 深入剖析k8s中pod的意義和用法
本文是《深入剖析k8s》學習筆記的第二篇,主要解析pod的意義及其使用方法。
pod,是k8s中最小的API對象,是原子調度單位。是超親密關系容器之間組織和部署的單位。類比地說,pod就是虛擬機,其中的容器就是這個虛擬機裡面運行的用戶進程。
pod中的所有容器共享network、volume、IP地址,在pod啟動的時候,需要先啟動一個Infra中間容器,而其它容器都是通過join的方式加入到Infra容器的資源中的。Infra容器是一個用匯編語言編寫的,永遠處於暫停狀態的容器,其唯一的作用就是hold住資源,和pod同生命周期。
initcontainers是一種容器類型,相較於containers類型,前者總是先於後者啟動,initcontainers如果有多個,則會按照定義的順序先後啟動,只有當所有的initcontainers都啟動成功且退出了,containers用戶容器才會啟動。
sidecar,是一種容器設計模式,指的是我們可以在一個pod中,啟動一個輔助容器來完成一些獨立於主容器之外的工作。比如initcontainers容器、Infra容器,都屬於sidecar。
在進行上雲工作的時候,我們可以把虛擬機類同為一個pod,把裡面的進程類同為容器鏡像,把有順序關系的容器,定義為initcontainers。如此才是合理的、松耦合的容器編排訣竅,也是傳統應用架構演變到微服務架構最自然的過渡方式。
pod有如下幾個重要的屬性需要掌握。
pod有如下幾個狀態需要掌握:
有幾種特殊的volume,它們並不是為了存放容器中的數據,也不是為了進行容器之間或者和宿主機之間進行數據共享,而是為了給容器提供預先定義好的數據。這種數據卷被稱為」投射數據卷「,projected volume。
pod可以為其中的容器配置探針(probe),用以監控容器的健康檢查,而不是以容器鏡像是否運行來作為健康檢查的依據,因為會存在很多情況,容器是正常運行的,但是無法對外提供服務了,因此探針的健康檢查方式更加准確。k8s一旦檢測到容器探針發生異常,就會根據設置好的pod恢復機制進行操作,恢復機制restartPolicy有如下幾種:
默認情況下pod的恢復機制是always,但並不是所有場景下都是合適的,比如initContainers初始化容器執行任務之後就結束了,就不應該設置為always。
如下文檔提供了全量的yaml屬性,特別是關於PodSpec的屬性可以在3050行看到: api/types.go at master · kubernetes/api · GitHub
④ K8S——Pod入門理解
1Pod的含義?
官方解釋:在Kubernetes集群中,Pod是所有業務類型的基礎,它是一個或多個容器的組合。這些容器共享存儲、網路和命名空間,以及如何運行的規范。在Pod中,所有容器都被同一安排和調度,並運行在共享的上下文中。對於具體應用而言,Pod是它們的邏輯主機,Pod包含業務相關的多個應用容器。Kubernetes不只是支持Docker容器,它也支持其他容器。
個人理解:Pod是容器組的一個抽象,類似於一棟出租樓裡面的房子,房子的其他小房間像容器,房間里的水,電充當應用服務。出租屋內的小房間門跟容器埠差不多,出租屋大門像pod上的埠,整棟樓大門像service對外暴露的埠。
2使用Pod的原因?
pod是K8s最小的運行,部署單位。用K8s作容器管理,比如Mysql,Redis等服務運行需要創建Pod,才能對外提供服務。
3Pod操作相關命令
3.0 pod創建
kubectl run httpd--image=httpd -n 空間名
3.1 查看所有空間上的pod節點
kubectlget pods --all-namespaces
3.2 查看指定空間上的pod
kubectlgetpod -n 空間名
3.3 查看pod詳情
kubectlget pod -o wide -n 空間名kubectldescribe pod name
3.4 指定變更pod空間清單腳本
kubectledit pod pod名 -n 空間名
3.5 刪除pod
刪除某個空間下pod
kubectldeleteall--all -n 空間名
刪除默認空間 pod
kubectldeletepodsname
刪除指定空間下pod
kubectldeletepodsname-n 空間名
3.6 登錄Pod裡面容器 (container-name 通過3.3 可以獲取)
kubectl exec -ti-c/bin/bash container-name 有多個是要指定 只有一個去掉
kubectl exec-cdate container-name 有多個是要指定 只有一個去掉 指定pod裡面某個容器執行命令
3.7 查看Pod裡面容器日誌
kubectl logs -f-c
4腳本創建Pod
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: tomcat:8.0
name: test-container
ports:
- containerPort: 8080
protocol: TCP
⑤ Pod的特性
Pod是k8s系統中可以創建和管理的最小單元,是資源對象模型中由用戶創建或部署的最小資源對象模型,也是在k8s上運行容器化應用的資源對象,其他的資源對象都是用來支撐或者擴展Pod對象功能的,比如控制器對象是用來管控Pod對象的,Service或者Ingress資源對象是用來暴露Pod引用對象的,PersistentVolume資源對象是用來為Pod提供存儲等等,k8s不會直接處理容器,而是Pod,Pod是由一個或者多個container組成的。
每個Pod都是運行應用的單個實例,如果需要水平擴展應用(例如,運行多個實例),則應該使用多個Pods,每個實例一個Pod。在Kubernetes中,這樣通常稱為Replication。Replication的Pod通常由Controller創建和管理。
Pod可以被理解成一群可以共享網路、存儲和計算資源的容器化服務的集合。再打個形象的比喻,在同一個Pod里的幾個Docker服務/程序,好像被部署在同一台機器上,可以通過localhost互相訪問,並且可以共用Pod里的存儲資源(這里是指Docker可以掛載Pod內的數據卷,數據卷的概念,後文會詳細講述,暫時理解為「需要手動mount的磁碟」)。筆者總結Pod如下圖,可以看到:同一個Pod之間的Container可以通過localhost互相訪問,並且可以掛載Pod內所有的數據卷;但是不同的Pod之間的Container不能用localhost訪問,也不能掛載其他Pod的數據卷。
1.1、為什麼需要pod
我們先談談為什麼k8s會使用pod這個最小單元,而不是使用docker的容器,k8s既然使用了pod,當然有它的理由。
1、更利於擴展
k8s不僅僅支持Docker容器,也支持rkt甚至用戶自定義容器,為什麼會有這么多不同的容器呢,因為容器並不是真正的虛擬機,docker的一些概念和誤區總結,此外,Kubernetes不依賴於底層某一種具體的規則去實現容器技術,而是通過CRI這個抽象層操作容器,這樣就會需要pod這樣一個東西,pod內部再管理多個業務上緊密相關的用戶業務容器,就會更有利用業務擴展pod而不是擴展容器。
2、更容易定義一組容器的狀態
如果我們沒有使用pod,而是直接使用一組容器去跑一個業務呢,那麼當其中一個或者若干個容器出現問題呢,我們如何去定義這一組容器的狀態呢,通過pod這個概念,這個問題就可以很好的解決,一組業務容器跑在一個k8s的pod中,這個pod中會有一個pause容器,這個容器與其他的業務容器都沒有關系,以這個pause容器的狀態來代表這個pod的狀態.
3、利於容器間文件共享,以及通信。
pod里的多個業務容器共享pause容器的ip和存儲卷Volume,pod中的其他容器共享pause容器的ip地址和存儲,這樣就做到了文件共享和互信。
1.2 Pod 特性:
1 資源共享:IP和Volume
一個Pod里的多個容器可以共享存儲和網路IP,可以看作一個邏輯的主機。共享的如 namespace,cgroups或者其他的隔離資源。
多個容器共享同一個network namespace,由此在一個Pod里的多個容器共享Pod的IP和埠namespace,所以一個Pod內的多個容器之間可以通過localhost來進行通信,所需要注意的是不同容器要注意不要有埠沖突即可。不同的Pod有不同的IP,不同Pod內的多個容器之前通信,不可以使用IPC(如果沒有特殊指定的話)通信,通常情況下使用Pod的IP進行通信。
k8s要求底層網路支持集群內任意兩個pod直接的TCP/IP直接通信,這通常才有虛擬二層網路技術來實現,例如Flannel,Openswitch等。
一個Pod里的多個容器可以共享存儲卷,這個存儲卷會被定義為Pod的一部分,並且可以掛載到該Pod里的所有容器的文件系統上。
2 生命周期短暫
Pod屬於生命周期比較短暫的組件,比如,當Pod所在節點發生故障,那麼該節點上的Pod會被調度到其他節點,但需要注意的是,被重新調度的Pod是一個全新的Pod,跟之前的Pod沒有半毛錢關系。
3 平坦的網路
K8s集群中的所有Pod都在同一個共享網路地址空間中,也就是說每個Pod都可以通過其他Pod的IP地址來實現訪問。
1.3 Pod使用和管理
1、核心原則是:將多個應用分散到多個Pod中 原因:基於資源的合理應用;擴縮容,不同應用應該有不同的擴縮容策略等。
結論:單Pod單容器應用,除非特殊原因。
你很少會直接在kubernetes中創建單個Pod。因為Pod的生命周期是短暫的,用後即焚的實體。當Pod被創建後(不論是由你直接創建還是被其他Controller),都會被Kubernetes調度到集群的Node上。直到Pod的進程終止、被刪掉、因為缺少資源而被驅逐、或者Node故障之前這個Pod都會一直保持在那個Node上。
Pod不會自愈。如果Pod運行的Node故障,或者是調度器本身故障,這個Pod就會被刪除。同樣的,如果Pod所在Node缺少資源或者Pod處於維護狀態,Pod也會被驅逐。Kubernetes使用更高級的稱為Controller的抽象層,來管理Pod實例。雖然可以直接使用Pod,但是在Kubernetes中通常是使用Controller來管理Pod的。
1.4、Pod和Controller
Controller可以創建和管理多個Pod,提供副本管理、滾動升級和集群級別的自愈能力。例如,如果一個Node故障,Controller就能自動將該節點上的Pod調度到其他健康的Node上。
包含一個或者多個Pod的Controller示例:
Deployment
StatefulSet
DaemonSet
通常,Controller會用你提供的Pod Template來創建相應的Pod。
在用戶定義范圍內,如果pod增多,則ReplicationController會終止額外的pod,如果減少,RC會創建新的pod,始終保持在定義范圍。例如,RC會在Pod維護(例如內核升級)後在節點上重新創建新Pod。
對Pod的定義可以通過Yaml或Json格式的配置文件來完成。關於Yaml或Json中都能寫哪些參數,參考官網 http://kubernetes.io/docs/user-guide/pods/multi-container/
Pod的yaml整體文件內容及功能註解如下:
我們來看一個段實際的例子
在使用docker時,我們可以使用docker run命令創建並啟動一個容器,而在Kubernetes系統中對長時間運行的容器要求是:其主程序需要一直在前台運行。如果我們創建的docker鏡像的啟動命令是後台執行程序,例如Linux腳本:
nohup ./startup.sh &
則kubelet創建包含這個容器的pod後運行完該命令,即認為Pod執行結束,之後根據RC中定義的pod的replicas副本數量生產一個新的pod,而一旦創建出新的pod,將在執行完命令後陷入無限循環的過程中,這就是Kubernetes需要我們創建的docker鏡像以一個前台命令作為啟動命令的原因。
對於無法改造為前台執行的應用,也可以使用開源工具supervisor輔助進行前台運行的功能。
Pod可以由一個或多個容器組合而成
場景1:單個應用多個容器
spring boot web:
kubectl create -f springboot-deployment.yml
kubectl get pods -o wide
加入 –o wide參數 查看額外信息:包括node和ip
pod處於pending的原因:通過 kubectl describe pods springbootweb 進一步查找問題。
可以看到pod的鏡像信息寫錯了:
先刪除pod,然後再創建: kubectl delete pod springbootweb
由於創建的埠號是9081,可以直接訪問:curl 10.0.86.2:9081
# curl 10.0.86.2:9081
Hello world
場景2:Pod不同應用多個容器組合而成
例如:兩個容器應用的前端frontend和redis為緊耦合的關系,應該組合成一個整體對外提供服務,則應該將這兩個打包為一個pod.
配置文件frontend-localredis-pod.yaml如下:
屬於一個Pod的多個容器應用之間相互訪問只需要通過localhost就可以通信,這一組容器被綁定在一個環境中。
使用kubectl create創建該Pod後,get Pod信息可以看到如下圖:
#kubectl get gods
可以看到READY信息為2/2,表示Pod中的兩個容器都成功運行了.
2.3 集群外部訪問Pod
上面的例子,在k8s集群的安裝有kube-proxy的node節點上,可以直接通過curl 10.0.86.2:9081 訪問集群的pod。但在集群外的客戶端系統無法通過Pod的IP地址或者Service的虛擬IP地址和虛擬埠號訪問到它們。為了讓外部客戶端可以訪問這些服務,可以將Pod或Service的埠號映射到宿主機,以使得客戶端應用能夠通過物理機訪問容器應用。
1、將容器應用的埠號映射到物理機
(2)通過設置Pod級別的hostNetwork-true,該Pod中所有容器的埠號都將被直接映射到物理機上。設置hostNetwork-true時需要注意,在容器的ports定義部分如果不指定hostPort,則默認hostPort等於containerPort,如果指定了hostPort,則hostPort必須等於containerPort的值。
靜態pod是由kubelet進行管理的僅存在於特定Node的Pod上,他們不能通過API Server進行管理,無法與ReplicationController、Deployment或者DaemonSet進行關聯,並且kubelet無法對他們進行健康檢查。靜態Pod總是由kubelet進行創建,並且總是在kubelet所在的Node上運行。
創建靜態Pod有兩種方式:配置文件或者HTTP方式
1)配置文件方式
首先,需要設置kubelet的啟動參數"--config",指定kubelet需要監控的配置文件所在的目錄,kubelet會定期掃描該目錄,並根據目錄中的 .yaml或 .json文件進行創建操作
假設配置目錄為/etc/kubelet.d/配置啟動參數:--config=/etc/kubelet.d/,然後重啟kubelet服務後,再宿主機受用docker ps或者在Kubernetes Master上都可以看到指定的容器在列表中
由於靜態pod無法通過API Server直接管理,所以在master節點嘗試刪除該pod,會將其變為pending狀態,也不會被刪除
#kubetctl delete pod static-web-node1
要刪除該pod的操作只能在其所在的Node上操作,將其定義的.yaml文件從/etc/kubelet.d/目錄下刪除
#rm -f /etc/kubelet.d/static-web.yaml
#docker ps
Volume類型包括:emtyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、scsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume等等,可以定義多個Volume,每個Volume的name保持唯一。在同一個pod中的多個容器能夠共享pod級別的存儲卷Volume。Volume可以定義為各種類型,多個容器各自進行掛載操作,講一個Volume掛載為容器內需要的目錄。
如下圖:
如上圖中的Pod中包含兩個容器:tomcat和busybox,在pod級別設置Volume 「app-logs」,用於tomcat想其中寫日誌文件,busybox讀日誌文件。
配置文件如下:
busybox容器可以通過kubectl logs查看輸出內容
#kubectl logs volume-pod -c busybox
tomcat容器生成的日誌文件可以登錄容器查看
#kubectl exec -ti volume-pod -c tomcat -- ls /usr/local/tomcat/logs
應用部署的一個最佳實踐是將應用所需的配置信息於程序進行分離,這樣可以使得應用程序被更好的復用,通過不用配置文件也能實現更靈活的功能。將應用打包為容器鏡像後,可以通過環境變數或外掛文件的方式在創建容器時進行配置注入。ConfigMap是Kubernetes v1.2版本開始提供的一種統一集群配置管理方案。
6.1 ConfigMap:容器應用的配置管理
容器使用ConfigMap的典型用法如下:
ConfigMap以一個或多個key:value的形式保存在Kubernetes系統中共應用使用,既可以用於表示一個變數的值,也可以表示一個完整的配置文件內容。
通過yuaml配置文件或者直接使用kubelet create configmap 命令的方式來創建ConfigMap
6.2 ConfigMap的創建
舉個小例子cm-appvars.yaml來描述將幾個應用所需的變數定義為ConfigMap的用法:
# vim cm-appvars.yaml
執行kubectl create命令創建該ConfigMap
#kubectl create -f cm-appvars.yaml
查看建立好的ConfigMap:
#kubectl get configmap
kubectl describe configmap cm-appvars
kubectl get configmap cm-appvars -o yaml
另:創建一個cm-appconfigfile.yaml描述將兩個配置文件server.xml和logging.properties定義為configmap的用法,設置key為配置文件的別名,value則是配置文件的文本內容:
在pod "cm-test-app"定義中,將configmap "cm-appconfigfile"中的內容以文件形式mount到容器內部configfiles目錄中。
Pod配置文件cm-test-app.yaml內容如下:
創建該Pod:
#kubectl create -f cm-test-app.yaml
Pod "cm-test-app"created
登錄容器查看configfiles目錄下的server.xml和logging.properties文件,他們的內容就是configmap 「cm-appconfigfile」中定義的兩個key的內容
#kubectl exec -ti cm-test-app -- bash
root@cm-rest-app:/# cat /configfiles/server.xml
root@cm-rest-app:/# cat /configfiles/ logging.properties
6.3使用ConfigMap的條件限制
使用configmap的限制條件如下:
Pod在整個生命周期過程中被定義為各種狀態,熟悉Pod的各種狀態有助於理解如何設置Pod的調度策略、重啟策略
Pod的狀態包含以下幾種,如圖:
Pod的重啟策略(RestartPolicy)應用於Pod內所有的容器,並且僅在Pod所處的Node上由kubelet進行判斷和重啟操作。當某哥容器異常退出或者健康檢查石柏師,kubelet將根據RestartPolicy的設置進行相應的操作
Pod的重啟策略包括Always、OnFailure及Nerver,默認值為Always。
kubelet重啟失效容器的時間間隔以sync-frequency乘以2n來計算,例如1、2、4、8倍等,最長延時5分鍾,並且成功重啟後的10分鍾後重置該事件。
Pod的重啟策略和控制方式息息相關,當前可用於管理Pod的控制器寶庫ReplicationController、Job、DaemonSet及直接通過kubelet管理(靜態Pod),每種控制器對Pod的重啟策略要求如下:
RC和DaemonSet:必須設置為Always,需要保證該容器持續運行
Job:OnFailure或Nerver,確保容器執行完成後不再重啟
kubelet:在Pod失效時重啟他,不論RestartPolicy設置什麼值,並且也不會對Pod進行健康檢查
對Pod的健康檢查可以通過兩類探針來檢查:LivenessProbe和ReadinessProbe
LivenessProbe探針:用於判斷容器是否存活(running狀態),如果LivenessProbe探針探測到容器不健康,則kubelet殺掉該容器,並根據容器的重啟策略做響應處理
ReadinessProbe探針:用於判斷容器是否啟動完成(ready狀態),可以接受請求。如果ReadinessProbe探針探測失敗,則Pod的狀態被修改。Endpoint Controller將從service的Endpoint中刪除包含該容器所在的Pod的Endpoint。
kubelet定製執行LivenessProbe探針來診斷容器的健康狀況。LivenessProbe有三種事項方式。
1)ExecAction:在容器內部執行一個命令,如果該命令的返回值為0,則表示容器健康。例:
(2)TCPSocketAction:通過容器ip地址和埠號執行TCP檢查,如果能夠建立tcp連接表明容器健康。例:
3)HTTPGetAction:通過容器Ip地址、埠號及路徑調用http get方法,如果響應的狀態嗎大於200且小於400,則認為容器健康。例:
對於每種探針方式,都需要設置initialDelaySeconds和timeoutSeconds兩個參數,它們含義如下:
initialDelaySeconds:啟動容器後首次監控檢查的等待時間,單位秒
timeouSeconds:健康檢查發送請求後等待響應的超時時間,單位秒。當發生超時就被認為容器無法提供服務無,該容器將被重啟
九.玩轉Pod調度
在Kubernetes系統中,Pod在大部分場景下都只是容器的載體而已,通常需要通過RC、Deployment、DaemonSet、Job等對象來完成Pod的調度和自動控制功能。
9.1 RC、Deployment:全自動調度
RC的主要功能之一就是自動部署容器應用的多份副本,以及持續監控副本的數量,在集群內始終維護用戶指定的副本數量。
在調度策略上,除了使用系統內置的調度演算法選擇合適的Node進行調度,也可以在Pod的定義中使用NodeName、NodeSelector或NodeAffinity來指定滿足條件的Node進行調度。
1)NodeName
Pod.spec.nodeName用於強制約束將Pod調度到指定的Node節點上,這里說是「調度」,但其實指定了nodeName的Pod會直接跳過Scheler的調度邏輯,直接寫入PodList列表,該匹配規則是強制匹配。
2)NodeSelector:定向調度
Kubernetes Master上的scheler服務(kube-Scheler進程)負責實現Pod的調度,整個過程通過一系列復雜的演算法,最終為每個Pod計算出一個最佳的目標節點,通常我們無法知道Pod最終會被調度到哪個節點上。實際情況中,我們需要將Pod調度到我們指定的節點上,可以通過Node的標簽和pod的nodeSelector屬性相匹配來達到目的。
Pod.spec.nodeSelector是通過kubernetes的label-selector機制進行節點選擇,由scheler調度策略MatchNodeSelector進行label匹配,調度pod到目標節點,該匹配規則是強制約束。
啟用節點選擇器的步驟為:
kubectl label nodes <node-name> <label-key>=<label-value>
例: #kubectllabel nodes k8s-node-1 zonenorth
運行kubectl create -f命令創建Pod,scheler就會將該Pod調度到擁有zone=north標簽的Node上。 如果多個Node擁有該標簽,則會根據調度演算法在該組Node上選一個可用的進行Pod調度。
需要注意的是:如果集群中沒有擁有該標簽的Node,則這個Pod也無法被成功調度。
3)NodeAffinity:親和性調度
該調度策略是將來替換NodeSelector的新一代調度策略。由於NodeSelector通過Node的Label進行精確匹配,所有NodeAffinity增加了In、NotIn、Exists、DoesNotexist、Gt、Lt等操作符來選擇Node。調度側露更加靈活。
9.2 DaemonSet:特定場景調度
DaemonSet用於管理集群中每個Node上僅運行一份Pod的副本實例,如圖:
這種用法適合一些有下列需求的應用:
在實際生產環境中,我們經常遇到某個服務需要擴容的場景,也有可能因為資源精確需要縮減資源而需要減少服務實例數量,此時我們可以Kubernetes中RC提供scale機制來完成這些工作。
以redis-slave RC為例,已定義的最初副本數量為2,通過kubectl scale命令可以將Pod副本數量
#kubectl scale rc redis-slave --replicas=3
ReplicationController"redis-slave" scaled
#kubectl get pods
除了可以手工通過kubectl scale命令完成Pod的擴容和縮容操作以外,新版本新增加了Horizontal Podautoscaler(HPA)的控制器,用於實現基於CPU使用路進行啟動Pod擴容縮容的功能。該控制器基於Mastger的kube-controller-manager服務啟動參數 --horizontal-pod-autoscler-sync-period定義的時長(默認30秒),周期性監控目標Pod的Cpu使用率並在滿足條件時對ReplicationController或Deployment中的Pod副本數量進行調整,以符合用戶定義的平均Pod Cpu使用率,Pod Cpu使用率來源於heapster組件,所以需預先安裝好heapster。
當集群中的某個服務需要升級時,我們需要停止目前與該服務相關的所有Pod,然後重新拉取鏡像並啟動。如果集群規模較大,因服務全部停止後升級的方式將導致長時間的服務不可用。由此,Kubernetes提供了rolling-update(滾動升級)功能來解決該問題。
滾動升級通過執行kubectl rolling-update命令一鍵完成,該命令創建一個新的RC,然後自動控制舊版本的Pod數量逐漸減少到0,同時新的RC中的Pod副本數量從0逐步增加到目標值,最終實現Pod的升級。需要注意的是,系統要求新的RC需要與舊的RC在相同的Namespace內,即不能把別人的資產轉到到自家名下。
例:將redis-master從1.0版本升級到2.0:
需要注意的點:
運行kubectl rolling-update來完成Pod的滾動升級:
#kubectl rolling-update redis-master -f redis-master-controller-v2.yaml
另一種方法就是不使用配置文件,直接用kubectl rolling-update加上--image參數指定新版鏡像名來完成Pod的滾動升級
#kubectl rolling-update redis-master --image=redis-master:2.0
與使用配置文件的方式不同的是,執行的結果是舊的RC被刪除,新的RC仍然使用就的RC的名字。
如果在更新過程總發現配置有誤,則用戶可以中斷更新操作,並通過執行kubectl rolling-update-rollback完成Pod版本的回滾。
⑥ k8s之存儲
k8s的存儲常用的就是上面幾種模式,分為臨時存儲,半持久化存儲,與持久化存儲這三類,本章我們著重講解emptydir與hostpath與pvc跟pv等
當pod的存儲方案設定為emptydir的時候,pod啟動時,就會在pod所在節點的磁碟空間開辟出一塊空卷,最開始裡面是什麼都沒有的,pod啟動後容器產生的數據會存放到那個空卷中。空卷變成了一個臨時卷
供pod內的容器讀取和寫入數據,一旦pod容器消失,節點上開辟出的這個臨時卷就會隨著pod的銷毀而銷毀
一般來說emptydir的用途都是用來充當臨時存儲空間,例如一些不需要數據持久化的微服務,我們都可以用emptydir來當做微服務pod的存儲方案
k8s存儲emptdir實戰例子:以之前的myapp應用為例
創建應用
觀察是否生產data目錄,並在/data目錄創建文件test.txt
手動刪除容器模擬容器銷毀,用於是pod是被控制器管理的,刪除後會被重建新的pod
這時候在看我們之前創建的data.txt已經不見了
hostPath類型則是映射node文件系統中的文件或者目錄到pod里。在使用hostPath類型的存儲卷時,也可以設置type欄位,支持的類型有文件、目錄、File、Socket、CharDevice和BlockDevice(我只映射過文件與目錄)。
其實這個功能就相當於docker中的-v 目錄映射,只不過在k8s中的時候,pod會漂移,當pod漂移到其他node節點的時候,pod不會跨節點的去讀取目錄。所以說hostpath只能算一種半持久化的存儲方式
實戰例子
創建應用
在node節點可以看到生成了/data/volume目錄,在裡面創建測試文件
檢驗pod裡面的/data是否存在這個映射目錄的文件
可以看到剛才創建的文件已經映射到我們的目錄里邊了
為了驗證是否映射到容器裡面的目錄也會隨著pod生命周期的消失而消失,我們手動刪除pod模擬容器終止
可以看到容器被刪除後,新建的pod也可以看到我們映射的目錄,而且裡面數據test.txt還在。
這有個缺點就是不能夠跨容器去讀取數據,如果刪除後的pod被調度到其他節點的話,原來的數據也就沒有了,如果能不受節點的影響,並且掛載的數據不會隨生命周期的結束而結束,我們應該怎麼解決呢?就是我們後面講到的持久化存儲了
上面介紹了倆種臨時存儲於半持久化存儲的方案。在k8s實際生產環境中,一般會選用私有雲持久化存儲方案還有公有雲持久化存儲方案,私有雲存儲方案包括nfs,ceph,glusterfs等方案。公有雲存儲會用到AWS等方案
存儲方案各有各的優缺點,可參考 https://www.cnblogs.com/yswenli/p/7234579.html 這篇文章。今天我們主要講解pvc,pv,nfs之間的關系。
簡單來說,要使用持久化存儲,就需要使用pvc去跟pv去申請,然後pv查看自己有沒有合適的存儲空間卷,有合適的就與pvc進行綁定。pv與pvc是一一對應綁定的。現在我們用一幅圖來說明pvc,pv,nfs的關系
實戰例子
准備存儲服務安裝nfs
接下來創建pv與nfs綁定
創建pvc與pv關聯
創建並查看結果
注意:STATUS為Bound說明該pvc已經與pv綁定了
接下來創建pod來引用pvc
創建pod
接下來驗證一下在nfs創建一個測試文件,看是否被映射到容器中
可以看到容器裡面也可以看到創建的文件
手動刪除容器,或者調度到其他節點原來的文件還是不會被刪除的,這里就省略驗證了,這就是nfs的好處,不過企業一般不會選用nfs,磁碟IO,性能讀取方面不太理想,畢竟是本地存儲,還是有一定的風險。推薦用用雲存儲。
文件存儲提供無限擴展的文件系統來給雲伺服器存取數據實際上相當於nfs
1、已注冊阿里雲賬號,並完成實名認證。
如果沒有,請先注冊阿里雲賬號,詳情請參見阿里雲賬號注冊流程。
2、已開通NAS服務。
首次登錄NAS控制台時,根據頁面提示開通NAS服務。
3、在需要創建文件系統的地域,已有可用的雲伺服器ECS。
k8s應用NAS實戰例子
1、打開阿里雲NAS控制台確認已創建好文件系統
2、把復制掛載參數把它掛載到伺服器中創建測試目錄,前提是伺服器需要安裝nfs客戶端。
安裝完成掛載到本地目錄並創建test目錄作為測試目錄
並在裡面創建一個測試文件1.txt
接下來可以創建pvc和pv了
創建並查看
接下來創建pod去引用pvc
創建pod
檢驗剛才的1.txt是否掛到容器的/data/nas下
雲存儲適合於生產環境k8s的存儲解決方案
⑦ microk8s上給Pod掛載NFS
團隊新開發的區域醫療平台包含一個課件上傳與播放模塊,其實際的業務包含如下的步驟:
1. 縣級和鄉鎮衛生院的醫生們通過在線視頻參加培訓、並錄制視頻。
2. 醫生上傳視頻,並共享課件。
3. 平台上的其他醫生可以在課件學習欄目學習錄制的培訓會議。
通常,課件的上傳和保存我們都是通過對象存儲做的,對象存儲的好處顯而易見:不擔心文件丟失(三份備份), 不擔心容量(雲服務商集群),https/http訪問, 數據安全(對象訪問簽名); 但是這個平台需要部署到區域醫療機構的機房裡,多數情況下是沒有對象存儲的,外購對象存儲也成本過高, 所以我們採用了折中的方案,存儲系統換為NFS, 只需要一個大存儲量的機器就可以了,大概服務如下:
我們開發了一個uploader服務,用於上傳文件,同時使用Nginx提供http/https服務,兩個服務之間共享存儲,使用NFS存儲掛載給他們。
這里我們來看看怎麼給microk8s上的pod掛載NFS存儲。
首先,我們需要安裝NFS server 用於測試
我們使用ubuntu來進行測試,先執行命令:sudo apt install nfs-kernel-server
這個命令將安裝伺服器端,以及所有相關的包:
在home目錄下創建一個nfsshare的文件夾用作共享目錄。然後我們來編輯nfs配置文件,配置該共享目錄,默認允許所有IP段掛在:
啟動nfs: sudo /etc/init.d/nfs-kernel-server start
可以使用服務命令查看服務狀態: service nfs-kernel-server status
使用ip addr命令查看以下當前主機的IP:
現在我們來使用nfsclient端測試一下:
sudo mount 10.0.2.15:/home/nfsshare /mnt
查詢 /mnt 目錄,可以看到在 /home/nsshare下面創建的文件和子目錄
測試完後umount 掛載點: sudo umount -v /mnt
1. 先寫一個PV, 用來表示可以掛載的NFS存儲:
2. 接著寫一個PVC,用於綁定PV:
3. 然後寫包裝了nginx的存儲服務的deployment文件:
4. 最後寫存儲服務對應的service文件:
完成了所有配置文件,使用microk8s kubectl apply -f 命令,依次創建資源對象。
查詢創建的pod:
使用exec 登陸docker,
查詢/usr/share/storage目錄,如下圖,nfs已經掛載好了,可以看見我們在test目錄下創建了幾個文件:
最後,我們來驗證一下http方式訪問文件
現在storage的nfs目錄下創建兩個子目錄,attachement、video , 對應docker的掛在路徑為: /usr/share/storage/atttachment、/usr/share/storage/video。
在兩個文件夾下面,分別創建兩個文件 helloA.txt helloB.txt, 並隨意寫寫內容。
完成後我們通過30080埠,從microk8s節點的瀏覽器訪問兩個文件:
至此,在k8s下,給pod掛在nfs的工作,並通過http訪問的任務就完了。另外:
1. 後續需要考慮,通過Lua寫一個腳本和nginx集成,實現訪問資源簽名驗證,這個機制可以很容易的參考對象存儲的驗證。
2. 這里沒有討論,如何寫upload和製作storage服務的鏡像,upload服務也需要掛載nfs,原理是一樣的,所以就不再討論。 storage服務是基於Nginx鏡像實現,下面的附錄了帶上了可參考的配置文件。
==============================================================
附錄A: storageservice服務中,nginx的配置文件storage.conf
附錄B: storageservice服務中dockerfile文件
附錄C: Storageservice的build.gradle文件
參考 一個Springboot項目的build.gradle和Dockerfile , 這里的buiild.gradle文件不需要java build, 僅需要用來生成新docker鏡像。