K8S 快速入門

k8s 快速入門

本文爲 The kubernetes book 的筆記. 作者 Nigel Poulton, 譯者劉康 https://github.com/get-set

K8s 是一個應用編排器, 可以部署應用, 動態擴縮容, 故障自愈, 不停機回滾, 同時還是一個支撐應用運行的集羣, 集羣由一個或多個主節點和若干個工作節點構成. 集羣內部的 DNS 服務還提供了服務發現與負載均衡的能力.

k8s 與容器技術是互補的技術, 容器用於應用的開發. k8s 用於應用編排.

節點

集羣由一個或多個主節點和若干個工作節點構成. 其中主節點負責管理整個集羣, 做調度決策, 監控集羣, 響應事件的工作. 工作節點負責運行應用服務, 每個工作節點都有自己的主節點.

主節點

master-node

k8s 的主節點 (或者說控制平面, 官方文檔是這麼稱呼的) 通常有一個或多個系統服務組成.  他爲集羣提供了故障轉移和高可用性.

k8s 中所有的組件之間都通過 API Server 進行通信. API Server 默認是一組 RESTful 風格的接口.

API Server 位於控制平面的最前端, 所有的指令與通信都需要通過 API Server 來完成.

k8s 默認使用 etcd 存儲着整個集羣的配置和狀態, etcd 更注重一致性.

Controller 用於確保集羣的當前狀態與預期狀態相匹配. Controller 有很多個, 比如終端 Controller, 工作節點 Controller, 副本 Controller(一般指 Pod 副本). 每個 Controller 都在後臺啓動獨立的 循環監聽功能.

::: tip 循環監聽邏輯

  1. 獲取期望狀態

  2. 觀察當前狀態

  3. 對比狀態

  4. 調整差異使當前狀態去儘量符合期望狀態 :::

Controller Manager 負責創建 Controller 並監控他們的執行.

調度器監聽 API Server 來啓動新的工作任務, 並將其分配到合適的節點中, 合適與否由調度器判定. 判定條件有: 節點是否存活, 任務依賴端口在節點中是否可用, 節點是否還有足夠資源等等.

如果集羣運行在公有云平臺, 控制平面會啓動一個 Cloud Controller Manager 來負責集成底層的公有云服務. 如實例, 負載均衡等.

工作節點

工作節點託管作爲應用負載的 Pod.

kubelet 是工作節點的核心, 新的工作節點加入集羣后, kubelet 就會被部署到新節點上.

kubelet 負責向集羣彙報當前節點的資源狀態, 如 CPU, 內存等.

kubelet 還會監聽 API Server 分配的新任務, 每監聽到一個就去執行這個任務, 並且與主節點維護一個通信頻道, 當任務結束後返回執行結果.

容器運行時供 kubelet 使用, 用於執行依賴容器的任務. 容器運行時可以由 docker 提供, 也可以由 containerd 或其他容器技術提供.

containerd 是 docker 的精簡版, 在某些時候更適合作爲 k8s 的容器運行時.

kube-proxy 與 kubelet 和 Container Runtime 一樣在每個工作節點中都存在, 它是維護節點上的網絡規則, 允許從集羣內部或外部與 Pod 進行網絡通信.

DNS / 網絡

K8s 有自己的 DNS, 他有一個靜態 IP, 並且被硬編碼在每個節點中. 他讓我們可以使用一致的 DNS 名稱而非 IP 地址來訪問服務. 集羣中的每個 Service 都會有一個 DNS 名稱.

聲明式模型

當我們要將一個應用運行在 k8s 上時, 通常需要先將應用打包爲鏡像, 然後封裝到 pod 中去運行. 我們可以通過定義 manifest 文件 (yml) 的方式去部署.

我們在 yml 中告知 k8s 集羣我們希望的應用運行狀態. 也就是期望狀態. 比如運行多少個副本. 然後提交到 k8s 中. k8s 會確保應用的運行符合我們的期望狀態.

整個流程如下:

  1. 在 mainfest 文件中聲明一個應用的期望達到的狀態

  2. 將 manifest 文件發送到 API 服務

  3. k8s 將 manifest 存儲到集羣存儲中, 並作爲應用的期望狀態

  4. k8s 在集羣中實現期望狀態

  5. k8s 啓動循環監聽, 保證當前狀態符合期望狀態

循環監聽會有很多個, 有監聽副本數量的, 有監聽存儲卷掛載的, 下面會有講解.

Pod

pod 是 k8s 調度的原子單位, 就像容器是 docker 調度的原子單位一樣.

k8s 是無法直接運行容器的, 它使用 pod 來對容器進行一層簡單的封裝, 以允許容器運行在 k8s 中. pod 就是爲用戶在宿主機系統中劃出一部分區域, 構建一個網絡棧, 創建一組內核命名空間, 並且在其中運行一個或多個容器.

pod 中可以包含一個或多個容器, 或者說 pod 是被一個或多個容器共享的執行環境. 比如我們可以一個 pod 包含一個微服務, 也可以在一個 pod 中同時包含微服務與其依賴的基礎設施服務 (打比方, 通常不會這麼做). 注意是包含不是運行

pod 的部署是原子操作, 只有 pod 中的容器都啓動成功運行後, pod 提供的服務才被認爲可用.

由於 pod 運行中可能會出現意外銷燬, 而 k8s 的自愈功能會啓動一個新 pod 來取到原 pod(pod 本身沒有這個能力, Deployment 纔有). 新的 pod 會有新的 id 與新的 ip, 所以我們切記不要在程序中去 依賴某個特定的 pod

manifest

在聲明式模型中提到我們可以使用 manifest 文件的方式去告訴 k8s 部署一個什麼樣的應用, 下面簡單配置一個 manifest.

apiVersion: v1
kind: Pod
metadata:
  name: hello-pod
  labels:
    zone: prod
    version: v1
spec:
  containers:
  - name: hello-ctr
    image: nigelpoulton/k8sbook:latest
    ports:
    - containerPort: 8080

deploy

在編寫完 manifest 文件後. 我們需要將這個文件提交給 k8s 去讓他運行把 Pod 副本到可以工作的節點上去. 下面是會涉及到的一些 kubectl 與 API Server 交互的一些命令.

# POST manifest文件到API Serve
F:\學\k8s\TheK8sBook\pods>kubectl apply -f pod.yml  
pod/hello-pod created

# 查看部署的pod
F:\學\k8s\TheK8sBook\pods>kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
hello-pod   1/1     Running   0          66s

當看到 pod 的狀態爲 Running 時, 代表 pod 已經在 k8s 中運行了. 我們可以使用kubectl exec命令進入 pod 操作, 也可以使用kubectl logs命令查看 pod 的日誌. 這一點與 docker 大同小異. 不再贅述.

上面用到的kubectl apply -f命令是通用的 k8s 的 manifest 文件部署啓動命令, 除了啓動 Pod 以外, 啓動後面會講到的所有的 k8s 對象的 manifest 文件也都是通過這個命令來部署.

還有kubectl get命令也是通用. kubectl describe命令則可以更詳細的查看 k8s 對象的信息.

Deployment

Pod 對象本身是沒有故障自愈, 滾動升級等能力的. 所以 Pod 的部署一般是通過更上層的 Deployment 來完成的. 它是對 Pod 的更進一步的封裝, 提供了擴縮容管理, 不停機更新和版本控制功能.

一個 Deployment 對象只能管理一個 Pod 模版, 如果有多個 Pod 對象, 那麼每個 Pod 對象都應該有自己的 Deployment

Deployment 的內部使用 ReplicaSet 對象來完成 Pod 的自愈, 滾動升級等, 他是聲明式模型 (期望狀態) 實現的關鍵, 它實現了監視循環.

當滾動升級的時候, Deployment 會保留原來的 ReplicaSet 對象的情況下, 啓動一個新的 ReplicaSet 對象來啓動新的 Pod 副本, 如果需要回滾, 只需要停止新的 ReplicaSet, 然後啓動舊 ReplicaSet 就可以了.

k8s-replicaSet

manifest

下面定義一個 deploy.yml 文件來通過 Deployment 對象啓動一組 pod 對象

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deploy
spec:
  replicas: 10
  selector:
    matchLabels:
      app: hello-world
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-pod
        image: nigelpoulton/k8sbook:latest
        ports:
        - containerPort: 8080

apiVersion 與 kind 和 metadata 屬性不再贅述. 都是 k8s 對象 manifest 的通用屬性. 這裏只解釋 spec 中的信息含義

deploy

使用如下命令來提交一個 Deployment:

# POST manifest文件到API Server
F:\學\k8s\TheK8sBook\deployments>kubectl apply -f deploy.yml
deployment.apps/hello-deploy created

# READY列中爲已啓動數量/預期啓動數量
F:\學\k8s\TheK8sBook\deployments>kubectl get deploy
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
hello-deploy   4/10    10           0           11s

# 所有pod副本啓動完畢
F:\學\k8s\TheK8sBook\deployments>kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
hello-deploy-65cbc9474c-2qmkz   1/1     Running   0          58s
hello-deploy-65cbc9474c-669rg   1/1     Running   0          58s
hello-deploy-65cbc9474c-9nzp7   1/1     Running   0          58s
hello-deploy-65cbc9474c-f9hhd   1/1     Running   0          58s
hello-deploy-65cbc9474c-gknps   1/1     Running   0          58s
hello-deploy-65cbc9474c-lq9cz   1/1     Running   0          58s
hello-deploy-65cbc9474c-m6r2l   1/1     Running   0          58s
hello-deploy-65cbc9474c-mfgqx   1/1     Running   0          58s
hello-deploy-65cbc9474c-pc7tg   1/1     Running   0          58s
hello-deploy-65cbc9474c-r8ztb   1/1     Running   0          58s

RollingUpdate

當修改 manifest 文件後再次執行 apply 命令提交. 然後使用kubectl rollout status命令查看更新過程

F:\學\k8s\TheK8sBook\deployments>kubectl rollout status deployment hello-deploy
Waiting for deployment "hello-deploy" rollout to finish: 4 of 10 updated replicas are available...
Waiting for deployment "hello-deploy" rollout to finish: 9 of 10 updated replicas are available...

Rollback

執行回滾操作需要一個 deploy 的版本號. 這個版本號可以在執行 apply 命令時通過--record參數記錄下來. 然後通過一下命令查看歷史版本號

F:\學\k8s\TheK8sBook\deployments>kubectl rollout history deployment hello-deploy
deployment.apps/hello-deploy
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl apply --filename=deploy.yml --record=true

因爲我們已經執行過了一次滾動升級, 所以 hello-deploy 對象中現在應該有兩個 ReplicaSet 對象

F:\學\k8s\TheK8sBook\deployments>kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
hello-deploy-65cbc9474c   0         0         0       16m
hello-deploy-6f797c4b74   10        10        10      3m15s

可以看到更早創建的 hello-deploy-65cbc9474c 中 Pod 副本數量已經爲零, 新創建的 hello-deploy-6f797c4b74 中 Pod 數量爲 10.

現在通過 undo 命令回滾版本

F:\學\k8s\TheK8sBook\deployments>kubectl rollout undo deployment hello-deploy --to-revision=1
deployment.apps/hello-deploy rolled back

再次查看 ReplicaSet 對象, 舊的 ReplicaSet 中的 Pod 對象已經開始逐個啓動, 新的 ReplicaSet 中的 Pod 對象開始逐個銷燬

F:\學\k8s\TheK8sBook\deployments>kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
hello-deploy-65cbc9474c   3         3         2       20m
hello-deploy-6f797c4b74   8         8         8       6m59s

Service

由於 Pod 可能會出現擴縮容, 故障時替換的情況, 導致了 Pod 的 IP 地址變化. 因此 Pod 時不可靠的, 我們不能直接去依賴 Pod. Service 解決了這個問題, 它提供了穩定的網絡.

我們可以把 Service 對象想象爲具備前後兩端. 前端有自己的 DNS 名稱, IP 和端口號; 後端有對 Pod 的動態負載均衡機制, 並且實現了自我監控, 可以自動更新.

Service 使用標籤和標籤選擇器來決定將流量負載均衡到哪個 Pod.

Endpoint

Service 在創建後都會得到一個關聯的 Endpoint 對象, 這個對象是一個動態的列表, 包含了所有匹配 Service Selector 的健康 Pod.

當 Service 有流量需要轉發時, 通過 Endpoint 對象中的 Pod 列表來查找 Pod

啓動會 Service 後可以查看對應的 Endpint 對象

F:\學\k8s\TheK8sBook\services>kubectl get ep hello-svc
NAME        ENDPOINTS                                                  AGE
hello-svc   10.1.0.37:8080,10.1.0.38:8080,10.1.0.39:8080 + 7 more...   9m21s

type

Service 有多種類型, 不同類型的 Service 對應不同的外部訪問策略

ClusterIP 類型的 Service 有固定的 IP 和端口號, 只能從集羣的內部訪問.

ClusterIP 與 Service 名稱一起被註冊到集羣內部的 DNS 服務中. 因爲所有 Pod 都知道集羣的 DNS 服務, 所以所有的 Pod 都能夠解析 Service 名稱. 之所以是內部訪問是因爲要訪問集羣的 DNS 才能夠找到對應的 IP, 所以 ClusterIP 類型的 Service 只對 Pod 和集羣中的其他對象奏效.

NodePort 在 ClusterIP 的基礎上增加了外部訪問的能力.

NodePort 在集羣的人與節點上都是相同的, 也就是從集羣外部訪問任一節點上的 NodePort 指定的端口號都可以找到 Service, 即使這個節點上並沒有 Service 及其選擇器對應的 Pod

集羣內部的 Pod 找到 Service 要通過名稱, 而外部的則是通過 NodePort.

NodePort 的端口範圍在 30000-32767 之間

LoadBalancer 同樣能夠在外部訪問, 同時他還能夠與雲服務上提供的負載均衡服務集成. 他是基於 NodePort 實現的.

ExternalName 能夠將流量路由到 k8s 之外的系統

manifest

前面的實例中已經通過 Deployment 部署了 10 個監聽 8080 端口的 Pod. 但是因爲沒有配置網絡, 所以我們通過宿主機 IP+8080 並不能訪問到內部的 Pod. 正好可以通過 NodePort Service 的方式去接收流量.

apiVersion: v1
kind: Service
metadata:
  name: hello-svc
  labels:
    app: hello-world
spec:
  type: NodePort
  ports:
  - port: 8080
    nodePort: 30001
    targetPort: 8080
    protocol: TCP
  selector:
    app: hello-world

metadata 中的 labels 並不是用來篩選 Pod 的, 而是用來匹配 Service 的.

spec 中定義信息解釋:

通過 apply 提交後訪問 http://127.0.0.1:30001

使用 logs 命令可以查看這次流量被路由到了哪個節點

F:\學\k8s\TheK8sBook\services>kubectl logs -f service/hello-svc
Found 10 pods, using pod/hello-deploy-65cbc9474c-77xx9

服務註冊與服務發現

填坑之前微服務專欄中提到的虛擬 IP 實現服務發現部分

服務註冊

k8s 內部使用一個 DNS 服務作爲服務註冊中心, 他實際上是運行在 kube-system 命名空間中的一個名爲 coredns 的由 Deployment 管理的一組 Pod. 是 k8s 的原生應用.

F:\學\k8s\TheK8sBook\services>kubectl get pods -n kube-system -l k8s-app=kube-dns
NAME                      READY   STATUS    RESTARTS   AGE
coredns-f9fd979d6-9sg5x   1/1     Running   0          3h3m
coredns-f9fd979d6-bpkhw   1/1     Running   0          3h3m
  1. POST Service 的 manifest 文件到 API Server

  2. Service 被分配 ClusterIP

  3. 配置持久化到集羣存儲

  4. Endpoint 對象被創建

  5. 集羣的 DNS 發現新的 Service

DNS 內部有一個 Controller 會監聽 API Server

  1. 創建新的 DNS 記錄

  2. 每個節點上的 kube proxy 拉取 Service 配置

  3. 創建 IPVS 規則

每個節點的 kubelet 進程都會監視 Endpoint 對象的創建

服務發現

k8s 會自動配置所有容器, 讓他們能夠找到集羣的 DNS, 並用來將 Service 名字解析爲 ClusterIP. 實際操作是 k8s 爲每個容器都注入了一個 / etc/resolv.conf.

  1. 通過 DNS 解析到 Service 對應的 ClusterIP

  2. 將流量發送到 ClusterIP

  3. 但是沒有路由, 將流量發送到容器的默認網關

因爲這個 ClusterIP 是在一個特殊的名爲 servicenetwork 的網絡上, 是沒有路由可達的, 所以容器不知道該把流量發到哪, 只能發送到默認網關.

  1. 轉發到集羣節點

  2. 集羣節點也沒有路由, 在轉發到節點的默認網關

由於主機節點同樣沒有 servicenetwork 的路由, 所以只能再發往自身的默認網關.

  1. 流量被節點內核處理

  2. 流量被 IPVS 規則捕獲

  3. 將流量發往的目標 IP 重寫爲 Pod 的 IP

故障排查

由於 k8s 使用集羣內置 DNS 服務作爲服務的註冊中心使用, 所以如果有服務註冊發現相關的問題應該先排查這裏.

集羣的 DNS 是一組運行在 kube-system 命名空間中的 pod 和一個用來提供穩定網絡入庫的 Service 對象構成. 他主要包括三個組件:

所有與集羣 DNS 相關的對象都有一個 k8s-app=kube-dns 的標籤. 這個需要記住. 方便命令排查.

針對 DNS 的排查過程如下:

  1. 先確定 coredns Deployment 及其管理的 pods 運行狀態.

  2. 查看 coredns 的每個 pod 的日誌

  3. 查看 Service 是否在運行中, 且監聽端口是 53, 且有 ClusterIP

  4. 查看 Endpoint 對象是否正在運行且擁有所有 coredns pod 的 IP 地址

當 DNS 排查沒有問題後, 可以再通過啓動一個單例的名爲 dnsutils 的 pod 來排查. 這個 pod 中內置了一些常用的網絡工具如 ping, curl, nslookup.

存儲

k8s 的存儲與 docker 的存儲不一樣, 它要複雜的多, pod 將資源掛載到外部存儲需要很多個步驟, 不過也正因如此, k8s 對存儲的控制也更靈活多變.

k8s 的存儲分爲三部分: 持久化卷子系統, 插件, 存儲提供者.

pod 對象通過持久化卷子系統來完成數據與外部存儲的掛載. 而 k8s 的持久化卷子系統通過插件層訪問存儲提供者.

持久化卷中有三個主要的組件: PV: 持久化卷, PVC: 持久化卷的訪問許可, CS: 存儲類.

Volume

k8s 爲不同的部署環境, 使用需求提供了支持非常多的卷類型. 主要分爲兩類: 節點宿主機存儲與節點外存儲.

節點外存儲有awsElasticBlockStore(亞馬遜ES卷存儲), azureDisk(微軟Azure)等等等等.

節點宿主機存儲如hostPath, local則是將節點宿主機的文件系統上的文件或目錄掛載到使用這個卷的 Pod 中.

卷不能掛載到其他卷之上, 也不能與其他卷有硬連接. Pod 配置中的每個容器都必須獨立指定各個卷的掛在位置.

manifest

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
    - image: busybox
      name: test-container
      command: ["sleep""3000"]
      volumeMounts:
        - mountPath: /test-pd
          name: test-volume
  volumes:
    - name: test-volume
      hostPath:
        # 宿主上目錄位置
        path: /data

在這個聲明 Pod 的模版中的 spec 部分一併聲明瞭一個 hostPath 的卷, 指定掛載到主機的 / data 目錄下

Persistent Volume

k8s 中的 PV 對象不像 docker, 它可以綁定本地磁盤以及其他外部存儲, 他定義了集羣的存儲採用什麼介質.

一個外部存儲只能被一個 PV 使用, 一個 PV 可以被多個 POD 訪問, 但是要定義規則來確保正常訪問, 同時 Pod 不能直接訪問 PV, 必須經過 PVC.PV 對象可以通過 PVC 對 Pod 實現共享.

PV 針對於 PVC 有三種訪問模式:

PV 的核心是一個目錄, 其中可能存有數據, Pod 中的容器可以訪問這個目錄中的數據, 所採用的特定的卷類型將決定目錄如何形成, 使用何種介質保存數據以及目錄中存放的內容.

PV 可以事先通過 manifest 文件提供, 也可以通過 SC 動態創建. 他的生命週期獨立於使用它的 Pod.

manifest

apiVersion: v1
kind: PersistentVolume
metadata:
  name: test-pv
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data

這個文件的聲明含義是掛載主機的 / data 目錄上, 並且只佔用 10G 存儲空間; 除此之外, 對 PVC 允許的訪問模式是 ReadWriteOnce. storageClassName 指定爲 manual. 後面的 SC 部分會將.

PVC

Persistent Volume Claim 表達的是 Pod 對 PV 的使用請求. 概念與 Pod 相似. Pod 會佔用節點的資源, 而 PVC 申領會佔用 PV 的資源. Pod 可以請求特定數量的資源如 CPU 和內存. PVC 申領也可以請求特定的大小和訪問模式.

當 pod 通過 PVC 訪問 PV 時, 可以選擇將 PV 中的某個目錄掛載到 pod 中的容器目錄上.

當不再使用 PV 時, 我們可以通知 API Server 將 PVC 刪除, 來回收再利用資源. PVC 有三種回收策略來告訴集羣, 當 PV 從申領中釋放時應該如何處理這個數據卷.

manifest

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

這個文件的含義是 PVC 申領 storageClassName 爲 manual 的 PV 對象大小爲 3G 的存儲空間. 這裏 spec.storageClassName 要與 PV 的 manifest 中的 spec.storageClassName 相對應.

在執行 apply 提交後, 控制平面將會查找有相同 storageClassName 的且滿足申領要求的 PV 對象, 如果找的到, 則綁定到那個 PV 對象上.

F:\學\k8s\TheK8sBook\storage>kubectl get pvc
NAME            STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
test-pv-claim   Bound    test-pv   10Gi       RWO            manual         2s

Pod 使用 PVC:

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: test-pv-storage
      persistentVolumeClaim:
        claimName: test-pv-claim
  containers:
    - name: test-pv-container
      image: busybox
      command: ["sleep","3000"]
      volumeMounts:
        - mountPath: "/test-vol"
          name: test-pv-storage

spec.volumes 中不再使用 hostPath. 使用 persistentVolumeClaim 去指定要使用的 PVC.

SC

在大規模集羣中, 手動創建大量 PV 和 PVC 是非常煩瑣的. 這時可以使用 StorageClass 來動態分配. SC 可以讓我們不用手動創建 PV, 只需要創建一個 SC 對象, 然後使用一個插件與 具體的某個存儲後端聯繫起來.

SC 創建後會觀察 API Server 上是否有新的被關聯的 PVC 對象, 如果匹配的 PVC 出現, SC 會自動在後端存儲系統上創建所需要的卷, 以及在 k8s 上創建 PV.

manifest

還是在本地測試一下

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

在定義 SC 的 manifest 時注意 apiVersion 爲storage.k8s.io/v1

SC 對象是不可變得, 在部署之後不可以再修改.

定義一個 PVC 對象供 Pod 使用

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pv-ticket
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 1Gi

部署之後查看 PVC 狀態處於 Pending, 也就是等待綁定的狀態.

再啓動一個 Pod 的使用 PVC

apiVersion: v1
kind: Pod
metadata:
  name: class-pod
spec:
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: pv-ticket
  containers:
  - name: ubuntu-ctr
    image: ubuntu:latest
    command:
    - /bin/bash
    - "-c"
    - "sleep 60m"
    volumeMounts:
    - mountPath: /data
      name: data

發現啓動報錯

  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  10m    default-scheduler  0/1 nodes are available: 1 node(s) didn't find available persistent volumes to bind.
  Warning  FailedScheduling  10m    default-scheduler  0/1 nodes are available: 1 node(s) didn't find available persistent volumes to bind.

這是因爲本地卷還不支持動態製備. 所以我們要先提供一個 PV.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-storage-pv
  labels:
    type: local
spec:
  storageClassName: local-storage
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /tmp

再次查看 Pod 狀態已經運行成功.

更多的 StorageClass  https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

ConfigMap

ConfigMap 對象將配置數據從 Pod 中剝離出來. 並可以動態的在 Pod 運行時注入數據.

創建方式

F:\學\k8s\TheK8sBook\storage>kubectl create configmap profile --from-literal name=楊同港 --from-literal wx=ytg2097
configmap/profile created
F:\學\k8s\TheK8sBook\storage>kubectl create cm profile-json --from-file=profile.json
configmap/profile-json created
{
  "姓名""楊同港",
  "微信號""ytg2097"
}

注入方式

apiVersion: v1
kind: Pod
metadata:
  labels:
    chapter: configmaps
  name: envpod
spec:
  restartPolicy: OnFailure
  containers:
    - name: ctr1
      image: busybox
      command: [  "sleep""300" ]
      env:
        - name: WX
          valueFrom:
            configMapKeyRef:
              name: profile
              key: wx
        - name: NAME
          valueFrom:
            configMapKeyRef:
              name: profile
              key: name

查看環境變量

F:\學\k8s\TheK8sBook\configmaps>kubectl exec -it envpod sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # echo name:$NAME  wx: $WX
name:楊同港 wx: ytg2097
apiVersion: v1
kind: Pod
metadata:
  labels:
    chapter: configmaps
  name: envpod
spec:
  restartPolicy: OnFailure
  containers:
    - name: ctr1
      image: busybox
      command: [ "/bin/sh""-c""echo 博客作者: $(NAME) 微信號 $(WX) 濟南的爺就是爺, 除了喫就是玩, 沒別噠!" ]
      env:
        - name: WX
          valueFrom:
            configMapKeyRef:
              name: profile
              key: wx
        - name: NAME
          valueFrom:
            configMapKeyRef:
              name: profile
              key: name
F:\學\k8s\TheK8sBook\configmaps>kubectl apply -f envpod.yml
pod/envpod created

F:\學\k8s\TheK8sBook\configmaps>kubectl logs -f envpod
博客作者: 楊同港 微信號 ytg2097 濟南的爺就是爺, 除了喫就是玩, 沒別噠!
apiVersion: v1
kind: Pod
metadata:
  name: cmvol
spec:
  volumes:
    - name: volmap
      configMap:
        name: profile
  containers:
    - name: ctr
      image: nginx
      volumeMounts:
        - name: volmap
          mountPath: /etc/name
F:\學\k8s\TheK8sBook\configmaps>kubectl exec -it cmvol ls /etc/name
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
name  wx

StatefulSet

StatefulSet 也是 k8s 中的一等公民, 與 Deployment 的具備相同的能力, 可以管理 Pod, 進行動態擴縮容, 故障自愈, 滾動升級.

不同點

以上三個特性在哪些要求 Pod 保持不變的應用中非常有用.

headlessService

由 StatefulSet 部署的 Pod 是可預知的. 所以我們的一些應用可能會直接連接到某個 Pod 上去. 爲了實現這一功能, k8s 提供了一個特殊的 Service 對象 headlessService 來爲 StatefulSet 使用 headlessService 來爲 StatefulSet 中的每個 Pod 副本創建一個可預知的 DNS 主機名.

headlessService 是一個將 spec.clusterIP 設置爲 None 的 Service 對象. 當這個 headlessService 設置爲 StatefulSet 的spec.service.Name時就成爲了 StatefulSet 的 governingService.

volumeClaimTemplate

在使用 StatefulSet 時, 如果沒有 Pod 有獨立存儲的需求時, 可以使用 volumeClaimTemplates.

volumeClaimTemplate 在每次創建一個新的 Pod 副本時, 會自動創建一個 PVC, 還會自動爲 PVC 命名, 用來準確關聯 Pod, PVC 名爲 <volumeClaimTemplate 名稱>-<Pod 名 >

更多內容見官方文檔  https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/

注意 在刪除 StatefulSet 之前最好先縮容 Pod 副本數量到 0 來保證本地緩存安全落庫

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/lNok35NuwXgVRIAmt4KQbQ