天天看點

深入淺出 Kubernetes:初識 Pod(下)深入淺出 Kubernetes:初識 Pod(下)

深入淺出 Kubernetes:初識 Pod(下)

一 Projected Volume

作為 Kubernetes 比較核心的編排對象,Pod 攜帶的資訊極其豐富。在 Kubernetes 中,有幾種特殊的 Volume,它們存在的意義不是為了存放容器裡的資料,也不是用來進行容器和主控端之間的資料交換。這些特殊 Volume 的作用,是為容器提供預先定義好的資料。從容器的角度來看,這些 Volume 裡的資訊就是仿佛是被 Kubernetes“投射”。Kubernetes 支援的 Projected Volume 有如下四種:

  • Secret
  • ConfigMap
  • DownWarAPI
  • ServiceAccountToken

1.1 Secret

Kubernetes 把 Pod 想要通路的東西存放在 etcd 中,然後通過在 Pod 的容器裡挂載 volume 的方式來進行通路。存放資料庫的憑證資訊就是 Secret 最典型的應用場景之一。

apiVersion: v1
kind: Pod
metadata:
  name: secret-gysl
spec:
  containers:
  - name: secret-gysl
    image: busybox
    args:
    - sleep
    - "3600"
    volumeMounts:
    - name: secret-mysql-gysl
      mountPath: "/projected-volume-secret"
      readOnly: true
  volumes:
  - name: secret-mysql-gysl
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: passwd           

在這個例子中,聲明挂載的 Volume 的類型是 projected 類型。這個 Volume 的資料來源(sources)是名為 user 和 passwd 的 Secret 對象,分别對應的是資料庫的使用者名和密碼。

在編寫 yaml 檔案的時候需要最後幾行,secret 下面依然是需要進一步縮進的。

在 apply 以上 yaml 檔案之後,我們會發現 Pod 的狀态一直是 ContainerCreating ,原因是我們還沒有建立相關的 secret 。使用以下指令進行建立:

kubectl create secret generic user --from-file=username.txt
kubectl create secret generic passwd --from-file=passwd.txt           

username.txt 和 passwd.txt 兩個檔案的内容分别如下:

cat username.txt
gysl
cat passwd.txt
E#w23%dj2JK@           

進入該 Pod:

kubectl exec -it secret-gysl sh           

正常情況下,可以看到如下内容:

/ # ls -al /projected-volume-secret/
total 0
drwxrwxrwt    3 root     root           120 Aug  1 07:25 .
drwxr-xr-x    1 root     root            60 Aug  1 07:25 ..
drwxr-xr-x    2 root     root            80 Aug  1 07:25 ..2019_08_01_07_25_08.650769588
lrwxrwxrwx    1 root     root            31 Aug  1 07:25 ..data -> ..2019_08_01_07_25_08.650769588
lrwxrwxrwx    1 root     root            17 Aug  1 07:25 passwd.txt -> ..data/passwd.txt
lrwxrwxrwx    1 root     root            19 Aug  1 07:25 username.txt -> ..data/username.txt
/ # cat /projected-volume-secret/username.txt
gysl
/ # cat /projected-volume-secret/passwd.txt
E#w23%dj2JK@           

也可以通過 yaml 檔案的方式來進行建立,以上指令轉化為 yaml 如下:

apiVersion: v1
kind: Secret
metadata:
  name: user
type: Opaque
data:
  username.txt: Z3lzbAo=
---
apiVersion: v1
kind: Secret
metadata:
  name: passwd
type: Opaque
data:
  passwd.txt: RSN3MjMlZGoySktACg==           

該yaml 檔案中的 data 部分的字段都是經過 base64 轉碼的:

cat username.txt |base64
Z3lzbAo=
cat passwd.txt |base64
RSN3MjMlZGoySktACg==           

使用上面的指令進入該 Pod 我們就可以看到跟之前操作一樣的内容。上面我們使用了2個 secret 對象來進行本次實驗。我們能否使用一個 secret 來達到一樣的目标呢?

請看以下 yaml :

apiVersion: v1
kind: Pod
metadata:
  name: secret-gysl
spec:
  containers:
  - name: secret-gysl
    image: busybox
    args:
    - sleep
    - "3600"
    volumeMounts:
    - name: secret-mysql-gysl
      mountPath: "/projected-volume-secret"
      readOnly: true
  volumes:
  - name: secret-mysql-gysl
    projected:
      sources:
#      - secret:
#          name: passwd
      - secret:
          name: secret-gysl
---
apiVersion: v1
kind: Secret
metadata:
  name: secret-gysl
type: Opaque
data:
  user: Z3lzbAo=
  passwd: RSN3MjMlZGoySktACg==           

進入 Pod 觀察:

/projected-volume-secret # ls -al
total 0
drwxrwxrwt    3 root     root           120 Aug  1 06:55 .
drwxr-xr-x    1 root     root            60 Aug  1 06:55 ..
drwxr-xr-x    2 root     root            80 Aug  1 06:55 ..2019_08_01_06_55_22.687706782
lrwxrwxrwx    1 root     root            31 Aug  1 06:55 ..data -> ..2019_08_01_06_55_22.687706782
lrwxrwxrwx    1 root     root            13 Aug  1 06:55 passwd -> ..data/passwd
lrwxrwxrwx    1 root     root            11 Aug  1 06:55 user -> ..data/user           

從目錄結構和内容來看,差異并不大,多層軟連接配接,一些隐藏檔案。使用這樣的方法建立的 secret 僅僅進行了轉碼,并未進行加密,生産環境中使用一般情況下需要使用加密插件。

1.2 ConfigMap

ConfigMap 與 Secret 的差別在于,ConfigMap 儲存的是不需要加密的、應用所需的配置資訊,用法幾乎與 Secret 完全相同:可以使用 kubectl create configmap 從檔案或者目錄建立 ConfigMap,也可以直接編寫 ConfigMap 對象的 YAML 檔案。我們以 kube-controller-manager.conf 檔案來示範一下。

建立 configMap:

kubectl create configmap kube-scheduler --from-file=/etc/kubernetes/conf.d/kube-controller-manager.conf           

同樣,我們也可以使用 yaml 來建立:

apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-controller-manager-gysl
data:
  kube-controller-manager.conf: |
    KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=true --v=4 --master=127.0.0.1:8080 --leader-elect=true --address=127.0.0.1 --service-cluster-ip-range=10.0.0.0/24 --cluster-name=kubernetes --cluster-signing-cert-file=/etc/kubernetes/ca.d/ca.pem --cluster-signing-key-file=/etc/kubernetes/ca.d/ca-key.pem  --root-ca-file=/etc/kubernetes/ca.d/ca.pem --service-account-private-key-file=/etc/kubernetes/ca.d/ca-key.pem"           

就這樣,kube-controller-manager.conf 配置檔案的内容就被儲存到了 kube-controller-manager-gysl 這個ConfigMap 中。

1.3 Downward API

Downward API 能讓 Pod 裡的容器能夠直接擷取到這個 Pod API 對象本身的資訊。

apiVersion: v1
kind: Pod
metadata:
  name: downward-api-gysl
  labels:
    zone: beijing
    cluster: gysl-cluster1
    rack: rack-gysl
spec:
  containers:
    - name: client-container
      image: 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           

Pod 的 Labels 字段的值,就會被 Kubernetes 自動挂載成為容器裡的 /etc/podinfo/labels 檔案。執行指令:

kubectl logs downward-api-gysl           

看到的結果:

cluster="gysl-cluster1"
rack="rack-gysl"
zone="beijing"

cluster="gysl-cluster1"
rack="rack-gysl"
zone="beijing"

cluster="gysl-cluster1"
rack="rack-gysl"
zone="beijing"           

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           

Downward API 能夠擷取到的資訊,一定是 Pod 裡的容器程序啟動之前就能夠确定下來的資訊。而如果你想要擷取 Pod 容器運作後才會出現的資訊。比如,容器程序的 PID,那就肯定不能使用 Downward API 了,而應該考慮在 Pod 裡定義一個 sidecar 容器。

1.4 Service Account

Service Account 對象的作用,就是 Kubernetes 系統内置的一種“服務賬戶”,它是 Kubernetes 進行權限配置設定的對象。比如,Service Account A,可以隻被允許對 Kubernetes API 進行 GET 操作,而 Service Account B,則可以有 Kubernetes API 的所有操作的權限。

像這樣的 Service Account 的授權資訊和檔案,實際上儲存在它所綁定的一個特殊的 Secret 對象裡的。這個特殊的 Secret 對象,就叫作ServiceAccountToken。任何運作在 Kubernetes 叢集上的應用,都必須使用這個 ServiceAccountToken 裡儲存的授權資訊,也就是 Token,才可以合法地通路 API Server。

是以,Kubernetes 項目的 Projected Volume 其實隻有三種,因為第四種 ServiceAccountToken,隻是一種特殊的 Secret 而已。

為了友善使用,Kubernetes 已經為你提供了一個的預設“服務賬戶”(default Service Account)。并且,任何一個運作在 Kubernetes 裡的 Pod,都可以直接使用這個預設的 Service Account,而無需顯示地聲明挂載它。Kubernetes 在每個 Pod 建立的時候,自動在它的 spec.volumes 部分添加上了預設 ServiceAccountToken 的定義,然後自動給每個容器加上了對應的 volumeMounts 字段。這個過程對于使用者來說是完全透明的。一旦 Pod 建立完成,容器裡的應用就可以直接從這個預設 ServiceAccountToken 的挂載目錄裡通路到授權資訊和檔案。這個容器内的路徑在 Kubernetes 裡是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount。

$ kubectl exec -it downward-api-gysl sh
/ # ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt     namespace  token           

這種把 Kubernetes 用戶端以容器的方式運作在叢集裡,然後使用 default Service Account 自動授權的方式,被稱作“InClusterConfig”,也是我最推薦的進行 Kubernetes API 程式設計的授權方式。

考慮到自動挂載預設 ServiceAccountToken 的潛在風險,Kubernetes 允許你設定預設不為 Pod 裡的容器自動挂載這個 Volume。

二 相關資料

2.1 文中涉及到的yaml,參見本人GitHub。

Kubernetes之Pod

2.2 不足之處還望諸位多多指教。