kubernetes中那些不為存儲數據而存在的volume

  • 2020 年 1 月 20 日
  • 筆記

這kubernetes中,這類Volume不是為了存放數據,也不是用來做數據交換,而是為容器提供預先定義好的數據。所以從容器角度來看,這類Volume就像是被投射進容器一樣。

到目前為止,kubernetes支援4種這類Volume: (1)、Secret (2)、ConfigMap (3)、DownloadAPI (4)、ServiceAccountToken

Secret

Secret的作用是把Pod想要訪問的加密數據存放到Etcd中,然後可以在Pod容器中通過掛載的方式訪問Secret里保存的資訊。

一旦Secret被創建,就可以通過下面三種方式使用它: (1)在創建Pod時,通過為Pod指定Service Account來自動使用該Secret。 (2)通過掛載該Secret到Pod來使用它。 (3)在Docker鏡像下載時使用,通過指定Pod的spc.ImagePullSecrets來引用它。

例如,我們定義下面這個Pod:

apiVersion: v1  kind: Pod  metadata:    name: pod-volume-test    labels:      app: pod-volume-test  spec:    containers:    - name: volume-secret-test      image: busybox      command:      - "/bin/sh"      - "-c"      - "sleep 3600"      volumeMounts:      - name: my-secret        mountPath: "/project-volume"        readOnly: true    volumes:    - name: my-secret      projected:        sources:        - secret:            name: my-secret-volume

從上面的yaml文件中可以看到定義了一個簡單的容器volume-secret-test,它裡面掛載了一個my-secret的volume,這個volume是project類型,而這個數據來源是叫user和password得secret對象。

這裡的secret對象,可以直接通過文件生成,也可以通過定義YAML文件的方式來創建。 (1)、通過文件生成

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

(2)、通過YAML方式生成

apiVersion: v1  kind: Secret  metadata:    name: my-secret-volume  type: Opaque  data:    user: cm9vdA==    password: UEBzc1cwcmQ=

其中user和password的值是需要經過base64轉碼,如下:

[root@master ~]# echo -n "root" | base64  cm9vdA==  [root@master ~]# echo -n "P@ssW0rd" | base64  UEBzc1cwcmQ=

這個轉碼未經過加密,在實際生產中是需要添加一個secret的插件,用來做加密操作。 然後我們來創建這個兩個pod:

# kubectl apply -f creat-volume-secret.yaml  # kubectl apply -f pod-volume-test.yaml

然後通過如下命令查看創建的secret:

然後通過kubectl exec進去容器查看是否掛載上:

[root@master volume]# kubectl exec -it pod-volume-test -- /bin/sh  / # cd project-volume/  /project-volume # ls  password  user  /project-volume # cat password  P@ssW0rd  /project-volume # cat user  root

通過這種方式掛載的Secret,如果某個數據被更新,這些Volume里的內容不會被更新,如果要更新,我們需要重新apply一下或者刪除重建。

比如,我們修改password的值:

apiVersion: v1  kind: Secret  metadata:    name: my-secret-volume  type: Opaque  data:    user: cm9vdA==    password: cGFzc3dvcmQxMjM0NTY=

然後重新執行一下:

# kubectl apply -f creat-volume-secret.yaml

然後我們進入容器查看password變化(大概等了2分鐘):

[root@master volume]# kubectl exec -it pod-volume-test -- /bin/sh  / # cd project-volume/  /project-volume # cat password  password123456

說明: 每個單獨的Secret大小不能超過1MB,Kubernetes不鼓勵創建大的Secret,因為如果使用大的Secret,則將大量佔用API Server和kubelet的記憶體。當然,創建許多小的Secret也能耗盡APIServer和kubelet的記憶體。

綜上,我們可以通過Secret保管其他系統的敏感資訊(比如資料庫的用戶名和密碼),並以Mount的方式將Secret掛載到Container中,然後通過訪問目錄中文件的方式獲取該敏感資訊。當Pod被API Server創建時,API Server不會校驗該Pod引用的Secret是否存在。一旦這個Pod被調度,則kubelet將試著獲取Secret的值。如果Secret不存在或暫時無法連接到API Server,則kubelet按一定的時間間隔定期重試獲取該Secret,並發送一個Event來解釋Pod沒有啟動的原因。一旦Secret被Pod獲取,則kubelet將創建並掛載包含Secret的Volume。只有所有Volume都掛載成功,Pod中的Container才會被啟動。在kubelet啟動Pod中的Container後,Container中和Secret相關的Volume將不會被改變,即使Secret本身被修改。為了使用更新後的Secret,必須刪除舊Pod,並重新創建一個新Pod。

ConfigMap

ConfigMap和Serect類似,不同之處在於ConfigMap保存的數據資訊是不需要加密的,比如一些應用的配置資訊,其他的用法和Secret一樣。同樣,我們可以使用兩種方式來創建ConfigMap:

  • 通過命令行方式,也就是kubectl create configmap;
  • 通過YAML文件方式;
(1)、通過命令方式創建

如果我們不知道ConfigMap的命令方式,可以使用kubectl create configmap -h查看使用方法,如下:

Examples:    # Create a new configmap named my-config based on folder bar    kubectl create configmap my-config --from-file=path/to/bar      # Create a new configmap named my-config with specified keys instead of file basenames on disk    kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt      # Create a new configmap named my-config with key1=config1 and key2=config2    kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2      # Create a new configmap named my-config from the key=value pairs in the file    kubectl create configmap my-config --from-file=path/to/bar      # Create a new configmap named my-config from an env file    kubectl create configmap my-config --from-env-file=path/to/bar.env

從上面可以看出,創建ConfigMap可以從給定一個目錄來創建。例如,我們定義了如下一些配置文件:

[root@master volume]# cd configmap-daemo/  [root@master configmap-daemo]# ll  total 8  -rw-r--r-- 1 root root 25 Sep  6 17:07 mysqld.conf  -rw-r--r-- 1 root root 25 Sep  6 17:07 redis.conf  [root@master configmap-daemo]# cat mysqld.conf  host=127.0.0.1  port=3306  [root@master configmap-daemo]# cat redis.conf  host=127.0.0.1  port=6379

然後使用一下命令來進行創建:

# kubectl create configmap my-configmap --from-file=configmap-daemo/

然後通過一下命令查看創建完的configmap:

[root@master volume]# kubectl get configmap  NAME           DATA   AGE  my-configmap   2      19s  [root@master volume]# kubectl describe configmap my-configmap  Name:         my-configmap  Namespace:    default  Labels:       <none>  Annotations:  <none>    Data  ====  mysqld.conf:  ----  host=127.0.0.1  port=3306    redis.conf:  ----  host=127.0.0.1  port=6379    Events:  <none>

我們可以看到兩個key對應的是文件的名字,value對應的是文件的內容。如果要看鍵值的話可以通過如下命令查看:

[root@master volume]# kubectl get configmap my-configmap -o yaml  apiVersion: v1  data:    mysqld.conf: |      host=127.0.0.1      port=3306    redis.conf: |      host=127.0.0.1      port=6379  kind: ConfigMap  metadata:    creationTimestamp: "2019-09-06T09:10:49Z"    name: my-configmap    namespace: default    resourceVersion: "252070"    selfLink: /api/v1/namespaces/default/configmaps/my-configmap    uid: cd8d3752-c1c4-48e0-b523-bafa20f1ba1f

當然,我們還可以通過文件來創建一個configmap,比如我們定義一個如下的配置文件:

[root@master configmap-daemo]# cat nginx.conf  user  nobody;  worker_processes  1;  error_log  logs/error.log;  error_log  logs/error.log  notice;  error_log  logs/error.log  info;  pid        logs/nginx.pid;  events {      worker_connections  1024;  }  http {      include       mime.types;      default_type  application/octet-stream;      log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                        '$status $body_bytes_sent "$http_referer" '                        '"$http_user_agent" "$http_x_forwarded_for"';      access_log  logs/access.log  main;      sendfile        on;      tcp_nopush     on;      keepalive_timeout  65;      gzip  on;      server {          listen       80;          server_name  localhost;          location / {              root   html;              index  index.html index.htm;          }          error_page   500 502 503 504  /50x.html;          location = /50x.html {              root   html;          }      }  }

然後啟動如下命令創建一個nginx的configmap:

# kubectl create configmap nginx-configmap --from-file=nginx.conf

然後查看創建後的資訊:

[root@master configmap-daemo]# kubectl get configmap nginx-configmap -o yaml  apiVersion: v1  data:    nginx.conf: |      user  nobody;      worker_processes  1;      error_log  logs/error.log;      error_log  logs/error.log  notice;      error_log  logs/error.log  info;      pid        logs/nginx.pid;      events {          worker_connections  1024;      }      http {          include       mime.types;          default_type  application/octet-stream;          log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                            '$status $body_bytes_sent "$http_referer" '                            '"$http_user_agent" "$http_x_forwarded_for"';          access_log  logs/access.log  main;          sendfile        on;          tcp_nopush     on;          keepalive_timeout  65;          gzip  on;          server {              listen       80;              server_name  localhost;              location / {                  root   html;                  index  index.html index.htm;              }              error_page   500 502 503 504  /50x.html;              location = /50x.html {                  root   html;              }          }      }  kind: ConfigMap  metadata:    creationTimestamp: "2019-09-06T09:25:42Z"    name: nginx-configmap    namespace: default    resourceVersion: "253356"    selfLink: /api/v1/namespaces/default/configmaps/nginx-configmap    uid: c4804bb0-dec3-461e-8fc4-0b0bb516815c

註:在一條命令中–from-file可以指定多次。 另外,通過幫助文檔我們可以看到我們還可以直接使用字元串進行創建,通過–from-literal參數傳遞配置資訊,同樣的,這個參數可以使用多次,格式如下:

[root@master configmap-daemo]# kubectl create configmap my-cm-daemo --from-literal=db.host=localhost --from-literal=db.port=3306  configmap/my-cm-daemo created  [root@master configmap-daemo]# kubectl get configmap my-cm-daemo -o yaml  apiVersion: v1  data:    db.host: localhosy    db.port: "3306"  kind: ConfigMap  metadata:    creationTimestamp: "2019-09-06T09:29:49Z"    name: my-cm-daemo    namespace: default    resourceVersion: "253713"    selfLink: /api/v1/namespaces/default/configmaps/my-cm-daemo    uid: ee8823e7-f2b3-46bf-ba6c-2f32303622c4

(2)、通過YAML文件創建

通過YAML文件創建就比較簡單,我們可以參考上面輸出的yaml資訊,比如定義如下一個YAML文件:

[root@master configmap-daemo]# cat my-cm-daemo2.yaml  apiVersion: v1  kind: ConfigMap  metadata:    name: my-cm-daemon2    labels:      app: cm-daemon  data:    redis.conf: |      host=127.0.0.1      port=6379

查看結果資訊:

[root@master configmap-daemo]# kubectl apply -f my-cm-daemo2.yaml  configmap/my-cm-daemon2 created  [root@master configmap-daemo]# kubectl describe configmap my-cm-daemon2  Name:         my-cm-daemon2  Namespace:    default  Labels:       app=cm-daemon  Annotations:  kubectl.kubernetes.io/last-applied-configuration:                  {"apiVersion":"v1","data":{"redis.conf":"host=127.0.0.1nport=6379n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"cm...    Data  ====  redis.conf:  ----  host=127.0.0.1  port=6379    Events:  <none>

(3)、使用ConfigMap

ConfigMap中的配置數據可以通過如下方式進行使用:

  • 設置環境變數值
  • 在容器里設置命令行參數
  • 在數據卷中創建config文件

1、通過設置環境變數值來使用ConfigMap 定義如下YAML文件:

[root@master configmap-daemo]# cat env-configmap.yaml  apiVersion: v1  kind: Pod  metadata:    name: env-configmap    labels:      app: env-configmap-mysql  spec:    containers:    - name: test-configmap      image: busybox      command:      - "/bin/sh"      - "-c"      - "env"      env:        - name: DB_HOST          valueFrom:            configMapKeyRef:              name: my-cm-daemo              key: db.host        - name: DB_PORT          valueFrom:            configMapKeyRef:              name: my-cm-daemo              key: db.port      envFrom:        - configMapRef:            name: my-cm-daemo

查看配置結果:

[root@master configmap-daemo]# kubectl apply -f env-configmap.yaml  pod/env-configmap created  [root@master configmap-daemo]# kubectl logs env-configmap  KUBERNETES_PORT=tcp://10.68.0.1:443  KUBERNETES_SERVICE_PORT=443  HOSTNAME=env-configmap  DB_PORT=3306  DB_HOST=localhost  ......

我們可以看到我們期望得到的變數資訊已經從ConfigMap中得到了。

2、在容器中設置命令行參數來獲取ConfigMap配置資訊 定義YAML問價如下:

[root@master configmap-daemo]# cat command-configmap.yaml  apiVersion: v1  kind: Pod  metadata:    name: command-configmap  spec:    containers:    - name: command-configmap-test      image: busybox      command:      - "/bin/sh"      - "-c"      - "echo $(DB_HOST) $(DB_PORT)"      env:        - name: DB_HOST          valueFrom:            configMapKeyRef:              name: my-cm-daemo              key: db.host        - name: DB_PORT          valueFrom:             configMapKeyRef:               name: my-cm-daemo               key: db.port

查看結果

[root@master configmap-daemo]# kubectl logs command-configmap  localhosy 3306

3、在數據卷中使用ConfigMap 定義YAML文件如下:

[root@master configmap-daemo]# cat volume-configmap.yaml  apiVersion: v1  kind: Pod  metadata:    name: volume-configmap-test  spec:    containers:      - name: volume-configmap-test        image: busybox        command: [ "/bin/sh", "-c", "cat /etc/config/redis.conf" ]        volumeMounts:        - name: config-volume          mountPath: /etc/config    volumes:      - name: config-volume        configMap:          name: my-configmap 

運行Pod,查看結果

[root@master configmap-daemo]# kubectl logs volume-configmap-test  host=127.0.0.1  port=6379

我們也可以在ConfigMap值被映射的數據卷里去控制路徑,如下Pod定義:

[root@master configmap-daemo]# cat volume-path-configmap.yaml  apiVersion: v1  kind: Pod  metadata:    name: volume-path-configmap  spec:    containers:      - name: volume-path-configmap-test        image: busybox        command: [ "/bin/sh","-c","cat /etc/config/path/to/msyqld.conf" ]        volumeMounts:        - name: config-volume          mountPath: /etc/config    volumes:      - name: config-volume        configMap:          name: my-configmap          items:          - key: mysqld.conf            path: path/to/msyqld.conf

然後運行Pod,查看執行結果:

[root@master configmap-daemo]# kubectl logs volume-path-configmap  host=127.0.0.1  port=3306

另外需要注意的是,當ConfigMap以數據卷的形式掛載進Pod的時,這時更新ConfigMap(或刪掉重建ConfigMap),Pod內掛載的配置資訊會熱更新。這時可以增加一些監測配置文件變更的腳本,然後reload對應服務。

DownloadAPI

讓這個Pod里的容器可以直接獲取這個Pod API對象本身的資訊。 比如:

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 'nn'; 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文件。

當前DownloadAPI支援的欄位如下:

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

注意:DownloadAPI能夠獲取到的資訊一定是Pod里的容器進程啟動之前就能夠確定下來的資訊。

ServiceAccountToken

ServiceAccountToken是kubernetes內置的一種"服務賬戶",它是Kubernetes進行許可權分配的對象。ServiceAccount的 授權資訊和文件實際上是保存在Secret對象中,它是一個特殊的Secret對象。任何運行在Kubernetes集群上的應用,都必須使用ServiceAccountToken里保存的授權資訊,也就是Token,這樣才能合法的訪問API Server。

目前,Kubernetes已經提供了一個默認的"服務賬戶"(Default Service Account),任何一個運行在Kubernetes里的Pod,都可以無顯示聲明的掛載這個ServiceAccount並使用它。

我們隨意找一個Pod查看其詳細資訊,就可以看到一個default-token-xxxx的Volume自動掛載到容器的/var/run/secrets/kubernetes.io/serviceaccount目錄上。如下:

[root@master k8s]# kubectl describe pod nginx-deployment-6f655f5d99-q4fhk  ......  Containers:    nginx:  .......      Mounts:        /var/run/secrets/kubernetes.io/serviceaccount from default-token-gmdbb (ro)  Conditions:  ......  Volumes:    default-token-gmdbb:      Type:        Secret (a volume populated by a Secret)      SecretName:  default-token-gmdbb      Optional:    false  QoS Class:       BestEffort

我們可以看到這個Volume是Secret類型,正式默認Service Account對應的ServiceAccountToken,所以說,Kubernetes在每個Pod創建的時候,自動為其添加默認的ServiceAccountToken的定義,然後自動為每個容器加上對應的VolumeMounts欄位,這個過程對應用戶來說是透明的,一旦這個Pod創建完成,容器里的應用可以直接從這個默認的ServiceAccountToken掛載的目錄里訪問到授權資訊和文件,這個路徑在Kubernetes里是固定的。

我們進入Pod會看到掛載的目錄下的文件資訊如下:

[root@master k8s]# kubectl exec -it nginx-deployment-6f655f5d99-q4fhk -- /bin/sh  # ls /var/run/secrets/kubernetes.io/serviceaccount  ca.crt	namespace  token  # 

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

這種把Kubernetes的配置資訊以容器的方式運行在集群中,然後使用ServiceAccountToken方式自動授權,被稱為"InClusterConfig"。

———————–

公眾號:喬邊故事(ID:qiaobiangushi)

知乎: 喬邊故事

頭條號:喬邊故事

只要臉皮夠厚,整個世界都將被你踩在腳下。

———————–

感謝您的閱讀,歡迎轉發!