經常使用的kubectl命令
kubectl run kubia --image=luksa/kubia --port=8080 --generator=run/v1
--image 指定鏡像
--port 是告訴kubernetes 應用監聽8080端口
--generator 一般不會用到,它讓kubernetes建立一個replicationController . 通常不帶這個參數 ,建立的是depoly資源,deploy 在調用replicaset資源,replicaset 和replicationController很像,好像是它的升級版本有更強的selector表達能力,並且這裏的--generator=run/v1 並非建立出的rc名稱是run/v1,建立出的rc就是kubia, run/v1多是標示rc的版本吧,具體不知道,反正後面不用這個,知道有這麼個東西便可
後續學習中,得知--generator=run/v1 就是告訴kubernetes須要建立一個rc來管理pod
若是是:kubectcl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1 --command -- sleep infinity
這裏的--generator=run-pod/v1選項就是讓kubectl直接建立pod,而不須要經過replicationController之類的資源來建立。
kubectl get pods
kubectl expose rc kubia --type=loadBalancer --name kubia-http
暴露 名稱爲kubia的rc 爲名稱kubia-http 的service,而且使用loadbalnacer,會將建立kubia時 port端口8080 映射出來
kubectl get service
kubectl get repliactioncontrollers
kubectl scale rc kubia --replicas=3 擴容爲3個pod
kubectl get pods -o wide 查看pod在哪一個node節點上
kubectl describe pod pod-id
查看進羣狀態
kubectl cluster-info
使用kubectl explain 來發現可能的API對象字段,如:
kubectl explain pods
想查看某個對象下某個具體字段的使用方法
kubectl explain pod.spec
使用kubectl create 來建立pod
kubectl create -f kubia-manual.yaml
獲得運行中pod的完整定義
kubectl get pod kubia-manual -o yaml
kubectl get pod kubia-manual -o json
查看應用程序日誌
kubectl logs kubia-manual
當一個pod中有多個容器時
kubectl logs kubia-manual -c kubia
在不經過service外界相與pod通訊,可使用port-forward命令將短褲轉發到指定pod
如下命令會將機器的本地端口8888轉發到咱們的kubia-manual pod的端口8080:
kubectl port-forward kubia-manual 8888:8080
在kubernetes中 標籤時能夠組織kubernetes全部資源。
kubernetes 中建立出來的具體的對象都是資源。某一個資源 屬於某一個資源類如 pod資源類,或者對象。
查看全部pod上有什麼標籤
kubectl get po --show-labels
若是你想將標籤做爲顯示列的列頭可使用-L
kubectl get po -L env,app
添加pod標籤
kubectl label po kubia-manual env=test
修改pod的現有標籤
kubectl label po kubia-manual env=debug --overwrite
經過標籤選擇器列出pod
kubectl get po -l env=debug
列出包含evn標籤的pod,無論env是什麼值
kubectl get po -l env
列出不含evn標籤的pod
kubectl get po -l '!env' (確保是單引號)
env !=
env in (test,debug)
env not in (prod,devel)
kubernetes中調用pod到哪一個節點上是可有可無的,但因爲實際狀況,每臺node的硬件環境不一致,因此某些狀況要求將不一樣pod調到指定節點上運行。也能夠經過label實現。
kubectl label node node-id gpu=true
將pod調用到此節點只須要在yaml中描述到
apiVersion: v1
kind: pod
metadata:
kubia-gpu
spec:
nodeSelector:
gpu: "true"
containers:
- image: luksa/kubia
name: kubia
探針:對於pod中容器要進行監控,能夠用探針。
3種探針方式:
http get 返回2xx,3xx 也就是沒錯誤4xx,5xx
tcp 套接字 能創建鏈接正常,反之不正常
exec 執行某個命令,成功即0,不然失敗
http get的描述方法:
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveneess
spec:
containers:
- image: luksa/kubia-unhealthy
name: kubia
livenessProbe:
httpGet:
path: /
port: 8080
luksa/kubia-unhealthy 這個鏡像爲node.js,提供web服務,其中應用中添加了讓此web服務只在前5次正常返回,5次後就會返回錯誤。
通常錯誤3次後,就會重啓容器,那麼你想看容器錯誤日誌就要看上次的日誌所以要用 --previous參數
kuectl logs mypod --previous
使用kubectl describe po pod-id能看到具體的錯誤代碼,以及在底部顯示了容器爲何終止,--kubernetes發現容器不健康,因此終止並從新建立
默認在描述了探針,pod在啓動的時候就會在剛剛啓動時進行一次檢測,所以最好的作法是給檢測加一個初始延遲。
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveneess
spec:
containers:
- image: luksa/kubia-unhealthy
name: kubia
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
初始延遲爲15秒
replicationcontroller
選擇器
模版
副本數
若是更改選擇器,則會建立新的pod
若是更改pod的標籤,那麼也會建立新的pod進行替換,可是老的pod不會被刪除
若是更改模版,好比給加入新標籤,那麼將在下次替換時用到新模版,這個能夠用於升級pod使用
kubectl edit rc kubia 這將在你的默認編輯器中打開Replicationcontroller 的YAML配置。
使用export KUBE_EDITOR="/usr/bin/nano"設置默認編輯器
kubectl delete rc kubia 這是刪除replicationcontroller命令,刪除rc的同時,pod也會被刪除。咱們知道rc只是pod的管理器,rc建立的pod不是rc的組成部分,因此咱們也能夠只刪除rc而不刪除pod,使用--cascade=false 參數、
kubectl delete rc kubia --cascade=false
最初replicationcontroller 是用於複製和在異常時從新調度節點的惟一kubernetes組建,後來被replicaSet代替了。如今基本上見不到replicationcontroller,可是有的環境仍是有可能會有,因此咱們仍是知道的好。
replicaset咱們一般也不會直接建立,而是在建立最高層級的deployment資源時自動建立。
replicaset 與replicationcontroller的區別
replicaset 標籤選擇器的能力更強。
replicationcontroller只能指定標籤名:標籤值
replicaset 能夠指定env=pro,env=devel ,也能夠指定只要包含env標籤就行,理解爲env=*
雖然說不用直接建立,爲了學習咱們手動建立,
定義replicaset
apiVersion: apps/v1beta2
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLables:
app: kubia
template:
metadata:
lables:
app:kubia
spec:
containers:
- name:kubia
image: luska/kubia
template部分和replicationController 同樣
selector處不同。replicationController 用selector ,replicaSet用 selector.matchLables選擇器 更簡單,更具備表達力
replicaSet 的簡寫 rs
kubectl get rs
kubectl describe rs
rs 的matchlables 是精確匹配,說rs支持更強表達能力的選擇器,是由於rs還有matchExpressions選擇器
selector:
matchExpressions:
- key: app
operator: In
values:
- kubia
operator(運算符)有四個
In
NotIn
Exists: 必須包含某個key,值不重要,values不該指定
DoesNotExists: 必須不包含某個key, values不該指定
當你的replicaSet 的選擇器同時定義了matchLables和 matchExpressions ,必須兩個同時知足才能是pod和選擇器匹配
kubectl delete rs kubia 刪除rs的同時pod也被刪除,刪除前建議列出pod進行確認
rc,rs 都用於在kubernetes集羣上運行指定數量的pod,但他們不關心在哪一個節點上運行。有種狀況是讓每一個節點都運行某一個pod好比日誌收集,和監控應用。這時候要用到另一個控制器了DaemonSet.
DaemonSet也使用Pod模版,默認是將模版中的pod,在每一個節點中建立pod,但也有特殊狀況,只在某特定節點上建立pod,好比不一樣的硬盤類型。這能夠用pod模版的nodeSelector屬性指定
apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
name: ssd-monitor
spec:
selector:
matchLables:
app: ssd-monitor
template:
metadata:
lables:
app: ssd-monitor
spec:
nodeSelector:
disk: ssd
containers:
- name: main
image: luksa/ssd-monitor
DaemonSet 的簡寫ds
kubectl get ds
一樣刪除ds,同時也會刪除pod
rc,rs,ds都是持續運行pod也就是pod執行結束或者手動刪除pod,這些控制器都會重啓pod,有些時候須要讓pod運行完成退出後不在重啓,而且保證pod若是在運行時異常退出了能夠重啓的狀況。這就須要用到job了。
job管理的pod,正常完成退出後不重啓,當節點故障時,會按照replicaSet的pod方式,從新安排到一個新節點。也能夠配置job,若是進程異常退出返回錯誤碼時,重啓容器。
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
template:
metadata:
lables:
app: batch-job
spec:
restartPolicy: onFailure Job不能使用Always爲默認的重啓策略
containers:
- name: main
image: luksa/batch-job
這裏麼有定義標籤選擇器,它將根據pod模版中的標籤自動建立標籤選擇器
onFailure 當容器出錯時重啓容器,若是時Never將永遠不重啓
kubectl get jobs
kubectl get po -a 查看被刪除的pod
能夠配置job 按順序運行幾回
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
completions: 5
template:
... ...
也能夠聲明能夠並行的數量
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
completions: 5 共計運行5次
parallelism: 2 能夠並行2個
template:
... ...
parallelism也能夠像replicaSet同樣擴縮容
kubectl scale job batch-job --replicas 3
job是一次性任務,萬一運行卡住了,你永遠不知道它的狀態,因此你能夠限制它運行的時長。超過期長標記爲失敗,這就須要用到pod中activeDeadlineSeconds 屬性來定義運行時長。
同時能夠定義job 中spec.backoffLimit配置job被標記爲失敗以前能夠重試的次數。默認爲6.
job建立後,當即會運行,但有些時候咱們但願定時運行job,這就須要用到kubernetes的CronJob資源
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *" 每15分鐘執行一次而且在0,15,30,45
JobTemplate:
spec:
template:
matedata:
lables:
app: periodic-batch-job
spec:
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job
假如咱們的任務運行時間要求很是準確,不但願本來定在10:30:00運行的任務拖到10:30:16運行,可能超過15秒運行結果就有誤差了。這時能夠設置startingDeadlineSeconds。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *" 每15分鐘執行一次而且在0,15,30,45
startingDeadlineSeconds: 15
JobTemplate:
replicationController,replicaSet,DaemonSet, Job, CronJob 這幾種管理pod的控制器的基本內容就這些了。高級用法碰到在瞭解。
kubernetes service資源
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
kubectl get svc
雙橫缸表明着kubectl 命令項的結束,下面的是容器內部執行的命令
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
sessionAffinity: ClientIP
sessionAffinity屬性默認爲None,ClientIP 是保證特定客戶端產生的請求每次都指向同一個pod
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
target: 8443
selector:
app: kubia
標籤選擇器應用於整個服務,不能對每一個端口作單獨的配置
上面是採用端口號進行映射,還有一種方式給端口命名,這樣在作映射的時候直接指向名稱。好處是pod的端口隨便改,而不用改service的配置以下:
kind: Pod
metadata:
name: kubia
spec:
containers:
- name: kubia
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
apiVersion: v1
kind: Service
spec:
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
selector:
app: kubia
kubectl delete po --all 刪除全部pod ,而無論pod的id
kubectl delete all --all all表明全部資源,--all表明全部資源對象
backend-database.default.svc.cluster.local
backend-database 服務名稱
default 命名空間
svc.cluaster.local是在全部集羣本地服務名稱中使用的可配置集羣域後綴
kubectl exec -ti kubia-3inly bash 運行bash很像 docker exec -ti id bash
不要ping kubernetes中建立的服務名稱,這是由於服務的ip是一個虛擬的IP,只有在與服務端口結合時纔有意義
endpoint資源
kubernetes service不只能夠暴露pod給外部,一樣也能夠把外部服務建立爲服務讓內部pod進行訪問。服務並非和pod直接相連的,有一種資源-endpoint介於二者之間。
endpoint資源就是暴露一個服務的IP地址和端口的列表,endpoint資源和其餘kubernetes資源同樣,因此可使用kubectl info 來獲取它的基本信息
kubectl describe svc kubia 執行此命令能看到endpoint資源
kubectl get endpoints kubia
我知道在建立service時定義了selector pod選擇器,但在重定向傳入鏈接時不會直接使用它。
選擇器用於構建IP和端口列表,而後存儲在EndPoint資源中。當客戶端鏈接到服務時,服務代理選擇這些IP和端口對中的一個,並將傳入鏈接重定向到改位置監聽的服務器。
EndPoint是一個單獨的資源,而不是service的屬性,因此咱們能夠單獨的建立endpoint資源對象。
咱們在建立service時不聲明pod選擇器就不會建立endpoint
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- port: 80
這裏並無定義selector
下面咱們手動建立endpoint
apiVersion: v1
kind: Endpoints
metadata:
name: external-service 這裏的名稱必定和service的一致
subsets:
- addresses:
- ip: 1.1.1.1
- ip: 2.2.2.2
ports:
- port: 80 這裏的port是endpoint的目標端口,是service中的targetPort
以上就作了一個將外部服務經過service讓內部pod能夠訪問。
還有一種簡單的方式,給外部服務建立一個別名服務。
apiVersion: v1
kind: Service
matedata:
name: external-service
spec:
type: ExternalName 代碼的type被設置成ExternalName
externalName: someapi.somecompany.com 實際服務的徹底限定名
ports:
- port: 80
內部就可使用external-service來訪問服務了
kubectl get po --all-namespaces 很是好用
kubernetes關於pod掛載卷的知識
首先要知道卷是pod資源的屬性,pv,pvc是單獨的資源。pod 資源的volumes屬性有多種type,其中就包含有掛載pvc的類型。這也幫我理清了之間的關係。
pv通常是系統管理員作的
pvc 是通常k8s用戶聲明的,大概意思就是說我須要一個 什麼權限的,多少存儲空間的卷,而後k8s api收到這個請求就去找pv資源對象,若是二者相匹配,那麼pv就和pvc綁定了。
從這裏咱們也想到了,pv若是是手動建立的話,那就麻煩大了。幾個,幾十個還好說,上萬個,管理員該如何建立這麼多。因此纔有了動態建立pv的需求。這就引出另一個資源 storageClass ,這個資源聲明後端掛載什麼樣的存儲服務,好比nfs,chef等,有了這個通常用戶在定義pvc的時候,在提出存儲空間和讀寫權限的同時聲明用那個storageClass了,以下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata: mysql-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: managed-nfs-storage (注意這裏)
resources:
requests:
storage: 5Gi
以上即是pvc使用storageClass資源對象建立pv,pvc的綁定了
api收到對storageClass聲明後,就會調用這個storageClass的生產者進行生產。
咱們就會想到,在建立storageClass資源對象的時候確定會指定生產者。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: example-nfs
provisioner: example.com/nfs 這裏指定生產者,其中example.com/nfs只是一個標籤,你在deploymend裏定義什麼這裏就寫什麼。
mountOptions:
- vers=4.1
那麼問題又來了。在k8s中生產者組件不支持nfs,因此纔會安裝第三方組件。第三方組件的安裝就須要建立相關的rbac的角色和帳戶。第三方組件是用deploymend的資源託管相關組件pod的。
那麼經過deploy部署的pod怎麼就是provisioner了?這個我不清楚,後面學習後在總結吧。
回到正題上。
volumes 的4種
1. emptyDir
2. gitRepo
3. hostpath
4.PersistentVolumeClaim
5.configMap,secret
6. 各類雲平臺的存儲磁盤卷如google的gce,aws的ebs,azure的azureDisk
其實4只是一個歸納,nfs,chef 這些網絡存儲統統能夠單獨來使用。但我以爲實際使用中仍是講這些網絡存儲轉化成pv,pvc
從簡單開始學習
emptyDir 兩種應用場景: 1. 同一個pod中,各個容器間共享文件。 2. 當程序對大數據處理時內存空間不夠時臨時寫入文件(固然也可使用宿主主機的內存)
例子:
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
containers:
- image: luksa/fortune
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPaht: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {} (爲{}表示使用節點服務器的文件系統)
- name: html-2
emptyDir:
medium: Memory (使用節點服務器的內存)
以上就是emptyDir的用法。gitRepo實際上是依賴於emptyDir的,你能夠把它理解成,聲明瞭一個emptyDir而後在把gitrepo下載填充到emptyDir
例子:
apiVersion: v1
kind: Pod
metadata:
name: gitrepo-volume-pod
spec:
containers:
- image: nginx: alpine
name: web-nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
gitRepo:
revision: master
directory: . (這個.很重要,表示在當前emptyDir目錄下否則就會建立一個kubia-website-example目錄)
以上就是gitRepo的用法,其中有個問題就是pod中的gitrepo並不會及時更新。若是想要及時更新須要用到sidecar ,加入到pod中,在Docker Hub搜索 「git ryc」獲取相關鏡像。
還有一個狀況不得不使用sidecar container 。gitrepo 不支持鏈接私有庫,也就是不能是ssh密鑰鏈接,也不能夠有用戶名和密碼。這時候就須要使用支持驗證鏈接的sidecar container來實現了。具體怎麼使用,用到的時候在研究吧.
至此gitRepo卷類型算是簡單介紹了,下面學習hostpath
大多數時候pod應該忽略他們的主機節點,所以他們也不須要訪問節點文件系統上的文件。可是某些系統級別的pod(一般由DaemonSet管理)確實須要讀取節點的文件或使用節點文件系統來訪問節設備,這時候就須要用到hostPath
hostPath 算是第一個持久化存儲捲了,可是這個不多用到,由於這個卷只能在某一單一節點上,pod重啓後極可能在另一個節點上。固然可使用NodeSelector但這樣看起來也不高明。因此建議使用網絡存儲。hostPath過
接下來是網絡存儲和雲平臺提供的存儲磁盤卷。這兩種在用到的時候找相關的屬性進行配置便可。也沒什麼要注意的,實際應用場景用到最多的持久存儲是pv,pvc,storageClass
configmap
kubectl create configmap fortune-config --from-literal=sleep-interval=25
通常configmap包含多個映射條目因此建立時能夠屢次使用--from-literal參數
kubectl create conigmap fortune-config --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
kubectl get configmap fortune-config -o yaml
configmap一樣能夠存儲粗力度的配置數據,好比完整的配置文件。kubectl create configmap 命令支持從硬盤上讀取文件,並將文件內容單獨存儲爲ConfigMap中的條目:
kubectl create configmap my-config --from-file=config-file.conf
運行此命令,kubectl 會在當前目錄下找config-file.conf文件,並將文件內容存儲在configmap中以config-file.conf 爲鍵名的條目下。固然也能夠手動指定鍵名
kubectl create configmap my-config --from-file=customkey=config-file.conf
kubectl create configmap my-config --from-file=/path/to/dir
這時候,kubectl會爲文件中每一個文件單首創建條目,僅限文件名可做爲合法ConfigMap鍵名的文件。
當讓也能夠將上面的參數混合使用
configmap設置完成了,如何將映射的值傳遞給pod的容器?三種方法
一,設置環境變量,例子以下:
apiVersion: v1
kind: Pod
metadata:
name: fortune-env-from-configmap
spec:
containers:
- image: luksa/fortune:env
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
很好奇當pod 容器中引用不存在的configmap會發生什麼?
pod會嘗試運行全部容器,只有這個容器啓動失敗,其餘正常(假若有多個容器),當你過後建立處這個configmap,無需重啓pod,容器就會成功。固然也能夠引用爲可選的,設置configMapKeyRef.optional: true便可,這樣即便ConfigMap不存在,容器也能正常啓動。
若是configmap中條目不少,用env屬性列出麻煩且容易出錯。那麼有沒有辦法一次導入呢。用envFrom, 例如configmap中有三個條目FOO、BAR和FOO-BAR
spec:
containers:
- image: some-image
envFrom:
- prefix: CONFIG_ 全部環境變量均包含前綴CONFIG_ ,不設置就將引用configmap中的鍵名
configMapRef:
name: my-config-map
如此,容器中多出兩個變量 CONFIG_FOO 、CONFIG_BAR
爲何是兩個,由於另一個FOO-BAR包含破折號,不是一個合法的環境變量名稱。被忽略了,因此咱們應該注意建立configmap時 不要使用-
上面咱們學習瞭如何將configmap中的條目以變量的形式傳入到容器中
那麼如何將configmap中的條目做爲容器運行的參數args呢?例子:
apiVersion: v1
kind: Pod
metadata:
name: fortune-env-from-configmap
spec:
containers:
- image: luksa/fortune:env
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
args: ["$(INTERVAL)"]
環境變量和參數都適合於變量值較短的場景。configmap是能夠包含完整配置文件內容的,當你想要將其暴露給容器時,可使用上一章中提到的卷的類型。configmap卷會將ConfigMap中的每一個條目均暴露成一個文件。運行在容器的進程經過讀取文件內容得到對應的條目值。
configmap-files爲一個目錄
kubectl create configmap fortune-config --from-file=configmap-files
例子
apiVersion: v1
kind: Pod
metadata:
name: fortune-configmap-volume
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
...
- name: config
mountPaht: /etc/nginx/config.d
readOnly: true
....
volumes:
...
- name: config
configMap:
name: fortune-config
...
一種特殊狀況,當你只想暴露configmap-files目錄下的某一個配置文件,該如何作:
volumes:
- name: config
configmap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: gzip.conf
這樣配置後,掛載fortune-config 卷後,就只有my-nginx-config.conf 而且掛載後的名稱爲gzip.conf
另外一種特殊狀況,咱們經過上述掛載configmap卷後會發現,被掛載的目錄以前的文件都被隱藏掉了。那麼若是你需求不想隱藏以前的文件,該如何作:
spec:
containers:
- image: some/image
volumeMounts:
- name: myvolume
mountPath: /etc/someconfig.conf 掛載到指定的某一個文件,而不是某個文件夾
subPath: myconfig.conf 掛載指定的條目,而不是完整的卷
爲configMap卷中的文件設置權限
volumes:
- name: config
configmap:
name: fortune-config
defaultMode: "6600"
更新應用配置且不重啓應用程序
使用環境變量或命令行參數做爲配置源的弊端在於沒法在進程運行時更新配置。將ConfigMap暴露爲卷能夠達到配置熱更新的效果,無需從新建立pod或重啓容器。
ConfigMap被更新後,卷中引用它的文件也會相應更新,進程發現文件被改變後進行重載。kubernetes一樣支持文件更新後手動通知容器。但要注意的是,更新configmap以後對應文件的更新會至關耗時。
咱們使用kubectl edit configmap fortune-config 來更改某一個值。
而後執行
kubectl exec fortune-configmap-volume -c web-server -- cat /etc/nginx/config.d/my-nginx-config.conf
查看文件是否是被修改了,若是沒有稍等一會再查看。在確認更改後,咱們來手動通知容器進行重載。
kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload
你可能會疑惑 Kubernetes更新完configmap卷中的全部文件以前(全部的文件被更新而不是一部分),應用是否會監聽到文件編號並主動進行重載。答案是不會,會在全部的文件更新後,一次性更新到pod容器中。kubernetes經過富豪連接作到這一點的。
kubectl exec -ti fortune-configmap-volume -c web-server -- ls -lA /etc/nginx/config.d
..4989_09_04_15_06.028485
..data -> ..4989_09_04_15_06.028485
my-nginx-config.conf -> ..data/my-nginx-config.conf
sleep-interval -> ..data/sleep-interval
能夠看到,被掛載configMap卷中的文件是..data文件夾中文件的符號連接,而..data又是鏈接到 ..4989的符號連接,每當ConfigMap被更新後,Kubernetes 會建立一個這樣的文件夾,卸任全部文件並從新將符號..data連接至新文件夾,經過這種方式能夠一次性修改全部文件。
若是掛載configmap中的某一個文件,而不是文件夾,configmap更新以後對應的文件不會被更新。若是如今你須要掛載單個文件且在修改Configmap後想自動更新,能夠將該卷掛載到其餘文件夾,而後作一個軟連接。
至於如何實現應用重載配置 ,須要應用本身實現了。
configmap都是以名文存儲的,有些信息比較敏感,就須要用祕文存儲了。
這就須要使用kubernetes 的secret資源了。
首先有一個狀況咱們須要瞭解
使用kubectl describe pod pod-id 查看任何一個pod你都會發現默認有掛載一個secret卷。
Volumes:
default-token-ps7ff:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-ps7ff
Optional: false
這個卷裏的內容咱們可使用kubectl describe secrets查看
# kubectl describe secrets default-token-ps7ff
Name: default-token-ps7ff
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 6efa7f7c-6a61-11e9-bfdb-0a382f97318e
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1359 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtp...
能夠看出這個Secret包含是哪一個條目ca.crt 、namespace與token
包含了從pod內部安全訪問Kubernetes API服務器所需的所有信息。儘管咱們但願作到應用對kubernetes無感知,可是直連Kubernetes沒有其餘方法,你只能使用secret卷提供的文件來訪問kubernetes API。
使用kubectl describe pod命令顯示secret卷北掛載的位置:
mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-ps7ff (ro)
注意: default-token Secret默認會被掛載至每一個容器。能夠經過設置pod定義中的automountServiceAccountToken字段爲false,或者設置pod使用的服務帳戶中的相同字段爲false來關閉這種默認行爲。
查看容器中掛載了哪些條目
# kubectl exec nginx-dnm9n -- ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token
建立Secret
openssl genrsa -out https.key 2048
openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.kubia-example.com
# kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo
secret/fortune-https created
也可使用 --from-file=fortune-https 囊括這個歌文件夾中的文件
# kubectl get secret fortune-https -o yaml
# kubectl describe secret fortune-https
對比configmap與secret
secret與configMap有比較大的區別,這也是爲什麼kubernetes開發者們在支持了Secret一段時間以後會選擇建立ConfigMap。穿件的Secret的YAML格式定義顯示
# kubectl get secret fortune-https -o yaml
apiVersion: v1
data:
foo: YmFyCg==
https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFekNDQWZ1Z0F3SUJBZ0lKQU96Y00rNzI3RWJHTUEwR0NTcUdTSWIzRFFFQkN3VUFNQ0F4SGpBY0JnTlYKQkF
https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBeFYvUVJiazJiRm8zRmdZdWpaTWxPVGg3MUxpY3AyUS9pL2pib2E1SExlUlpSTDBi
kind: Secret
. . .
將其與CoinfigMap的Yaml格式定義作對比
apiVersion: v1
data:
bar: baz
foo: bar
one: two
kind: ConfigMap
注意到Secret條目的內容會被以Base64個市編譯,而ConfigMap直接以純文本展現。這種區別致使在處理YAML和JSON格式的Secret時會稍許有些麻煩,須要在設置和讀取相關條目時對內容進行編解碼。
這個具體使用中是這樣的,
好比你如今想把一個配置文件加入到Secret中,那麼你首先將配置文件中的內容經過BASE64進行編碼後才能做爲條目。
固然你會問難道kubernetes不提供base64編碼?提供,只能對字符串,不能接受文件。以下
kund: Secret
apiVersion: v1
stringData:
foo: plain text
data:
https.cert: lksjkaldjldasjladgjsjl...
https.key: lsiodjsdlfjahcdo...
建立後使用kubectl get secret -o yaml會看到stringData字段中的全部條目會被Base64編碼後展現在data字段下。因此stringData字段是隻寫不可讀的。
如何在pod中使用Secret
apiVersion: v1
kind: Pod
metadata:
name: fortune-https
spec:
containers:
- image: luksa/fortune:env
name: html-generator
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
volumeMounts:
- name: html
mountPaht: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: certs
mountPath: /etc/nginx/certs/
readOnly: true
ports:
- containerPort: 80
- containerPort: 443
volumes:
- name: html
emptyDir: {}
- name: config
configmap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: https.conf
- name: certs
secret:
secretname: fortune-https
簡單點的實例:
apiVersion: v1
kind: Pod
metadata:
name: fortune-https
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
- name: certs
mountPath: /etc/nginx/certs/
readOnly: true
ports:
- containerPort: 80
- containerPort: 443
volumes:
- name: certs
secret:
secretName: fortune-https
固然也能夠將secret條目暴露爲環境變量。但不建議這麼作,應用一般會在錯誤報告時轉儲環境變量,或者是啓動時打印在應用日誌中,無心中就暴露Secret信息。另外,子進程會繼承父進程的全部環境變量,若是是經過第三方二進制程序啓動應用,你並不知道它使用敏感數據作了什麼。因此不建議用環境變量,建議使用secret卷的形式掛載到pod.
env:
- name: FOO_SECRET
valueFrom:
secretKeyRef:
name: fortune-https
key: foo
學會了secret,接下來就有一個比較經常使用的secret實際應用,dockerhub
kubectl create secret docker-registry mydockerhubsecret --docker-username=myusername --docker-password=mypassword --docker-email=
my.email@provider.com
使用:
apiVersion: v1
kind: Pod
metadata:
name: private-pod
spec:
imagePullSecrets:
- name: mydockerhubsecret
containers:
- image: username/private:tag
name: main
假設系統一般運行大量Pod,你可能會好奇是否須要爲每一個Pod都添加相同的鏡像拉取Secret.並非,能夠經過添加Secret至ServiceAccount 使全部pod都能自動添加上鏡像拉取的Secret.
Deployment 聲明式地升級應用
如今你已經知道如何將應用程序組件打包進容器,將他們分組到pod中,併爲它們提供臨時或者持久存儲,將密鑰或配置文件注入,並可使pod之間互相通訊。這就是微服務化:如何將一個大規模系統拆分紅哥哥獨立運行的組件。以後,你將會須要升級你的應用程序。如何升級再kubernetes集羣中運行的程序,以及kubernetes如何幫助你實現真正的零停機升級過程。升級操做能夠經過使用replicationController 或者 replicaSet實現,但Kubernetes提供了另外一種基於ReplicaSet的資源Deployment,並支持聲明式地更新應用程序。
看一個簡單的例子:
現有一個應用 ,版本爲V1 ,運行在Kubernetes的pod中,如今應用的鏡像有更新,標記爲v2,那麼如何將新版本運行的pod替換掉V1版本的pod.
有如下兩種方法更新pod:
1. 直接刪除全部現有的pod,而後建立新pod
2. 新建立一組pod,等待運行後刪除舊pod. 能夠一次性建立,一次性刪除,也能夠一部分一部分操做。
這兩種各有優缺點:第一種方法將會致使應用程序在必定的時間內不可用。第二種方法會致使新舊版本同時在線,若是對統一個數據的處理方式不同,將會給系統帶來數據損壞。
暫且無論優缺點,先來看看如何實現。
咱們用replicationController來託管pod v1,能夠直接經過將pod模版修改成v2,而後再刪除舊pod,這時候rc就會按照新模版建立pod.
若是你能夠接受短暫的服務不可用,那這是最簡單更新pod的方法。
接下來咱們用第二種方法。首先要保持兩個版本同時在線,因此要雙倍的硬件資源。
前提咱們是用service來暴露pod的。保持rc v1不變,而後建立 rc v2 ,rc v2的pod模版選擇新鏡像標籤V2. 建立rc v2 ,rc v2將運行pod v2 。等待pod v2運行正常後,咱們使用 kubectl set selector 命令來修改service的pod選擇器。
這種一次性從v1切到v2的方法即爲藍綠部署,主要問題就是硬件資源要兩倍。若是咱們不是一次切換,而是一點點切,資源將不須要兩倍。好比你建立rc v2,成功後,縮容 rc v1 ,更改service的pod選擇器。而後再擴容rc v2,在縮容 v1 這就是滾動更新。那麼這個就要求你的應用容許存在兩個不一樣版本,具體要根據實際需求來操做了。
以上第二種方法的兩種狀況,咱們大概知道是怎麼回事了。但咱們在實際操做中,要建立兩個rc,而且要修改service的pod selector. 這要是用滾動更新,pod副本越多,咱們手動操做的次數就越多,手動操做越多越容易出現問題。那麼問題來了,難道kubernetes沒有提供自動完成這些操做的方法嗎?答案是提供了。k8s中使用kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
咱們來經過一個例子瞭解下:
用rc建立一個應用v1 ,並使用service的 loadbalancer將服務對外暴露。
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia-v1
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
---
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
type: LoadBalancer
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
查看loaderbalancer的IP
#kubectl get svc kubia
訪問測試
# while true; do curl http://IP; done
this is v1 running in pod 主機名
接下來用v2版本升級,this is v2 running in pod 主機名
kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
使用 kubia v2版本應用替換運行着kubia-v1 的replicationController,將新的複製控制器命名爲kubia-v2,並使用luksa/kubia:v2做爲容器鏡像。
kubectl 經過複製kubia-v1 的replicationController並在其pod模版中改變鏡像版本。若是仔細觀察控制器的標籤選擇器,會阿賢它也被作了修改。它不只包含一個簡單的app=kubia標籤,並且還包含一個額外的deployment標籤,爲了由這個ReplicationController管理,pod必須具有這個標籤。這也是爲了不使用新的和舊的RC來管理同一組Pod.可是即便新建立的pod添加了deployment標籤,pod中仍是有app=kubia標籤,因此不只新的RC要加deployment標籤,舊的RC一樣也要加上deployment標籤,標籤的值爲 一個鏡像hash(先這樣理解)。要保證舊的RC添加deployment標籤後依然能夠管理以前建立的pod,所以也要修改舊pod,進行添加標籤,而實際上正是在修改舊RC以前,kubectl修改了舊pod的標籤:
kubectl get po --show-labels 進行查看標籤
設置完成後,就開始執行更新操做了,過程就是咱們上面描述的滾動更新過程。
爲何 kubectl rolling-update已通過時
咱們能夠在使用rolling-update命令的時候添加 --v 6 來提供日誌級別,使得全部kubectl 發起的到API服務器的請求都會被輸出。
你會看到一個put請求:
/api/v1/namespace/default/replicationcontrollers/kubia-v1
它是表示kubia-v1 ReplicationController資源的RESTful URL.這些請求減小了RC的副本數,這代表伸縮的請求是由kubectl 客戶端執行的,而不是kubernetes master執行的。那麼當kubectl執行升級時失去了網絡鏈接,升級過程就會中斷。對於中斷後的結果處理起來將很麻煩。因此咱們想盡可能把這個過程讓master負責。這就引出了DeployMent資源。
Deployment是一個高階資源,replicationController和replicaSet都被認爲是底層的概念。當建立一個Deployment時,ReplicaSet資源就會被建立,實際的pod是由Deployment的Replicaset建立和管理的,而不是由Deployment直接建立和管理的。
接下來咱們建立一個簡單的deployment,將上面的replicationController稍做修改:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
由於以前RC只維護和管理了一個特定的版本的pod,並須要命名爲kubia-v1,而一個deployment資源高於版本自己。能夠同時管理多個版本的pod,因此在命名時不須要指定應用的版本號。
kubectl create -f kubia-deployment-v1.yaml --record
使用 --record選項會記錄歷史版本號,在以後操做中很是有用。
kubectl get deployment kubia
kubectl describe deployment kubia
還有一個命令,專門用於查看部署狀態:
kubectl rollout status deployment kubia
查看pod,會發現使用deployment資源部署的pod名稱有必定的規律,
deployment的名字 + replicaset pod模版的hash + 隨機數, 如:
kubia-15098375-otnsd
kubia-15098375-djc6s
而rc建立出來的名稱是 : rc名稱 + 隨機數
kubia-v1-kgysg
能夠經過查看replicaSet來確認pod模版hash
kubectl get replicasets
kubia-15098375 ...
deployment正是根據pod模版的hash值,對給定版本的pod模版建立相同的(或使用已有的)ReplicaSet.
Deployment的高明之處
有不一樣的更新策略
Recreate 策略在刪除舊的Pod以後纔開始建立新的Pod。若是不容許多個版本共存,使用這個,但會有短暫的不可用。
RollingUpdate 策略會漸進地刪除舊的pod,於此同時建立新的pod.這是deployment默認使用的策略。若是支持多個版本共存,推薦使用這個。
咱們來演示下deployment滾動升級的過程。
在演示以前咱們先減慢滾動升級的速度,以方便咱們觀察
kubectl path deployment kubia -p '{"spec": {"minReadySeconds": 10} }'
使用path對於修改單個或少許資源屬性很是有用,不須要在經過編輯器編輯,使用minReadySeconds 配置正常檢查後多少時間才屬於正常。
注:使用patch命令更改Deployment的自有屬性,並不會致使pod的任何更新,由於pod模版並無被修改。更改其餘Deployment的屬性,好比所須要的副本數或部署策略,也不會觸發滾動升級,現有運行中的pod也不會受影響。
觸發滾動升級
kubectl set image deployment kubia nodejs=luksa/kubia:v2
執行完成後pod模版的鏡像會被更改成luksa/kubia:v2
deployment的優勢
使用控制器接管整個升級過程,不用擔憂網絡中斷。
僅僅更改pod模版便可。
注:若是Deployment中的pod模版引用了一個ConfigMap(或secret),那麼更改ConfigMap字眼自己將不會觸發升級操做。若是真的須要修改應用程序的配置並想觸發更新的話,能夠經過建立一個新的ConfigMap並修改pod模版引用新的ConfigMap.
Deployment背後完成的升級過程和kubectl rolling-update命令類似。
一個新的ReplicaSet會被建立而後慢慢擴容,同時以前版本的Replicaset會慢慢縮容至0
deployment的另一個好處是能夠回滾
kubectl rollout undo deployment kubia
deployment會被回滾到上一個版本
undo命令也能夠在滾動升級過程當中運行,並直接中止滾動升級。在升級過程當中一建立的pod會被刪除並被老版本的pod替代。
顯示deployment的滾動升級歷史
kubectl rollout history deployment kubia
reversion change-cause
2 kubectl set image deployment kubia nodejs=luksa/kubia:v2
3 kubectl set image deployment kubia nodejs=luksa/kubia:v3
還記得建立Deployment的時候--record 參數嗎?若是不給這個參數,版本歷史中的Change-cause這一欄會空。這也會使用戶很難辨別每次的版本作了哪些修改。
回滾到一個特定的Deployment版本
kubectl rollout undo deployment kubia --to-reversion=1
這些歷史版本的replicaset都用特定的版本號保存Deployment的完整的信息,因此不該該手動刪除ReplicaSet。若是刪除會丟失Deployment的歷史版本記錄而致使沒法回滾。
ReplicaSet默認保留最近的兩個版本,能夠經過制定Deployment的reversionHistoryLimit屬性來限制歷史版本數量。apps/v1beta2 版本的Deployment默認值爲10.
控制滾動更新的速率
maxSurge 最多超過時望副本數多少個pod,默認爲25%,能夠設置爲整數
maxUanavailable 最多容許指望副本數內多少個pod爲不可用狀態。
看圖比較容易理解
暫停滾動更新:
kubectl rollout pause deployment kubia
恢復滾動更新:
kubectl rollout resume deployment kubia
阻止出錯版本的滾動升級
使用minReadySeconds屬性指定至少要成功運行多久以後,才能將其視爲可用。在pod可用以前,滾動升級的過程不會繼續。是由maxSurge屬性和maxUanavailable屬性決定。
例子:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas:3
minReadySeconds: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavilable: 0
type: RollingUpdate
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v3
name: nodejs
readinessProbe:
periodSeconds: 1
httpGet:
path: /
port: 8080
kubectl apply -f kubia-deployment-v3-with-readinesscheck.yaml
kubectl rollout status deployment kubia
就緒探針是如何阻止出錯版本的滾動升級的
首先luksa/kubia:v3鏡像應用前5個應用是正常反饋,後面會500狀態返回。所以pod會從service的endpoint中移除。當執行curl時,pod已經被標記爲未就緒。這就解釋了爲何請求不會到新的pod上了。
使用kubectl rollout status deployment kubia查看狀體,發現只顯示一個新副本啓動,以後滾動升級便沒有進行下去,由於新的pod一直處於不可用狀態。即便變爲就緒狀態後,也至少須要保持10秒,纔是真正可用。在這以前滾動升級過程將再也不建立任何新的pod,由於當前maxUnavailable屬性設置爲0,因此不會刪除任何原始的pod。若是沒有定義minReadySeconds,一旦有一次就緒探針調用成功,便會認爲新的pod已經處於可用狀態。所以最好適當的設置minReadySeconds.另外就緒探針默認間隔爲1秒。
deployment資源介紹完結。
複製有狀態的Pod
replicaSet經過一個pod模版建立多個pod副本。這些副本除了它們的名字和IP地址不一樣外,沒有被的差別。若是pod模版裏描述了一個關聯到特定持久卷聲明的數據卷,那麼ReplicaSet的全部副本都將共享這個持久卷聲明,也就是綁定到同一個持久卷聲明。
由於是在pod模版裏關聯持久卷聲明的,又會依據pod模版建立多個副本,則不能對每一個副本都指定獨立的持久卷聲明。因此也不能經過一個ReplicaSet來運行一個每一個實例都須要獨立存儲的分佈式數據存儲服務,至少經過單個ReplicaSet是作不到的。老實說,以前你學習到的全部API對象都不能提供這樣的數據存儲服務,還須要一個其餘的對象--StatefulSet
咱們先看不使用StatefulSet的狀況下有沒有方法實現多個副本有本身的持久卷聲明。
三種取巧的方法。
第一種方法,不使用ReplicaSet,使用Pod建立多個pod,每一個pod都有獨立的持久卷聲明。須要手動建立它們,當有的pod消失後(節點故障),須要手動建立它們。所以不是一個好方法。
第二種方法,多個replicaSet ,每一個rs只有一個pod副本。但這看起來很笨重,並且沒辦法擴縮容。
第三種方法,使用同一個ReplicaSet,你們也都掛載同一個持久卷聲明,應用內部作好互斥,建立多個data數據目錄,每個pod用一個標記爲在用,後面應用不能選被標記爲在用的目錄。這樣作很難保證協調的一點沒問題,同時你們用同一個持久卷,讀寫io將成爲整個應用的瓶頸。
除了上面的存儲需求,集羣應用也會要求每個實例擁有生命週期內惟一標識。pod能夠隨時被刪掉,而後被新的pod替代。當一個ReplicaSet中的pod被替換時,儘管新的pod也可能使用被刪除pod數據卷中的數據,但它倒是擁有全新主機名和IP的嶄新pod.在一些應用中,當啓動的實例擁有徹底新的網絡標識,但還使用舊實例的數據時,極可能引發問題,好比etcd存儲服務。
固然也能夠建立多個service ,每個replicaset對應一個service,那麼同樣很笨重,且顯得很低級。辛運的是,Kubernetes爲咱們提供了這類需求的完美解決方案--StatefulSet.
瞭解StatefulSet
能夠建立一個StatefulSet資源代替ReplicaSet來運行這類pod.它們是專門定製的一類應用,這類應用中每個實例都是不可替代的。