Kubernetes — 個人第一個容器化應用

而在這篇文章中,咱們就來扮演一個應用開發者的角色,使用這個 Kubernetes 集羣發佈第一個容器化應用。html

在開始實踐以前,我先給你講解一下 Kubernetes 裏面與開發者關係最密切的幾個概念。node

做爲一個應用開發者,你首先要作的,是製做容器的鏡像。而有了容器鏡像以後,你須要按照+Kubernetes+項目的規範和要求,將你的鏡像組織爲它可以「認識」的方式,而後提交上去。nginx

那麼,什麼纔是 Kubernetes 項目能「認識」的方式呢?git

這就是使用 Kubernetes 的必備技能:編寫配置文件。docker

備註:這些配置文件能夠是 YAML 或者 JSON 格式的。爲方便閱讀與理解,在後面的講解中,我會統一使用 YAML 文件來指代它們。 Kubernetes 跟 Docker 等不少項目最大的不一樣,就在於它不推薦你使用命令行的方式直接運行容器(雖然 Kubernetes 項目也支持這種方式,好比:kubectl run),而是但願你用 YAML 文件的方式,即:把容器的定義、參數、配置,通通記錄在一個 YAML 文件中,而後用這樣一句指令把它運行起來:api

$ kubectl create -f 個人配置文件

 小案例 發佈一個nginx應用

這麼作最直接的好處是,你會有一個文件能記錄下 Kubernetes 到底「run」了什麼。好比下面這個例子:bash

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

 

 像這樣的一個 YAML 文件,對應到 kubernetes 中,就是一個 API Object(API 對象)。當你爲這個對象的各個字段填好值並提交給 Kubernetes 以後,Kubernetes 就會負責建立出這些對象所定義的容器或者其餘類型的 API 資源。app

能夠看到,這個 YAML 文件中的 Kind 字段,指定了這個 API 對象的類型(Type),是一個 Deployment。運維

所謂+Deployment,是一個定義多副本應用(即多個副本 Pod)的對象。此外,Deployment 還負責在 Pod 定義發生變化時,對每一個副本進行滾動更新(Rolling+Update)。dom

在上面這個 YAML 文件中,我給它定義的 Pod 副本個數 (spec.replicas)是:2。

而這些 Pod 具體的又長什麼樣子呢?

爲此,我定義了一個 Pod 模版(spec.template),這個模版描述了我想要建立的 Pod 的細節。在上面的例子裏,這個 Pod 裏只有一個容器,這個容器的鏡像(spec.containers.image)是nginx=1.7.9,這個容器監聽端口(containerPort)是 80。

 

Pod 就是 Kubernetes 世界裏的「應用」;而一個應用,能夠由多個容器組成。

 須要注意的是,像這樣使用一種 API 對象(Deployment)管理另外一種 API 對象(Pod)的方法,在 Kubernetes 中,叫做「控制器」模式(controller pattern)。在咱們的例子中,Deployment 扮演的正是 Pod 的控制器的角色。

 你可能還注意到,這樣的每個 API 對象都有一個叫做 Metadata 的字段,這個字段就是 API 對象的「標識」,即元數據,它也是咱們從 Kubernetes 裏找到這個對象的主要依據。這其中最主要使用到的字段是 Labels。

顧名思義,Labels 就是一組 key-value 格式的標籤。而像 Deployment 這樣的控制器對象,就能夠經過這個 Labels 字段從 Kubernetes 中過濾出它所關心的被控制對象。

好比,在上面這個 YAML 文件中,Deployment 會把全部正在運行的、攜帶「app=nginx」標籤的 Pod 識別爲被管理的對象,並確保這些 Pod 的總數嚴格等於兩個。

而這個過濾規則的定義,是在 Deployment 的「spec.selector.matchLabels」字段。咱們通常稱之爲:Label+Selector。

另外,在 Metadata 中,還有一個與 Labels 格式、層級徹底相同的字段叫 Annotations,它專門用來攜帶 key-value 格式的內部信息。所謂內部信息,指的是對這些信息感興趣的,是 Kubernetes 組件自己,而不是用戶。因此大多數 Annotations,都是在 Kubernetes 運行過程當中,被自動加在這個 API 對象上。

一個 Kubernetes 的 API 對象的定義,大多能夠分爲 Metadata 和 Spec 兩個部分。前者存放的是這個對象的元數據,對全部 API 對象來講,這一部分的字段和格式基本上是同樣的;然後者存放的,則是屬於這個對象獨有的定義,用來描述它所要表達的功能。

 在瞭解了上述 Kubernetes 配置文件的基本知識以後,咱們如今就能夠把這個 YAML 文件「運行」起來。正如前所述,你可使用 kubectl create 指令完成這個操做:

 

kubectl create -f nginx-deployment.yaml

而後,經過+kubectl+get+命令檢查這個 YAML 運行起來的狀態是否是與咱們預期的一致:  

kubectl get pods -l app=nginx

 

 

kubectl get 指令的做用,就是從 Kubernetes 裏面獲取(GET)指定的 API 對象。能夠看到,在這裏我還加上了一個 -l 參數,即獲取全部匹配 app=nginx 標籤的 Pod。須要注意的是,在命令行中,全部 key-value 格式的參數,都使用「=」而非「:」表示。 從這條指令返回的結果中,咱們能夠看到如今有兩個 Pod 處於 Running 狀態,也就意味着咱們這個 Deployment 所管理的 Pod 都處於預期的狀態。

此外, 你還可使用 kubectl describe 命令,查看一個 API 對象的細節,好比:

kubectl describe pods nginx-deployment-5c678cfb6d-44pjt

詳細的輸出信息以下

Name:               nginx-deployment-5c678cfb6d-44pjt
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               localhost.localdomain/10.104.204.21
Start Time:         Mon, 18 Mar 2019 01:33:58 -0400
Labels:             app=nginx
                    pod-template-hash=1723479628
Annotations:        <none>
Status:             Running
IP:                 10.32.0.13
Controlled By:      ReplicaSet/nginx-deployment-5c678cfb6d
Containers:
  nginx:
    Container ID:   docker://8b93348544c4407829c670040e32dd5613e16037ad4ed444767bc7dd3dbbcc43
    Image:          nginx:1.8
    Image ID:       docker-pullable://nginx@sha256:c97ee70c4048fe79765f7c2ec0931957c2898f47400128f4f3640d0ae5d60d10
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 18 Mar 2019 01:33:58 -0400
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from nginx-vol (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-9mzlh (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  nginx-vol:
    Type:    EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:  
  default-token-9mzlh:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-9mzlh
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From                            Message
  ----    ------     ----  ----                            -------
  Normal  Scheduled  35m   default-scheduler               Successfully assigned default/nginx-deployment-5c678cfb6d-44pjt to localhost.localdomain
  Normal  Pulled     35m   kubelet, localhost.localdomain  Container image "nginx:1.8" already present on machine
  Normal  Created    35m   kubelet, localhost.localdomain  Created container
  Normal  Started    35m   kubelet, localhost.localdomain  Started container

 

在 kubectl describe 命令返回的結果中,你能夠清楚地看到這個 Pod 的詳細信息,好比它的 IP 地址等等。其中,有一個部分值得你特別關注,它就是Events(事件)。

在 Kubernetes 執行的過程當中,對 API 對象的全部重要操做,都會被記錄在這個對象的 Events 裏,而且顯示在 kubectl describe 指令返回的結果中。

好比,對於這個 Pod,咱們能夠看到它被建立以後,被調度器調度(Successfully assigned)到了 node-1,拉取了指定的鏡像(pulling image),而後啓動了 Pod 裏定義的容器(Started container)。

因此,這個部分正是咱們未來進行 Debug 的重要依據。若是有異常發生,你必定要第一時間查看這些Events,每每能夠看到很是詳細的錯誤信息。

對nginx應用進行升級

接下來,若是咱們要對這個 Nginx 服務進行升級,把它的鏡像版本從 1.7.9 升級爲 1.8,要怎麼作呢?

很簡單,咱們只要修改這個 YAML 文件便可。

...    
    spec:
      containers:
      - name: nginx
        image: nginx:1.8 # 這裏被從 1.7.9 修改成 1.8
        ports:
      - containerPort: 80

  

但是,這個修改目前只發生在本地,如何讓這個更新在 Kubernetes 裏也生效呢? 咱們可使用+kubectl+replace+指令來完成這個更新:

kubectl replace -f nginx-deployment.yaml

不過,在本專欄裏,我推薦你使用 kubectl apply 命令,來統一進行 Kubernetes 對象的建立和更新操做,具體作法以下所示:  

kubectl apply -f nginx-deployment.yaml

# 修改 nginx-deployment.yaml 的內容

kubectl apply -f nginx-deployment.yaml

  

這樣的操做方法,是 Kubernetes「聲明式 API」所推薦的使用方法。也就是說,做爲用戶,你沒必要關心當前的操做是建立,仍是更新,你執行的命令始終是 kubectl apply,而 Kubernetes 則會根據 YAML 文件的內容變化,自動進行具體的處理。

而這個流程的好處是,它有助於幫助開發和運維人員,圍繞着能夠版本化管理的 YAML 文件,而不是「行蹤不定」的命令行進行協做,從而大大下降開發人員和運維人員之間的溝通成本。

舉個例子,一位開發人員開發好一個應用,製做好了容器鏡像。那麼他就能夠在應用的發佈目錄裏附帶上一個 Deployment 的 YAML 文件。

而運維人員,拿到這個應用的發佈目錄後,就能夠直接用這個 YAML 文件執行 kubectl apply 操做把它運行起來。

這時候,若是開發人員修改了應用,生成了新的發佈內容,那麼這個 YAML 文件,也就須要被修改,而且成爲此次變動的一部分。

而接下來,運維人員可使用 git diff 命令查看到這個 YAML 文件自己的變化,而後繼續用 kubectl apply 命令更新這個應用。

因此說,若是經過容器鏡像,咱們可以保證應用自己在開發與部署環境裏的一致性的話,那麼如今,Kubernetes 項目經過這些 YAML 文件,就保證了應用的「部署參數」在開發與部署環境中的一致性。

而當應用自己發生變化時,開發人員和運維人員能夠依靠容器鏡像來進行同步;當應用部署參數發生變化時,這些 YAML 文件就是他們相互溝通和信任的媒介。

以上,就是 Kubernetes 發佈應用的最基本操做了。

接下來,咱們再在這個 Deployment 中嘗試聲明一個 Volume。在 Kubernetes 中,Volume 是屬於 Pod 對象的一部分。因此,咱們就須要修改這個 YAML 文件裏的 template.spec字段,以下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.8
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: nginx-vol
      volumes:
      - name: nginx-vol
        emptyDir: {}

 

能夠看到,咱們在 Deployment 的 Pod 模板部分添加了一個 volumes 字段,定義了這個 Pod 聲明的全部 Volume。它的名字叫做 nginx-vol,類型是 emptyDir。

那什麼是 emptyDir 類型呢?

它其實就等同於咱們以前講過的 Docker 的隱式 Volume 參數,即:不顯式聲明宿主機目錄的 Volume。因此,Kubernetes 也會在宿主機上建立一個臨時目錄,這個目錄未來就會被綁定掛載到容器所聲明的 Volume 目錄上。

備註:不難看到,Kubernetes 的 emptyDir 類型,只是把 Kubernetes 建立的臨時目錄做爲 Volume 的宿主機目錄,交給了 Docker。這麼作的緣由,是 Kubernetes 不想依賴 Docker 本身建立的那個 _data+目錄。

而 Pod 中的容器,使用的是 volumeMounts 字段來聲明本身要掛載哪一個 Volume,並經過 mountPath 字段來定義容器內的 Volume 目錄,好比:/share/nginx/html。

固然,Kubernetes 也提供了顯式的 Volume 定義,它叫作 hostPath。好比下面的這個 YAML 文件:

 ...   
    volumes:
      - name: nginx-vol
        hostPath: 
          path: /var/data

  

這樣,容器 Volume 掛載的宿主機目錄,就變成了 /var/data。 在上述修改完成後,咱們仍是使用 kubectl apply 指令,更新這個 Deployment:

kubectl apply -f nginx-deployment.yaml

接下來,你能夠經過 kubectl get 指令,查看兩個 Pod 被逐一更新的過程:  

kubectl get pods
  • NAME READY STATUS RESTARTS AGE
  • nginx-deployment-5c678cfb6d-v5dlh 0/1 ContainerCreating 0 4s
  • nginx-deployment-67594d6bf6-9gdvr 1/1 Running 0 10m
  • nginx-deployment-67594d6bf6-v6j7w 1/1 Running 0 10m
kubectl get pods
  • NAME READY STATUS RESTARTS AGE
  • nginx-deployment-5c678cfb6d-lg9lw 1/1 Running 0 8s
  • nginx-deployment-5c678cfb6d-v5dlh 1/1 Running 0 19s

從返回結果中,咱們能夠看到,新舊兩個 Pod,被交替建立、刪除,最後剩下的就是新版本的 Pod。這個滾動更新的過程,我也會在後續進行詳細的講解。

而後,你可使用 kubectl describe 查看一下最新的 Pod,就會發現 Volume 的信息已經出如今了 Container 描述部分:

...
Containers:
  nginx:
    Container ID:   docker://07b4f89248791c2aa47787e3da3cc94b48576cd173018356a6ec8db2b6041343
    Image:          nginx:1.8
    ...
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from nginx-vol (rw)
...
Volumes:
  nginx-vol:
    Type:    EmptyDir (a temporary directory that shares a pod's lifetime)

  

 最後,你還可使用+kubectl+exec+指令,進入到這個+Pod+當中(即容器的+Namespace+中)查看這個+Volume+目錄:

kubectl exec -it nginx-deployment-5c678cfb6d-lg9lw -- /bin/bash
ls /usr/share/nginx/html
exit

  

此外,你想要從 Kubernetes 集羣中刪除這個 Nginx Deployment 的話,直接執行:

kubectl delete -f nginx-deployment.yaml

  

備忘 

部署和更新一個pod(應用)

kubectl apply -f ****.yaml

  

查看一個具體應用的名字和咱們預期的是否一致

kubectl get pods -l app=name
# 如
kubectl get pods -l app=nginx

 

查看一個 API 對象的細節,好比:  

kubectl describe pods nginx-deployment-5c678cfb6d-44pjt

此外,你想要從 Kubernetes 集羣中刪除這個 Nginx Deployment 的話,直接執行:  

kubectl delete -f nginx-deployment.yaml
相關文章
相關標籤/搜索