YAML語言似乎已經成為了事實標准的“雲配置”語言,無論是容器事實標准docker(主要是docker-compose使用)、SDN,還是容器編排王者kubernetes,又或是虛擬機時代的王者openstack采用的配置文件都是yaml文件格式。不過需要承認的是我個人最初剛接觸yaml時還不是很適應(個人更適應json),在后續運維kubernetes時,每每都要去參考k8s doc中的各種k8s對象的模板才能把yaml文件寫“正確”。本文是一篇譯文,這篇文章很好地講解了yaml語言的語法格式,並用kubernetes deployment配置來作為示例。至少我看完這篇文章后是受益多多,因此這里將該文章快速翻譯出來,供廣大的k8s愛好者、實踐者參考。
本文翻譯自《Introduction to YAML: Creating a Kubernetes deployment》。(譯注:CNCF也轉發了Openstack開發背后的主力推手Mirantis公司博客的這篇文章。)
在之前的文章中,我們一直在討論如何使用Kubernetes來啟動和操作資源實例。到目前為止,我們一直都專注於命令行操作。但其實有一種更簡單,更有用的方法:使用YAML創建配置文件。在本文中,我們將了解YAML的工作原理,並使用它來先定義一個Kubernetes Pod,然后再定義一個Kubernetes Deployment。
YAML基礎
如果您正在做與一些軟件領域相關的事情 – 尤其是涉及Kubernetes,SDN和OpenStack等領域,那么你將很難“擺脫”YAML 。YAML是一種人類可讀的、專門用於配置信息的文本格式,例如,在本文中,我們將使用YAML定義創建第一個Pod,然后是Deployment。YAML可以理解為Yet Another Markup Language的縮寫,也可以理解為”YAML Ain’t Markup Language”的縮寫,這取決於你問的是誰。
使用YAML進行K8s定義會帶來許多優勢,包括:
- 方便:您不再需要將所有參數都添加到命令行中
- 可維護: YAML文件可以添加到源代碼版本控制倉庫中,因此你可以跟蹤文件的修改
- 靈活性:通過YAML,您能夠創建比在命令行上更為復雜的結構
YAML是JSON的超集,這意味着任何有效的JSON文件也是有效的YAML文件。所以一方面,如果你知道JSON並且你只想寫自己的YAML(而不是閱讀其他人的那些),那么你就完全可以開始了。另一方面,不幸的是,這不太可能。即使你只是想在網上找些例子,他們更有可能是YAML格式(非JSON),所以我們不妨來習慣這種格式。盡管如此,在某些情況下JSON格式可能更為方便,因此最好知道JSON仍然可供您使用。
幸運的是,在YAML中你只需要了解兩種類型的結構:
- Lists(列表)
- Maps
沒錯!你可能會用maps of lists和lists of maps,等等,但是一旦你掌握了這兩個結構,那么你就可以開始了。這並不是說你不能做更復雜的事情,但總的來說,這就是你開始時需要的全部內容了。
YAML Maps
讓我們先來看看YAML maps。maps允許您關聯鍵值對(name-value pairs),在嘗試設置配置信息時,這非常方便。例如,您可能有一個如下所示的配置文件:
---
apiVersion: v1
kind: Pod
第一行是分隔符,除非您嘗試在單個文件中定義多個結構,否則它是可選的。在那之后,如您所見,我們有兩個值:v1 和Pod ,映射到兩個鍵:apiVersion 和kind 。
當然,這種事情非常簡單,它等價於下面的JSON內容:
{
"apiVersion": "v1",
"kind": "Pod"
}
請注意,在我們的YAML版本中,引號是可選的; 處理程序可以告訴您正在查看基於這種格式的一個字符串。
您還可以通過創建一個映射到另一個map而不是字符串的鍵來指定更為復雜的結構,如下所示:
---
apiVersion: v1
kind: Pod
metadata:
name: rss-site
labels:
app: web
在這種情況下,我們有一個鍵: metadata,其值為一個帶有2個鍵:name和labels的map。該labels鍵本身有一個map作為其值。您可以根據需要嵌套這些。
YAML處理程序之所以知道所有這些部分是如何相互關聯的,是因為我們做了行縮進。在這個例子中,我使用了2個空格以便於閱讀,但空格的數量並不重要 – 只要它至少為1,並且只要你的縮進是一致的。例如,name和labels處於相同的縮進級別,因此處理程序知道它們都是同一個map的一部分; 它知道app 是labels的值,因為它進一步縮進了。
**注意:永遠不要在YAML文件中使用tab **
因此,如果我們將其轉換為JSON,它將是如下所示這樣的:
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "rss-site",
"labels": {
"app": "web"
}
}
}
現在我們來看list類型。
YAML Lists
YAML lists實際上是一個對象序列。例如:
args:
- sleep
- "1000"
- message
- "Bring back Firefly!"
正如您在此處所看到的,您可以在list中包含幾乎任意數量的元素,這些元素為以短划線( – )開始並相對於父項縮進一級。所以如果用JSON展示,將是這樣:
{
"args": ["sleep", "1000", "message", "Bring back Firefly!"]
}
當然,list中的元素也可以是maps:
---
apiVersion: v1
kind: Pod
metadata:
name: rss-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: rss-reader
image: nickchase/rss-php-nginx:v1
ports:
- containerPort: 88
正如您在這里看到的,我們有一個container“對象” 列表,每個container都包含一個name,一個image和一個port列表。ports下的每個列表項本身都是一個containerPort及其值的map。
為了完整起見,讓我們快速查看等效的JSON:
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "rss-site",
"labels": {
"app": "web"
}
},
"spec": {
"containers": [{
"name": "front-end",
"image": "nginx",
"ports": [{
"containerPort": "80"
}]
},
{
"name": "rss-reader",
"image": "nickchase/rss-php-nginx:v1",
"ports": [{
"containerPort": "88"
}]
}]
}
}
正如你所看到的,我們的例子開始變得更為復雜了,不過我們還沒有遇到特別復雜的例子!難怪YAML如此快地取代JSON。
所以讓我們回顧一下。我們了解了:
- maps,它們是鍵值對的組
- lists,它們包含獨立的元素(成員)
- maps of maps
- maps of lists
- lists of lists
- lists of maps
基本上,無論你想要組合什么結構,你都可以用這兩種結構來做。
使用YAML創建Pod
好了,現在我們已經掌握了基礎知識,讓我們看看如何使用它。我們將首先使用YAML創建Pod,然后再創建Deployment。
如果您尚未安裝Kubernetes集群和kubectl,請在繼續之前查看本系列中有關搭建Kubernetes的集群的文章。沒關系,我們等一下……
回來了嗎?好!讓我們從Pod開始吧。
創建pod文件
在前面的示例中,我們使用YAML描述了一個簡單的Pod:
—
apiVersion: v1
kind: Pod
metadata:
name: rss-site
labels:
app: web
spec:
containers:
– name: front-end
image: nginx
ports:
– containerPort: 80
– name: rss-reader
image: nickchase/rss-php-nginx:v1
ports:
– containerPort: 88
我們一行行分開看,我們從API版本開始; 這里只是v1。(當我們講解Deployment時,我們必須指定不同的版本,因為v1中不存在Deployment。)
接下來,我們指定要創建Pod; 這里我們可能會指定deployment,job,service等其他類型,具體取決於我們要實現什么。
接下來我們指定metadata。這里我們指定Pod的name,以及我們用來識別Kubernetes pod的label。
最后,我們將指定構成pod的實際對象。該規范(spec)的屬性包括容器,存儲卷,或其他Kubernetes需要了解的屬性,比如:重新在啟動容器失敗時重啟的選項。您可以在Kubernetes API規范中找到Kubernetes Pod屬性的完整列表。讓我們仔細看看典型的容器定義:
...
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: rss-reader
...
在這種情況下,我們有一個簡單、短小的定義:name(前端),它所基於容器鏡像(nginx ),以及容器將在內部監聽的一個端口(80 )。在這些中,實際上只是name是必須的,但一般來說,如果你想要它做任何有用的事情,你需要更多的信息。
您還可以指定更復雜的屬性,例如在容器啟動時運行的命令,使用的參數,工作目錄,或者每次實例化容器時是否拉取鏡像的新副本等。您還可以指定一些更深入的信息,例如容器退出日志的存放位置。以下是您可以為Container設置的屬性:
- name
- image
- command
- args
- workingDir
- ports
- env
- resources
- volumeMounts
- livenessProbe
- readinessProbe
- lifecycle
- terminationMessagePath
- imagePullPolicy
- securityContext
- stdin
- stdinOnce
- tty
現在讓我們繼續並實際創建pod。
使用YAML文件創建Pod
當然,第一步是創建一個文本文件。將其命名為pod.yaml 並添加以下文本,就像我們之前指定的那樣:
---
apiVersion: v1
kind: Pod
metadata:
name: rss-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: rss-reader
image: nickchase/rss-php-nginx:v1
ports:
- containerPort: 88
保存文件。接下來告訴Kubernetes創建pod:
> kubectl create -f pod.yaml
pod "rss-site" created
如您所見,K8引用了我們Pod的名稱。如果你要求一個pod列表,你可以看到下面內容:
> kubectl get pods
NAME READY STATUS RESTARTS AGE
rss-site 0/2 ContainerCreating 0 6s
如果您提前檢查,您可以看到仍在創建中的pod。幾秒鍾后,您應該看到容器正在運行:
> kubectl get pods
NAME READY STATUS RESTARTS AGE
rss-site 2/2 Running 0 14s
從這里開始,您可以測試Pod(就像我們在上一篇文章中所做的那樣),但最終我們想要創建一個Deployment,所以讓我們繼續並刪除它,這樣就沒有任何名稱沖突:
> kubectl delete pod rss-site
pod "rss-site" deleted
Pod創建故障診斷
當然,有時事情並沒有像你期望的那樣發展。也許您遇到了網絡問題,或者您在YAML文件中輸入了錯誤的內容。您可能會看到如下錯誤:
> kubectl get pods
NAME READY STATUS RESTARTS AGE
rss-site 1/2 ErrImagePull 0 9s
在這種情況下,我們可以看到我們的一個容器啟動得很好,但是另一個容器出了問題。要追查問題,我們可以向Kubernetes詢問有關Pod的更多信息:
> kubectl describe pod rss-site
Name: rss-site
Namespace: default
Node: 10.0.10.7/10.0.10.7
Start Time: Sun, 08 Jan 2017 08:36:47 +0000
Labels: app=web
Status: Pending
IP: 10.200.18.2
Controllers: <none>
Containers:
front-end:
Container ID: docker://a42edaa6dfbfdf161f3df5bc6af05e740b97fd9ac3d35317a6dcda77b0310759
Image: nginx
Image ID: docker://sha256:01f818af747d88b4ebca7cdabd0c581e406e0e790be72678d257735fad84a15f
Port: 80/TCP
State: Running
Started: Sun, 08 Jan 2017 08:36:49 +0000
Ready: True
Restart Count: 0
Environment Variables: <none>
rss-reader:
Container ID:
Image: nickchase/rss-php-nginx
Image ID:
Port: 88/TCP
State: Waiting
Reason: ErrImagePull
Ready: False
Restart Count: 0
Environment Variables: <none>
Conditions:
Type Status
Initialized True
Ready False
PodScheduled True
No volumes.
QoS Tier: BestEffort
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
45s 45s 1 {default-scheduler } Normal Scheduled Successfully assigned rss-site to 10.0.10.7
44s 44s 1 {kubelet 10.0.10.7} spec.containers{front-end} Normal Pulling pulling image "nginx"
45s 43s 2 {kubelet 10.0.10.7} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy.
43s 43s 1 {kubelet 10.0.10.7} spec.containers{front-end} Normal Pulled Successfully pulled image "nginx"
43s 43s 1 {kubelet 10.0.10.7} spec.containers{front-end} Normal Created Created container with docker id a42edaa6dfbf
43s 43s 1 {kubelet 10.0.10.7} spec.containers{front-end} Normal Started Started container with docker id a42edaa6dfbf
43s 29s 2 {kubelet 10.0.10.7} spec.containers{rss-reader} Normal Pulling pulling image "nickchase/rss-php-nginx"
42s 26s 2 {kubelet 10.0.10.7} spec.containers{rss-reader} Warning Failed Failed to pull image "nickchase/rss-php-nginx": Tag latest not found in repository docker.io/nickchase/rss-php-nginx
42s 26s 2 {kubelet 10.0.10.7} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "rss-reader" with ErrImagePull: "Tag latest not found in repository docker.io/nickchase/rss-php-nginx"
41s 12s 2 {kubelet 10.0.10.7} spec.containers{rss-reader} Normal BackOff Back-off pulling image "nickchase/rss-php-nginx"
41s 12s 2 {kubelet 10.0.10.7} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "rss-reader" with ImagePullBackOff: "Back-off pulling image \"nickchase/rss-php-nginx\""
正如您所看到的,這里有很多信息,但我們對事件(event)最感興趣- 特別是一旦警告和錯誤開始出現。從這里我能夠很快發現我忘了將”:v1″ label添加到我的image中,所以它正在尋找”:latest”標簽,但該標簽並不存在。
為了解決這個問題,我首先刪除了Pod,然后修復了YAML文件並重新啟動。相反,我可以修復鏡像倉庫(譯注:比如增加:latest標簽),以便Kubernetes可以找到它正在尋找的東西,並且它會繼續,好像什么也沒發生過一樣。
現在我們已經成功運行起來一個Pod,接下來讓我們看看為deployment做得同樣的事情。
使用YAML創建Deployment
最后,我們要創建一個實際的deployment。然而,在我們這樣做之前,很值得去了解一下我們實際上在做什么。
記住,K8管理基於容器的資源。在使用deployment的情況下,您將創建一組要管理的資源。例如,我們在上一個示例中創建了Pod的單個實例,我們可能會創建一個Deployment來告訴Kubernetes管理該Pod的一組副本 – 字面意思就是ReplicaSet – 以確保它們中的一定數量是始終可用。所以我們可以像這樣開始我們的deployment定義:
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: rss-site
spec:
replicas: 2
在這里,我們將apiVersion指定為”extensions/v1beta1″ – 記住,我們想要一個deployment, 但deployment不像pod那樣在v1中。接下來我們指定name。我們還可以指定我們想要的任何其他元數據,但現在讓我們保持簡單。
最后,我們進入規范(spec)。在Pod規范中,我們提供了有關實際進入Pod的內容的信息; 我們將在這里使用deployment做同樣的事情。在這種情況下,我們先描述我們要部署什么Pod,我們總是希望有 2個副本。當然,您可以根據需要設置此數字,並且還可以設置其他屬性,例如定義受此部署影響的Pod的selector,或者在被認為“ready”之前,pod必須啟動且沒有任何錯誤的最小秒數。您可以在Kuberenetes v1beta1 API參考中找到Deployment規范屬性的完整列表。
好的,現在我們知道我們需要2個副本,我們需要回答這個問題:“什么的副本?”它們由模板定義:
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: rss-site
spec:
replicas: 2
template:
metadata:
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: rss-reader
image: nickchase/rss-php-nginx:v1
ports:
- containerPort: 88
看起來熟悉?就應該是這樣; 它與上一節中的Pod定義幾乎完全相同,而且就是這樣設計的。模板只是要復制的對象的定義 – 在其他情況下,可以通過自己創建的對象。
現在讓我們繼續創建deployment。將YAML添加到名為deployment.yaml 的文件中,並讓Kubernetes創建它:
> kubectl create -f deployment.yaml
deployment "rss-site" created
要了解它是如何做的,我們可以檢查deployment列表:
> kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
rss-site 2 2 2 1 7s
正如您所看到的,Kubernetes已經啟動了兩個副本,但只有一個可用。您可以像以前一樣通過描述deployment來檢查事件日志:
> kubectl describe deployment rss-site
Name: rss-site
Namespace: default
CreationTimestamp: Mon, 09 Jan 2017 17:42:14 +0000=
Labels: app=web
Selector: app=web
Replicas: 2 updated | 2 total | 1 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
OldReplicaSets: <none>
NewReplicaSet: rss-site-4056856218 (2/2 replicas created)
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
46s 46s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set rss-site-4056856218 to 2
正如你在這里看到的,沒有問題,它還沒有完成擴展(scale)。再過幾秒鍾,我們可以看到兩個Pod都在運行:
> kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
rss-site 2 2 2 2 1m
到這里我們得到了什么
好的,讓我們回顧一下。我們基本上涵蓋了三個主題:
- YAML是一種人類可讀的基於文本的格式,通過使用鍵值對的map和list(以
- YAML是使用Kubernetes對象最方便的方法,在本文中我們研究了創建Pod和Deployments。
- 通過要求Kubernetes 描述(describe)它們,您可以獲得有關運行(或應該運行)對象的更多信息。
摘抄自 大神 Tony Bai https://tonybai.com/2019/02/25/introduction-to-yaml-creating-a-kubernetes-deployment/