kubernetes中那些不為存儲數(shù)據(jù)而存在的volume

微信圖片_20200119163111.jpg

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

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

Secret

Secret的作用是把Pod想要訪問的加密數(shù)據(jù)存放到Etcd中,然后可以在Pod容器中通過掛載的方式訪問Secret里保存的信息。

一旦Secret被創(chuàng)建,就可以通過下面三種方式使用它:
(1)在創(chuàng)建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類型,而這個數(shù)據(jù)來源是叫user和password得secret對象。

這里的secret對象,可以直接通過文件生成,也可以通過定義YAML文件的方式來創(chuàng)建。
(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的值是需要經(jīng)過base64轉碼,如下:

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

這個轉碼未經(jīng)過加密,在實際生產(chǎn)中是需要添加一個secret的插件,用來做加密操作。
然后我們來創(chuàng)建這個兩個pod:

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

然后通過如下命令查看創(chuàng)建的secret:


image.png

然后通過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,如果某個數(shù)據(jù)被更新,這些Volume里的內(nèi)容不會被更新,如果要更新,我們需要重新apply一下或者刪除重建。

比如,我們修改password的值:

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

然后重新執(zhí)行一下:

# 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不鼓勵創(chuàng)建大的Secret,因為如果使用大的Secret,則將大量占用API Server和kubelet的內(nèi)存。當然,創(chuàng)建許多小的Secret也能耗盡APIServer和kubelet的內(nèi)存。

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

ConfigMap

ConfigMap和Serect類似,不同之處在于ConfigMap保存的數(shù)據(jù)信息是不需要加密的,比如一些應用的配置信息,其他的用法和Secret一樣。同樣,我們可以使用兩種方式來創(chuàng)建ConfigMap:

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

如果我們不知道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

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

[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

然后使用一下命令來進行創(chuàng)建:

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

然后通過一下命令查看創(chuàng)建完的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對應的是文件的內(nèi)容。如果要看鍵值的話可以通過如下命令查看:

[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

當然,我們還可以通過文件來創(chuàng)建一個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;
        }
    }
}

然后啟動如下命令創(chuàng)建一個nginx的configmap:

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

然后查看創(chuàng)建后的信息:

[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可以指定多次。
另外,通過幫助文檔我們可以看到我們還可以直接使用字符串進行創(chuàng)建,通過--from-literal參數(shù)傳遞配置信息,同樣的,這個參數(shù)可以使用多次,格式如下:

[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文件創(chuàng)建

通過YAML文件創(chuàng)建就比較簡單,我們可以參考上面輸出的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.1\nport=6379\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"cm...

Data
====
redis.conf:
----
host=127.0.0.1
port=6379

Events:  <none>
(3)、使用ConfigMap

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

  • 設置環(huán)境變量值
  • 在容器里設置命令行參數(shù)
  • 在數(shù)據(jù)卷中創(chuàng)建config文件

1、通過設置環(huán)境變量值來使用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
......

我們可以看到我們期望得到的變量信息已經(jīng)從ConfigMap中得到了。

2、在容器中設置命令行參數(shù)來獲取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、在數(shù)據(jù)卷中使用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值被映射的數(shù)據(jù)卷里去控制路徑,如下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,查看執(zhí)行結果:

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

另外需要注意的是,當ConfigMap以數(shù)據(jù)卷的形式掛載進Pod的時,這時更新ConfigMap(或刪掉重建ConfigMap),Pod內(nèi)掛載的配置信息會熱更新。這時可以增加一些監(jiān)測配置文件變更的腳本,然后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 '\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文件。

當前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內(nèi)置的一種"服務賬戶",它是Kubernetes進行權限分配的對象。ServiceAccount的 授權信息和文件實際上是保存在Secret對象中,它是一個特殊的Secret對象。任何運行在Kubernetes集群上的應用,都必須使用ServiceAccountToken里保存的授權信息,也就是Token,這樣才能合法的訪問API Server。

目前,Kubernetes已經(jīng)提供了一個默認的"服務賬戶"(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創(chuàng)建的時候,自動為其添加默認的ServiceAccountToken的定義,然后自動為每個容器加上對應的VolumeMounts字段,這個過程對應用戶來說是透明的,一旦這個Pod創(chuàng)建完成,容器里的應用可以直接從這個默認的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)

知乎: 喬邊故事

頭條號:喬邊故事

只要臉皮夠厚,整個世界都將被你踩在腳下。
-----------------------
感謝您的閱讀,歡迎轉發(fā)!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容