深入淺出 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 不足之處還望諸位多多指教。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章