雲原生入門 第五章:kubernetes學習實踐
- 2022 年 3 月 18 日
- 筆記
- Kubernetes, 雲原生
1. 簡介
在本章中,我們將學習不同的Kubernetes對象,它們的用途以及如何與它們交互。
在設置集群或使用現有集群之後,我們可以開始部署一些工作負載。Kubernetes中最小的計算單元不是一個容器,而是一個Pod對象。也就是說,Pod不是我們用於工作負載的唯一抽象。Kubernetes有各種各樣的工作負載對象來控制如何部署、擴展和管理pod。
部署工作負載並不是開發人員或管理員必須執行的唯一任務。Kubernetes為容器和編配的一些固有問題提供了解決方案,比如配置管理、跨節點網路、外部流量路由、負載平衡或pod的擴展。
2. 學習目標
在本章結束時,你應該能夠:
- 解釋什麼是Kubernetes對象以及如何描述它。
- 討論Pod的概念及其解決的問題。
- 了解如何使用工作負載資源來擴展和安排pod。
- 了解如何用服務抽象Pods,以及如何公開它們。
3. Kubernetes對象
Kubernetes的核心概念之一是提供大量抽象資源(也稱為對象),您可以使用這些資源來描述應該如何處理工作負載。其中一些用於處理容器編排的問題,如調度和自愈,另一些用於解決容器的一些固有問題。
Kubernetes對象可以區分為面向工作負載的對象(用於處理容器工作負載)和面向基礎設施的對象(例如處理配置、網路和安全)。其中一些對象可以放在一個名稱空間中,而其他對象可以跨整個集群使用。
作為用戶,我們可以用流行的數據序列化語言YAML描述這些對象,並將它們發送到api伺服器,在創建它們之前要對它們進行驗證。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.19
ports:
- containerPort: 80
紅色突出顯示的欄位是必填欄位。它們包括:
apiVersion:每個對象都可以進行版本控制。這意味著對象的數據結構可以在不同的版本之間變化。kind:應該創建的對象類型。
metadata:可以用來識別它的數據。每個對象都需要一個名稱,並且必須是唯一的。如果需要多個具有相同名稱的對象,可以使用名稱空間。spec:對象的說明。在這裡你可以描述你想要的狀態。要小心,因為對象的結構可能會隨著它的版本而改變!
創建,修改或刪除一個對象只是一個意圖記錄,在那裡你描述你的對象應該處於的狀態,你不像你在本地機器上做的那樣主動啟動pods或甚至容器,並獲得直接回饋,如果它工作與否。
4. 與Kubernetes交互
要訪問API,用戶可以使用名為kubectl的官方命令行介面客戶端。讓我們看看Kubernetes日常使用的一些基本命令。
注意:您可以在官方文檔中了解如何安裝kubectl。
你可以用下面的命令列出集群中可用的對象:
$ kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
...
configmaps cm v1 true ConfigMap
...
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
...
pods po v1 true Pod
...
services svc v1 true Service
注意對象是如何使用短名稱的。這對於名稱較長的對象(如configmaps或persistentvolumeclaims)非常有用。該表還顯示了哪些對象具有名稱空間以及它們的可用版本。
如果你想了解更多關於對象的資訊,kubectl有一個內置的explain函數!
讓我們進一步了解pod:
$ kubectl explain pod
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values.
...
kind <string>
...
metadata <Object>
...
spec <Object>
要了解更多關於pod規範的資訊,您可以深入了解對象定義。使用如下格式:<type>.<fieldName>[.<fieldName>]。
$ kubectl explain pod.spec
KIND: Pod
VERSION: v1
RESOURCE: spec <Object>
DESCRIPTION:
Specification of the desired behavior of the pod. More info:
//git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
PodSpec is a description of a pod.
FIELDS:
activeDeadlineSeconds <integer>
Optional duration in seconds the pod may be active on the node relative to
StartTime before the system will actively try to mark it failed and kill
associated containers. Value must be a positive integer.
affinity <object>
If specified, the pod's scheduling constraints
automountServiceAccountToken <boolean>
AutomountServiceAccountToken indicates whether a service account token
should be automatically mounted.
containers <[]Object> -required-
...
讓我們看看基本的kubectl命令。你可以使用——help標誌來查看它們:
$ kubectl --help
kubectl controls the Kubernetes cluster manager.
Find more information at: //kubernetes.io/docs/reference/kubectl/overview/
Basic Commands (Beginner):
create Create a resource from a file or from stdin
expose Take a replication controller, service, deployment or pod and expose it as a new Kubernetes service
run Run a particular image on the cluster
set Set specific features on objects
Basic Commands (Intermediate):
explain Get documentation for a resource
get Display one or many resources
edit Edit a resource on the server
delete Delete resources by file names, stdin, resources and names, or by resources and label selector
要在Kubernetes中從YAML文件創建一個對象,你可以使用以下命令:
kubectl create -f <your-file>.yaml
Kubernetes有許多圖形用戶介面和儀錶板,它們允許與集群進行可視化交互。

Kubernetes官方儀錶盤的截圖
與Kubernetes交互的其他工具:
儘管有許多CLI工具和gui,但還有一些高級工具允許創建模板和打包Kubernetes對象。也許今天Kubernetes最常用的工具是Helm。
Helm是一個Kubernetes的包管理器,它允許更簡單的更新和與對象的交互。Helm將Kubernetes對象封裝在所謂的Charts中,可以通過註冊表與他人共享。要開始使用Kubernetes,您可以搜索ArtifactHub,找到您最喜歡的軟體包,準備部署。
4.1 Demo: kubectl
5. Pod 概念
Kubernetes中最重要的對象是Pod。pod描述一個或多個容器的單元,這些容器共享一個名稱空間和cgroups隔離層。它是Kubernetes中最小的可部署單元,這也意味著Kubernetes不會直接與容器交互。pod概念的引入是為了允許運行相互依賴的多個進程的組合。pod中的所有容器共享一個IP地址,並且可以通過文件系統共享。

多個容器共享名稱空間形成一個pod
下面是一個帶有兩個容器的簡單Pod對象的例子:
apiVersion: v1
kind: Pod
metadata:
name: nginx-with-sidecar
spec:
containers:
- name: nginx
image: nginx:1.19
ports:
- containerPort: 80
- name: count
image: busybox:1.34
args: [/bin/sh, -c,
'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
您可以向主應用程式添加任意數量的容器。但是要小心,因為您失去了單獨縮放它們的能力!使用支援主應用程式的第二個容器稱為sidecar容器。
所有定義的容器都是在同一時間啟動的,沒有順序,但是您也可以使用initContainers在主應用程式啟動之前啟動容器。在這個例子中,init容器init-myservice試圖到達另一個服務。一旦完成,主容器就會啟動。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
請務必瀏覽有關pod的文檔,因為還有更多設置有待發現。對於Pod中的每個容器,可以設置的一些重要設置示例如下:
- resources: 設置一個資源請求和CPU和記憶體的最大限制
- livenessProbe: 配置定期檢查應用程式是否仍處於活動狀態的運行狀況檢查。如果檢查失敗,可以重新啟動容器。
- securityContext: 設置用戶和組設置,以及內核功能。
5.1 Demo: Pods
docker run -d nginx:1.19
kubectl run nginx --image=nginx:1.19
kubectl get pods
kubectl describe pod nginx
#獲取IP
curl //IP
6. 負載均衡
在容器編排平台中,僅僅使用Pods是不夠靈活的。例如,如果一個Pod因為一個節點失敗而丟失,那麼它就永遠消失了。為了確保始終運行已定義數量的Pod副本,我們可以使用控制器對象來為我們管理Pod。
ReplicaSet
確保在任何給定時間運行所需數量的pod的控制器對象。replicaset可以用於向外擴展應用程式並提高其可用性。它們通過啟動一個pod定義的多個副本來實現這一點。Deployment
Kubernetes中功能最豐富的對象。部署可以用來描述完整的應用程式生命周期,它們通過管理多個replicaset來實現這一點,當應用程式被更改時,這些replicaset會被更新,例如,提供一個新的容器映像。部署非常適合在Kubernetes中運行無狀態應用程式。StatefulSet
StatefulSets可以用於在Kubernetes上運行像資料庫這樣的有狀態應用程式,這在很長一段時間內都被認為是一個不好的實踐。有狀態應用程式有特殊的需求,這些需求不適合pod和容器的短暫性。與部署不同,StatefulSets嘗試保留pod的IP地址,並給它們一個穩定的名稱、持久的存儲和更優雅的伸縮和更新處理。DaemonSet
確保Pod的副本在集群的所有(或部分)節點上運行。守護進程集非常適合運行與基礎設施相關的工作負載,例如監視或日誌工具。Job
創建一個或多個執行任務的Pods,然後終止該任務。作業對象非常適合運行資料庫遷移或管理任務等一次性腳本。CronJob
CronJobs為作業添加基於時間的配置。這允許定期運行Jobs,例如每天晚上4點執行備份作業。
互動式教程-部署一個應用程式並探索它
在Kubernetes文檔提供的互動式教程的第2部分中,您可以了解如何在Minikube集群中部署應用程式。
應用您從「與Kubernetes交互」中學到的知識,在互動式教程的第三部分探索您的應用程式。
6.2 demo: pod、replicats、deployments
apiVersion: v1
kind: Pod
metadata:
name: simple-nginx-pod
labels:
role: myrole
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
kubectl apply -f simple-nginx-pod.yaml
replicas部署
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
apps: nginx
template:
metadata:
labels:
apps: nginx
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80
$ kubectl apply -f replicas.yaml
$ k get pods
NAME READY STATUS RESTARTS AGE
nginx-5psrm 1/1 Running 0 2m12s
nginx-68x8p 1/1 Running 0 2m12s
nginx-q9zlq 1/1 Running 0 2m12s
$ kubectl scale --replicas=4 rs/nginx
deployment部署
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
$ k apply -f deployment.yaml
$ k get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-66b6c48dd5-55jrb 1/1 Running 0 2m35s
nginx-deployment-66b6c48dd5-6x9cj 1/1 Running 0 2m35s
nginx-deployment-66b6c48dd5-pj7qr 1/1 Running 0 2m35s
$ k scale --replicas=4 deployment/nginx-deployment
$ k get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-66b6c48dd5-55jrb 1/1 Running 0 4m15s
nginx-deployment-66b6c48dd5-6x9cj 1/1 Running 0 4m15s
nginx-deployment-66b6c48dd5-cxv8g 1/1 Running 0 3s
nginx-deployment-66b6c48dd5-pj7qr 1/1 Running 0 4m15s
$ k set image deployment/nginx-deployment nginx=nginx:1.20
7. 網路
由於大量的Pods需要大量的手工網路配置,我們可以使用Service和Ingress對象來定義和抽象網路
ClusterIP
最常見的服務類型。ClusterIP是Kubernetes內部的一個虛擬IP,可以作為一組pods的單個端點使用。這種服務類型可以用作輪詢負載均衡器。

NodePort
NodePort服務類型通過添加簡單的路由規則擴展了ClusterIP。它在集群中的每個節點上打開一個埠(默認在30000-32767之間),並將其映射到ClusterIP。這種服務類型允許將外部流量路由到集群。loadbalance
LoadBalancer服務類型通過部署外部LoadBalancer實例來擴展NodePort。只有當你在一個有API來配置LoadBalancer實例的環境中,比如GCP、AWS、Azure甚至OpenStack,這才會起作用。ExternalName
一種沒有任何路由的特殊服務類型。ExternalName使用Kubernetes內部DNS伺服器創建DNS別名。您可以使用它創建一個簡單的別名來解析一個相當複雜的主機名,比如:my-cool-database-az1-uid123.cloud-provider-i-like.com。如果您想從Kubernetes集群獲取外部資源,這一點尤其有用。

ClusterIP、NodePort和LoadBalancer相互擴展
如果需要更大的靈活性來公開應用程式,可以使用Ingress對象。入口提供了一種從集群外部為集群內的服務公開HTTP和HTTPS路由的方法。它通過配置路由規則來實現這一點,用戶可以通過入口控制器設置和實現路由規則。

一個Ingress將所有流量發送到一個Service的例子,從Kubernetes文檔中獲取
入口控制器的標準特性可能包括:
- LoadBalancing
- TLS offloading/termination
- Name-based virtual hosting
- Path-based routing
許多入口控制器甚至提供了更多的功能,比如:
- Redirects
- Custom errors
- Authentication
- Session affinity
- Monitoring
- Logging
- Weighted routing
- Rate limiting.
Kubernetes還提供了一個具有NetworkPolicy概念的集群內部防火牆。NetworkPolicies是一個簡單的IP防火牆(OSI三層或四層),可以基於規則控制流量。您可以為傳入(進入)和傳出(出口)流量定義規則。NetworkPolicies的一個典型用例是限制兩個不同名稱空間之間的流量。
互動式教程-展示你的應用程式
現在,您可以在Kubernetes文檔提供的互動式教程的第4部分中了解如何使用Service公開應用程式。
7.1 demo
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
$ k apply -f nginx-deployment.yaml
$ k expose deployment nginx-deployment 80
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 51d
nginx-deployment ClusterIP 10.101.106.248 <none> 80/TCP 8s
$ curl 10.101.106.248:80
<!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="//nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="//nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
8. Volume & Storage
如前所述,在設計容器時並沒有考慮到持久存儲,特別是當存儲跨越多個節點時。Kubernetes介紹了一些解決方案,但請注意,這些解決方案並沒有自動消除使用容器管理存儲的所有複雜性。
集裝箱已經有了安裝卷的概念,但由於我們沒有直接使用集裝箱,Kubernetes將卷作為Pod的一部分,就像集裝箱一樣。
下面是一個hostPath卷掛載的例子,類似於Docker引入的主機掛載:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /data
# this field is optional
type: Directory

卷允許在同一個Pod中的多個容器之間共享數據。當您想要使用側車模式時,這個概念允許極大的靈活性。它們的第二個用途是在Pod崩潰並在同一節點上重新啟動時防止數據丟失。pod以乾淨的狀態啟動,但所有數據會丟失,除非寫入卷。
不幸的是,包含多個伺服器的集群環境在持久性存儲方面需要更多的靈活性。根據環境的不同,我們可以使用像Amazon EBS、GooglePersistent Disks、Azure Disk storage這樣的雲塊存儲,也可以使用像Ceph、GlusterFS這樣的存儲系統或更傳統的系統,比如NFS。
這些只是Kubernetes中可以使用的存儲的幾個例子。為了讓用戶體驗更加統一,Kubernetes使用了容器存儲介面CSI (Container Storage Interface),它允許存儲供應商編寫一個可以在Kubernetes中使用的插件(存儲驅動程式)。
為了使用這個抽象,我們還有兩個可以使用的對象:
- PersistentVolumes (PV)
存儲片的抽象描述。對象配置包含卷的類型、卷大小、訪問模式和唯一標識符以及如何掛載它的資訊。 - PersistentVolumeClaims (PVC)
用戶對存儲的請求。如果集群有多個持久化卷,用戶可以創建一個PVC,根據用戶的需要預留一個持久化卷。
apiVersion: v1
kind: PersistentVolume
metadata:
name: test-pv
spec:
capacity:
storage: 50Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
csi:
driver: ebs.csi.aws.com
volumeHandle: vol-05786ec9ec9526b67
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: centos
command: ["/bin/sh"]
args:
["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
這個例子展示了一個PersistentVolume,它使用了一個使用CSI驅動程式實現的AWS EBS卷。在配置了PersistentVolume之後,開發人員可以使用PersistentVolumeClaim來預留它。最後一步是在Pod中使用PVC作為卷,就像我們之前看到的hostPath示例一樣。
可以直接在Kubernetes中操作存儲集群。像Rook這樣的項目提供雲本地存儲業務編排,並與經過實戰測試的存儲解決方案(如Ceph)集成。

Rook架構,從Rook文檔中檢索
9. 配置對象
12因素應用程式建議將配置存儲在環境中。但這到底是什麼意思呢?運行應用程式需要的不僅僅是應用程式程式碼和一些庫。應用程式有配置文件,連接到其他服務、資料庫、存儲系統或快取,這需要像連接字元串這樣的配置。
將配置直接合併到容器構建中被認為是不好的做法。任何配置更改都需要重新構建整個映像,並重新部署整個容器或吊艙。當使用多個環境(開發、登台、生產)並為每個環境構建映像時,這個問題只會變得更糟。12因素應用程式更詳細地解釋了這個問題:Dev/prod奇偶性。
在Kubernetes中,這個問題是通過使用ConfigMap將配置從Pods中解耦來解決的。ConfigMaps可用於將整個配置文件或變數存儲為鍵-值對。有兩種可能的方式使用ConfigMap:
- 將ConfigMap掛載為Pod中的卷
- 將ConfigMap中的變數映射到Pod中的環境變數。
下面是一個包含nginx配置的ConfigMap示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
data:
nginx.conf: |
user nginx;
worker_processes 3;
error_log /var/log/nginx/error.log;
...
server {
listen 80;
server_name _;
location / {
root html;
index index.html index.htm; } } }
一旦ConfigMap被創建,你就可以在Pod中使用它:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.19
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx
name: nginx-conf
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
從一開始,Kubernetes也提供了一個對象來存儲敏感資訊,如密碼、密鑰或其他憑證。這些對象被稱為Secrets。秘密與ConfigMaps非常相關,基本上它們唯一的區別是秘密是base64編碼的。
關於使用「秘密」的風險,人們一直在爭論不休,因為「秘密」(與名稱相反)並不被認為是安全的。在原生雲環境中,已經出現了專門創建的秘密管理工具,它們可以很好地與Kubernetes集成。HashiCorp Vault就是一個例子。
10. Autoscaling
自動伸縮機制
- Horizontal Pod Autoscaler (HPA)
Horizontal Pod Autoscaler (HPA)是Kubernetes中最常用的自動定標器。HPA可以監視deployments或ReplicaSets,並在達到某個閾值時增加副本的數量。成像Pod可以使用500MiB的記憶體,並且您配置了80%的閾值。如果利用率超過400MiB(80%),將調度第二個Pod。現在您的容量為1000MiB。如果使用了800MiB,將調度第三個Pod,以此類推。 - Cluster Autoscaler
當然,如果集群容量是固定的,那麼啟動越來越多的pod副本是沒有意義的。如果需求增加,Cluster Autoscaler可以向集群添加新的工作節點。集群自動伸縮器與水平自動伸縮器協同工作。 - Vertical Pod Autoscaler
Vertical Pod Autoscaler 相對較新,允許吊艙動態增加資源請求和限制。如前所述,垂直擴展受到節點容量的限制。
不幸的是,Kubernetes的(水平)自動伸縮是無法開箱即用的,需要安裝一個名為metrics-server的附加組件。
但是,用Kubernetes Metrics api的Prometheus Adapter替換度量伺服器是可能的。prometheus-adapter允許您在Kubernetes中使用自定義指標,並根據系統上的請求或用戶數量等因素進行放大或縮小。
像KEDA這樣的項目可以根據外部系統觸發的事件來擴展Kubernetes工作負載,而不是僅僅依賴於指標。KEDA是基於kubernetes的事件驅動自動scaler的縮寫,於2019年作為微軟和紅帽公司的合作夥伴啟動。與HPA類似,KEDA可以擴展部署、複製集、pod等,還可以擴展Kubernetes作業等其他對象。通過大量現成的擴展器的選擇,KEDA可以擴展到特殊的觸發器,比如資料庫查詢,甚至Kubernetes集群中pod的數量。
互動式教程-縮放您的應用程式
在互動式教程的第五部分:運行應用程式的多個實例中,你可以學習如何手動擴展應用程式。
11. Additional Resources
Learn more about...
Differences between Containers and Pods
- What are Kubernetes Pods Anyway?, by Ian Lewis (2017)
- Containers vs. Pods - Taking a Deeper Look, by Ivan Velichko (2021)
kubectl tips & tricks
Storage and CSI in Kubernetes
- Container Storage Interface (CSI) for Kubernetes GA, by Saad Ali
(2019) - Kubernetes Storage: Ephemeral Inline Volumes, Volume Cloning,Snapshots and more!, by Henning Eggers (2020)
Autoscaling in Kubernetes
- Architecting Kubernetes clusters - choosing the best autoscaling strategy, by Daniele Polencic (2021)
關注公眾號:愛死亡機器人

