一,yaml 文件
#基本語法:
1,大小寫敏感
2.使用縮進表示層級關系。
3.縮進時不允許使用Tab鍵,只允許使用空格。(可以通過編譯器將tab鍵設置成2個空格或者4個空格)
4.縮進的空格數目不重要,只要相同層級的元素左側對齊即可。(使用一個空格縮進,都使用一個。使用二個空格縮進,都使用二個),建議還是2個空格,這樣好看些。
5.# 表示注釋,從這個字符一直到行尾,都會被解析器忽略。
6.破折號后面跟一個空格(a dash and space)表示列表
7.用冒號和空格表示鍵值對 key: value
值的寫法
1. k: v:字面直接來寫;2. 字符串默認不用加上單引號或者雙引 號;
"":雙引號;不會轉義字符串里面的特殊字符;特殊字符會作為本身想表示的意思 name: "idig8 \n com":輸出;idig8換行com
'':單引號;會轉義特殊字符,特殊字符最終只是一個普通的字符串數據 name: 'idig8\n com':輸出;idig8\n com①對象、Map(屬性和值)(鍵值對)
#yaml 格式
map,散列表 字典
使用冒號(:)表示鍵值對,同一縮進的所有鍵值對屬於一個map
①對象、Map(屬性和值)(鍵值對)
---是分隔符,是可選的,在單一文件中,可用連續三個連字號---區分多個文件
有兩個鍵:friends,他們對應的值分別是:lastName: zhangsan 和 age: 20
---
friends
lastName: zhangsan
age: 20
對應json
{
"friends"::{
"lastname":"zhangshan",
"age":20
}
}
②數組(List、Set)
用- 值表示數組中的一個元素
pets:
- pig
- cat
- dog
{
"pets": ["pig","cat","dog"]
}
數組List和Map的混合
person:
lastname: liming
age: 34
boss: false
birth: 2019/08/03
maps: [k1: v1,k2: v2]
lists:
- lizi
- zhaoliu
dog:
name: 小狗
age: 10
json 格式
{
"person": {
"lastname": "liming",
"age": 34,
"boss": false,
"birth": '2019/08/03',
"maps": {
"k1": 'v1' ,
"k2": 12
} ,
"lists": [
"lishi",
"xholiu"
],
"dog": {
"name": '小狗',
"age": 10
}
}
}
縮進相同的都是同級元素,縮進比上一個元素長就是上一個元素的子元素
- apple1:
color: red
- apple2:
color: green
相同樣式:
-
apple1:
color: red
-
apple2:
color: green
json:
[
{
"apple1": null,
"color": "red"
},
{
"apple2": null,
"olor": "green"
}
]
YAML 的純量(Scalar)
null
~ / null / Null / NULL 還有空字符都被解析成 null 類型,最標准的寫法是 ~。
bool
最新標准里 y / Y / yes / Yes / YES / n / N / no / No / NO / true / True / TRUE / false / False / FALSE / on / On / ON / off / Off / OFF 全部都會被解析成正確的 bool 類型,為了兼容性比較好建議用 true 和 false。
int
float
str
參考網站
http://blog.harrisonxi.com/2018/07/YAML%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95%E6%89%8B%E5%86%8C%E4%BB%A5%E5%8F%8A%E5%92%8CJSON%E7%9A%84%E5%AF%B9%E7%85%A7.html
k8s yaml 語法格式:
大小寫敏感
使用縮進表示層級關系
縮進時不允許使用Tal鍵,只允許使用空格
縮進的空格數目不重要,只要相同層級的元素左側對齊即可
”#” 表示注釋,從這個字符一直到行尾,都會被解析器忽略
1,k8syaml
#Map 指的是字典,即一個Key:Value 的鍵值對信息
---
apiVersion: v1
kind: Pod
--- 為可選的分隔符 ,當需要在一個文件中定義多個結構的時候需要使用
表示有兩個鍵apiVersion和kind,分別對應的值為v1和Pod
##Maps的value既能夠對應字符串也能夠對應一個Maps:
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
metadata這個KEY對應的值為一個Maps,而嵌套的labels這個KEY的值又是一個Map
#List即列表 數組
args:
- beijing
- shanghai
- shenzhen
- guangzhou
# Lists的子項也可以是Maps,Maps的子項也可以是List:
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports: 8080
#創建Pod
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports:
- containerPort: 5000
定義了一個普通的Pod文件:
apiVersion:此處值是v1,這個版本號需要根據安裝的Kubernetes版本和資源類型進行變化,記住不是寫死的。
kind:此處創建的是Pod,根據實際情況,此處資源類型可以是Deployment、Job、Ingress、Service等。
metadata:包含Pod的一些meta信息,比如名稱、namespace、標簽等信息。
spe:包括一些container,storage,volume以及其他Kubernetes需要的參數,以及諸如是否在容器失敗時重新啟動容器的屬性。可在特定Kubernetes API找到完整的Kubernetes Pod的屬性
###########
#Deployment的概念 ,目的是讓Kubernetes去管理一組Pod的副本,也就是副本集 ,這樣就能夠保證一定數量的副本一直可用,不會因為某一個Pod掛掉導致整個服務掛掉
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kube100-site
spec:
replicas: 2
template:
metadata:
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports:
- containerPort: 5000
yaml格式的pod定義文件
apiVersion: v1 #必選,版本號,例如v1
kind: Pod #必選,Pod
metadata: #必選,元數據
name: string #必選,Pod名稱
namespace: string #必選,Pod所屬的命名空間
labels: #自定義標簽
- name: string #自定義標簽名字
annotations: #自定義注釋列表
- name: string
spec: #必選,Pod中容器的詳細定義
containers: #必選,Pod中容器列表
- name: string #必選,容器名稱
image: string #必選,容器的鏡像名稱
imagePullPolicy: [Always | Never | IfNotPresent] #獲取鏡像的策略 Alawys表示下載鏡像 IfnotPresent表示優先使用本地鏡像,否則下載鏡像,Nerver表示僅使用本地鏡像
command: [string] #容器的啟動命令列表,如不指定,使用打包時使用的啟動命令
args: [string] #容器的啟動命令參數列表
workingDir: string #容器的工作目錄
volumeMounts: #掛載到容器內部的存儲卷配置
- name: string #引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名
mountPath: string #存儲卷在容器內mount的絕對路徑,應少於512字符
readOnly: boolean #是否為只讀模式
ports: #需要暴露的端口庫號列表
- name: string #端口號名稱
containerPort: int #容器需要監聽的端口號
hostPort: int #容器所在主機需要監聽的端口號,默認與Container相同
protocol: string #端口協議,支持TCP和UDP,默認TCP
env: #容器運行前需設置的環境變量列表
- name: string #環境變量名稱
value: string #環境變量的值
resources: #資源限制和請求的設置
limits: #資源限制的設置
cpu: string #Cpu的限制,單位為core數,將用於docker run --cpu-shares參數
memory: string #內存限制,單位可以為Mib/Gib,將用於docker run --memory參數
requests: #資源請求的設置
cpu: string #Cpu請求,容器啟動的初始可用數量
memory: string #內存清楚,容器啟動的初始可用數量
livenessProbe: #對Pod內個容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器,檢查方法有exec、httpGet和tcpSocket,對一個容器只需設置其中一種方法即可
exec: #對Pod容器內檢查方式設置為exec方式
command: [string] #exec方式需要制定的命令或腳本
httpGet: #對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #對Pod內個容器健康檢查方式設置為tcpSocket方式
port: number
initialDelaySeconds: 0 #容器啟動完成后首次探測的時間,單位為秒
timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒
periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged:false
restartPolicy: [Always | Never | OnFailure] #Pod的重啟策略,Always表示一旦不管以何種方式終止運行,kubelet都將重啟,OnFailure表示只有Pod以非0退出碼退出才重啟,Nerver表示不再重啟該Pod
nodeSelector: obeject #設置NodeSelector表示將該Pod調度到包含這個label的node上,以key:value的格式指定
imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定
- name: string
hostNetwork:false #是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡
volumes: #在該pod上定義共享存儲卷列表
- name: string #共享存儲卷名稱 (volumes類型有很多種)
emptyDir: {} #類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值
hostPath: string #類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄
path: string #Pod所在宿主機的目錄,將被用於同期中mount的目錄
secret: #類型為secret的存儲卷,掛載集群與定義的secre對象到容器內部
scretname: string
items:
- key: string
path: string
configMap: #類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部
name: string
items:
- key: string
path: string
2,kubernetes中的對象
Kubernetes 對象 是持久化的條⽬。Kubernetes 使⽤這些條⽬去表示整個集群的狀態。特別
地,它們描述了如下信息:
什么容器化應⽤在運⾏(以及在哪個 Node 上)
可以被應⽤使⽤的資源
關於應⽤如何表現的策略,⽐如重啟策略、升級策略,以及容錯策略
與 Kubernetes 對象⼯作 —— 是否創建、修改,或者刪除 —— 需要使⽤ Kubernetes API。當使⼝時,⽐如,CLI 會使⽤必要的 Kubernetes API 調⽤,也可以在程序中直接使⽤ Kubernetes AP
對象 Spec 與狀態
每個 Kubernetes 對象包含兩個嵌套的對象字段,它們負責管理對象的配置:對象 spec 和 對象 status。spec 必須提供,它描述了對象的 期望狀態—— 希望對象所具有的特征。status 描述了對象的 實際狀態,它是由 Kubernetes 系統提供和更新。在任何時刻,Kubernetes 控制平⾯⼀直處於活躍狀態,管理着對象的實際狀態以與我們所期望的狀態相匹
Kubernetes對象也被稱為資源,就是Kubernetes集群中的各種可持久化實體,如Deployment,Pod,Service等。
從Kubernetes集群的角度,任何Kubernetes對象都有如下兩個側面:
spec,表示Kubernetes對象期望的狀態,往往通過YAML配置文件給出
status,表示Kubernetes對象在當前集群中實際的狀態,往往通過資源的Controller控制
事實上,Kubernetes集群的狀態,就是通過大量Kubernetes對象的status表示出來的。
首先通過YAML文件定義Kubernetes對象的期望狀態,然后調用Kubernetes API,將YAML文件作為參數交給Kubernetes集群,Kubernetes集群根據YAML文件創建必要的Kubernetes對象,以滿足spec的期望。此后,Kubernetes的Master通過各種Controllers保證Kubernetes對象的status與spec一致。
1. Kubernetes Workload對象
1)Deployment
一個Deployment就是Kubernetes中運行着的一個應用,可能由一個或多個Pods組成,其中的每個Pod就是一個應用副本。
通過Deployment,一個應用可以被安裝部署多次,每次部署都在Nodes上生成一定數量的應用實例。即一個應用可以有多個應用實例,每個應用實例有一個唯一的名字。
通過Deployment,可以實現應用的滾動升級,逐步替換容器鏡像。
2)Pod
Pod是Kubernetes集群負責創建、管理的,可部署的、可水平擴展的、可創建副本的最小計算單元。
一個Pod可以被視為一個靈活調度的邏輯主機,擁有唯一的IP和hostname。但是其IP和hostname對Kubernetes集群外部是不可見的。
Pod的IP在集群內部是唯一的,所以集群中的Pod對任何其他Pod都是可見的,無論這些Pods在哪個Node上。登錄到任意一個Node上,都可以ssh訪問任何一個Pod(通過Pod的IP,無需Pod的端口)。
通常,一個Pod只包含一個容器實例。Pod的hostname就是Pod內部的容器實例的hostname。
個別情況下,一個Pod也可以包含若干個聯系密切的容器實例。這些容器實例彼此不同,但是作為一個整體代表一個應用。Pod中的容器實例,共享上下文環境(如Linux namespaces, cgroups等)、網絡(共用Pod的hostname,IP和對外開放的端口)、存儲。因此,在Pod內部,容器之間彼此通過localhost即可互相訪問。但是不同Pod的容器實例之間的訪問,只能通過Pod的IP和端口,以及Pod的端口映射到容器實例的端口實現。容器實例之間共享存儲volumes作為Pod的組成部分,可以被mount到應用的文件系統
通常,用戶不應該直接創建Pod,而是通過Controllers創建Pod(有多個Controllers可以創建Pod),如通過創建Deployment創建Pod。
pod
當創建 Kubernetes 對象時,必須提供對象的 spec,用來描述該對象的期望狀態,以及關於對象的一些基本信息(例如,名稱)。當使用 Kubernetes API 創建對象時(或者直接創建,或者基於kubectl),API 請求必須在請求體中包含 JSON 格式的信息。更常用的是,需要在 .yaml 文件中為 kubectl 提供這些信息。 kubectl 在執行 API 請求時,將這些信息轉換成 JSON 格式。查看已經部署好的pod的資源定義格式
pod 配置格式
當創建 Kubernetes 對象時,必須提供對象的 spec,用來描述該對象的期望狀態,以及關於對象的一些基本信息(例如,名稱)。當使用 Kubernetes API 創建對象時(或者直接創建,或者基於kubectl),API 請求必須在請求體中包含 JSON 格式的信息。更常用的是,需要在 .yaml 文件中為 kubectl 提供這些信息。 kubectl 在執行 API 請求時,將這些信息轉換成 JSON 格式。查看已經部署好的pod的資源定義格式:
kubectl get pod myapp-848b5b879b-5f69p -o yaml
創建資源的方法:
apiserver在定義資源時,僅接收JSON格式的資源定義;
yaml格式提供配置清單,apiservere可自動將其轉為json格式,而后再提交;
大部分資源的配置清單格式都由5個一級字段組成:
apiVersion: group/version 指明api資源屬於哪個群組和版本,同一個組可以有多個版本
$ kubectl api-versions
kind: 資源類別,標記創建的資源類型,k8s主要支持以下資源類別
Pod,ReplicaSet,Deployment,StatefulSet,DaemonSet,Job,Cronjob
metadata:元數據,主要提供以下字段
name:同一類別下的name是唯一的
namespace:對應的對象屬於哪個名稱空間
labels:標簽,每一個資源都可以有標簽,標簽是一種鍵值數據
annotations:資源注解
每個的資源引用方式(selflink):
/api/GROUP/VERSION/namespace/NAMESPACE/TYPE/NAME
spec: 定義目標資源的期望狀態(disired state),資源對象中最重要的字段
status: 顯示資源的當前狀態(current state),本字段由kubernetes進行維護
kubectl explain pods
apiVersion,kind等定義的鍵值都是<string>,而metadata和spec看到是一個<Object>,當看到存在<Object>的提示,說明該字段可以存在多個二級字段,那么可以使用如下命令繼續查看二級字段的定義方式
kubectl explain pods.metadata
kubectl explain pods.spec
二級字段下,每一種字段都有對應的鍵值類型,常用類型大致如下:
<[]string>:表示是一個字串列表,也就是字串類型的數組
<Object>:表示是可以嵌套的字段
<map[string]string>:表示是一個由鍵值組成映射
<[]Object>:表示是一個對象列表
<[]Object> -required-:required表示該字段是一個必選的字段
vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name:myapp
image: ikubernetes/myapp:v1
- name: busybox
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
kubectl create -f pod-demo.yaml
kubectl get pods
kubectl describe pods pod-demo #獲取pod詳細信息
kubectl logs pod-demo myapp
kubectl exec -it pod-demo -c myapp -- /bin/sh
kubectl explain pods.spec.containers
如果Pod不提供command或args使用Container,則使用Docker鏡像中的cmd或者ENTRYPOINT。
如果Pod提供command但不提供args,則僅使用提供 command的。將忽略Docker鏡像中定義EntryPoint和Cmd。
如果Pod中僅提供args,則args將作為參數提供給Docker鏡像中EntryPoint。
如果提供了command和args,則Docker鏡像中的ENTRYPOINT和CMD都將不會生效,Pod中的args將作為參數給command運行
- Service
Service是一個抽象訪問層,能夠將一組Pods以一定的Policy暴露出來響應服務請求,同時實現請求在多個Pods之間的負載均衡。通過YAML/JSON定義,將擁有指定Labe的Pods歸入到一個Service。
因為Pod是短暫的、動態產生和消亡的,所以不能直接暴露出來。Service的CLUSTER-IP是虛擬的IP(Virtual IP, VIP),只要服務存在,該IP就不變。因而可以將Service的CLUSTER-IP暴露給請求。
Service的Port也是一個抽象端口,通過該端口的請求被映射到對應的Endpoint,轉發給Pod中的容器實例進行處理。
Service的Endpoints通常是一組Pods的訪問信息,包括Pod的IP及Pod中容器的端口,以<pod_IP>:
Service的4個類型(type):
ClusterIP(默認),服務只有一個集群內部可訪問的CLUSTER-IP,只能供集群內部訪問
NodePort,通過<Node_IP>:
LoadBalancer,通過外部雲提供者的負載均衡器,將服務直接暴露給集群外部
ExternalName,一個集群外部的獨立服務,通過--external-name屬性被聲明到Kubernetes集群中
Service的類型不同,有的Service只暴露給Kubernetes集群內部的其他應用;有的Service還能夠暴露給Kubernetes集群外部的應用。
2.Kubernetes Metadata對象
-
Name
唯一標志 -
Namespace
為Name設置了一個有效的范圍,僅適合有大量用戶的Kubernetes集群。
Namespace用於區分用戶,Name只需要在Namespace中唯一即可。
但是,Namespace不用於區分Kubernetes集群中的資源,通過Label區分Kubernetes集群中的資源。
Kubernetes集群中初始默認的3個Namespace:
default,未指明Namespace的任何用戶創建的Kubernetes對象
kube-system,Kubernetes系統創建的對象
kube-public,所有用戶(包括未授權用戶)都可讀的Kubernetes對象
此外,不屬於任何Namespace的Kubernetes對象:
Namespace本身不屬於任何Namespace
Node
persistentVolume
3) UID
唯一標志
- Label
Label是一組鍵值對,附加到Kubernetes對象上的標志性信息(identifying information)。
相對於用戶,Label用以區分Kubernetes資源(通過貼標簽),用於后續的分組和排序。相對於Kubernetes系統,Label沒有任何意義。
key=value
key:只能使用 字母 數字 _ - . (只能以字母數字開頭,不能超過63給字符)
value: 可以為空 只能使用 字母 數字開頭
kubectl get pods --show-labels #查看pod標簽
kubectl get pods -l app #過濾包含app的標簽
kubectl get pods -L app
kubectl label pods pod-demo release=canary #給pod-demo打上標簽
kubectl get pods -l app --show-labels
kubectl label pods pod-demo release=stable --overwrite #修改標簽
kubectl get pods -l release
kubectl get pods -l release,app
標簽選擇器
等值關系標簽選擇器:=, == , != (kubectl get pods -l app=test,app=dev)
集合關系標簽選擇器: KEY in (v1,v2,v3), KEY notin (v1,v2,v3) !KEY (kubectl get pods -l "app in (test,dev)")
許多資源支持內嵌字段
matchLabels: 直接給定建值
matchExpressions: 基於給定的表達式來定義使用標簽選擇器,{key:"KEY",operator:"OPERATOR",values:[V1,V2,....]}
操作符: in notin:Values字段的值必須是非空列表 Exists NotExists: Values字段的值必須是空列表
節點選擇器
kubectl explain pod.spec
nodeSelector可以限定pod創建在哪個節點上,舉個例子,給節點k8s-node01打上標簽disktype=ssd,讓pod-demo指定創建在k8s-node01上
(1)給k8s-node01節點打標簽
[root@k8s-master mainfests]# kubectl label nodes k8s-node01 disktype=ssd
(2)修改yaml文件,增加標簽選擇器
[root@k8s-master mainfests]# cat pod-demo.yaml
(3)重新創建pod-demo,可以看到固定調度在k8s-node01節點上
[root@k8s-master mainfests]# kubectl delete -f pod-demo.yaml
- Annotation
Annotation是一組鍵值對,附加到Kubernetes對象上的非標志性信息(Non-identifying information)。
Annotation相對於用戶,用以補充說明Kubernetes資源(通過貼標簽)。Annotation相對於Kubernetes系統,沒有任何意義
3.Kubernetes Storage對象
- Volume
Pod級別的存儲
- PersistentVolume/PersistentVolumeClaim
集群級別的存儲
1.kubernetes中的資源對象
資源對象名稱 縮寫
componentstatuses cs
daemonsets ds
deployments
events ev
endpoints ep
horizontalpodautoscalers hpa
ingress ing
jobs
limitranges limits
nodes no
namespaces ns
pods po
persistentvolumes pv
persistentvolumeclaims pvc
resourcequotas quota
secrets
serviceaccounts
services svc
POD
Kubernetes為每個Pod都分配了唯一的IP地址,稱之為Pod IP,一個Pod里的多個容器共享Pod IP地址。Kubernetes要求底層網絡支持集群內任意兩個Pod之間的TCP/IP直接通信,這通常采用虛擬二層網絡技術來實現,例如Flannel、Open vSwitch等。因此,在Kubernetes里,一個Pod里的容器與另外主機上的Pod容器能夠直接通信。
Pod有兩種類型:普通的Pod和靜態Pod(Static Pod),靜態Pod不存放在etcd存儲里,而是存放在某個具體的Node上的一個具體文件中,並且只在此Node上啟動運行。普通的Pod一旦被創建,就會被存儲到etcd中,隨后會被Kubernetes Master調度到某個具體的Node上並進行綁定(Binding),該Node上的kubelet進程會將其實例化成一組相關的Docker容器並啟動起來。當Pod里的某個容器停止時,Kubernetes會自動檢測到這個問題並且重新啟動這個Pod(重啟Pod里的所有容器);如果Pod所在的Node宕機,則會將這個Node上的所有Pod重新調度到其他節點上運行
pod 定義文件
apiVersion: v1
kind: Pod
metadata:
name: myweb #pod名稱
labels: #資源對象的標簽
name: myweb
spec: #pod包含的容器組定義
containers:
- name: myweb #名稱為mywwb,鏡像為kubeguide/tomcat-app: v1的容器
image: kubeguide/tomcat-app: v1
ports: #容器的8080端口啟動容器進程 pod ip+ 端口組成了Endpoint,一個pod里可以存在多個
- containerPort: 8080
env: #注入的環境變量
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
Pod Volume,Pod Volume有一些擴展,比如可以用分布式文件系統GlusterFS等實現后端存儲功能;Pod Volume是定義在Pod之上,然后被各個容器掛載到自己的文件系統中的
Event概念,Event是一個事件的記錄,記錄了事件的最早產生時間、最后重現時間、重復次數、發起者、類型,以及導致此事件的原因等眾多信息。Event通常會關聯到某個具體的資源對象上,是排查故障的重要參考信息,當我們發現某個Pod遲遲無法創建時,可以用kubectl describe pod xxx來查看它的描述信息,用來定位問題的原因
每個Pod都可以對其能使用的服務器上的計算資源設置限額,當前可以設置限額的計算資源有CPU和Memory兩種,其中CPU的資源單位為CPU(Core)的數量,是一個絕對值。
對於容器來說一個CPU的配額已經是相當大的資源配額了,所以在Kubernetes里,通常以千分之一的CPU配額為最小單位,用m來表示。通常一個容器的CPU配額被定義為100-300m,即占用0.1-0.3個CPU。與CPU配額類似,Memory配額也是一個絕對值,它的單位是內存字節數。
對計算資源進行配額限定需要設定以下兩個參數:
Requests:該資源的最小申請量,系統必須滿足要求。
Limits:該資源最大允許使用的量,不能超過這個使用限制,當容器試圖使用超過這個量的資源時,可能會被Kubernetes Kill並重啟。
通常我們應該把Requests設置為一個比較小的數值,滿足容器平時的工作負載情況下的資源需求,而把Limits設置為峰值負載情況下資源占用的最大量
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
#最小0.25個CPU及64MB內存,最大0.5個CPU及128MB內存
標簽 label
某個資源對象定義一個Label,就相當於給它打了一個標簽,隨后可以通過Label Selector(標簽選擇器)查詢和篩選擁有某些Label的資源對象,Kubernetes通過這種方式實現了類似SQL的簡單又通用的對象查詢機制
Label Selector的表達式有兩種:基於等式的(Equality-based)和基於集合的(Set-based)。下面是基於等式的匹配例子。
name=redis-slave:匹配所有標簽為name=redis-slave的資源對象。
env != production:匹配所有標簽env不等於production的資源對象。
name in (redis-master, redis-slave):匹配所有標簽為name=redis-master或者name=redis-slave的資源對象。
name not in (php-frontend):匹配所有標簽name不等於php-frontend的資源對象。
還可以通過多個Label Selector表達式的組合實現復雜的條件選擇,多個表達式之間用“,”進行分隔即可,幾個條件之間是“AND”的關系,即同時滿足多個條件
name=redis-slave, env!=production
name not in (php-frontend), env!=production
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
app: myweb
role: backend
env: testing
#RC和Service在spec中定義Selector與Pod進行關聯:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 1
selector:
app: myweb
template:
#Deployment、ReplicaSet、DaemonSet和Job則可以在Selector中使用基於集合的篩選條件:
selector:
matchLabels:
app: myweb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
matchLabels用於定義一組Label,與直接寫在Selector中作用相同;matchExpressions用於定義一組基於集合的篩選條件,可用的條件運算符包括:In、NotIn、Exists和DoesNotExist。
如果同時設置了matchLabels和matchExpressions,則兩組條件為“AND”關系,即所有條件需要同時滿足才能完成Selector的篩選。
Label Selector在Kubernetes中的重要使用場景如下:
Kube-controller進程通過資源對象RC上定義的Label Selector來篩選要監控的Pod副本的數量,從而實現Pod副本的數量始終符合預期設定的全自動控制流程。
Kube-proxy進程通過Service的Label Selector來選擇對應的Pod,自動建立起每個Service到對應Pod的請求轉發路由表,從而實現Service的智能負載均衡機制。
通過對某些Node定義特定的Label,並且在Pod定義文件中使用NodeSelector這種標簽調度策略,kube-scheduler進程可以實現Pod“定向調度”的特性。
Replication Controller
RC的作用是聲明Pod的副本數量在任意時刻都符合某個預期值,所以RC的定義包括如下幾個部分。
Pod期待的副本數量(replicas)。
用於篩選目標Pod的Label Selector。
當Pod的副本數量小於預期數量時,用於創建新Pod的Pod模板(template)。
下面是一個完整的RC定義的例子,即確保擁有tier=frontend標簽的這個Pod(運行Tomcat容器)在整個Kubernetes集群中始終有三個副本:
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 3
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
當我們定義了一個RC並提交到Kubernetes集群中后,Master節點上的Controller Manager組件就得到通知,定期巡檢系統中當前存活的目標Pod,並確保目標Pod實例的數量剛好等於此RC的期望值。如果有過多的Pod副本在運行,系統就會停掉多余的Pod;如果運行的Pod副本少於期望值,即如果某個Pod掛掉,系統就會自動創建新的Pod以保證數量等於期望值。
通過RC,Kubernetes實現了用戶應用集群的高可用性,並且大大減少了運維人員在傳統IT環境中需要完成的許多手工運維工作(如主機監控腳本、應用監控腳本、故障恢復腳本等)
通過修改RC的副本數量,可以實現Pod的動態縮放(Scaling)功能。
kubectl scale rc redis-slave --replicas=3
Replica Set
由於Replication Controller與Kubernetes代碼中的模塊Replication Controller同名,同時這個詞也無法准確表達它的意思,所以從Kubernetes v1.2開始,它就升級成了另外一個新的對象——Replica Set,官方解釋為“下一代的RC”。它與RC當前存在的唯一區別是:Replica Set支持基於集合的Label selector(Set-based selector),而RC只支持基於等式的Label selector(equality-based selector),所以Replica Set的功能更強大。
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
Replica Set很少單獨使用,它主要被Deployment這個更高層的資源對象所使用,從而形成一整套Pod創建、刪除、更新的編排機制。
RC和RS的特性與作用如下:
在大多情況下,我們通過定義一個RC實現Pod的創建過程及副本數量的自動控制。
RC里包括完整的Pod定義模板。
RC通過Label Selector機制實現對Pod副本的自動控制。
通過改變RC里的Pod副本數量,可以實現Pod的擴容或縮容功能。
通過改變RC里Pod模板中的鏡像版本,可以實現Pod的滾動升級功能
Deployment
Deployment相對於RC的最大區別是我們可以隨時知道當前Pod“部署”的進度。一個Pod的創建、調度、綁定節點及在目標Node上啟動對應的容器這一完整過程需要一定的時間,所以我們期待系統啟動N個Pod副本的目標狀態,實際上是一個連續變化的“部署過程”導致的最終狀態。
Deployment的典型使用場景有以下幾個:
創建一個Deployment對象來生成對應的Replica Set並完成Pod副本的創建過程。
檢查Deployment的狀態來看部署動作是否完成(Pod副本的數量是否達到預期的值)。
更新Deployment以創建新的Pod(比如鏡像升級)。
如果當前Deployment不穩定,則回滾到一個早先的Deployment版本。
暫停Deployment以便於一次性修改多個Pod Template Spec的配置項,之后再恢復Deployment,進行新的發布。
擴展Deployment以應對高負載。
查看Deployment的狀態,以此作為發布是否成功的指標。
清理不再需要的舊版本ReplicaSet。
Deployment的定義與Replica Set的定義類似,只是API聲明與Kind類型不同。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
--------------------------------
apiVersion: v1
kind: ReplicaSet
metadata:
name: nginx-repset
Deployment定義的一個完整例子
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
通過命令kubectl get deployment來查看Deployment的信息
ESIRED::Pod副本數量的期望值,即Deployment里定義的Replica。
CURRENT:當前Replica的值,如果小於DESIRED的期望值,會創建新的Pod,直到達成DESIRED為止。
UP-TO-DATE:最新版本的Pod的副本數量,用於指示在滾動升級的過程中,有多少個Pod副本已經成功升級。
AVAILABLE:當前集群中可用的Pod副本數量,即集群中當前存活的Pod數量。
Pod的管理對象,除了RC、ReplicaSet、Deployment,還有DaemonSet、StatefulSet、Job等,分別用於不同的應用場景
Horizontal Pod Autoscaler
HPA與RC、Deployment一樣,也屬於Kubernetes資源對象。通過追蹤分析RC或RS控制的所有目標Pod的負載變化情況,來確定是否需要針對性地調整目標Pod的副本數。
HPA有以下兩種方式作為Pod負載的度量指標:
CPUUtilizationPercentage
應用程序自定義的度量指標,比如服務在每秒內的相應的請求數(TPS或QPS)。
CPUUtilizationPercentage是一個算術平均值,即目標Pod所有副本自帶的CPU利用率的平均值。一個Pod自身的CPU利用率是該Pod當前CPU的使用量除以它的Pod Request的值,比如我們定義一個Pod的Pod Request為0.4,而當前Pod的CPU使用量為0.2,則它的CPU使用率為50%,這樣我們就可以算出來一個RC或RS控制的所有Pod副本的CPU利用率的算術平均值了。如果某一時刻CPUUtilizationPercentage的值超過80%,則意味着當前的Pod副本數很可能不足以支撐接下來更多的請求,需要進行動態擴容,而當請求高峰時段過去后,Pod的CPU利用率又會降下來,此時對應的Pod副本數應該自動減少到一個合理的水平
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 2
scaleTargetRef:
kind: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
通過HPA控制php-apache的Pod副本,當Pod副本的CPUUtilizationPercentage的值超過90%時,會進行自動擴容增加Pod副本的數量,擴容或縮容時Pod的副本數量要介於2-10之間。
除了通過yaml文件來定義HPA對象之外,還可以通過命令的方式創建:
kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10
StatefulSet
Pod的管理對象RC、Deployment、DaemonSet和Job都是面向無狀態的服務,但實際中有很多服務是有狀態的,比如Mysql集群、MongoDB集群、ZooKeeper集群等,可以使用StatefulSet來管理有狀態的服務。
StatefulSet有如下一些特性:
StatefulSet里的每個Pod都有穩定、唯一的網絡標識,可以用來發現集群內的其他成員。假設StatefulSet的名字叫kafka,那么第1個Pod叫kafka-0,第2個叫kafka-1,以此類推。
StatefulSet控制的Pod副本的啟停順序是受控的,操作第n個Pod時,前n-1個Pod已經是運行且准備好的狀態。
StatefulSet里的Pod采用穩定的持久化存儲卷,通過PV/PVC來實現,刪除Pod時默認不會刪除與StatefulSet相關的存儲卷(為了保證數據的安全)。
StatefulSet除了要與PV卷捆綁使用以存儲Pod的狀態數據,還要與Headless Service配合使用,即在每個StatefulSet的定義中要聲明它屬於哪個Headless Service。Headless Service與普通Service的區別在於,它沒有Cluster IP,如果解析Headless Service的DNS域名,則返回的是該Service對應的全部Pod的Endpoint列表。StatefulSet在Headless Service的基礎上又為StatefulSet控制的每個Pod實例創建了一個DNS域名,這個域名的格式為:
$(podname).$(headless service name)
比如一個3節點的kafka的StatefulSet集群,對應的Headless Service的名字為kafka,StatefulSet的名字為kafka,則StatefulSet里面的3個Pod的DNS名稱分別為kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,這些DNS名稱可以直接在集群的配置文件中固定下來。
Service(服務)
Service其實就是我們經常提起的微服務架構中的一個“微服務”,Pod、RC等資源對象其實都是為它作“嫁衣”的。Pod、RC或RS與Service的邏輯關系如下圖所示
Kubernetes的Service定義了一個服務的訪問入口地址,前端的應用(Pod)通過這個入口地址訪問其背后的一組由Pod副本組成的集群實例,Service與其后端Pod副本集群之間則是通過Label Selector來實現“無縫對接”的。而RC的作用實際上是保證Service的服務能力和服務質量始終處於預期的標准。
通過分析、識別並建模系統中的所有服務為微服務——Kubernetes Service,最終我們的系統由多個提供不同業務能力而又彼此獨立的微服務單元所組成,服務之間通過TCP/IP進行通信,從而形成了強大而又靈活的彈性集群,擁有了強大的分布式能力、彈性擴展能力、容錯能力。因此,我們的系統架構也變得簡單和直觀許多。
既然每個Pod都會被分配一個單獨的IP地址,而且每個Pod都提供了一個獨立的Endpoint(Pod IP+ContainerPort)以被客戶端訪問,多個Pod副本組成了一個集群來提供服務,那么客戶端如何來訪問它們呢?一般的做法是部署一個負載均衡器(軟件或硬件),但這樣無疑增加了運維的工作量。在Kubernetes集群里使用了Service(服務),它提供了一個虛擬的IP地址(Cluster IP)和端口號,Kubernetes集群里的任何服務都可以通過Cluster IP+端口的方式來訪問此服務,至於訪問請求最后會被轉發到哪個Pod,則由運行在每個Node上的kube-proxy負責。kube-proxy進程其實就是一個智能的軟件負載均衡器,它負責把對Service的請求轉發到后端的某個Pod實例上,並在內部實現服務的負載均衡與會話保持機制
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
selector:
tier: frontend
#定義了一個名為“tomcat-service”的Service,它的服務端口為8080,擁有“tier=frontend”這個Label的所有Pod實例
很多服務都存在多個端口的問題,通常一個端口提供業務服務,另外一個端口提供管理服務,比如Mycat、Codis等常見中間件。Kubernetes Service支持多個Endpoint,要求每個Endpoint定義一個名字來區分,下面是tomcat多端口的Service定義樣例
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
name: service-port
- port: 8005
name: shutdown-port
selector:
tier: frontend
Kubernetes的服務發現機制
ubernetes通過Add-On增值包的方式引入了DNS系統,把服務名作為DNS域名,這樣程序就可以直接使用服務名來建立連接
.外部系統訪問Service的問題
Node IP:Node節點的IP地址,即物理網卡的IP地址。
Pod IP:Pod的IP地址,即docker容器的IP地址,此為虛擬IP地址。
Cluster IP:Service的IP地址,此為虛擬IP地址。
外部訪問Kubernetes集群里的某個節點或者服務時,必須要通過Node IP進行通信。
Pod IP是Docker Engine根據docker0網橋的IP地址段進行分配的一個虛擬二層網絡IP地址,Pod與Pod之間的訪問就是通過這個虛擬二層網絡進行通信的,而真實的TCP/IP流量則是通過Node IP所在的物理網卡流出的。
Service的Cluster IP具有以下特點:
Cluster IP僅僅作用於Service這個對象,並由Kubernetes管理和分配IP地址。
Cluster IP是一個虛擬地址,無法被ping。
Cluster IP只能結合Service Port組成一個具體的通信端口,供Kubernetes集群內部訪問,單獨的Cluster IP不具備TCP/IP通信的基礎,並且外部如果要訪問這個通信端口,需要做一些額外的工作。
Node IP、Pod IP和Cluster IP之間的通信,采用的是Kubernetes自己設計的一種特殊的路由規則,與我們熟悉的IP路由有很大的區別。
我們的應用如果想讓外部訪問,最常用的作法是使用NodePort方式
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31002
selector:
tier: frontend
NodePort的實現方式是在Kubernetes集群里的每個Node上為需要外部訪問的Service開啟一個對應的TCP監聽端口,外部系統只要用任意一個Node的IP地址+具體的NodePort端口號即可訪問此服務。
NodePort還沒有完全解決外部訪問Service的所有問題,比如負載均衡問題,常用的做法是在Kubernetes集群之外部署一個負載均衡器
Load balancer組件獨立於Kubernetes集群之外,可以是一個硬件負載均衡器,也可以是軟件方式實現,例如HAProxy或者Nginx。這種方式,無疑是增加了運維的工作量及出錯的概率。
於是Kubernetes提供了自動化的解決方案,如果我們使用谷歌的GCE公有雲,那么只需要將type: NodePort改成type: LoadBalancer,此時Kubernetes會自動創建一個對應的Load balancer實例並返回它的IP地址供外部客戶端使用
Volume(存儲卷
Volume是Pod中能夠被多個容器訪問的共享目錄。Volume定義在Pod上,被一個Pod里的多個容器掛載到具體的文件目錄下,當容器終止或者重啟時,Volume中的數據也不會丟失。Kubernetes支持多種類型的Volume,例如GlusterFS、Ceph等分布式文件系統。
除了可以讓一個Pod里的多個容器共享文件、讓容器的數據寫到宿主機的磁盤上或者寫文件到網絡存儲中,Kubernetes還提供了容器配置文件集中化定義與管理,通過ConfigMap對象來實現。
Kubernetes支持多種Volume類型,下面我們一一進行介紹。
1.emptyDir
emptyDir是在Pod分配到Node時創建的,它的初始內容為空,並且無須指定宿主機上對應的目錄文件,它是Kubernetes自動分配的一個目錄,當Pod從Node上移除時,emptyDir中的數據也會被永久刪除。
emptyDir的用途如下:
臨時空間,例如用於某些應用程序運行時所需的臨時目錄,且無須永久保存。
長時間任務的中間過程CheckPoint的臨時保存目錄。
一個容器需要從另一個容器中獲取數據的目錄(多容器共享目錄)。
emptyDir的定義如下:
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: tomcat-demo
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
imagePullPolicy: IfNotPresent
2.hostPath
使用hostPath掛載宿主機上的文件或目錄,主要用於以下幾個方面:
容器應用程序生成的日志文件需要永久保存時,可以使用宿主機的文件系統存儲。
需要訪問宿主機上Docker引擎內部數據時,可以定義hostPath的宿主機目錄為docker的數據存儲目錄,使容器內部應用可以直接訪問docker的數據文件。
使用hostPath時,需要注意以下幾點:
在不同的Node上的Pod掛載的是本地宿主機的目錄,如果要想讓不同的Node掛載相同的目錄,則可以使用網絡存儲或分布式文件存儲。
如果使用了資源配額管理,則Kubernetes無法將其在宿主機上使用的資源納入管理。
hostPath的定義如下:
volumes:
- name: "persistent-storage"
hostPath:
path: "/data"
3.gcePersistentDisk
使用這種類型的Volume表示使用谷歌公有雲提供的永久磁盤(Persistent Disk,PD)存放數據,使用gcePersistentDisk有以下一些限制條件:
Node需要是谷歌GCE雲主機。
這些雲主機需要與PD存在於相同的GCE項目和Zone中。
通過gcloud命令創建一個PD:
gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
定義gcePersistentDisk類型的Volume的示例如下:
volumes:
- name: test-volume
gcPersistentDisk:
pdName: my-data-disk
fsType: ext4
5.NFS
使用NFS網絡文件系統提供的共享目錄存儲數據時,我們需要在系統中部署一個NFS Server。
定義NFS類型的Volume的示例如下:
volumes:
- name: nfs-volume
nfs:
server: nfs-server.localhost
path: "/"
6.其他類型的Volume
iscsi:使用iSCSI存儲設備上的目錄掛載到Pod中。
flocker:使用Flocker來管理存儲卷。
glusterfs:使用GlusterFS網絡文件系統的目錄掛載到Pod中。
rbd:使用Ceph塊設備共享存儲(Rados Block Device)掛載到Pod中。
gitRepo:通過掛載一個空目錄,並從GIT庫clone一個git repository以供Pod使用。
secret:一個secret volume用於為Pod提供加密的信息,可以將定義在Kubernetes中的secret直接掛載為文件讓Pod訪問。Secret volume是通過tmfs(內存文件系統)實現的,所以這種類型的volume不會持久化。
Persistent Volume
上面提到的Volume是定義在Pod上的,屬於“計算資源”的一部分,而實際上,“網絡存儲”是相對獨立於“計算資源”而存在的一種實體資源。比如在使用雲主機的情況下,我們通常會先創建一個網絡存儲,然后從中划出一個“網盤”並掛載到雲主機上。Persistent Volume(簡稱PV)和與之相關聯的Persistent Volume Claim(簡稱PVC)實現了類似的功能。
PV與Volume的區別如下:
PV只能是網絡存儲,不屬於任何Node,但可以在每個Node上訪問。
PV並不是定義在Pod上的,而是獨立於Pod之外定義。
下面是NFS類型PV的yaml定義內容,聲明了需要5G的存儲空間:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somepath
server: 172.17.0.2
PV的accessModes屬性有以下類型:
ReadWriteOnce:讀寫權限、並且只能被單個Node掛載。
ReadOnlyMany:只讀權限、允許被多個Node掛載。
ReadWriteMany:讀寫權限、允許被多個Node掛載。
如果Pod想申請使用PV資源,則首先需要定義一個PersistentVolumeClaim(PVC)對象
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
然后在Pod的volume定義中引用上述PVC即可
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
PV是有狀態的對象,它有以下幾種狀態:
Available:空閑狀態。
Bound:已經綁定到某個PVC上。
Released:對應的PVC已經刪除,但資源還沒有被集群收回。
Failed:PV自動回收失敗。
Namespace(命名空間)
通過將Kubernetes集群內部的資源對象“分配”到不同的Namespace中,形成邏輯上分組的不同項目、小組或用戶組,便於不同的分組在共享使用整個集群的資源的同時還能被分別管理。
Kubernetes集群在啟動后,會創建一個名為“default”的Namespace,通過kubectl可以查看到:
kubectl get namespaces
如果不特別指明Namespace,則用戶創建的Pod、RC、RS、Service都獎被系統創建到這個默認的名為default的Namespace中。
下面是Namespace的定義示例:
apiVersion: v1
kind: Namespace
metadata:
name: development
定義一個Pod,並指定它屬於哪個Namespace:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
使用kubectl get命令查看Pod狀態信息時,需要加上--namespace參數,指定查看哪個namespace下的資源對象,不加這個參數則默認查看 default下的資源對象。
kubectl get pods --namespace=development
當我們給每個租戶創建一個Namespace來實現多租戶的資源隔離時,還能結合Kubernetes的資源配額管理,限定不同租戶能占用的資源,例如CPU使用量、內存使用量等
Annotation
Annotation與Label類似,也使用key/value鍵值對的形式進行定義。不同的是Label具有嚴格的命名規則,它定義的是Kubernetes對象的元數據(Metadata),並且用於Label Selector。而Annotation則是用戶任意定義的“附加”信息,以便於外部工具進行查找。通常Kubernetes的模塊會通過Annotation的方式標記資源對象的一些特殊信息。
使用Annotation來記錄的信息如下:
build信息、release信息、Docker鏡像信息等,例如時間戳、release id號、PR號、鏡像bash值、docker registry地址等。
日志庫、監控庫、分析庫等資源庫的地址信息。
程序調試工具信息,例如工具名稱、版本號等。
團隊的聯系信息,例如電話號碼、負責人名稱、網址等
docker info 查看默認運行時 runtime
Kubernetes 集群狀態異常排錯
https://zhuanlan.zhihu.com/p/34722886