深入剖析Kubernetes學習筆記:Pod基本概念進階(15)


一、特殊的 Volume 

這種特殊的 Volume,叫作 Projected Volume,你可以把它翻譯為“投射數據卷”

備注:Projected Volume 是 Kubernetes v1.11 之后的新特性

作用

 含義

Projected Volume種類

一、Secret

1、作用

2、使用場景

Secret 最典型的使用場景,莫過於存放數據庫的 Credential 信息,比如下面這個例子:

3、方式一:kubectl create secret

apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume 
spec:
  containers:
  - name: test-secret-volume
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: mysql-cred
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: mysql-cred
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass

這里用到的數據庫的用戶名、密碼,正是以 Secret 對象的方式交給 Kubernetes 保存的。完成這個操作的指令,如下所示:

$ cat ./username.txt
admin
$ cat ./password.txt
c1oudc0w!
 
$ kubectl create secret generic user --from-file=./username.txt
$ kubectl create secret generic pass --from-file=./password.txt

其中,username.txt 和 password.txt 文件里,存放的就是用戶名和密碼;而 user 和 pass,則是我為 Secret 對象指定的名字。而我想要查看這些 Secret 對象的話,只要執行一條 kubectl get 命令就可以了:

$ kubectl get secrets
NAME           TYPE                                DATA      AGE
user          Opaque                                1         51s
pass          Opaque                                1         51s

4、方式二:編寫YAML

當然,除了使用 kubectl create secret 指令外,我也可以直接通過編寫 YAML 文件的方式來創建這個 Secret 對象,比如:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  user: YWRtaW4=
  pass: MWYyZDFlMmU2N2Rm

接下來,我們嘗試一下創建這個 Pod:

$ kubectl create -f test-projected-volume.yaml

驗證這些Secret對象是不是已經在容器里了

$ kubectl exec -it test-projected-volume -- /bin/sh
$ ls /projected-volume/
user
pass
$ cat /projected-volume/user
root
$ cat /projected-volume/pass
1f2d1e2e67df

 自動更新

 注意事項

 5、生產最佳實踐

二、ConfigMap

1、與 Secret 的區別

2、使用方式

比如,一個 Java 應用所需的配置文件(.properties 文件),就可以通過下面這樣的方式保存在 ConfigMap 里:

# .properties 文件的內容
$ cat example/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
 
# 從.properties 文件創建 ConfigMap
$ kubectl create configmap ui-config --from-file=example/ui.properties
 
# 查看這個 ConfigMap 里保存的信息 (data)
$ kubectl get configmaps ui-config -o yaml
apiVersion: v1
data:
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  name: ui-config
  ...

備注:kubectl get -o yaml 這樣的參數,會將指定的 Pod API 對象以 YAML 的方式展示出來。

三、Downward API

 1、作用

舉個例子:

apiVersion: v1
kind: Pod
metadata:
  name: test-downwardapi-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
          readOnly: false
  volumes:
    - name: podinfo
      projected:
        sources:
        - downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels

2、Yaml文件注解

$ kubectl create -f dapi-volume.yaml
$ kubectl logs test-downwardapi-volume
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

目前,Downward API 支持的字段已經非常豐富了,比如:

1. 使用 fieldRef 可以聲明使用:
spec.nodeName - 宿主機名字
status.hostIP - 宿主機 IP
metadata.name - Pod 的名字
metadata.namespace - Pod 的 Namespace
status.podIP - Pod 的 IP
spec.serviceAccountName - Pod 的 Service Account 的名字
metadata.uid - Pod 的 UID
metadata.labels['<KEY>'] - 指定 <KEY> 的 Label 值
metadata.annotations['<KEY>'] - 指定 <KEY> 的 Annotation 值
metadata.labels - Pod 的所有 Label
metadata.annotations - Pod 的所有 Annotation
 
2. 使用 resourceFieldRef 可以聲明使用:
容器的 CPU limit
容器的 CPU request
容器的 memory limit
容器的 memory request

3、生產最佳實踐

 4、需要注意

 5、建議

四、Service Account

1、作用

 2、什么是Service Account

3、默認“服務賬戶

 4、這是如何做到的呢?

當然還是靠 Projected Volume 機制。

如果你查看一下任意一個運行在 Kubernetes 集群里的 Pod,就會發現,每一個 Pod,都已經自動聲明一個類型是 Secret、名為 default-token-xxxx 的 Volume,然后 自動掛載在每個容器的一個固定目錄上。比如:

$ kubectl describe pod nginx-deployment-5c678cfb6d-lg9lw
Containers:
...
  Mounts:
    /var/run/secrets/kubernetes.io/serviceaccount from default-token-s8rbq (ro)
Volumes:
  default-token-s8rbq:
  Type:       Secret (a volume populated by a Secret)
  SecretName:  default-token-s8rbq
  Optional:    false

這個 Secret 類型的 Volume,正是默認 Service Account 對應的 ServiceAccountToken。所以說,Kubernetes 其實在每個 Pod 創建的時候,自動在它的 spec.volumes 部分添加上了默認 ServiceAccountToken 的定義,然后自動給每個容器加上了對應的 volumeMounts 字段。這個過程對於用戶來說是完全透明的。

這樣,一旦 Pod 創建完成,容器里的應用就可以直接從這個默認 ServiceAccountToken 的掛載目錄里訪問到授權信息和文件。這個容器內的路徑在 Kubernetes 里是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount ,而這個 Secret 類型的 Volume 里面的內容如下所示:

$ ls /var/run/secrets/kubernetes.io/serviceaccount 
ca.crt namespace  token

所以,你的應用程序只要直接加載這些授權文件,就可以訪問並操作 Kubernetes API 了。而且,如果你使用的是 Kubernetes 官方的 Client 包(k8s.io/client-go)的話,

它還可以自動加載這個目錄下的文件,你不需要做任何配置或者編碼操作。InClusterConfig

當然,考慮到自動掛載默認 ServiceAccountToken 的潛在風險,Kubernetes 允許你設置默認不為 Pod 里的容器自動掛載這個 Volume。

除了這個默認的 Service Account 外,我們很多時候還需要創建一些我們自己定義的 Service Account,來對應不同的權限設置。

這樣,我們的 Pod 里的容器就可以通過掛載這些 Service Account 對應的 ServiceAccountToken,來使用這些自定義的授權信息。在后面講解為 Kubernetes 開發插件的時候,我們將會實踐到這個操作。

五、容器健康檢查

在 Kubernetes 中,你可以為 Pod 里的容器定義一個健康檢查“探針”(Probe)。這樣,kubelet 就會根據這個 Probe 的返回值決定這個容器的狀態,而不是直接以容器進行是否運行(來自 Docker 返回的信息)作為依據。這種機制,是生產環境中保證應用健康存活的重要手段。

1、容器健康檢查

我們一起來看一個 Kubernetes 文檔中的例子。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: test-liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

1、有趣的容器

2、livenessProbe(健康檢查)

2、現在,讓我們來具體實踐一下這個過程。

1、首先,創建這個 Pod:

$ kubectl create -f test-liveness-exec.yaml

2、然后,查看這個 Pod 的狀態:

$ kubectl get pod
NAME                READY     STATUS    RESTARTS   AGE
test-liveness-exec   1/1       Running   0          10s

可以看到,由於已經通過了健康檢查,這個 Pod 就進入了 Running 狀態。

3、而 30 s 之后,我們再查看一下 Pod 的 Events:

$ kubectl describe pod test-liveness-exec

4、你會發現,這個 Pod 在 Events 報告了一個異常:

FirstSeen LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
2s        2s      1   {kubelet worker0}   spec.containers{liveness}   Warning     Unhealthy   Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

顯然,這個健康檢查探查到 /tmp/healthy 已經不存在了,所以它報告容器是不健康的。那么接下來會發生什么呢?

3、restartPolicy

我們不妨再次查看一下這個 Pod 的狀態:

$ kubectl get pod test-liveness-exec
NAME           READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m

這時我們發現,Pod 並沒有進入 Failed 狀態,而是保持了 Running 狀態。這是為什么呢?  

 

 需要注意的是

 Pod 恢復機制

但一定要強調的是

 如何出現在其他可用結點上

六、容器健康檢查和恢復機制

 1、Pod 的恢復策略

1、分類

2、合理設計

1、非Always

2、Never

3、設計原理

1、第一條

 2、第二條

$ kubectl get pod test-liveness-exec
NAME           READY     STATUS    RESTARTS   AGE
liveness-exec   0/1       Running   1          1m

現在,我們一起回到前面提到的 livenessProbe 上來。

除了在容器中執行命令外,livenessProbe 也可以定義為發起 HTTP 或者 TCP 請求的方式,定義格式如下:

...
livenessProbe:
     httpGet:
       path: /healthz
       port: 8080
       httpHeaders:
       - name: X-Custom-Header
         value: Awesome
       initialDelaySeconds: 3
       periodSeconds: 3

 

    ...
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

所以,你的 Pod 其實可以暴露一個健康檢查 URL(比如 /healthz),或者直接讓健康檢查去檢測應用的監聽端口。這兩種配置方法,在 Web 服務類的應用中非常常用。

readinessProbe和livenessProbe的區別

1、相同點

2、區別

七、Kubernetes 能不能自動給 Pod 填充某些字段呢?

可是,如果運維人員看到了這個 Pod,他一定會連連搖頭:這種 Pod 在生產環境里根本不能用啊!

所以,這個時候,運維人員就可以定義一個 PodPreset 對象。在這個對象中,凡是他想在開發人員編寫的 Pod 里追加的字段,都可以預先定義好。比如這個 preset.yaml:

apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: allow-database
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}

selector

 Spec

接下來,我們假定運維人員先創建了這個 PodPreset,然后開發人員才創建 Pod:

$ kubectl create -f preset.yaml
$ kubectl create -f pod.yaml

這時,Pod 運行起來之后,我們查看一下這個 Pod 的 API 對象:

$ kubectl get pod website -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
  containers:
    - name: website
      image: nginx
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      ports:
        - containerPort: 80
      env:
        - name: DB_PORT
          value: "6379"
  volumes:
    - name: cache-volume
      emptyDir: {}

注意事項

如果你定義了同時作用於一個 Pod 對象的多個 PodPreset,會發生什么呢?

實際上,Kubernetes 項目會幫你合並(Merge)這兩個 PodPreset 要做的修改。而如果它們要做的修改有沖突的話,這些沖突字段就不會被修改。


免責聲明!

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



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