1. 程式人生 > 其它 >深入探究 K8S ConfigMap 和 Secret

深入探究 K8S ConfigMap 和 Secret

ConfigMap

1、什麼是 ConfigMap?
ConfigMap 是用來儲存配置檔案的 Kubernetes 資源物件,配置物件儲存在 Etcd 中,配置的形式可以是完整的配置檔案、key/value 等形式。

2、ConfigMap 能帶來什麼好處?
傳統的應用服務,每個服務都有自己的配置檔案,各自配置檔案儲存在服務所在節點,對於單體應用,這種儲存沒有任何問題,但是隨著使用者數量的激增,一個節點不能滿足線上使用者使用,故服務可能從一個節點擴充套件到十個節點,這就導致,如果有一個配置出現變更,就需要對應修改十次配置檔案。這種人肉處理,顯然不能滿足線上部署要求,故引入了各種類似於 ZooKeeper 中介軟體實現的配置中心,但配置中心屬於 “侵入式” 設計,需要修改引入第三方類庫,它要求每個業務都呼叫特定的配置介面,破壞了系統本身的完整性,而Kubernetes 利用了 Volume 功能,完整設計了一套配置中心,其核心物件就是ConfigMap,使用過程不用修改任何原有設計,即可無縫對 ConfigMap;為什麼呢?

如圖(1)所示,

1.ConfigMap 相當於放入原生應用的配置檔案,可以是一個或者多個;
2.容器啟動之後,到宿主機中拉取 ConfigMap 的內容,生成本地檔案,通過 volume 形式對映到容器內部指定目錄上;
3.容器中應用程式按照原有方式讀取容器特定目錄上的配置檔案。

在容器看來,配置檔案就像是打包在容器內部特定目錄,整個過程對應用沒有任何侵入。

3、ConfigMap 三種建立方式

  • 指定字面鍵值對進行建立,建立命令如下所示:
kubectl create configmap configmaptest --from-literal=foo=bar --from-literal=one=two

建立完成後通過如下方式檢視:

# kubectl get configmap configmaptest -o yaml
apiVersion: v1
data:
  foo: bar
  one: two
kind: ConfigMap
metadata:
  creationTimestamp: "2020-04-14T13:53:42Z"
  name: configmaptest
  namespace: default
  resourceVersion: "613402"
  selfLink: /api/v1/namespaces/default/configmaps/configmaptest
  uid: 59b91eb4-7e57-11ea-83c7-509a4c36e19d
  • 指定特定檔案進行建立
kubectl create configmap config-files --from-file=/home/conf/db.properties

可以通過如下方式進行檢視,(內容過長,影響閱讀,省略 ConfigMap 元資訊。)

# kubectl get configmap test-config -o yaml
apiVersion: v1
data:
  db.properties: |
    driverClassName=com.mysql.jdbc.Driver
    ......
  • 指定特定資料夾進行建立
kubectl create configmap config-dir --from-file=/home/conf/config-test

通過如下方式進行檢視

# kubectl get configmap config-test -o yaml
apiVersion: v1
data:
  db.properties: |
    # 資料來源配置
    driverClassName=com.mysql.jdbc.Driver
   ......
  logback.xml: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration debug=\"true\"
   ......
  svc.properties: |
    #server
    protocol=tcp
    .......
  system.properties: |
    time=100
    .......

如上描述三種基本的 ConfigMap 建立方式,當然也可以使用合併不同選項進行建立配置檔案,具體如下所示:

kubectl create configmap config-mix --from-file=/home/conf/biz/ --from-file=/home/conf/db.xml  --from-literal=one=two

看到這麼多,你可能會想到,--from-file最後一級如果是資料夾會怎樣呢?如你所想,資料夾其實不會被包含,只會查詢最後一級目錄下的檔案。

4、ConfigMap 作為環境變數三種使用方式

  • 單個引用

1.首先建立 ConfigMap

kubectl create configmap nginx-config --from-literal=code=25 --from-literal=foo=bar --from-literal=one=two

2.Deployment yaml中引用 ConfigMap 設定環境變數,如圖(2)所示

鍵名是CODE-TIME,值是configmap中名為nginx-config的鍵是code的值

3.通過如下方式進行檢視,環境變數是否生效,可以發現,容器環境中已經存在引用ConfigMap中的環境變數

# kubectl exec nginx-7c958f6448-z5q56 -it /bin/bash
[root@nginx-7c958f6448-z5q56 /]# env|grep CODE
CODE-TIME=25
  • 多個引用

1.一次性傳遞所有ConfigMap條目作為環境變數,如圖(3)所示

可以通過如下方式進行檢視環境變數是否生效,如下所示每個環境變數都按照預設,添加了配置的字首,有人可能要說,我的配置檔案中原來是什麼配置現在還保留什麼配置,不需要新增預設字首,那麼請檢視如圖(4)通過把字首設定為空串,即可保持原有配置方式。

# kubectl exec nginx-84ccdff98d-vgzcw -it /bin/bash
[root@nginx-84ccdff98d-vgzcw /]# env|grep CODE
CODE_foo=bar
CODE_code=25
CODE_one=two
  • args 方式傳遞環境變數

容器啟動時,傳遞該變數到服務,執行 shell 指令碼,可能會用到,具體設定方式如圖(5)所示:

以上解釋了通過在 yaml 設定 env 引用 ConfigMap 中配置作為環境變數的使用,在使用過程中,我參考了 《Kubernetes In Action》這本書,發現此書中有一段是這樣描述的,如圖(6)所示:

其大概意思是,配置鍵中不能包含破折號,如果包含則不能設定到環境變數中,此書這部分是基於 Kubernetes 1.6 進行編撰,而我使用的是 kubernetes 1.14,不清楚是不是因為 Kubernetes 已經改進的原因,還是其他原因,我有兩點不解的地方。破折號(——)大多都是指特別長的符號,在編碼過程中很少有人使用這個,即使使用了,Kubernetes 根本無法儲存成功。又何談環境變數一說呢?會提示如圖(7),圖(8)所示錯誤:

如果破折號換成英文半形字元 - 中劃線呢?如圖(9)所示,是可以儲存成功的。當然也可以用於環境變數中。

當然通過如上方式設定完成之後,就可以直接在容器內部使用環境變數讀取已經設定的配置,但是使用環境變數的方式有一個致命的缺點是,當外部 ConfigMap 更新配置完成之後,容器內部環境變數並不會隨之改變,這是因為 ENV 是容器啟動時候注入的,啟動之後 Kubernetes 就不會改變 ENV 的值,即配置不能同步更新,只能通過重啟容器方式,配置才能生效。

5.掛載 volume
這種方式則是通過 volume 形式對映到容器內部指定目錄上,容器內部程序直接讀取該目錄下特定檔案,這種方式是我們常用的一種方式,具體使用時如下所示:

       ......
        - containerPort: 80
        volumeMounts:
          - mountPath: /usr/local/nginx/conf/vhost/
            name: http
          - mountPath: /usr/local/nginx/html/foo
            subPath: foo
            name: nginx-html
      volumes:
      - name: http
        configMap:
          name: nginx-conf
      - name: nginx-html
        configMap:
          name: configmaptest
          items: 
          - key: foo
            path: foo
   ......

volumeMounts 是容器內部指定掛載目錄,volumes 是引用目錄,即宿主機設定 ConfigMap 檔案地址。

  • 可以直接掛載一個目錄到容器內部,當宿主機通過如下方式修改 configmap 那麼容器內部配置將隨之改變,一次性修改所有檔案。但是使用這種方式有一個問題需要注意,如果掛載到容器內部的資料夾下存在其它檔案,這種掛載方式將直接覆蓋原有資料夾下的檔案。
# kubectl edit configmap configmaptest
configmap/configmaptest edited
  • 如果有特定需求,需要掛載某個特定檔案,而不允許覆蓋原有檔案,可以掛載到指定檔案,通過 subPath 配合指定檔案。但是單個檔案掛載這種方式不能實現熱更新,即宿主機 ConfigMap 檔案發生變化,容器內部不會自動過載。

  • 至於 items 使用就比較簡單了,如果一個 ConfigMap 中包含多個配置檔案,但是隻想暴露出來其中一部分,那麼可以通過 items 方式進行指定。當然你也可以對檔案設定讀寫許可權。

Secret

Secret 使用類似於 ConfigMap,支援兩種形式的使用:

  • 將 Secret 作為環境變數暴露給容器程序使用。
  • 將 Secret 通過volume 資料卷提供給容器程序使用。

看到這裡你可能要說了,什麼都一樣,為啥還要 Secret,一個 ConfigMap 解決問題不就完事了,其實不然,Secret 顧名思義,是用於儲存加密資料的,老版本 Kubernetes 只支援 base64 加密,學過計算機的都知道 base64 那就不是什麼加密,只是對字串進行了 encode 編碼,通過 decode 直接可以解出明文。但後來新版本的 Kubernetes 已經實現了真正意義上的加解密,所以 Secret 存在是有一定意義的,使用方式跟 ConfigMap 類似,但是命令確不一樣。

1.建立 Secret 輸入如下:

kubectl create secret generic nginx-ssl --from-file=ca.key --from-file=ca.cert

2.檢視 Secret 輸入如下所示:

# kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-7h5z9   kubernetes.io/service-account-token   3      6d13h
nginx-ssl             Opaque                                2      21h

# kubectl get secret nginx-ssl -o yaml
apiVersion: v1
data:
  ca.crt: QmFnIEF0dHJpYnV0ZXMKICAgIGZy.......................
  ca.key: QmFnIEF0dHJpYnV0ZXMKICAgIGZy......................
kind: Secret
.....................

3.Pod 引用方式

.....................
- containerPort: 80
        volumeMounts:
          - mountPath: /home/nginx/nginx/conf/cert/
            name: nginx-ssl
      volumes:
      - name: nginx-ssl
        secret:
          secretName: nginx-ssl
.....................

應用程式怎麼做到不重啟情況下讀取最新配置

上面已經提及使用環境變數和單檔案掛載形式,無法實現熱更新,但是通過 資料卷形式可以實現宿主機和 Pod 內部讀取配置的實時更新,但是有一點需要注意的是 ConfigMap 更新,資料卷也更新了,如果你的應用程序不進行配置過載,即實時讀取配置資料,同樣還是使用的老配置。這個問題可以通過把 Pod 的副本數減少到 0 進行重建 Pod 解決。這種方式雖然能夠解決服務重新載入問題,但是也會帶來問題。因為可能會導致同一套服務,配置不一致的問題,因此,如果業務對實時性要求高,建議改成服務實時載入配置。

總結一下,Kubernetes 只是把配置實時同步到資料卷配置檔案中,至於載入時機,還要看自己的應用程式。

舉個例子,nginx 配置儲存在 Kubernetes ConfigMap 裡面,公鑰資訊儲存在 Secret 中,nginx 充當服務裡面的反向代理,因為埠資源規劃問題,需要修改 nginx 配置檔案中埠,修改完成後,Pod 中的資料卷配置資訊發生變化,但 nginx 並不會過載已經修改的配置資訊。通過如下命令列修改,修改完成後,發現 Pod 中 nginx 配置生效。

kubectl exec nginx -c nginx -- /usr/local/nginx/sbin/nginx -s reload