在之前的文章中,我們已經提到過如何使用Kubernetes去創建資源。到目前爲止,我們一直僅僅通過命令行去執行,但是這裏有一個更加簡單有效的方式去創建資源:通過使用YAML創建一個配置文件。在這篇文章,我們將會關注YAML的工作方式以及如何使用YAML創建一個Kubernetes Pod,然後使用Kubernetes創建一個Depolyment。當然您如果是新手可以先了解Kubernetes上運行你的第一個容器。
YAML 基礎
如果你正在做的事與很多軟件領域相關,那麼將很難不涉及到YAML,特別是Kubernetes,SDN,和OpenStack。YAML,它代表着另一種標誌語言,或者YAML不是標誌語言(取決於你問誰)而是特定配置類型基於人類可讀的文本格式的信息,例如,在本文中,我們將會分開說說明YAML定義創建Pod和使用Kubernetes創建一個Depolyment。
使用YAML用於k8s的定義將給你一些好處,包括:
- 便捷性:你將不再需要添加大量的參數到命令行中執行命令
- 可維護性:YAML文件可以通過源頭控制,可以跟蹤每次的操作
- 靈活性:通過YAML你將可以創建比命令行更加複雜的結構
YAML是一個JSON的超集,意味着任何有效JSON文件也都是一個有效的YAML文件。所以一方面,如果你知道JSON,你只是要去寫自己的YAML(而不是閱讀別人的)也就可以了。另一方面,不太可能,不幸的是,儘管你嘗試去網上找到例子,但是他們通常都不是JSON,所以我們可能需要去習慣它。不過,有JSON的情況下可能會更方便,這樣你將會很開心你懂得JSON。
幸運的是,YAML只有兩種結構類型你需要知道:
- Lists
- Maps
那就是說,將有可能存在lists的maps和maps的lists,等等,但是,你只要掌握了這兩種結構也就可以了。這並不是說你不能做更加複雜的事,但是通常,這些也就夠了。
YAML Maps
我們先開始看YAML maps。Maps讓你將鍵值組合,你就可以更加方便的去設置配置信息。例如,你可能有以下這樣一個配置信息:
--- apiVersion: v1 kind: Pod
第一行是分隔符,並且是可選的,除非你試圖在單個文件中定義多個結構。從這裏你可以看到,我們有兩個值,V1和Pod,對應他們的鍵是apiVersion和kind。
這種比較簡單,當然你也可以將之轉換爲json格式,如下:
{ "apiVersion": "v1", "kind": "Pod" }
注意,在我們的YAML版本中,引號是可選的,處理器可以知道你正在查看基於格式化的字符串。
你也可以指定一個複雜的結構,創建一個key其對應的值不是字符串而是一個maps如下所示:
--- apiVersion: v1 kind: Pod metadata: name: rss-site labels: app: web
這種情況下,我們有metadata這個key對應的值中又有兩個key分別爲name和labels。labels 這個key的值又是一個map。你可以根據場景進行多層嵌套。
YAML處理器可以根據行縮進來知道內容之間的關聯。在這個例子中我用了兩個空格使之可讀,但是空格的數量不重要,但是至少要求一個,並且所有縮進都保持一致的空格數。例如,name和labels是相同縮進級別,因此YAML處理器知道他們屬於同一map;它知道app是lables的值因爲app的縮進更大。
注意:在YAML文件中絕對不要使用tab鍵
因此,如果我們將上述內容翻譯成JSON,它看起來結果如下所示:
{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "rss-site", "labels": { "app": "web" } } }
現在讓我們來看看lists。
YAML lists
YAML lists 簡直是一個序列的對象,例如:
args - sleep - "1000" - message - "Bring back Firefly!"
正如你可以看到,你可以有任何數量的項在列表中,項的定義以破折號(-)開頭,並且與父元素之間存在縮進。在JSON格式中,它將表示如下:
{ "args": ["sleep", "1000", "message", "Bring back Firefly!"] }
當然,list的子項也可以是maps,maps的子項也可以是list如下所示:
--- 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的list對象,每個子項都由name、image、ports組成,每個ports都由一個key爲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的maps
- maps的lists
- lists的lists
- lists的maps
基本上,無論你想要什麼樣結構,你都可以通過這兩個結構去組合實現。
使用YAML創建Pod
所以現在我們已經有了基礎知識,讓我們將他們用起來。我們將使用YAML創建一個Pod然後創建Deployment。
如果你還沒有搭建起集羣和kubectl,那麼在你繼續下去之前需要閱讀之前關於如何搭建kubernets集羣的文章,等搭建完成了我們再繼續下去。
回來了?好的!讓我們開始創建一個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
讓我們分開來解析這些內容,先從apiVersion 這個關鍵字開始;這裏它的值是v1.(當我們要創建Depolyment的時候,這個值將會指定一個不一樣的值,因爲Depolyment沒有v1版本。)
接下來,我們指定我們要創建的是一個Pod;根據我們想要得到的結果,我們將會指定Pod而不是Deployment,Job,Services等等。
最後我們將指定組成pod的實際對象。spec屬性包括一些containers,storage,volumes,或者其他Kubernetes需要知道的參數,以及諸如是否在容器失敗時重新啓動容器的屬性。你可以在特定Kubernetes API找到完整的Kubernetes Pod的屬性,讓我們進一步來看一個典型的容器的定義:
…
spec:
containers:
– name: front-end
image: nginx
ports:
– containerPort: 80
– name: rss-reader
…
在這個例子中,這是一個簡單的最小定義:一個名字(front-end),基於nginx的鏡像,以及容器 將會監聽的一個端口(80)。在這些當中,只有名字是非常需要的,但是通常如果你想做更多的事情,你需要更多的定義。
你也可以指定一個更加複雜的屬性,例如在容器啓動時運行的命令,應使用的參數,工作目錄,或每次實例化時是否拉取映像的新副本。 您還可以指定更深入的信息,例如容器的退出日誌的位置。
以下有一些容器可以設置的屬性:
- name
- image
- command
- args
- workingDir
- ports
- env
- resources
- volumeMounts
- livenessProbe
- readinessProbe
- livecycle
- 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去執行內容所描述的動作:
> kubectl create -f pod.yaml pod "rss-site" created
正如你所見,k8s 引用了你給pod的命名。如果你想看pods列表可以通過以下方式:
> kubectl get pods
NAME READY STATUS RESTARTS AGE
rss-site 0/2 ContainerCreating 0 6s
如果你更早的查看的話,你可以看到它仍然處於created的狀態,大概幾秒之後,你就可以看到容器處於running狀態:
> kubectl get pods NAME READY STATUS RESTARTS AGE rss-site 2/2 Running 0 14s
至此,你可以測試以下Pod(正如我們之前文章做的),但是最後我們要創建一個Deployment,因此我們先刪除這個pod以免發生名字衝突:
> 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
在這個情況下,我們可以看到我們其中一個容器啓動正常,但是另一個出現了問題。我們可以用以下方式去查看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\""
正如你所見,這裏有很多信息,但是最有用的信息在Events中,詳細的顯示出了曾經出現的警告和錯誤信息。從這裏我們可以很快的看出,我忘記將:有v1 標籤的鏡像,所以它用了最新的鏡像,但是它不存在。
爲了解決這個問題,我先刪了這個Pod,然後修復YAML文件,重新啓動。我修復了之後Kubernetes 將能夠從鏡像庫中到到,之後也就能夠安裝下去了。
現在我們已經成功運行了一個Pod,接下讓讓我們同樣的運行Deployment。
使用YAML文件創建Deployment
最後,我們將要進行真正的創建Depolyment。在那之前,我們必須瞭解它實際上是在做什麼。
k8s,記住和管理基於容器的資源。在這個Deployment的例子中,你正在創建一組要管理的資源。例如,在之前的例子中,我們給Pod創建了一個實例,我們將創建一個Deployment去讓Kubernetes管理一組Pod的副本,也就是副本集,保證一定數量的副本一直可用。因此我們可以這樣定義一個Deployment:
—
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: rss-site
spec:
replicas: 2
這裏我們指定了apiVersion爲extensions/v1beta1,記住,Deployments並不像Pods是v1,我們這裏想要的是Deployments。接下來我們指定名字,我們也可以指定我們想要的元數據,但是我們現在儘量保持簡單。
最後,我們進入配置spec項,我們之前在其中指定我們想要在Pod中做的事;同樣的我們也需要在其中指定Deployment中做的事。我們將開始,在這種情況下,通過說我們部署的任何Pod,我們總是想有2個副本。 當然,你可以設置這個數字,你還可以設置屬性,如定義受此Deployment影響的Pod的選擇器,或者一個Pod在沒有任何錯誤變成準備的情況下必須達到的最小秒數。
你可以在Kubernetes v1beta1 API參考中找到一個完整的Depolyment可指定的參數列表。
因此目前我們想要創建兩個副本,我們需要知道我們做的是什麼的副本?他們可以通過以下模板定義:
—
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。添加一個叫做deployment.yaml文件並執行以下命令:
> 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啓動了所有的副本,但是隻有一個可用。你可以如之前一樣查看event日誌對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
你可以看到,並沒有什麼問題,他只是還沒創建好副本。再過一會之後,我們發現所有的Pods正在運行:
> kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE rss-site 2 2 2 2 1m
到目前爲止我們所看到的
讓我們複習以下,我們基本上涵蓋了三個主題:
- YAML是一種基於人類可讀的基於文本的格式,通過使用名稱 – 值對映射和項目列表(以及每個項目的嵌套版本)的組合,您可以輕鬆地指定配置類型信息。
- YAML是使用Kubernetes對象的最方便的方法,在本文中,我們考慮創建Pods和Deployments。
- 您可以通過要求Kubernetes描述它們來獲取關於運行(或應該運行)對象的更多信息。
這是我們的基本YAML教程。我們將在未來幾個月內處理大量與Kubernetes相關的內容。
本文轉自中文社區-使用YAML創建一個 Kubernetes Depolyment