Kubernetes in Action中文版.pdf 觀后筆記 一


一. K8S 介紹

Kubernetes 是一個自動調度(位置)、配置(資源)、監管和故障處理 微服務容器的系統。

說到為何推廣微服務,就要說說單體應用的危害:

  • 擴容復雜(通常垂直擴容)
  • 環境依賴復雜(動態鏈接庫版本太多)
  • 開發語言不靈活
  • 遷移成本巨大(環境難統一)

原文地址 https://www.cnblogs.com/clockq/p/12283169.html

1.1 容器介紹

一個容器里運行的進程實際上運行在宿主機的操作系統上,就像所有其他進程一樣(不像虛擬機,進程是運行在不同的操作系統上的)。但在容器里的進程仍然是和其他進程隔離的。

對於容器內進程本身而言,就好像是在機器和操作系統上運行的唯一一個進程。同時容器較虛擬機更節省硬件資源。

1.2 K8S 組件介紹

Master中:

  • kube-apiserver: 對外提供 RESTful 接口,使外部可以操作 K8S 集群,將用戶的操作保存到 etcd 中。
  • etcd:CoreOS 開發的一款輕量級、分布式的鍵值存儲系統,用於記錄 K8S 集群狀態和集群內的服務發現。
  • kube-controller-manager:監控 etcd 的改動,讀取任務的期望(需要的實例數量),交由 kube-scheduler 調度到節點執行任務。
  • kube-scheduler:負責在指定的一個或多個節點上生成任務容器並執行。同時追蹤每個節點的資源利用情況,確保容器不會超出指定節點的可利用資源。

Slave中:

  • kubelet:負責從 Master 接收消息,同時也向 Master 傳遞消息。最終告訴 Container 需要執行的任務。
  • Container(Docker):負責在節點上拉取、創建並運行特定容器。
  • kube-proxy:解決單個主機的子網對外提供服務。負責轉發請求到真正的容器,和基本的復雜均衡。

總結

K8S 可提供的功能如下:

  1. 保持容器的運行(健康檢查與自修復)
  2. 動態擴展容器實例數(K8S 可以動態修改容器數量,甚至幫助決定最佳容器數量)
  3. 命中移動目標(保證多個相同容器可以在一個固定ip提供服務,並且在容器調度時也始終有效)

二. 使用和快速入門

1. Docker Started
2. Kubernetes started

三. Pod 的介紹和使用

容器是服務實例的承載體,而 Pod 就是 K8S 中對容器的承載體的抽象。它是 K8S 中最小粒度的調度單位。
Pod 作為 K8S 中最基礎最重要的概念,我們之后使用的所有對象和技術,也都是在圍繞它和管理它。

3.1 為什么需要 Pod 概念

前面我們已經知道,K8S 就是在管理和調度容器,而容器技術已經如此成熟,為何還需要一層 Pod 的抽象來承載容器呢?

加入一層抽象只是為了更好的解耦,將 K8S 的調度功能與實際的容器技術分離,不再局限 Docker 或其他某個容器。

更可以實現一些強大功能。比如說可以在一個 Pod 內包含多個容器,以此更方便的管理一整組容器。
同時在一個 Pod 內的容器共享 Linux 命名空間,便可以擁有相同的主機名,網絡配置甚至是 IPC,使的兩個微服務之間必須要的網絡通信進化成進程間通信。

所有 Pod 都在同一個共享網絡地址空間中,也就意味着 Pod 間沒有 NAT 網關(平坦網絡),每個 Pod 都知道目標 IP 地址進行之間通信。

這么一看,Pod 是不是很像真實的物理主機,而每個容器就像是主機里的一個獨立進程,只不過每個進程都有自己的文件系統(因為容器的層級概念)。

3.2 Pod 容器划分

Pod 內的容器盡量少

如上所說,既然 Pod 好比一個獨立的物理主機,那把所有的容器放到一個 Pod 里面執行,不是又簡單又方便。但是為什么不推薦呢?

首先, K8S 集群情況下,每個 Pod 之后在一個真正的 K8S Node 上運行,Pod 的粒度太大不能最大限度的利用物理資源。

其次,同一個應用的每部分服務,他們的伸縮值不同。比如說一套前后分離的 BS 應用,簡單的分為前端頁面層,中間邏輯層和后端存儲層。當用戶並發量不斷增多的時候,項目瓶頸在哪里?
如果數據庫的讀寫最先到達瓶頸,我們可以建立存儲集群,而不用增加另外兩個服務來搶占存儲層的資源。
如果邏輯層的處理速度到達瓶頸(最可能先到瓶頸的),我們可以直接加大邏輯層 Pod 的實例數量,來滿足用戶並發需求。

這也正是 Docker 和 K8S 所期望的效果。所以除了必須情況,我們盡量使 Pod 內只包含單個容器,而容器內也只有一個服務。做到三者 1:1 關系,方便項目在之后的調度和維護工作。

3.3 Pod 的組成

  • metadata: 關於 Pod 的元數據信息,包括名稱,命名空間,標簽和注解等
  • spec: 描述 Pod 期望的運行狀態,比如要運行的容器,運行參數,需要掛載的卷等
  • status: 描述 Pod 當前的運行狀態,如運行容器,容器的狀態,Pod的IP等

可以通過 kubectl explain podskubectl explain pod.spec 查看對象包含的詳細信息,添加參數 --recursive 遞歸列出簡化的信息。

3.4 Pod 的啟動

假如存在如下的kubia-manual.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: kubia-manual
  labels:
    app: test
    noderole: master
spec:
  containers:
  - name: kubia-manual
    image: kubia/manual
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 8080
    - name: https
      containerPort: 8443

使用命令 kubectl create -f kubia-manual.yaml 創建一個 Pod。

3.5 Pod 的標簽

標簽是一種簡單卻功能強大的 Kubemetes 特性,不僅可以組織 Pod,也可以組織所有其他的 Kubemetes 資源。標簽是可以附加到資源的任意鍵值對,用以選擇具有該確切標簽的資源(這是通過標簽選擇器完成的)。

通過給這些pod添加標簽,可以得到一個更有組織化的系統,以便我們理解。
此時每個pod都標有幾個標簽:

  • enable,它指定資源是否要啟用。
  • app, 它指定pod屬於哪個應用、組件或微服務。
  • rel, 它顯示在pod中運行的應用程序版本是stable、beta還是canary。
  1. 使用命令 kubectl get po --show-labels 列出所有 Pod 的所以 lables
NAME            READY   STATUS  RESTARTS    AGE     LABELS
kubia-manual    1/1     Running 0           16m     <none>
kubia-manual-v2 1/1     Running 0           2m      creat—method=manual,env=prod ld run=kubia
kubia-zxzij     1/1     Running 0           1d      run=kubia
  1. -L 也可以使用命令 kubectl get po -L creation_method,env 將標簽展開
NAME                READY   STATUS      RESTARTS    AGE     CREATION METHOD     ENV
kubia-manual        1/1     Running     0           16m     <none>              <none>
kubia-rnanual-v2    1/1     Running     0           2m      manual              prod
kubia-zxzij         1/1     Running     0           1d      <none>              <none>
  1. 添加標簽 kubect label po kubia-manual method=manual
  2. 修改標簽 kubect label po kubia-manual-v2 env=debug --overwrite
  3. -l 利用標簽篩選 kubectl get po -l creation_method=manual
NAME                READY   STATUS      RESTARTS    AGE     CREATION METHOD     ENV
kubia-manual        1/1     Running     0           16m     manual              <none>
kubia-rnanual-v2    1/1     Running     0           2m      manual              prod

3.6 利用標簽調度節點

集群內的每台物理主機可能擁有不同的硬件資源,比如個別幾個節點擁有 GPU 加速能力,我們就可以在將該主機加入集群的時候,加入一些 label,來給之后的自動化調度提供便利。

  1. 給一個 node 添加標簽
    kubectl label node gpu-node1 gpu=true

  2. 將 pod 調度到指定 node,yaml 啟動文件中加入下列內容

spec:
  nodeSelector:
    gpu: "true"

3.7 命名空間(namespace)概念

K8S 的命名空間不同於 Linux 的命名空間,它只是提供一個簡單的作用域功能。可以實現將對象分割成完全獨立且不重疊的多個組。也就是在不同命名空間內,可以有兩個名稱、標簽等等完全相同的 Pod。

我們可以利用這一點,把集群內的應用分出另一個維度(開發,生產,QA)的資源。

雖然大多數類型的資源都與命名空間相關,即被約束在某一命名空間。但也有一些資源時命名空間共享的。同時,各個命名空間之間網絡是否互通,取決於 K8S 所使用的網絡解決方法。

  1. 查看所有命名空間 kubectl get ns
  2. 查看某個命名空間的資源 kubectl get po --namespace kube-system 或者 kubectl get po -n kube-system
  3. 創建一個新的命名空間 kubectl create namespace custom-namespace

3.8 Pod 的刪除

在刪除 Pod 的過程中,實際上是在終止該 pod 中的所有容器。
K8S 向容器的進程發送一個 SIGTERM 信號並等待一定的秒數(默認30),使其可以正常關閉。
如果他沒有及時關閉,則通過 SIGKILL 終止該進程。
因此,為了確保容器進程總是可以正常關閉,進程要可以正確的處理 SIGTERM 信號。

  1. 按照名字刪除 kubectl delete po kubia-manual
  2. 同時刪除多個Pod kubectl delete po pod1 pod2
  3. 按照 label 刪除 kubectl delete po -l creation method=manual

四. 部署托管的 Pod

按照之前的方式部署 Pod,已經可以正確的運行和訪問服務了,但還有些隱患。
假如 Pod 在某個節點開始執行,K8S 就會監控這個 Pod。並且會在 Pod 內的容器出錯后自動重啟,保持服務健康(也就是在 K8S 中運行的服務不需要什么特殊處理就擁有自恢復能力)。

  • 但是如果整個節點出錯,會導致整個節點上的全部 Pod 丟失,並且不會在新的節點重生。
  • 即使進程沒有崩潰,容器和 Pod 還在運行,但也會停止正常的功能。比如 OOM 的 JVM 程序並不會自動關閉。
  • 無限循環和死鎖的程序也不能正常工作,也不會繼續提供正常服務。

4.1 存活探針

Kubemetes 可以通過存活探針 (liveness probe) 檢查容器是否還在運行。可以為 Pod 中的每個容器單獨指定存活探針。如果探測失敗,Kubenetes 將定期執行探針並重新啟動容器。

K8S 一共存在三種存活探針機制,分別是:

  • HTTP GET 探針
  • TCP 套接字探針
  • Exec 探針

HTTP GET 探針用例

apiVersion: v1
kind: Pod
metadata:
  name: kubia-manual
  labels:
    app: test
    noderole: master
spec:
  containers:
  - name: kubia-manual
    image: kubia/manual
    imagePullPolicy: IfNotPresent
    livenessProbe:
      httpGet:
        path: /
        port: 8088
      initialDelaySeconds: 100  #首次探針延遲時間,也就是預計的系統啟動時間
      timeoutSeconds: 5         #每次探針的超時時間

4.2 了解 RC(Replication Controller)

用戶直接創建的 Pod 由 Kubelet 直接管理,但由於 Kubelet 本身運行在某個特定節點上。這樣也就導致,當某個節點異常終止時,Kubelet 也就會停止,他所管理的 Pod 也就無法維護和工作。

為了確保我們的服務可以始終運行,需要使用 RC 或類似的機制來管理 Pod。

1、 RC 的工作原理

image

可以看到,RC 主要由三部分組成:

  • label selector(標簽選擇器),用於確定 RC 作用域中有哪些 pod
  • replica count(副本個數),用於指定應該運行的 pod 數量
  • pod template(pod 模板),用於創建新的 pod 副本

注意,更改 RC 的標簽選擇器或者模板,對現有 Pod 都沒有任何影響。更改標簽選擇器,只會導致 RC 不再去關注之前的標簽。而創建 Pod 后,RC 也不會繼續關心 Pod 的實際內容是否和現在的模板一致,只會在新創建的 Pod 時使用新的模板。

2、RC 用例

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia
spec:
  replicas: 3       #需要的實例數量
  selector:
    app: test       #要監控的標簽選擇器,可以不寫,也建議不寫,RC會自動推導
  template:
    metadata:
      labels:
        app: test   #RC 監控的依據
    spec:
      containers:
      - name: kubia-manual
        image: kubia/manual
        imagePullPolicy: IfNotPresent

3、修改 RC 的實例數量

kubectl scale rc kubia --replicas=10
或者
kubectl edit rc kubia

4、刪除 RC

如果刪除 RC 的同時,想保留由它創建的所有 Pod,可以執行
kubectl delete rc kubia --cascade=false

4.3 了解 RS(Replica Set)

最初,RC 是用於復制和在異常時重新調度節點的唯一組件,后來又引入了一個名為 ReplicaSet 的類似資源。它是新一代的 RC, 並且將其完全替換掉(ReplicationController 最終將被棄用)。

RS 和 RC 幾乎完全相同,K8S 也推薦使用 RS,但我們幾乎不用親自創建它,而是在創建更高等級的 Deployment 時自動創建。

1、RC 和 RS 的比較

標簽比較能力 RC RS
根據 K=V 匹配 可以 可以
匹配多組 K=V 不可以 可以
包含某個 K(V = *) 不可以 可以
不包含某個 K 不可以 可以

2、RS 用例

apiVersion: apps/v1beta2        #不是 v1 了
kind: ReplicaSet
metadata:
  name: kubia
spec:
  replicas: 3                   #需要的實例數量
  selector:
    matchLabels:                #不是直接的 KV 選擇
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
      - name: kubia-manual
        image: kubia/manual
        imagePullPolicy: IfNotPresent

3、selector 進階

selector:
  matchExpressions:
  - key: app
    operator: In
    values:
    - kubia

通過將簡單的 matchLabels 換成更具有表達能力的 matchExpressions,上面的例子中,產生的結果與之前一樣,但明顯可以進行更強大的擴展。

  • key: 要操作的標簽
  • operator: 會進行的運算符
    • In: key 需要與 values 中某個值匹配
    • NotIn: key 不能與 values 中任何一個值匹配
    • Exists: Pod 中必須包含指定的 key,不能指定 values 字段
    • DoesNotExist: Pod 中不能包含指定 key,不能指定 values
  • values: 可選的值列表,取決於運算符

需要注意,matchExpressions 內存在多個表達式時,它們的組合關系是 and, 也就是每個表達式都必須為 true,標簽選擇器才生效。如:

selector:
 matchExpressions:
 - key: app
   operator: In
   values:
   - kubia
 - key: type
   operator: In
   values:
   - test

如果想要 or 的組合關系,需要存在多個 matchExpressions 表達式。如:

selector:
 matchExpressions:
 - key: app
   operator: In
   values:
   - kubia
 matchExpressions:
 - key: type
   operator: In
   values:
   - test

4.4 了解 DS(Daemon Set)

RC 和 RS 都用於在 K8S 集群上運行部署特定數量的 Pod,但有時候希望 Pod 在集群中的每個節點上剛好運行一個實例時,就不是那么容易了。

舉個栗子:希望在每個節點上運行日志收集器和資源監控器。或者 K8S 自己的 kube-proxy 服務,都是在每個節點上運行單一實例的栗子。

使用 DaemonSet 只在特定節點創建 Pod

需要知道的是,DS 常用來運行系統服務,也就導致 DS 創建的 Pod 會繞過調度器。這也就導致我們無法利用調度器的一些功能和配置,比如當我們配置了一些節點是不可調度的,但 DS 還是會在該節點創建 Pod。

DS 默認會在所有節點創建 Pod,除非進行特殊配置,而有時候我們正好需要這種配置。

通過 Pod 模板的 nodeSelector 屬性就可以完成上述操作。

使用的 yaml 如下:

apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
  name: ssd-monitor
spec:
  selector:
    matchLabels:
      app: ssd-monitor
  template:
    metadata:
      labels:
        app: ssd-monitor
    spec:
      nodeSelector:
        disk: ssd
      containers:
      - name: main
        image: luksa/ssd-monitor

4.5 運行執行單次任務的臨時 Pod

之前說到的各種資源 RC、RS、DS都是持續運行任務,即便容器內的進程執行結束,也會被調度重啟,永遠沒有完成態。但對於一個真正的任務,可能就是一個臨時的,執行后就銷毀的臨時 Pod,不應該重新啟動。

1、了解 Job

Job 資源類型就是對上述問題的支持。如果節點發生故障,則由 Job 管理的 Pod 還是會在其他節點上被調度。

2、Job 用例

apiVersion: batch/v1
kind: Job
metadata:
  name: batch-job
spec:
  template:
    metadata:
      labels:
        app: batch-job
    spec:
      completions: 5                #順序的執行5個 Pod,也就是執行 5 次
      parallelism: 2                #最多可以兩個 Pod 並行
      restartPolicy: OnFailure      #重啟策略,默認為 Always,如無必要,不要使用默認
      containers:
      - name: main
        image: luksa/batch-job

3、動態擴展 Job 內運行的 Pod 實例

kubectl scale job batch-job --replicas=3

4.6 讓 Job 定期運行或稍后運行

正常 Job 資源在創建時會立即運行 Pod,如果需要在特定時間運行(如 cron 任務一樣),K8S 也是支持的。

1、CornJob 用例

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cron-job
spec:
  schedule: "0,15,30,45 * * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: cron-job
        spec:
          restartPolicy: OnFailure      #重啟策略,默認為 Always,如無必要,不要使用默認
          containers:
          - name: main
            image: luksa/batch-job

和 Job 的配置相比,CronJob 就好像在外面包裹了一層 jobTemplate,再多加了一個屬性 schedule

實際上也的確是這樣,CronJob 會創建 Job,Job 會創建 Pod。

2、schedule 講解

快速分析就是該屬性一共有五個時間條目,分別用空格分隔

分鍾 小時 每月第幾天 每年第幾月 星期幾(0-6)

如例子中 0,15,30,45 * * * * 表示每 15min 運行一次。
如果改為 0,15,30,45 1 * * * 表示每天 0:15、0:30、0:45 執行一次。
如果改為 0,15,30,45 1 1 * * 表示每月一號的 0:15、0:30、0:45 執行一次。
如果改為 0 3 * * 0 表示每個星期天的 3:00 執行一次。

五. 服務介紹

Pod 通常需要對來自集群內部其他 Pod 或者外部的請求做出響應。

在最初沒有 K8S 的時候,管理員會通過配置文件指定服務和 IP 的映射關系,或者配置反向代理或 DNS 等服務來完成。但對於 K8S 這些操作就沒用了。 因為:

  • Pod 是臨時的:Pod 隨時可能啟動或停止
  • Pod 數量不固定:K8S 原生就支持動態伸縮服務,導致每種服務的實例數量並不固定
  • Pod 的 IP 不固定:因為前兩條,導致每個 Pod 或每種服務的 IP 並不是一直不變的

為了解決上述問題,同時可以實現原本想要的 服務 -> IP 解析的功能,K8S 提供了一種資源類型 —— 服務(Service)。

5.1 介紹服務SVC

1、SVC 原理圖

image

2、SVC 用例

最簡單的創建方式,使用 expose 即可,方式如下:

kubectl expose rc kubia --type=LoadBalancer --name kubia-http

也可以使用配置文件方式:

apiVersion: v1
kind: Service
metadata:
  name: kubia-http
spec:
  sessionAffinity: ClientIP     #會話親和性,默認為 None
  ports:
  - name: http          #只暴露一個端口時名字可選,但同時暴露多個端口時必須指定名字
    port: 80            #暴露到宿主的端口
    targetPort: 8080    #容器開放的端口,也可以使用別名
  selector:
    app: kubia          #篩選要代理的 Pod

3、配置會話親和性

SVC 會把請求隨機的轉發到一個 Pod 上,即使請求來自同一個客戶端。

如果希望請求優先連接到之前使用過的 Pod 上,可以配置會話親和性。如上例所示。上例會使服務將來自同一個客戶端的代理轉發到同一個Pod中。

K8S 也僅僅支持 NoneClientIP 兩種配置。竟然連基於 hash 和 cookie 的會話親和性都不支持?那是因為 K8S 服務不是在 HTTP 層面工作。SVC 處理 TCP 和 UDP 的包,並不關心上層網絡協議內容。而 cookie 是 HTTP 協議的一部分,SVC 並不知道它們。

4、服務發現

通過創建服務,已經可以在固定的 IP:Port 代理一類 Pod 並提供服務,不論 Pod 的數量如何改變或重啟,服務的暴露位置不變。

目前存在兩種服務發現方式 環境變量DNS,可以通過 Pod 配置中 spec 的 dnsPolicy 屬性控制

* 環境變量方式

但其他的 Pod 如何和有 SVC 的 Pod 通信呢?
不需要先創建服務,然后把地址記住傳遞給別的 Pod,那樣太不靈活了。K8S 提供服務發現機制。
在每個 Pod 運行之前,K8S 會初始化一系列環境變量指向現在存在的服務(和Docker一樣)。只有創建的服務早於該 Pod,Pod 上的進程就可以根據環境變量正確訪問到服務的地址,訪問到 Pod。

* DNS方式

kube-system 命名空間下有個 Pod 叫 kube-dns,負責運行 DNS 服務。在集群中的其他 Pod 都被配置成使用其作為 DNS(修改容器的/etc/resolv.conf),使每個容器都知道系統中運行的所有服務。

Pod 需要拼接全限定域名(FQDN)來訪問 DNS 獲得服務地址。

舉個栗子svc-test.ns-test.svc.cluster.local

  • svc-test:服務名稱
  • ns-test:服務所在的名稱空間
  • svc.cluster.local:是集群本地服務名稱中使用的可配置集群域后綴

當 Pod 和要訪問的 SVC 在一個命名空間下,可以省略集群域后綴,甚至是省略命名空間,直接以服務名查找到服務IP地址,但端口還需在環境變量中獲得。

可以進入 Pod 內部的 bash,執行命令測試一下,和 Docker 類似:

kubectl exec -it <pod-name> -- /bin/bash
  • -i 交互模式,確保 stdin 開放,可以在 shell 中輸入命令
  • -t 分配一個偽終端,使 stdout 可以回顯出來
  • -- 用來表示 kubectl 命令項的結束,如果之后的內容沒有參數(-*)則不加沒什么問題,但如果有參數還沒有雙橫杠標識,參數會被解析為 kubectl 的參數,造成一下意料之外的執行結果

需要注意,對於想要直接 ping 通服務 IP 的做法,是不會成功的,因為服務的集群IP 是一個虛擬IP,並且只有在和端口結合時才有意義。

5.2 連接集群外部的服務

目前服務可以連接集群內的一個或多個 Pod 服務,但也可能需要連接集群外的服務。

K8S 支持這種做法,並且充分利用了服務的負載均衡和服務發現,讓 Pod 無須知道服務提供者到底是集群內還是集群外,都能像之前的服務一樣簡單使用。

1、介紹 Endpoints

服務並不是直接和 Pod 直接相連的。而是有一種資源介於二者之間,他就是 Endpoints。
Endpoints 資源就是暴露一個服務的 IP 地址和端口的列表。

盡管服務在 spec 中定義了 selector 標簽選擇器,但在重定向之前,選擇器會構建IP和端口的列表,然后存入 Endpoints 資源中。當客戶端需要連接服務時,服務選擇 Endpoints 存的列表中的一個,並將傳入連接重定向到在該位置監聽的服務器上。

2、配置服務的 Endpoints

因為多了這么一層抽象,所以服務的實現和功能也就更加的靈活,甚至是手動配置和更新 Endpoints 來增加 SVC 的功能。

創建沒有選擇器的服務

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:            #監聽80端口的請求
  - port: 80

為沒有選擇器的服務配置 Endpoints

apiVersion: v1
kind: Endpoints
metadata:
  name: external-service        #需要和Service的名稱一致
subsets:
- addresses:
  - ip: 11.11.11.11             #服務重定向列表
  - ip: 22.22.22.22
  ports:
  - port: 80                    #Endpoints 的目標端口

3、為外部服務創建別名

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  type: ExternalName
  externalName: www.baidu.com
  ports:
  - port: 80

這樣服務創建后,可以使用 external-service 來訪問 www.baidu.com:80 的服務,這樣不使用外部服務實際的 FQDN,等於在中間多了一層抽象,以后修改外部的服務時,對於集群內無須任何修改。

5.3 將服務暴露給外部客戶端

有幾種方式可以在外部訪問服務:

  • 簡單粗暴的端口轉發:使用命令$ kubectl port-forward kubia 8080:80 開啟本機 8080 端口並將此端口的連接轉發到 kubia Pod 的 80 端口上。也支持高級資源,如kubectl port-forward deploy/kubia 8080:80 或者 rssvc等。端口轉發僅適用於 TCP 協議。
  • 服務類型設置為NodePort:對於NodePort服務,每個集群節點在節點本身打開一個端口,並將在該端口上接受到的請求重定向到基礎服務。可以通過任何節點IP和預留的端口訪問。(端口范圍30000-32767)
  • 服務類型設置為LoadBalancer:需要雲基礎設施的支持,來提供一個負載均衡器。屬於NodePort的擴展,使的服務可以通過專有的負載均衡器訪問,將請求重定向跨所有節點的端口上。客戶端通過負載均衡器提供的外網 IP 連接到服務。
  • 創建一個Ingress類型資源:通過一個IP地址公開多個服務,因為該資源運行在HTTP層上,所以可以提供比前兩種更多的功能,如cookie親和性等。

1、NodePort 用例

apiVersion: v1
kind: Service
metadata:
  name: kubia-nodeport
spec:
  externalTrafficPolicy: Local      # 只轉發到同一節點的 Pod 上
  type: NodePort                    # 服務類型,默認是ClusterIP
  ports:
  - port: 80                        # 服務集群IP的端口號
    targetPort: 8080                # Pod的目標端口號
    nodePort: 30123                 # 通過集群各節點IP可訪問的端口, 不配置則隨機指定
  selector:
    app: kubia

所以可以通過

  • cluster-ip:80
  • nodes:30123

兩種方式訪問 pod:8080 的服務。
image

關於 Minikube 的一些問題:

一、 SVC 開啟后還是無法訪問?
可以運行$ minikube service <service-name>開啟服務。

二、 如何修改 NodePort 的端口范圍?
對於 Minikube 進行下列操作:
在啟動時加入參數 $ minikube start --extra-config=apiserver.service-node-port-range=1-65535

對於 Kubernetes on Docker,進行如下操作,其他系統也是類似的方式

  1. 進入 Docker 終端 $ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty
  2. 修改 ApiServer 文件 $ vi /etc/kubernetes/manifests/kube-apiserver.yaml
  3. 在文件內的 command 中添加 - --advertise-address=192.168.65.3 並保存
  4. 關閉終端,然后等幾分鍾,ApiServer 會重新啟動
  5. 查看是否修改成功 kubectl edit -n kube-system pod kube-apiserver-docker-desktop

2、LoadBalancer 用例

apiVersion: v1
kind: Service
metadata:
  name: kubia-loadbalancer
spec:
  type: LoadBalancer        #服務類型,默認是ClusterIP
  ports:
  - port: 80                #服務集群IP的端口號
    targetPort: 8080        #Pod的目標端口號
  selector:
    app: kubia

創建服務后一段時間,雲基礎架構就會生成一個外部IP地址並寫入服務的 EXTERNAL-IP 中,並可以通過 external-ip:80 端口訪問負載均衡器的方式訪問到代理的 Pod。

image

5.4 了解 Ingress

實現原理:
image

為什么有了 LB 服務,還需要 Ingress 呢?因為每個 LB 服務都需要一個負載均衡器和一個外網 IP,從圖中可以看出 Ingress 只需要一個外網 IP 和一個自己維護的域名表即可實現 LB 服務的要求。

同時 Ingress 是 HTTP 層的數據包管理,可以提供一些 SVC 不能實現的功能。比如基於 cookie 的會話親和性等功能。

1、啟用 Ingress

推薦幾個不錯的項目和文章:

2、Ingress 用例

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubia
spec:
  tls:                          # 配置 TLS 需要的安全證書,可選
  - hosts:
    - kubia.example.com
    secretName: tls secret
  rules:
  - host: kubia.example.com     # Ingress 代理的域名
    http:
      paths:
      - path: /kubia1
        backend:                # host1 代理指定的后端服務 kubia1
          serviceName: kubia1
          servicePort: 80
      - path: /kubia2
        backend:                # host1 代理指定的后端服務 kubia2
          serviceName: kubia2
          servicePort: 80
  - host: demo.example.com      # Ingress 代理的域名
    http:
      paths:
      - path: /
        backend:                # host2 代理指定的后端服務 kubia
          serviceName: demo
          servicePort: 80

注意:某些雲提供商提供的 Ingress 需要代理到 type: NodePort 的服務上。

啟動上面用例,執行$ kubectl get ingress,在輸出信息中可以看到 Ingress 控制器的外部 IP,然后使用 DNS 或者修改 hosts 的域名映射,添加 kubia.example.com -> IP 后,即可使用域名訪問 Ingress 代理的服務和 Pod。

3、Ingress 原理

images

4、配置 Ingress 的 TLS 傳輸

當客戶端創建到 Ingress 控制器的 TLS 連接時,控制器將終止 TLS 向下的連接。也就是客戶端與控制器的連接是加密的,但控制器與后端服務和 Pod 的連接不需要加密,運行在 K8S 的 Pod 也不需要支持 TLS。

創建 TLS 秘鑰和證書,需要用到 Secret 中,現在理解即可:

$ openssl genrsa -out tls.key 2048
$ openssl req -new -x509 -key tls.key -out tls.cert -days 360 -subj /CN=kubia.example.com
$ kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key

5.5 等待 Pod 就緒信號

如果 Pod 啟動后需要時間加載配置或數據,又或者需要預熱數據,來防止第一次請求時間過長等問題。此時 Pod 啟動后不想立即加入 SVC 提供服務,直至該 Pod 准備接續。如何實現繼續往下看。

1、 介紹就緒探針

就緒探針和存活探針基本一樣,兩者之間對比如下:

比較項 存活探針 就緒探針
作用范圍 Pod 內的容器 SVC 下的 Pod
探針類型 HTTP GET 探針,TCP 套接字探針,Exec 探針 HTTP GET 探針,TCP 套接字探針,Exec 探針
檢查模式 周期檢查 周期檢查
默認屬性 delay=0s timeout=1s period=10s #success=1 #failure=3 delay=0s timeout=1s period=10s #success=1 #failure=3
失敗后反應 刪除容器並創建一個新的 從服務中移除失敗的 Pod,Pod 就緒后再添加回來

2、就緒探針用例

apiVersion: v1
kind: Pod
metadata:
  name: kubia-readiness
  labels:
    app: test
    noderole: master
spec:
  containers:
  - name: kubia-readiness
    image: kubia/manual
    imagePullPolicy: IfNotPresent
    readinessProbe:
      httpGet:
        path: /
        port: 8088
      initialDelaySeconds: 100  #首次探針延遲時間,也就是預計的系統啟動時間
      timeoutSeconds: 5         #每次探針的超時時間

5.6 headless服務來發現獨立的 Pod

現在已經可以通過 SVC 均衡分流客戶端到每個 Pod 的連接,SVC 會把請求隨機轉發到任意一個 Pod 上。
但是,如果一個請求需要同時連接所有的 Pod 呢?目前所學是無法實現的。

通常,到執行服務的 DNS 查找時,DNS 會返回單個 IP (服務在集群的 IP)。但是,如果不讓 K8S 提供服務集群 IP(將服務 spec 中的 clusterIP=None),則 DNS 服務器將返回 支持該服務的所有 Pod IP。

headless 用例

apiVersion: v1
kind: Service
metadata:
  name: kubia-headless
spec:
  clusterIP: None           # 設置服務為 headless 類型
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: test
    noderole: master

headless 服務同樣提供跨 pod 的負載均衡,但是方式是 DNS 輪詢而不是通過服務代理。

5.7 服務的故障排除

  1. 首先檢查 SVC 的 CLUSTER-IPEXTERNAL-IP 是否是客戶端可訪問的位置。即集群內連接 CLUSTER-IP,集群外訪問 EXTERNAL-IP
  2. 注意連接的端口應該是服務的公開的端口,而不是代理的 Pod 目標端口。
  3. 不要嘗試 ping 服務的IP,前面說了,服務 IP 是虛擬 IP,本身並不存在,只有和提供端口一起使用才有意義。
  4. 如果定義存活探針或就緒探針要確保 Pod 已經准備就緒。
  5. 使用 kubectl describe svckubectl get endpoints 檢查 SVC 是否正確代理 Pod。
  6. 如果使用 FQDN(全限定域名)來訪問服務但不起作用,可以試試使用 CLUSTER-IP 是否可以訪問。
  7. 直接使用 Pod IP 訪問服務,確保 Pod 可以正確處理請求。

初級篇小結

以上主要是 K8S 的基本功能使用,暫時先寫這么多。
同時也是避免篇幅太長,不方便回顧和閱讀。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM