iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0

本文目標:

  • 認識 Kubernetes
  • 介紹幾個 Kubernetes 的建置方案
  • 簡單的 Kubernetes 使用教學

Recap:什麼是 Image、什麼又是 Pod?

在真正了解 Kubernetes 之前,我們必須定義一下什麼是 image:

  • Image 是一個囊括依賴套件與設定檔的套件(它是靜態的,而非處於執行狀態的程式)

而 Pod 是 K8s 中的最小單位,它可以包含一個以上的 Container,但實務上通常只會包含一個 Container:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: bastion
spec:
  containers:
    - name: centos
      image: centos:latest
      command:
      - sh
      - -c
      - while true; do sleep 60; done
  • 上方程式碼描述了一個運作著 centos 的 pod,它會運作在 default 命名空間。
  • Kubernetes 的物件皆可以使用 yaml 檔案描述,提供一個通用、標準化的開發方式,這麼做還能夠降低物件與物件之間的耦合度。

什麼是 Kubernetes?


Kubernetes(或是 K8s)用於管理、部署、擴充容器化的應用程式,該專案最初由 Google 設計,並捐贈給 CNCF 維護。
本篇文章並不會詳細的介紹如何建構 Kubernetes 環境或是基於 Kubernetes 的應用程式,而是盡可能的加深讀者對於 Kubernetes 的認知。

架構圖

Kubernetes 可以由多個 worker node 組合成一個 cluster,為部署在 Kubernetes 上的服務增加保障,下圖是 Kubernetes 的架構圖:

image

上圖取自 Kubernetes 官方文件

參考上圖,我們可以將它拆成兩個面向來看:

  • Master node (control plane)
  • Worker node

Master node 負責調度所有的 worker node,在 Master node 上會運作四個非常重要的 process,它們分別是:

  1. API server
  2. etcd
  3. Controller
  4. Scheduler

API server

API server 是整個 Kubernetes cluster 的入口點,它負責處理來自 client(使用 kubectl)或是 kubelet 的請求。

etcd

etcd 是一個分散式的資料庫專案,它使用 raft 演算法作為共識機制,能夠保證分散式節點上的資料一致性。

  • Kubernetes 使用它存放最重要的資訊
  • 如果有 Pod 故障,它會以 etcd 上存放的資料復原 Pod
  • etcd 是 key/value based database
  • 被 Hyperledger fabric 採納

Controller

Kubernetes 的內部會有多個 controller,它們分別負責不同的任務。

  • 當使用者使用 kubectl,相關命令會改變 etcd 所儲存的內容,而非直接影響 kubernetes 的狀態。
  • Controller 負責觀察這些狀態的變化,並且以最快的速度響應這些變化

Scheduler

Scheduler 負責管理哪些 Pod 能夠獲得 cpu 資源。

  • 當它收到來自 API server 的請求,如果有 Pod 需要分配到資源,它會請求目標 target node 上的 kubelet 執行這個 Pod。

Worker node 可以是實體機器或是虛擬機器,它是應用程式的執行者,每個 Worker node 會運作:

  1. Container runtime
  2. kubelet
  3. kube-proxy

Container runtime

  • Container runtime 運作在每一個工作節點上
  • Container runtime 負責執行 container

kubelet

  • kubelet 負責節點上 pod 的排程
  • kubelet 能夠與 container 以及 worker node 溝通

kube-proxy

  • kube-proxy 同樣在每個節點上工作
  • 負責流量的轉發:外部流量或是 pod 之間的流量該導向哪(也就是 service 物件)
  • 底層使用 iptables 實現轉發功能,所以在效能上還有進步空間(像是利用先前介紹的 xdp 技術)

建置 K8s

目前市面上有多種建置 K8s 的方法,開發者可以根據不同的使用需求或是預算考量選擇一個最佳的方案。

公有雲

目前 GCP、AWS、Azure 都有提供 K8s cluster service,如果服務不到非常龐大,使用公有雲算是個不錯的選擇(不用花錢花時間維護設備,簡易的服務也不會燒太多錢)。

本地部署

如果是選擇在本地環境架設 Kubernetes,那麼就有非常多種選擇了:

  • Kubeadm
    由 Kubernetes 團隊維護的方案,設定多節點環境時會比較複雜,也需要自己處理 CNI。
  • K3D
    K3S(輕量化的 K8s)的 docker 版本,不需要自己處理 CNI,建置多節點也只需要透過指令就能做到。
  • Minikube
    透過 VM 架設 K8s,同樣不需要處理 CNI,並且 minikube 整合了許多功能包,算是入門 kubernetes 的好選擇。
  • KIND (Kubernetes In Docker)
    KIND 顧名思義是將 Kubernetes 以 container 的形式運作起來,比起 Minikube 它有更高的靈活性(建立多節點叢集),並且不需要自己處理 CNI。

筆者的話:
在這邊想要抱怨一下 kubernetes 的命名想法...筆者當初入門的時候一度搞不清楚 kubeadm、kubectl、kubelet 之間的關係,有時候建置方案的選擇太多對新手來說可能也是一種困擾啊...

如何使用 Kubernetes?

image

圖片來源:Kubernetes — Objects (Resources/Kinds) Overview

使用 kubectl apply 建立物件

kubectl apply -f YOUR_POD.yaml

kubectl apply 屬於 Declarative Management,我們可以使用它動態的調整先前已經套用的設定。
間單來說,如果我們已經建立好一個 Pod,並且我們希望修改它的部分設定,那我們可以再撰寫一個 yaml 檔案說明清楚想要調整的欄位後再使用 kubectl apply

使用 kubectl create 建立物件

kubectl create -f YOUR_POD.yaml

kubectl create 屬於 Imperative Management,使用 kubectl create 修改 Pod 的設定時,它會先刪除現有的全部設定再建立新的,因此,如果重複的使用 kubectl create 套件同一份 yaml 檔案會得到失敗的結果。

Pod 的溝通方式

主要有三種:

  1. 使用 shared volumes
  2. 使用 IPC (Inter-process communications)
  3. 使用 Network

補充一:Namespace

namespace 是一個用來組織、隔離 Cluster 資源的機制:

  1. 使用 RoleBindings 做到分隔租戶以及授權存取
  2. 分割出多個虛擬的 sub-clusters
  3. 執行資源管理策略,可以使用 ResourceQuatas 或是 LimitRanges 實現
  4. 使用 NetworkPolicy 區分網路環境

建立 namespace 的方法:

kubectl create namespace ian

建立一個名為 ian 的 namespace。

取得當前 cluster 的所有 namespaces:

kubectl get namespace

指定某個 namespace 為預設的 namespace:

kubectl config set-context -current -namespace=ian

補充二:Pod 資源管理

  • Request 代表請求的最小資源
  • Limit 代表最大上限

如果一個 pod 要求 500m CPU,並且主機有 2 CPU,那麼這一個 pod 可以獲得 2 CPU 的硬體資源

補充三:Labels & Selector

Labels 是一對可辨識的 key/value 形式的標籤,像是:

  • "release": "GA"
  • "env": "production"
  • "stage": "dev"

等等,key 與 value 可以由我們自由定義。
如果將多個 Label 貼上 Pod,我們就可以清楚地知道這個 Pod 的狀態、功能、開發進度...等細節。

此外,K8S 更是提供了 Label selectors 幫助我們根據 label 關鍵字找到我們想要使用的 Pod:

$ kubectl get pods -l environment=production,tier=frontend

或是使用 set-based 的方式尋找:

kubectl get pods -l 'environment in (production, qa),environment notin (frontend)'

Kubernetes 提供了許多物件,像是 Pod、Service、ReplicaSet、Deployment,為了降低物件之間的耦合性,它們在管理資源皆是使用 label 與 selector 做到的,比如說:
假設我撰寫了一份 replicaSet 規格文件:

# Source: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

當我使用 kubectl apply -f rs.yaml 後,相關的 controller 會去追蹤符合 tier=frontend 條件的 pod 的數量是否為 3,如果不是,Kubernetes 會為我們控制它的數量。

更多使用方法可以參考 kubernetes.io/docs/

補充三:Annotations

前面提到的 Labels 是具有識別用途的標籤,相對的,K8S 也提供了不具識別用途的標籤 Annotations,我們可以在 Annotations 上面打上開發者的聯絡方式或是其他方便開發者使用的資訊。
最後還是要強調,Annotations 與 Labels 最大的差異就是 Annotations 並不會被 K8S 使用

apiVersion: v1
kind: Pod
metadata:
  namespace: ian
  name: nginx-1
  labels:
    app: nginx
  annotations:
    version: latest
    contact: ychen.desl@gmail.com
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
      - containerPort: 80

為 Pod 打上標籤後,可以使用:

$ kubectl describe pods YOUR_POD

查看 Pod 的細節。

Deployment

a Deployment is a higher-level concept that manages ReplicaSets and provides declarative updates to Pods along with a lot of other useful features.
-- Kubernetes 官方文件

在 Kubernetes 中,Deployment、ReplicaSet 與 Pod 之間的關係是很緊密的,在介紹 Deployment 之前我們先來看看 ReplicaSet 是什麼:

# https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

上方是一個 ReplicaSet 的範例程式碼,它會利用 selector 選擇符合目標的 Pod,並且關注 Pod 的數量,如果 Pod 的數量低於 ReplicaSet 的期望值,那麼 Kubernetes 便會為我們擴展 Pod 的數量。

上面的範例可以讓我們了解一件事情:ReplicaSet 是一個用於管理 Pod 的 Kubernetes 物件。
而 Deployment 物件則是幫助我們管理 ReplicaSet 物件的物件,透過建立 Deployment 我們可以觀察到一些有趣的事情:

  • Deployment 會為我們建立 ReplicaSet
  • 由 Deployment 建立的 ReplicaSet 又會為我們建立 Pod

那麼問題來了:看起來 Deployment 只是幫助我們建立了 ReplicaSet,那麼 Deployment 物件有很重要嗎?
Deployment 最大的用途是幫助開發者做到滾動式的更新,滾動式更新是一種能夠不間斷服務的更新方式。在更新 Deployment 的期間,它會建立兩個 ReplicaSet(新版本與舊版本),並且根據使用者的設定逐步的替換舊有的 Pod。
此外,如果在更新後發現新版本不如之前穩定,我們也可以利用 Kubernetes 的紀錄幫助我們 rollout 到舊版本的 Deployment。

Deployment policy

我們可以將 Deployment policy 分為兩大類:

  • Recreate:將舊有的 ReplicaSet 中的 replica 歸零,並且建立新版本的 ReplicaSet。這樣的做法對於系統的效能要求是最小的,但卻有一個致命的缺點:服務會間斷
  • Rolling update:滾動式的更新會逐步的替換舊有的 Pod,直到舊有的 Pod 數量為 0。

要實現這兩種策略,我們需要仰賴 maxUnavailable 或是 maxSurge

  • maxUnavailable:代表最大的不可用 Pod 數量,它的 value 可以是絕對值或百分比,如果 value 為 1,那麼 K8s 對 Pod 逐一汰換(關掉一個後更新一個)。如果是設定為百分比,那麼公式為 replica of ReplicaSet X maxUnavailable。假設 replica 為 10maxUnavailable20%,那麼 K8s 每次都會為我們更新兩個 Pod。
  • maxSurge:它的工作原理會與 maxUnavailable 有些微的不同,使用它進行更新時,它會先建立新版本的 Pod,等到確定新版本的 Pod 建立完成,再關閉對數量的舊版本 Pod。舉例來說:如果 replica 為 10maxSurge20%,那麼 Pod 總數的變化會是 10(10 個舊版本) -> 12(2 個新版本加上 10 個舊版本) -> 10(2 個新版本加上 8 個舊版本)...以此類推。

作者補充:
這篇文章介紹了如何進行 rolling update 以及 rollout,有興趣的朋友們可以參考看看!

新增 Deployment

新增 Deployment 主要可以透過兩個方式:1. apply yaml2. kubectl run

先來談談第一個方式,我們先建立一個描述 Deployment 的 yaml file:

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

上面的範例出自 Kubernetes 的官方文件,它是一個用於部署 Nginx 的 deployment。
有了這份檔案後,使用 kubectl apply -f <YOUR_FILE_NAME>.yaml 就可以幫助我們建立資源啦!

第二種方式不需要預先建立檔案,而是用一行命令搞定:

kubectl run web-depl --image="<image>" --replicas=2 --labels="ver=1, app=web, env=production"

移除 Deployment

移除所有 Deployment:

kubectl delete deployment --all

或是使用 selector 指定刪除符合條件的 Deployment:

kubectl delete deployment --selector="<cond>"

如何重啟 Pod?

Kubernetes 並沒有像 Docker 一樣能夠重啟 Container 的機制,如果 Pod 發生問題,那麼就只有將 Pod 刪除再重建這個方法。不過,這麼做可能會帶來一些問題:

  • 如果沒有保留 Pod 的描述檔案,要怎麼重建?
  • 如果我有很多一樣的 Pod,難道要一個一個手動刪除嗎?

為了解決這個問題,我們可以利用 scale Deployment 達到這個目的:

kubectl scale deployment <DEPLOYMENT> --replicas=0 -n <NAME_SPACE>
kubectl scale deployment <DEPLOYMENT> --replicas=<EXPECTED_NUM> -n <NAME_SPACE>

Service

因為 Pods 會被 K8S 動態的控制,所以具有 volatile 的特性,為了讓服務實體可以穩定的將流量或是請求導向它們,我們會需要 Service 的幫忙。

Service 的種類

Type1: ClusterIP

ClusterIP 類型的 Service 只有 Cluster 內部的成員可見,這可以用於保護某些特定的 Service 不被外網存取

Type2: LoadBalancer

LoadBalancer 類型的 Service 就是一個 load balancer,外部成員可以透過存取這類的 Service 取得想要的服務。
如果是在 GKE 上使用 LoadBalancer,GKE 會為它分配一個 IP Address 並將流量導向該服務。

Type3: NodePort

NodePort 類型的 Service 比 ClusterIP 多了指定端口,若不指定端口則會交給 K8S 去處理。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80

Ingress

我們可以把 Ingress 想像成一個讓 Service 對外的工具,以上面的案例來看 Ingress 更像是 Service 的 Load balancer。
在預設情況下,Kubernetes 並不會安裝 Ingress,它就像是 CNI 一樣需要交給使用者自行挑選、安裝。

總結

這篇文章快速的帶大家認識 Kubernetes 以及基本的使用介紹,不過 Kubernetes 其實是一個相當複雜的工具,所以有很多概念(configMapsecretRBAC)以及底層的知識(CNICRICSI)是沒有在這篇文章提到的,還請讀者們見諒。
明天的文章會注重在如何建構一個 Kubernetes 環境,並且介紹 Helm 這套用於管理 Kubernetes 資源的工具,最後再以 free5GC 的衍生專案為例安裝到我們的 Kubernetes Cluster 上。

References


上一篇
使用 Prometheus 結合 free5GC 實作告警系統
下一篇
使用 Helm 管理 Kubernetes 資源
系列文
5G 核心網路與雲原生開發之亂彈阿翔36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言