YAML入門:以創建一個Kubernetes deployment為例


 

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/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM