Kubernetes |Pod 深入理解與實(shí)踐
這篇文章參考自《Kubernete權(quán)威指南》逾一,對(duì)其中的相關(guān)章節(jié)做了一些總結(jié)几蜻,從下面十個(gè)點(diǎn)對(duì)pod進(jìn)行深入講解喇潘,也會(huì)有些配置的實(shí)例,希望對(duì)大家學(xué)習(xí)kubernetes帶來(lái)些許幫助入蛆。
1pod定義詳解
2pod到底是什么
3靜態(tài)pod
4pod容器共享volume
5pod的配置管理
6pod的生命周期和重啟策略
7pod健康檢查
8玩轉(zhuǎn)pod調(diào)度
9pod的擴(kuò)容和縮容
10pod的滾動(dòng)升級(jí)
1pod定義詳解
下面是一個(gè)完整的yaml格式定義的文件,注意格式响蓉,子集包含關(guān)系,不要有tab哨毁,要用空格枫甲。不是所有的元素都要寫(xiě),按照實(shí)際應(yīng)用場(chǎng)景配置即可扼褪。
apiVersion: v1 //版本
kind: pod //類(lèi)型想幻,pod
metadata: //元數(shù)據(jù)
name: String //元數(shù)據(jù),pod的名字
namespace: String //元數(shù)據(jù)话浇,pod的命名空間
labels: //元數(shù)據(jù)脏毯,標(biāo)簽列表
- name: String //元數(shù)據(jù),標(biāo)簽的名字
annotations: //元數(shù)據(jù),自定義注解列表
- name: String //元數(shù)據(jù),自定義注解名字
spec: //pod中容器的詳細(xì)定義
containers: //pod中的容器列表幔崖,可以有多個(gè)容器
- name: String
image: String //容器中的鏡像
imagesPullPolicy: [Always|Never|IfNotPresent]//獲取鏡像的策略
command: [String] //容器的啟動(dòng)命令列表(不配置的話使用鏡像內(nèi)部的命令)
args: [String] //啟動(dòng)參數(shù)列表
workingDir: String //容器的工作目錄
volumeMounts: //掛載到到容器內(nèi)部的存儲(chǔ)卷設(shè)置
- name: String
mountPath: String
readOnly: boolean
ports: //容器需要暴露的端口號(hào)列表
- name: String
containerPort: int //容器要暴露的端口
hostPort: int //容器所在主機(jī)監(jiān)聽(tīng)的端口(容器暴露端口映射到宿主機(jī)的端口)
protocol: String
env: //容器運(yùn)行前要設(shè)置的環(huán)境列表
- name: String
value: String
resources: //資源限制
limits:
cpu: Srting
memory: String
requeste:
cpu: String
memory: String
livenessProbe: //pod內(nèi)容器健康檢查的設(shè)置
exec:
command: [String]
httpGet: //通過(guò)httpget檢查健康
path: String
port: number
host: String
scheme: Srtring
httpHeaders:
- name: Stirng
value: String
tcpSocket: //通過(guò)tcpSocket檢查健康
port: number
initialDelaySeconds: 0//首次檢查時(shí)間
timeoutSeconds: 0 //檢查超時(shí)時(shí)間
periodSeconds: 0 //檢查間隔時(shí)間
successThreshold: 0
failureThreshold: 0
securityContext: //安全配置
privileged: falae
restartPolicy: [Always|Never|OnFailure]//重啟策略
nodeSelector: object //節(jié)點(diǎn)選擇
imagePullSecrets:
- name: String
hostNetwork: false //是否使用主機(jī)網(wǎng)絡(luò)模式食店,默認(rèn)否
volumes: //在該pod上定義共享存儲(chǔ)卷
- name: String
meptyDir: {}
hostPath:
path: string
secret: //類(lèi)型為secret的存儲(chǔ)卷
secretName: String
item:
- key: String
path: String
configMap: //類(lèi)型為configMap的存儲(chǔ)卷
name: String
items:
- key: String
path: String
2pod到底是什么
kubernetes中的一切都可以理解為是一種資源對(duì)象,pod,rc,service,都可以理解是 一種資源對(duì)象赏寇。podd的組成示意圖如下:
由一個(gè)叫”pause“的根容器吉嫩,加上一個(gè)或多個(gè)用戶(hù)自定義的容器構(gòu)造。pause的狀態(tài)帶便了這一組容器的狀態(tài)嗅定,pod里多個(gè)業(yè)務(wù)容器共享pod的Ip和數(shù)據(jù)卷自娩。
我是這樣理解的,在kubernetes環(huán)境下渠退,pod是容器的載體忙迁,所有的容器都是在pod中被管理,一個(gè)或多個(gè)容器放在pod里作為一個(gè)單元方便管理碎乃。還有就是docker和kubernetes也不是一家公司的姊扔,如果做一個(gè)編排部署的工具,你也不可能直接去管理別人公司開(kāi)發(fā)的東西吧梅誓,然后就把docker容器放在了pod里旱眯,在kubernetes的集群環(huán)境下晨川,我直接管理我的pod,然后對(duì)于docker容器的操作,我把它封裝在pod里删豺,不直接操作共虑。
3靜態(tài)pod
靜態(tài)Pod是由kubelet進(jìn)行管理的僅存在于特定Node上的pod.它們不能通過(guò)API Server進(jìn)行管理,無(wú)法與ReplicationController,Ddeployment或者DaemonSet進(jìn)行關(guān)聯(lián)呀页,也無(wú)法進(jìn)行健康檢查妈拌。
所以我覺(jué)得這個(gè)靜態(tài)pod沒(méi)啥用武之地啊,就不詳細(xì)的寫(xiě)下去了蓬蝶,偷個(gè)懶尘分,嘻嘻。
4pod容器共享volume
在pod中定義容器的時(shí)候可以為單個(gè)容器配置volume丸氛,然后也可以為一個(gè)pod中的多個(gè)容器定義一個(gè)共享的pod 級(jí)別的volume培愁。 那為啥要這樣做呢,比如你在一個(gè)pod里定義了一個(gè)web容器缓窜,然后把生成的日志文件放在了一個(gè)文件夾定续,你還定義了一個(gè)分析日志的容器,那這個(gè)時(shí)候你就可以把這放日志的文件配置為共享的禾锤,這樣一個(gè)容器生產(chǎn)私股,一個(gè)容器度就好了巍虫。
下面是一個(gè)使用共享volume的配置示例
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: tomcat
image: tomcat
ports:
- containerPort: 8080
volumeMounts:
- name: app-logs
mountPath: /usr/local/tomcat/logs
- name: loganalysis
image: loganalysis
volumeMounts:
- name: app-logs
mountPath: /usr/local/tomcat/logs
volumes:
- name: app-logs
emptyDir: {}
這個(gè)配置文件除了“emptyDir: {}”這個(gè)地方有點(diǎn)詭異以為纺蛆,其他地方我估計(jì)大家一看就能明白呛每,在最下面定義了一個(gè)叫“app-logs”的volume,然后上面的兩個(gè)容器來(lái)使用它就好了锌奴。
然后現(xiàn)在來(lái)說(shuō)說(shuō)“emptyDir: {}”,其實(shí)這個(gè)地方有三種選擇
volumes:
- name: app-logs
emptyDir: {}
volumes:
- name: app-logs
hostPth:
path: "/data"
volumes:
- name: app-logs
gcePersistenDisk:
pdName: my-data-disk //my-data-disk需要先創(chuàng)建好
fsType: ext4
emptyDir是Pod分配到Node后創(chuàng)建的伶贰,他的初始內(nèi)容為空涂身,pod在Node上移除以后也就被銷(xiāo)毀了泊窘。
hostPath是掛載到宿主機(jī)上的目錄逼争,比較適用于需要永久保存的數(shù)據(jù)
gcePersistenDisk 表示使用谷歌公有云提供的磁盤(pán)
創(chuàng)建my-data-disk: gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
5pod的配置管理
應(yīng)用部署的一個(gè)最佳實(shí)踐优床,就是將應(yīng)用所需的配置信息與程序進(jìn)行分離
kubernetes 提供了一種的集群配置管理方案,即ConfigMap,就是將一些環(huán)境變量或者配置文件定義為configmap氮凝,放在kubernetes中羔巢,可以讓其他pod 調(diào)用
configmap 有以下典型的用法
1 生成為容器內(nèi)的環(huán)境變量
2 設(shè)置容器啟動(dòng)命令的啟動(dòng)參數(shù)(需設(shè)置為環(huán)境變量)
3 以volume的形式掛載為容器內(nèi)部的文件或目錄
局限:
1ConfigMap 必須在pod之前創(chuàng)建
2ConfigMap也可以定于屬于某個(gè)NameSpace望忆,只有處于相同NameSpace的pod可以應(yīng)用它
3ConfigMap中的配額管理還未實(shí)現(xiàn)
4如果是volume的形式掛載到容器內(nèi)部罩阵,只能掛載到某個(gè)目錄下,該目錄下原有的文件會(huì)被覆蓋掉
5靜態(tài)不能用configmap(靜態(tài)pod 不受API server 管理)
下面是使用ConfigMap的示例
1定義一個(gè)ConfigMap 配置文件 cm-appvars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appvars
data:
apploglevel: info
appdatadir: /var/date
2創(chuàng)建ConfigMap: kubectl create -f cm-appvars.yaml
3使用ConfigMap(環(huán)境變量的形式)
apiVersion: v1
kind: Pod
metadata:
name: cm-test-pod
spec:
containers:
- name: cm-test
image: busybux
env:
- name: APPLOGLEVEL
vlaueFrom:
configMapKeyRef:
name: cm-appvars //要和之前創(chuàng)建的ConfigMap的name對(duì)應(yīng)
key: apploglevel
- name: APPDATADIR
vlaueFrom:
configMapKeyRef:
name: cm-appvars //要和之前創(chuàng)建的ConfigMap的name對(duì)應(yīng)
key: appdatadir
除了可以定義簡(jiǎn)單的k-v鍵值對(duì)启摄,還可以將整個(gè)配置文件定義成ConfigMap
比如server.xml logging.properties(使用volumeMount的形式稿壁,掛載到容器內(nèi)部)
1定義一個(gè)ConfigMap 配置文件 cm-jdbcproperties.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-jdbcproperties
data:
key-jdbcproperties: |
JDBC_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver
JDBC_URL=jdbc:mysql://localhost:3306/bz_argon?useUnicode=true&characterEncoding=utf8
JDBC_USER_NAME=root
JDBC_PASSWORD=maojiancai
JDBC_INITIALSIZE=10
JDBC_MAXACTIVE=20
JDBC_MAXIDLE=20
JDBC_MINIDLE=10
JDBC_MAXWAIT=60000
JDBC_VALIDATIONQUERY=SELECT 1 FROM DUAL
JDBC_TESTONBORROW=false
JDBC_TESTONRETURN=false
JDBC_TESTWHILEIDLE=true
JDBC_TIMEBETWEENEVICTIONRUNSMILLIS=6000
JDBC_MINEVICTABLEIDLETIMEMILLIS=25200000
JDBC_REMOVEABANDONED=true
JDBC_REMOVEABANDONEDTIMEOUT=1800
JDBC_LOGABANDONED=true
2創(chuàng)建ConfigMap: kubectl create -f cm-jdbcproperties.yaml
3使用ConfigMap(使用volumeMount的形式)
apiVersion: v1
kind: Pod
metadata:
name: cm-test-app
spec:
containers:
- name: cm-test-app
image: cm-test-app
ports:
- containerPort: 8080
volumeMounts:
- name: jdbcproperties //應(yīng)用下面定義的volumes名
mountPath: /configfiles
volumes:
- name: jdbcproperties //volumes名
configMap:
name: cm-jdbcproperties//這個(gè)名字是第二步創(chuàng)建的configMap
items:
- key: key-jdbcproperties
path: jdbc.properties
再提醒一下;
如果是volume的形式掛載到容器內(nèi)部,只能掛載到某個(gè)目錄下歉备,該目錄下原有的文件會(huì)被覆蓋掉
6pod的生命周期和重啟策略
pod一共有四種狀態(tài)
狀態(tài)值 | 描述 |
---|---|
Pending | APIserver已經(jīng)創(chuàng)建該server,但pod內(nèi)有一個(gè)或多個(gè)容器的鏡像還未創(chuàng)建傅是,可能在下載中。 |
Running | Pod內(nèi)所有的容器已創(chuàng)建,且至少有一個(gè)容器處于運(yùn)行狀態(tài)喧笔,正在啟動(dòng)或重啟狀態(tài) |
Failed | Pod內(nèi)所有容器都已退出帽驯,其中至少有一個(gè)容器退出失敗 |
Unknown | 由于某種原因無(wú)法獲取Pod的狀態(tài)比如網(wǎng)絡(luò)不通。 |
重啟策略 | 描述 |
---|---|
Always | 容器失效時(shí)书闸,即重啟 |
OnFailure | 容器終止運(yùn)行尼变,且退出碼不為0 時(shí)重啟 |
Never | P不重啟 |
Pod的重啟策略應(yīng)用于Pod內(nèi)的所有容器,由Pod所在Node節(jié)點(diǎn)上的Kubelet進(jìn)行判斷和重啟操作浆劲。重啟策略有以下三種:
重啟策略 | 描述 |
---|---|
Always | 容器失效時(shí)嫌术,即重啟 |
OnFailure | 容器終止運(yùn)行,且退出碼不為0 時(shí)重啟 |
Never | P不重啟 |
7pod健康檢查
Kubernetes內(nèi)部通過(guò)2種探針牌借,實(shí)現(xiàn)了對(duì)Pod健康的檢查
LivenessProbe探針:判斷容器是否存活(running)
ReadinessProbe探針: 用于判斷容器是否啟動(dòng)完成(ready)
LivenessProbe探針通過(guò)三種方式來(lái)檢查容器是否健康
(1)ExecAction:在容器內(nèi)部執(zhí)行一個(gè)命令度气,如果返回碼為0,則表示健康
示例:
apiVersion: v1
kind: Pod
metadata:
name: liveness
spec:
containers:
- name: liveness
image: liveness
args:
- /bin/sh
- -c
- echo ok > /tmp/healthy: sleep 10; rm - rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/health
initialDelaySeconds: 15
timeoutSeconds: 1
(2)TcpAction:通過(guò)IP 和port ,如果能夠和容器建立連接則表示容器健康
示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-with-healthcheck
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 15
timeoutSeconds: 1
(3)HttpGetAction:發(fā)送一個(gè)http Get請(qǐng)求(ip+port+請(qǐng)求路徑)如果返回狀態(tài)嗎在200-400之間則表示健康
示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-with-healthcheck
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /_status/healthz //請(qǐng)求路徑
port: 80
initialDelaySeconds: 15
timeoutSeconds: 1
8玩轉(zhuǎn)pod調(diào)度
在kubernetes系統(tǒng)中膨报,pod在大部分場(chǎng)景下都只是容器的載體而已磷籍,通常需要通過(guò)Deployment,DaemonSet,Job等對(duì)象來(lái)完成Pod的調(diào)度與自動(dòng)控制功能。
(1)RC,Deployment: 全自動(dòng)調(diào)度
RC的主要功能之一就是自動(dòng)部署一個(gè)容器應(yīng)用的多份副本丙躏,以及持續(xù)監(jiān)控择示,保持集群內(nèi)有一定數(shù)量的副本數(shù)量(配置文件指定了副本數(shù)量)
NodeSelector: 定向調(diào)度
kubernetes中的Schduler 負(fù)責(zé)實(shí)現(xiàn)pode的調(diào)度,他會(huì)根據(jù)一些復(fù)雜的算法晒旅,把pod調(diào)度到某一個(gè)Node上栅盲,如果你想指定某個(gè)Pod需要指定在某個(gè)Node上則可以通過(guò)NodeSelector定向調(diào)度
示例:
1首先通過(guò)kubectl給node打上標(biāo)簽:
格式: kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label nodes node1 zone=north
2在pod定義里選擇某個(gè)node
apiVersion: v1
kind: Pod
metadata:
name: pod-with-healthcheck
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
nodeSelector:
zone: north
除了有定向的,還有親和性的調(diào)度 NodeAffinity废恋,符合某種條件的谈秫,比如,某個(gè)值大于1的(可以理解為模糊匹配),NodeAffinity有In NotIn Exists DoesNotExists Gt Lt 等操作規(guī)則來(lái)選擇Node.
(2)DaemonSet: 特點(diǎn)場(chǎng)景調(diào)度
DaemonSet,用于管理在集群中每個(gè)Node上只運(yùn)行一份Pod的副本實(shí)例鱼鼓,比如在每節(jié)點(diǎn)上都運(yùn)行有且只有一個(gè)fluentd
示例:配置使得在每個(gè)節(jié)點(diǎn)上都有一個(gè)fluentd 容器
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
template:
metadata:
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
containers:
- name: fluentd-cloud-logging
images: gcr.io/google_containers/fluentd-elasticsearch:1.17
resources:
limits:
cpu: 100m
memory: 200Mi
env:
- name: FLUENTD_ARGS
value: -q
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: false
- name: containers
mountPath: /var/lib/docker/containers
volumes:
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log
(3)Job: 批處理調(diào)度
我們可以通過(guò)Kubernetes job資源對(duì)象來(lái)定義并啟動(dòng)一個(gè)批處理任務(wù)拟烫。批處理任務(wù)通常并行(或者串行)啟動(dòng)多個(gè)計(jì)算機(jī)進(jìn)程去處理一批工作項(xiàng)∑荆·
9pod的擴(kuò)容和縮容
1通過(guò)scale來(lái)完成擴(kuò)容或縮容
假設(shè) redis-slave 這個(gè)pod原來(lái)定義了5個(gè)副本(reolics:5)
擴(kuò)容到10個(gè),執(zhí)行命令: kubectl scale rc redis-slave --replicas=10
縮容到2個(gè)硕淑,執(zhí)行命令:kubectl scale rc redis-slave --replicas=2
2動(dòng)態(tài)擴(kuò)容縮容(HPA)
通過(guò)對(duì)cpu使用率的監(jiān)控,HPA(Horizontal Pod Autoscaler),來(lái)動(dòng)態(tài)的擴(kuò)容或縮容嘉赎。pod cpu使用率是考heapster組件來(lái)獲取的置媳,所以要預(yù)先安裝好。
創(chuàng)建HPA:
在創(chuàng)建HPA前需要已經(jīng)存在一個(gè)RC或Deployment對(duì)象公条,并且該RC或Deployment中的Pod必須定義 resource.request.cpu的請(qǐng)求值拇囊,否則無(wú)法獲取cpu使用情導(dǎo)致HPA 無(wú)法工作
假設(shè)現(xiàn)在有一個(gè)php-apache RC
1通過(guò)kubectl autoscale 命令創(chuàng)建
kubectl autoscale rc php-apache --min=1 --max=10 --cpu-percent=50
含義:在1-10之間調(diào)整副本數(shù)量,使CPU使用率維持在50%左右
2通過(guò)配置文件的方式創(chuàng)建HPA
apiVersion: autoscaling/v1
kind: HorizaontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: v1
kind: ReplicationController
name: php-apache
minReplicas: 1
maxrReplicas: 10
targetCPUUtilizationPercentage: 50
10pod的滾動(dòng)升級(jí)
滾動(dòng)升級(jí)通過(guò)kubectl rolling-update 命令一鍵完成靶橱。
示例:假設(shè)現(xiàn)在運(yùn)行的redis-master的pod是1.0版本寥袭,現(xiàn)在需要升級(jí)到2.0版本路捧。
創(chuàng)建redis-master-controller-v2.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: redis-master-v2
labels:
name: redis-master
version: v2
spec:
replicas: 1
selector:
name: redis-master
version: v2
template:
metadata:
labels:
name: redis-master
version: v2
spec:
containers:
- name: master
images: kubeguide/redis-master:2.0
ports:
- containerPort: 6379
更新:kubectl rolling-update redis-master -f redis-master-controller-v2.yaml
需要注意到是:
rc的名字(name)不能與舊的rc的名字相同
在selector中至少有一個(gè)Label與舊的Label不同。以標(biāo)識(shí)其為新的RC