什麼是 Kubernetes? - ianchen0119/Introduce-to-5GC GitHub Wiki
Kubernetes(或是 K8s)用於管理、部署、擴充容器化的應用程式,該專案最初由 Google 設計,並捐贈給 CNCF 維護。 本篇文章並不會詳細的介紹如何建構 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 的架構圖:
上圖取自 Kubernetes 官方文件。
參考上圖,我們可以將它拆成兩個面向來看:
- Master node (control plane)
- Worker node
Master node 負責調度所有的 worker node,在 Master node 上會運作四個非常重要的 process,它們分別是:
- API server
- etcd
- Controller
- Scheduler
API server 是整個 Kubernetes cluster 的入口點,它負責處理來自 client(使用 kubectl)或是 kubelet 的請求。
etcd 是一個分散式的資料庫專案,它使用 raft 演算法作為共識機制,能夠保證分散式節點上的資料一致性。
- Kubernetes 使用它存放最重要的資訊
- 如果有 Pod 故障,它會以 etcd 上存放的資料復原 Pod
- etcd 是 key/value based database
- 被 Hyperledger fabric 採納
Kubernetes 的內部會有多個 controller,它們分別負責不同的任務。
- 當使用者使用 kubectl,相關命令會改變 etcd 所儲存的內容,而非直接影響 kubernetes 的狀態。
- Controller 負責觀察這些狀態的變化,並且以最快的速度響應這些變化
Scheduler 負責管理哪些 Pod 能夠獲得 cpu 資源。
- 當它收到來自 API server 的請求,如果有 Pod 需要分配到資源,它會請求目標 target node 上的 kubelet 執行這個 Pod。
Worker node 可以是實體機器或是虛擬機器,它是應用程式的執行者,每個 Worker node 會運作:
- Container runtime
- kubelet
- kube-proxy
- Container runtime 運作在每一個工作節點上
- Container runtime 負責執行 container
- kubelet 負責節點上 pod 的排程
- kubelet 能夠與 container 以及 worker node 溝通
- kube-proxy 同樣在每個節點上工作
- 負責流量的轉發:外部流量或是 pod 之間的流量該導向哪(也就是 service 物件)
- 底層使用 iptables 實現轉發功能,所以在效能上還有進步空間(像是利用先前介紹的 xdp 技術)
kubectl apply -f YOUR_POD.yaml
kubectl apply
屬於 Declarative Management,我們可以使用它動態的調整先前已經套用的設定。
間單來說,如果我們已經建立好一個 Pod,並且我們希望修改它的部分設定,那我們可以再撰寫一個 yaml 檔案說明清楚想要調整的欄位後再使用 kubectl apply
。
kubectl create -f YOUR_POD.yaml
kubectl create
屬於 Imperative Management,使用 kubectl create
修改 Pod 的設定時,它會先刪除現有的全部設定再建立新的,因此,如果重複的使用 kubectl create
套件同一份 yaml 檔案會得到失敗的結果。
主要有三種:
- 使用 shared volumes
- 使用 IPC (Inter-process communications)
- 使用 Network
namespace 是一個用來組織、隔離 Cluster 資源的機制:
- 使用 RoleBindings 做到分隔租戶以及授權存取
- 分割出多個虛擬的 sub-clusters
- 執行資源管理策略,可以使用 ResourceQuatas 或是 LimitRanges 實現
- 使用 NetworkPolicy 區分網路環境
建立 namespace 的方法:
kubectl create namespace ian
建立一個名為 ian
的 namespace。
取得當前 cluster 的所有 namespaces:
kubectl get namespace
指定某個 namespace 為預設的 namespace:
kubectl config set-context -current -namespace=ian
- Request 代表請求的最小資源
- Limit 代表最大上限
如果一個 pod 要求 500m CPU,並且主機有 2 CPU,那麼這一個 pod 可以獲得 2 CPU 的硬體資源
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/。
前面提到的 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 的細節。
- 部屬多個 Pod 的方式
kubectl run web-depl --image="<image>" --replicas=2 --labels="ver=1, app=web, env=production"
kubectl delete deployment --all
或是使用 selector:
kubectl delete deployment --selector="<cond>"
因為 Pods 會被 K8S 動態的控制,所以具有 volatile 的特性,為了讓服務實體可以穩定的將流量或是請求導向它們,我們會需要 Service 的幫忙。
ClusterIP
類型的 Service 只有 Cluster 內部的成員可見,這可以用於保護某些特定的 Service 不被外網存取。
LoadBalancer
類型的 Service 就是一個平衡附載器,外部成員可以透過存取這類的 Service 取得想要的服務。
如果是在 GKE 上使用 LoadBalancer
,GKE 會為它分配一個 IP Address 並將流量導向該服務。
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
以下 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