什麼是 Kubernetes? - ianchen0119/Introduce-to-5GC GitHub Wiki

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

了解 Kubernetes

在真正了解 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 可以由多個 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 技術)

如何使用 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: [email protected]
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
      - containerPort: 80

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

$ kubectl describe pods YOUR_POD

查看 Pod 的細節。

Deployment

  • 部屬多個 Pod 的方式

新增 Deployment

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

移除 Deployment

kubectl delete deployment --all

或是使用 selector:

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

Service

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

image

Service 的種類

Type1: ClusterIP

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

Type2: LoadBalancer

LoadBalancer 類型的 Service 就是一個平衡附載器,外部成員可以透過存取這類的 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

以下 Configure 使用 GKE 產生:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    ingress.kubernetes.io/backends: '{"k8s1-813d351a-lab-nginx-1-80-ed0b083e":"Unknown","k8s1-813d351a-lab-nginx-2-80-1be8d82e":"Unknown"}'
    ingress.kubernetes.io/forwarding-rule: k8s2-fr-1ze89fat-lab-outer-s7si9ddi
    ingress.kubernetes.io/target-proxy: k8s2-tp-1ze89fat-lab-outer-s7si9ddi
    ingress.kubernetes.io/url-map: k8s2-um-1ze89fat-lab-outer-s7si9ddi
  creationTimestamp: "2022-03-16T13:10:50Z"
  finalizers:
  - networking.gke.io/ingress-finalizer-V2
  generation: 1
  managedFields:
  - apiVersion: networking.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:spec:
        f:defaultBackend:
          .: {}
          f:service:
            .: {}
            f:name: {}
            f:port:
              .: {}
              f:number: {}
        f:rules: {}
    manager: GoogleCloudConsole
    operation: Update
    time: "2022-03-16T13:10:50Z"
  - apiVersion: networking.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:ingress.kubernetes.io/backends: {}
          f:ingress.kubernetes.io/forwarding-rule: {}
          f:ingress.kubernetes.io/target-proxy: {}
          f:ingress.kubernetes.io/url-map: {}
        f:finalizers:
          .: {}
          v:"networking.gke.io/ingress-finalizer-V2": {}
      f:status:
        f:loadBalancer:
          f:ingress: {}
    manager: glbc
    operation: Update
    time: "2022-03-16T13:12:05Z"
  name: outer
  namespace: lab
  resourceVersion: "500208"
  uid: 03c27b4d-f3e4-4753-a48a-64abcb638cf8
spec:
  defaultBackend:
    service:
      name: nginx-1
      port:
        number: 80
  rules:
  - http:
      paths:
      - backend:
          service:
            name: nginx-2
            port:
              number: 80
        path: /nginx-2/*
        pathType: ImplementationSpecific
      - backend:
          service:
            name: nginx-1
            port:
              number: 80
        path: /nginx-1/*
        pathType: ImplementationSpecific
status:
  loadBalancer:
    ingress:
    - ip: 34.111.224.185

References

⚠️ **GitHub.com Fallback** ⚠️