本文目標:
在真正了解 Kubernetes 之前,我們必須定義一下什麼是 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
Kubernetes(或是 K8s)用於管理、部署、擴充容器化的應用程式,該專案最初由 Google 設計,並捐贈給 CNCF 維護。
本篇文章並不會詳細的介紹如何建構 Kubernetes 環境或是基於 Kubernetes 的應用程式,而是盡可能的加深讀者對於 Kubernetes 的認知。
Kubernetes 可以由多個 worker node 組合成一個 cluster,為部署在 Kubernetes 上的服務增加保障,下圖是 Kubernetes 的架構圖:
上圖取自 Kubernetes 官方文件。
參考上圖,我們可以將它拆成兩個面向來看:
Master node 負責調度所有的 worker node,在 Master node 上會運作四個非常重要的 process,它們分別是:
API server 是整個 Kubernetes cluster 的入口點,它負責處理來自 client(使用 kubectl)或是 kubelet 的請求。
etcd 是一個分散式的資料庫專案,它使用 raft 演算法作為共識機制,能夠保證分散式節點上的資料一致性。
Kubernetes 的內部會有多個 controller,它們分別負責不同的任務。
Scheduler 負責管理哪些 Pod 能夠獲得 cpu 資源。
Worker node 可以是實體機器或是虛擬機器,它是應用程式的執行者,每個 Worker node 會運作:
目前市面上有多種建置 K8s 的方法,開發者可以根據不同的使用需求或是預算考量選擇一個最佳的方案。
目前 GCP、AWS、Azure 都有提供 K8s cluster service,如果服務不到非常龐大,使用公有雲算是個不錯的選擇(不用花錢花時間維護設備,簡易的服務也不會燒太多錢)。
如果是選擇在本地環境架設 Kubernetes,那麼就有非常多種選擇了:
筆者的話:
在這邊想要抱怨一下 kubernetes 的命名想法...筆者當初入門的時候一度搞不清楚 kubeadm、kubectl、kubelet 之間的關係,有時候建置方案的選擇太多對新手來說可能也是一種困擾啊...
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 檔案會得到失敗的結果。
主要有三種:
namespace 是一個用來組織、隔離 Cluster 資源的機制:
建立 namespace 的方法:
kubectl create namespace ian
建立一個名為 ian
的 namespace。
取得當前 cluster 的所有 namespaces:
kubectl get namespace
指定某個 namespace 為預設的 namespace:
kubectl config set-context -current -namespace=ian
如果一個 pod 要求 500m CPU,並且主機有 2 CPU,那麼這一個 pod 可以獲得 2 CPU 的硬體資源
Labels 是一對可辨識的 key/value 形式的標籤,像是:
等等,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: ychen.desl@gmail.com
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
為 Pod 打上標籤後,可以使用:
$ kubectl describe pods YOUR_POD
查看 Pod 的細節。
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 物件有很重要嗎?
Deployment 最大的用途是幫助開發者做到滾動式的更新,滾動式更新是一種能夠不間斷服務的更新方式。在更新 Deployment 的期間,它會建立兩個 ReplicaSet(新版本與舊版本),並且根據使用者的設定逐步的替換舊有的 Pod。
此外,如果在更新後發現新版本不如之前穩定,我們也可以利用 Kubernetes 的紀錄幫助我們 rollout 到舊版本的 Deployment。
我們可以將 Deployment policy 分為兩大類:
要實現這兩種策略,我們需要仰賴 maxUnavailable
或是 maxSurge
:
maxUnavailable
:代表最大的不可用 Pod 數量,它的 value 可以是絕對值或百分比,如果 value 為 1
,那麼 K8s 對 Pod 逐一汰換(關掉一個後更新一個)。如果是設定為百分比,那麼公式為 replica of ReplicaSet
X maxUnavailable
。假設 replica 為 10
且 maxUnavailable
為 20%
,那麼 K8s 每次都會為我們更新兩個 Pod。maxSurge
:它的工作原理會與 maxUnavailable
有些微的不同,使用它進行更新時,它會先建立新版本的 Pod,等到確定新版本的 Pod 建立完成,再關閉對數量的舊版本 Pod。舉例來說:如果 replica 為 10
且 maxSurge
為 20%
,那麼 Pod 總數的變化會是 10
(10 個舊版本) -> 12
(2 個新版本加上 10 個舊版本) -> 10
(2 個新版本加上 8 個舊版本)...以此類推。作者補充:
這篇文章介紹了如何進行 rolling update 以及 rollout,有興趣的朋友們可以參考看看!
新增 Deployment 主要可以透過兩個方式:1. apply yaml
、2. 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:
kubectl delete deployment --all
或是使用 selector 指定刪除符合條件的 Deployment:
kubectl delete deployment --selector="<cond>"
Kubernetes 並沒有像 Docker 一樣能夠重啟 Container 的機制,如果 Pod 發生問題,那麼就只有將 Pod 刪除再重建這個方法。不過,這麼做可能會帶來一些問題:
為了解決這個問題,我們可以利用 scale
Deployment 達到這個目的:
kubectl scale deployment <DEPLOYMENT> --replicas=0 -n <NAME_SPACE>
kubectl scale deployment <DEPLOYMENT> --replicas=<EXPECTED_NUM> -n <NAME_SPACE>
因為 Pods 會被 K8S 動態的控制,所以具有 volatile 的特性,為了讓服務實體可以穩定的將流量或是請求導向它們,我們會需要 Service 的幫忙。
ClusterIP
類型的 Service 只有 Cluster 內部的成員可見,這可以用於保護某些特定的 Service 不被外網存取。
LoadBalancer
類型的 Service 就是一個 load balancer,外部成員可以透過存取這類的 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
我們可以把 Ingress 想像成一個讓 Service 對外的工具,以上面的案例來看 Ingress 更像是 Service 的 Load balancer。
在預設情況下,Kubernetes 並不會安裝 Ingress,它就像是 CNI 一樣需要交給使用者自行挑選、安裝。
這篇文章快速的帶大家認識 Kubernetes 以及基本的使用介紹,不過 Kubernetes 其實是一個相當複雜的工具,所以有很多概念(configMap
、secret
、RBAC
)以及底層的知識(CNI
、CRI
、CSI
)是沒有在這篇文章提到的,還請讀者們見諒。
明天的文章會注重在如何建構一個 Kubernetes 環境,並且介紹 Helm 這套用於管理 Kubernetes 資源的工具,最後再以 free5GC 的衍生專案為例安裝到我們的 Kubernetes Cluster 上。