Kubernetes 初體驗 By minikube

August 27, 2018

Photo by Taelynn Christopher on Unsplash

前言

本篇文章是初學者(我)對於探索 Kubernetes 的記錄,包含相關名詞的認識、如何與 Kuberentes 互動,過程中查詢了許多的相關知識,都附在文末的 Reference 部分。文章內容若有任何的(理解)錯誤,請麻煩告知我改正,謝謝!🙏🏻

文章內容稍長,一開始會先介紹如何安裝 Kubernetes 叢集,接著再介紹 Kubernetes 一些常見的名詞,文章最後再透過指令簡單的與 Kubernetes 互動。

🐳 minikube 安裝

Logo

minikube 是 Kubernetes 輕量化的實作,它會在你本機的 VM 內建立並且執行一個單一節點的 Kubernetes 叢集。

請按照以下說明,安裝所需要的相關軟體

  • 📝 minikube 支援 macOS、Linux、Windows 平台,使用 minikube 之前你必須安裝好 VM 環境,若你的電腦沒有安裝任何的 VM 環境,可以使用 VirtualBox 作為你的 VM 環境,請根據你的作業系統下載對應的版本。

  • 📝 kubectl 可以用來方便操作 Kubernets 的 API,若是使用 macOS 可以直接透過 Homebrew 安裝:

$ brew install kubernetes-cli

$ kubectl version

或者可以透過 curl 來安裝 kubectl 的 binary 執行檔:

# latest release

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl

$ chmod +x ./kubectl

$ sudo mv ./kubectl /usr/local/bin/kubectl

kubectl 其他安裝方式,請參考此連結

  • 📝 minikube 安裝(macOS 作為範例):
$ brew cask install minikube

其他作業系統安裝 minikube 方式,請參考官方文件

以上三個步驟 📝 做完後,就完成了 minikube 的安裝,可以在終端機執行 kubectlminikube 測試一下是否安裝完成:

  • kubectl
kubectl
  • minikube
minikube

使用 minikube 執行以下指令把叢集跑起來:

$ minikube start

可以打開 Dashboard GUI 讓你可以概觀目前整個叢集的狀況:

$ minikube dashboard
overview

到這邊我們就完成 minikube 的安裝,同時也在本機電腦上執行了一個叢集。

📖 名詞解釋

當我在讀 Kubernetes 相關文章的時候,對於那些新名詞總是感到疑問,所以覺得在開始之前,先對幾個常用的名詞做些簡單的解釋:

overview
Source: Kubernetes 101: Pods, Nodes, Containers, and Clusters
  • Pod

在 Kubernetes 的世界中,Pod 是最小的單位。在 Pod 內,可以有一個或者是多個 Container,這些 Container 共享相同的資源,例如:Storage、Network,因為使用相同的資源,所以 Container 之間彼此可以方便溝通,但同時也可以保持與其他 Container 的隔離。

Source: Viewing Pods and Nodes
  • Node

Node 在 Kubernetes 上代表工作機器(Worker Machine),它可能是一個虛擬或是實體機器,而 Pod 是執行在 Node 上的,所以一個 Node 可能有一個或者是多個 Pod。

Source: Kubernetes 101: Pods, Nodes, Containers, and Clusters
  • Deployment

Deployment 是描述如何在 Kubernetes 上建立或者是更新你的應用程式,它是一個抽象的概念。透過 Deployment 可以管理 Pod 的 replicas、應用程式的滾動升級(rolling update)、回滾(rollback)。

Source: CoreOS - Overview of a Service
  • Service

Kubernetes 的 Service 是一個抽象化的概念,它主要定義一組邏輯(相同)的 Pod 以及存取它們的方針。

  • Kubernetes Object

在 Kubernetes 中,Kubernetes Object 是一個持久化的實體(persistent entities),Kubernetes 使用這些實體來表示目前整個叢集的狀態。Kubernetes 的 Object 都可以透過 yaml 格式的檔案來描述要如何建立 Object 以及狀態。

來個 Hello Kubernetes

在學習任何新事物前,都不免俗來要個 Hello World 😂,Google 提供了一個簡單的 echoserver Container,接著將這個 Container 服務執行在 minikube 上:

$ kubectl run hello-minikube --image=gcr.io/google_containers/echoserver:1.8 --port=8080
deployment "hello-minikube" created

$ kubectl expose deployment hello-minikube --type=NodePort
service "hello-minikube" exposed

$ kubectl get services
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-minikube   NodePort    10.104.163.46   <none>        8080:32101/TCP   13s
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          19h

解釋一下上面指令所代表的意思:

  • run:建立一個 Deployment 或是 Job 來管理被建立的 Container
  • --image:提供 Image 的來源,例如 Docker Hub 上的 nginx
    • 💡 nginx 是由官方所提供的 Image ,如果要使用自己或是他人的 Image 請使用 user/image 格式
  • --port:指定埠號
  • expose:讓存取 hello-minikube 服務可以被存取
  • get services:取得叢集上的 Service 列表

透過 kubectl get services 可以看到 hello-minikube 已經被建立,接著使用 minikube 來取得網站的 URL:

$ minikube service hello-minikube --url
http://192.168.99.100:32101

打開瀏覽器輸入網址後可以看到一些資訊:

hello minikube

🤟🏻 動手操作

接下來的內容會根據上面名詞解釋來建立對應的 Kubernetes Object。

📦 Pod

  • Pod 是由一個或者是多個 Container 所組成
  • 在 Pod 內會共享 Storage、Network 資源以及配置設定
  • Pod 內的 Container 就像是生命共同體,一旦 Pod 被調度,所有 Pod 內的 Container 也會一起被調度
  • Pod 具有生命週期
  • Pod 在建立後,會擁有一個 Unique ID
  • 每個 Pod 都會與調度它的 Node 綁定,並維持期望的狀態直到終止(根據重啟策略)或刪除,若 Node 發生故障,Pod 會被調度到其他的可用的 Node 上
  • 一個給定的 Pod(UID 被定義)不會被調度到新的 Node 上,而是由一個全新相同而且帶著不同的 UID Pod 取代

建立 Pod YAML 檔案

建立一個資料夾,並建立一個 my-first-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: echo-server
  labels:
    app: echo-server
spec:
  containers:
  - name: echo-server
    image: inanimate/echo-server
    ports:
    - containerPort: 8080

稍微說明 YAML 檔案的內容:

  • kind:說明 Kubernetes Object 是什麼類別
  • metadata:一些詮釋資料
    • name 表示 Pod 名稱
    • labels 實際上是一個 key/value,標籤可以有效的劃分 Pod,未來可以將不同種類的標籤 Pod 做管理
  • spec:定義 Container 的內容

使用 kubectl 來建立 Pod:

$ kubect apply -f my-pod.yaml
pod/echo-server created

$ kubectl get pods -o wide
NAME                                READY     STATUS              RESTARTS   AGE       IP           NODE
echo-server                         0/1       ContainerCreating   0          6s        <none>       minikube
hello-deployment-7f97bc8897-qzfn2   1/1       Running             4          1d        172.17.0.5   minikube
hello-minikube-5b8fc68cc9-qvpxj     1/1       Running             5          2d        172.17.0.4   minikube

這時候可以看到 Pod 的狀態是正在被建立當中,過一下可以看到已經建立完成:

$ kubectl get pods -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP           NODE
echo-server                         1/1       Running   0          46s       172.17.0.6   minikube
hello-deployment-7f97bc8897-qzfn2   1/1       Running   4          1d        172.17.0.5   minikube
hello-minikube-5b8fc68cc9-qvpxj     1/1       Running   5          2d        172.17.0.4   minikube

minikube 是單節點的叢集,所以在相同叢集內的 Pod 彼此可以存取。從上面的結果可以看到目前 echo-server 的 IP 是 172.17.0.6,我們再開一個測試的 Pod 來測試 echo-server 是否正常運作:

$ kubectl -n default run curl-pod --image=radial/busyboxplus:curl -i --tty --rm

我們開另外一個 Terminal tab 視窗看一下 Pod 的狀態:

$ kubectl get pods -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP           NODE
curl-pod-6f979c484f-hx7gn           1/1       Running   0          1m        172.17.0.7   minikube
echo-server                         1/1       Running   0          2h        172.17.0.6   minikube
hello-deployment-7f97bc8897-qzfn2   1/1       Running   4          1d        172.17.0.5   minikube
hello-minikube-5b8fc68cc9-qvpxj     1/1       Running   5          2d        172.17.0.4   minikube

可以看到剛剛建立的 curl-pod 已經好了,接著再切回原本執行 curl-pod 的視窗執行:

[ [email protected]:/ ]$ curl 172.17.0.6:8080
Welcome to echo-server!  Here's what I know.
  > Head to /ws for interactive websocket echo!

-> My hostname is: echo-server

-> Requesting IP: 172.17.0.7:55678

-> Request Headers |

  HTTP/1.1 GET /

  Host: 172.17.0.6:8080
  Accept: */*
  User-Agent: curl/7.35.0


-> Response Headers |

  Content-Type: text/plain
  X-Real-Server: echo-server

  > Note that you may also see "Transfer-Encoding" and "Date"!


-> ...

由輸出結果可以看到:

Requesting IP: 172.17.0.7:55678 ...

echo-server 收到了來自 curl-pod 所發出的請求,以上就是建立一個簡單 Pod 的範例。

💠 Node

  • 在 Kubernetes 叢集中,Node 可能是一個虛擬或是實體的主機
  • 每個 Node 上都會有 kubelet,它是一個管理機器上的 Pod 並負責與 Kubernetes Master 溝通的代理
  • 每個 Node 都會執行一個 Container 的 Runtime,主要是拉取 Image 並執行 Container
  • 每個 Node 都會執行一個 kube-proxy,它是對 iptables 的規則進行規劃來取得對 Service IP 的存取,並且將它們重導(Redirect)到正確的後端位置

📍 Replication Controller 和 ReplicaSet

  • Replication Controller(簡稱 rc),主要是用來確保 Pod 的 replicas(副本)維持在使用者所定義的副本數,透過 Replication Controller 可以一次建立多個相同的 Pod,若 Pod 出現 Crash 或 Failure 而無法提供服務時,Replication Controller 會自動的砍掉無法正常執行的 Pod,並且重新建立一個新的 Pod 來維持 replicas 的數量

  • ReplicaSet 和 Replication Controller 本質上是相同的,差別在於 ReplicaSet 支援集合式的 selector,而 Replication Controller 只支援等式的 selector

建立 Replication Controller YAML 檔案

接下來以 nginx 作為範例,請建立一個 my-first-rc.yaml 檔案內容如下:

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

使用 kubectl 來建立 Replication Controller:

$ kubectl apply -f my-first-rc.yaml
replicationcontroller/nginx created

確認 Replication Controller:

$ kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
nginx     3         3         3         1m

使用 kubectl get pods -o wide 可以看到三個 nginx 的 Pod:

NAME                              READY     STATUS    RESTARTS   AGE       IP           NODE
curl-pod-6f979c484f-hx7gn         1/1       Running   7          3d        172.17.0.3   minikube
hello-minikube-5b8fc68cc9-qvpxj   1/1       Running   11         5d        172.17.0.5   minikube
nginx-2hb8r                       1/1       Running   0          1m        172.17.0.8   minikube
nginx-4rwv5                       1/1       Running   0          1m        172.17.0.7   minikube
nginx-7fjvs                       1/1       Running   0          1m        172.17.0.6   minikube

在前面我們建立了 curl-pod,我們使用它來檢查 nginx 服務是否正常,首先:

$ kubectl attach curl-pod-6f979c484f-hx7gn -c curl-pod -i -t
If you don't see a command prompt, try pressing enter.
[ [email protected]:/ ]$

接著請選擇其中一個 nginx 的 Pod 來測試:

[ [email protected]:/ ]$ curl 172.17.0.7
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

啊!是熟悉的畫面內容呀(當然在 Terminal 上是看不到的啊! 😂)

擴展和刪除

擴展(Scale)

若要擴展 Replication Controller 物件的副本的數量,可以透過以下指令:

$ kubectl scale --replicas=5 rc/nginx
replicationcontroller/nginx scaled

現在 nginx 的副本數量為 5

$ kubectl get pods -o wide
NAME                              READY     STATUS    RESTARTS   AGE       IP            NODE
curl-pod-6f979c484f-hx7gn         1/1       Running   7          3d        172.17.0.3    minikube
hello-minikube-5b8fc68cc9-qvpxj   1/1       Running   11         5d        172.17.0.5    minikube
nginx-2hb8r                       1/1       Running   0          3m        172.17.0.8    minikube
nginx-4rwv5                       1/1       Running   0          3m        172.17.0.7    minikube
nginx-7fjvs                       1/1       Running   0          3m        172.17.0.6    minikube
nginx-m8hqv                       1/1       Running   0          11s       172.17.0.10   minikube
nginx-wdk7p                       1/1       Running   0          11s       172.17.0.9    minikube

可以看到 nginx 的 Pod 已經 scale 到我們所指定的數量了,透過 scale 指令可以彈性的調整 Pod 的數量。

刪除(Delete)

刪除 Replication Controller 時,請注意你的 Pod 也會隨著 rc 的刪除跟著被刪除,延續上面範例,將剛剛建立的 rc 刪除:

$ kubectl delete rc nginx
replicationcontroller "nginx" deleted
$ kubectl get pods -o wide
NAME                              READY     STATUS        RESTARTS   AGE       IP            NODE
curl-pod-6f979c484f-hx7gn         1/1       Running       7          3d        172.17.0.3    minikube
hello-minikube-5b8fc68cc9-qvpxj   1/1       Running       11         6d        172.17.0.5    minikube
nginx-2hb8r                       0/1       Terminating   0          14m       172.17.0.8    minikube
nginx-4rwv5                       0/1       Terminating   0          14m       172.17.0.7    minikube
nginx-7fjvs                       0/1       Terminating   0          14m       172.17.0.6    minikube
nginx-m8hqv                       0/1       Terminating   0          11m       172.17.0.10   minikube
nginx-wdk7p                       0/1       Terminating   0          11m       172.17.0.9    minikube

可以注意到 nginx 的 Pod 狀態變成 Terminating,最後會被完全刪除。

Replication Controller 雖然可以容易的擴展和刪除 Pod,但實務上還是有些不方便的地方,以上面範例來說,如果要升級 nginx 的版本,就需要額外再建立新的設定檔案來進行升級,更多詳細資訊可以參考 Perform Rolling Update Using a Replication Controller,而官方推薦的做法是使用 Deployment,除了具備原先 Replication Controller 的功能,也可以更容易的完成滾動升級。

🛠 Deployment

  • Deployment 提供了宣告式的方式來更新 Pod 和 ReplicaSet
  • 使用 Deployment 會建立 ReplicaSet,而 ReplicaSet 會在背景繼續建立所需的 Pod,所以你不應該手動的去管理由 Deployment 所建立的 ReplicaSet
  • 可以把 Deployment 是新一代的 Replication Controller,它除了具備 Replication Controller 的功能外,還多了以下的功能:
    • 版本記錄:每次對於 Deployment 的操作都會被記錄下來(ReplicaSet)
    • 回滾:若升級後遇到問題可以 rollback 到先前的版本
    • 暫停和啟動:更新 Deployment 時,可以隨時地暫停和恢復
    • 狀態查詢:查詢 Deployment 的升級狀態與詳細進度

建立 Deployment YAML 檔案

這裡使用我自己寫的 docker-fiveN1 作為範例,docker-fiveN1 是一個簡單的 591 租屋網的爬蟲 API。

請建立一個 my-first-deployment.yaml 檔案:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fiven1-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fiven1-backend
  template:
    metadata:
      labels:
        app: fiven1-backend
    spec:
      containers:
        - name: fiven1-pod
          image: neighborhood999/fiven1-backend:1.2.0
          ports:
            - containerPort: 8000

從上面 spec 部分說明建立了一個 ReplicaSet,而這個 ReplicaSet 會建立三個 fiven1-backend 副本。

使用 kubectl 來建立 Deployment:

$ kubectl apply -f my-first-deployment.yaml
deployment.apps/fiven1-deployment created
$ kubectl get deployments
NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
curl-pod            1         1         1            1           4d
fiven1-deployment   3         3         3            3           4s
hello-minikube      1         1         1            1           7d

接著查看 ReplicaSet 的狀態:

$ kubectl get rs
NAME                           DESIRED   CURRENT   READY     AGE
curl-pod-6f979c484f            1         1         1         4d
fiven1-deployment-68c48d6664   3         3         3         16s
hello-minikube-5b8fc68cc9      1         1         1         7d

最後查看 Pod 的狀態:

$ kubectl get pods --show-labels
NAME                                 READY     STATUS    RESTARTS   AGE       LABELS
curl-pod-6f979c484f-hx7gn            1/1       Running   10         4d        pod-template-hash=2953570409,run=curl-pod
fiven1-deployment-68c48d6664-5wm45   1/1       Running   0          29s       app=fiven1-backend,pod-template-hash=2470482220
fiven1-deployment-68c48d6664-gpvnq   1/1       Running   0          29s       app=fiven1-backend,pod-template-hash=2470482220
fiven1-deployment-68c48d6664-s7s78   1/1       Running   0          29s       app=fiven1-backend,pod-template-hash=2470482220
hello-minikube-5b8fc68cc9-qvpxj      1/1       Running   13         7d        pod-template-hash=1649724775,run=hello-minikube

透過 kubectl get pods --show-labels 可以看到有 3 個 Pod 都帶有 fiven1-backend 的 Label。

使用 curl-pod 來測試一下服務是否正常,如果先前有安裝過 curl-pod 可以查看你的 curl-pod 完整名稱並執行:

$ kubectl attach curl-pod-6f979c484f-hx7gn -c curl-pod -i -t

若沒有安裝該 Pod 請輸入下面指令:

$ kubectl -n default run curl-pod --image=radial/busyboxplus:curl -i --tty --rm

查看一下 Pod 的 IP 位置:

$ kubectl get pods -o wide
NAME                                 READY     STATUS    RESTARTS   AGE       IP           NODE
curl-pod-6f979c484f-hx7gn            1/1       Running   10         4d        172.17.0.4   minikube
fiven1-deployment-68c48d6664-5wm45   1/1       Running   0          56s       172.17.0.6   minikube
fiven1-deployment-68c48d6664-gpvnq   1/1       Running   0          56s       172.17.0.8   minikube
fiven1-deployment-68c48d6664-s7s78   1/1       Running   0          56s       172.17.0.7   minikube
hello-minikube-5b8fc68cc9-qvpxj      1/1       Running   13         7d        172.17.0.2   minikube

172.17.0.7 作為範例來測試,直接使用以下指令測試:

$ curl 172.17.0.7:8000
{
  "1": [
    {
      "preview": "https://hp2.591.com.tw/house/active/2016/06/15/146597739918187302_765x517.water3.jpg",
      "title": "南軟優質大套房!(雙捷運旁) 特價中!",
      "url": "https://rent.591.com.tw/rent-detail-6739629.html",
      "address": "南港區 - 中南街",
      "rentType": "沒有格局說明",
      "optionType": "分租套房",
      "ping": "15",
      "floor": "樓層:頂樓加蓋",
      "price": "17,500 元 / 月",
      "isNew": false
    },
    {
      "preview": "https://hp1.591.com.tw/house/active/2018/08/10/153388789733221608_765x517.water3.jpg",
      "title": "兩房一廳忠孝信義松山松德林口福德永春站",
      "url": "https://rent.591.com.tw/rent-detail-6679039.html",
      "address": "信義區 - 虎林街 242 巷",
      "rentType": "2",
      "optionType": "整層住家",
      "ping": "23",
      "floor": "樓層:2/4",
      "price": "23,012 元 / 月",
      "isNew": false
    }
    ...
}

滾動升級(Rolling Update)

這裡使用指令的方式作為範例來滾動升級 docker Image 的版本,我們把 fiven1-backend 的 Image 升級到 latest 版本,而在指令最後加上 --record 可以記錄每次操作的指令,這樣可以清楚了解 Deployment 不同的版本(revision)之間的操作:

$ kubectl set image deployment/fiven1-deployment fiven1-pod=neighborhood999/fiven1-backend:latest --record
deployment.extensions/fiven1-deployment image updated

查看升級的狀況:

$ kubectl rollout status deployment fiven1-deployment
deployment "fiven1-deployment" successfully rolled out

接著查看一下 Pod 的狀態:

$ kubectl get pods -o wide
NAME                                 READY     STATUS    RESTARTS   AGE       IP           NODE
curl-pod-6f979c484f-hx7gn            1/1       Running   10         4d        172.17.0.4   minikube
fiven1-deployment-54cc9d7954-2qgvx   1/1       Running   0          7m        172.17.0.6   minikube
fiven1-deployment-54cc9d7954-rdfvg   1/1       Running   0          7m        172.17.0.9   minikube
fiven1-deployment-54cc9d7954-xfkn8   1/1       Running   0          7m        172.17.0.8   minikube
hello-minikube-5b8fc68cc9-qvpxj      1/1       Running   13         7d        172.17.0.2   minikube

這邊請注意到 IP 的部分,在上面範例中,我使用 172.17.0.7 作為測試,但是發現 172.17.0.7 不存在了,這裡主要的原因是,在滾動升級時,並不會馬上把舊的 Pod 砍掉,而是建立新的 Pod 來取代舊版本的 Pod

查看 ReplicaSet 的狀態:

$ kubectl get rs
NAME                           DESIRED   CURRENT   READY     AGE
curl-pod-6f979c484f            1         1         1         4d
fiven1-deployment-54cc9d7954   3         3         3         10m
fiven1-deployment-68c48d6664   0         0         0         15m
hello-minikube-5b8fc68cc9      1         1         1         7d

可以看到有兩個 fiven1-deployment,其中 fiven1-deployment-68c48d6664 是沒有在執行的,每次在進行滾動升級時,都會保留舊的 ReplicaSet,未來可以方便的 rollback 到舊的版本。

在進行滾動升級時,除了透過以上的方式,也可以加入一些升級策略,像是 minReadySecondsmaxSurgemaxUnavailable,詳細可以參考這篇文章

回滾(Roll Back)

若當發現升級完之後有些問題,可以透過回滾的方式將應用程式回復到先前的狀態,可以透過以下指令來查詢升級的歷史紀錄:

$ kubectl rollout history deploy fiven1-deployment
deployments "fiven1-deployment"
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image deployment/fiven1-deployment fiven1-pod=neighborhood999/fiven1-backend:latest --record=true

第一筆是建立 Deployment 的時候,第二個則是在升級時,因為有加入的 --record 參數,在 CHANGE-CAUSE 部分就可以清楚看到升級時所下的指定。

接著我們來測試 rollback 到上一筆(剛建立)的狀態:

$ kubectl rollout undo deployment fiven1-deployment
deployment.extensions/fiven1-deployment

查看 ReplicaSet 的狀態:

$ kubectl get rs
NAME                           DESIRED   CURRENT   READY     AGE
curl-pod-6f979c484f            1         1         1         4d
fiven1-deployment-54cc9d7954   0         0         0         48m
fiven1-deployment-68c48d6664   3         3         3         53m
hello-minikube-5b8fc68cc9      1         1         1         7d

對照上面滾動升級的部分,可以看到 54cc9d7954 已經被停止,已經成功回滾到 68c48d6664 的 ReplicaSet。

若要 rollback 到特定版本:

$ kubectl rollout undo deploy <deployment> --to-revision=<revision>

最後查詢歷史紀錄會發現多一筆:

$ kubectl rollout history deploy fiven1-deployment
deployments "fiven1-deployment"
REVISION  CHANGE-CAUSE
2         kubectl set image deployment/fiven1-deployment fiven1-pod=neighborhood999/fiven1-backend:latest --record=true
3         <none>

🚥 Service

  • Service 是一個抽象化的概念,它定義一組邏輯的 Pod 和存取它們的方針
  • Pod 本身可能帶有一個或多個不同的標籤,Service 通常透過 LabelSelector 來選取對應的 Pod 集合
  • Service 有四種 Type:ClusterIPNodePortLoadBalancerExternalName,不同 Type 有各自不同的行為
  • Service 支援 TCP 和 UDF 協定

建立 Service YAML 檔案

這裡將會延續前面 Deployment 的範例,在 Deployment 範例中建立了 fiven1-deployment,這個 Deployment 有三個 fiven1-backend 的副本。

以下將會使用 NodePort type 的 Service 作為範例:

apiVersion: v1
kind: Service
metadata:
  name: fiven1-service
  labels:
    app: fiven1-service
spec:
  type: NodePort
  selector:
    app: fiven1-backend
  ports:
  - port: 8000
    protocol: TCP

spec 部分我們設定 typeNodePort 而且 selector 部分我們選擇了 app=fiven1-backend 的 Pod:

$ kubectl get pods --show-labels
NAME                                 READY     STATUS    RESTARTS   AGE       LABELS
curl-pod-6f979c484f-hx7gn            1/1       Running   24         8d        pod-template-hash=2953570409,run=curl-pod
fiven1-deployment-68c48d6664-jsnjd   1/1       Running   3          3d        app=fiven1-backend,pod-template-hash=2470482220
fiven1-deployment-68c48d6664-kcsrp   1/1       Running   3          3d        app=fiven1-backend,pod-template-hash=2470482220
fiven1-deployment-68c48d6664-kxtp7   1/1       Running   3          3d        app=fiven1-backend,pod-template-hash=2470482220
hello-minikube-5b8fc68cc9-qvpxj      1/1       Running   16         10d       pod-template-hash=1649724775,run=hello-minikube

接著使用 kubectl 來建立 Service:

$ kubectl apply -f my-first-service.yaml
service/fiven1-service created

確認 Service 的狀態:

$ kubectl get services
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
fiven1-service   NodePort    10.110.35.60    <none>        8000:30554/TCP   5m
hello-minikube   NodePort    10.104.163.46   <none>        8080:32101/TCP   9d
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          10d

fiven1-serviceCLUSTER-IP10.110.35.60,我們可以使用 curl-pod 來測試 Service 運作是否正常:

$ [ [email protected]:/ ]$ curl 10.110.35.60:8000
{
  "1": [
    {
      "preview": "https://hp2.591.com.tw/house/active/2017/08/04/150183382555696109_765x517.water3.jpg",
      "title": "雙捷運 * 近馬偕 - 鬧中取靜的舒適套房",
      "url": "https://rent.591.com.tw/rent-detail-6007811.html",
      "address": "中山區 - 中山北路二段 72 巷 12-1 號",
      "rentType": "沒有格局說明",
      "optionType": "獨立套房",
      "ping": "6",
      "floor": "樓層:2/5",
      "price": "17,000 元 / 月",
      "isNew": false
    }
    ...
}

CLUSTER-IP 是 Service 在叢集內的 IP,curl-podfiven1-service 位在同個叢集上,所以可以透過內部網路去存取該服務,若想要從外部(叢集外)存取該服務該怎麼做?

我們先使用以下指令來查看 fiven1-service Service 的詳細資訊:

$ kubectl describe service fiven1-service
Name:                     fiven1-service
Namespace:                default
Labels:                   app=fiven1-service
Annotations:              kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"fiven1-service"},"name":"fiven1-service","namespace":"default"},"spec...
Selector:                 app=fiven1-backend
Type:                     NodePort
IP:                       10.110.35.60
Port:                     <unset>  8000/TCP
TargetPort:               8000/TCP
NodePort:                 <unset>  30554/TCP
Endpoints:                172.17.0.5:8000,172.17.0.6:8000,172.17.0.7:8000
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

可以注意到 NodePort 的部分,它的 Port 為 30554,若要在叢集外存取 Service 服務,可以透過 <NodeIP>:<NodePort> 的方式,而這些請求透過 Service 會被自動地分配至 Endpoints,也就是我們的 Pod。

查詢 minikube 在我們本機上的 IP 位置:

$ minikube ip
192.168.99.100

可以看到 IP 位置為 192.168.99.100,所以可以透過 192.168.99.100:30554 來存取 fiven1-service

$ curl 192.168.99.100:30554

或者可以透過瀏覽器:

screenshot-2018-09-07-11.10.50.png

🤯 結語

學習 Kubernetes 才發現到它涵蓋的知識真的非常的廣,寫到這其實已經花了我不少時間,除了理解 Kubernetes 一些基本的架構和機制外,還有一些比較底層的東西,當然懂的也都還是皮毛,真的是不容易啊!

雖然現在不太有機會用到 Kubernetes,但是多學一些東西也不是壞處啦,希望未來可以把一些還不懂的地方給補起來(多到哭出來)。

🔗 Reference