快速了解 kubernetes 的 ConfigMap 和 Secrets

原文鏈接:kubernetes ConfigMap 和 Secrets

我們經常都需要為我們的應用程序配置一些特殊的數據,比如密鑰、Token 、資料庫連接地址或者其他私密的信息。你的應用可能會使用一些特定的配置文件進行配置,比如settings.py文件,或者我們可以在應用的業務邏輯中讀取環境變數或者某些標誌來處理配置信息。

當然你可以直接將這些應用配置信息直接硬編碼到你的應用程序中去,對於一個小型的應用,這或許是可以接受的,但是,對於一個相對較大的應用程序或者微服務的話,硬編碼就會變得難以管理了。比如你現在有10個微服務,都連接了資料庫A,如果現在需要更改資料庫A的連接地址的話,就需要修改10個地方,顯然這是難以忍受的。

當然,我們可以使用環境變數和統一的配置文件來解決這個問題,當我們想改變配置的時候,只需要更改環境變數或者配置文件就可以了,但是對於微服務來說的話,這也是比較麻煩的一件事情,Docker 允許我們在 Dockerfile 中指定環境變數,但是如果我們需要在不同的容器中引用相同的數據呢,如果我們的應用程序是運行在集群上的時候,對於配置主機的環境變數也是難以管理的了。接下來我們來寫一個應用程序,最後用kubernetes來管理我們的配置信息。

本文涉及到的所有代碼都位於此Gist中:gist.github.com/cnych/d

1. 編寫應用

下面是我們簡單定義的一個 WEB 服務,其中 TOKEN 和 LANGUAGE 是硬編碼在程序代碼中的,如下:(hardcode-app.py

# -*- coding: utf-8 -*-from flask import Flask, jsonifyapp = Flask(__name__)@app.route("/")def index(): TOKEN = abcdefg123456 LANGUAGE = English return jsonify(token=TOKEN, lang=LANGUAGE)if __name__ == __main__: app.run(host=0.0.0.0, port=5000)

如果我們現在想要改變 TOKEN 或者 LANGUAGE 的話,我們就需要手動去修改上面的代碼了,這就有可能會導致新的 BUG 或者安全漏洞之類的。

下面我們來用環境變數代替上面的參數吧。

2. 使用環境變數

使用環境變數還是比較容易的,大部分編程語言都有內置的方式去讀取環境變數,我們這裡是 python,直接使用os包下面的 getenv 方法即可獲取:(read-env-app.py

# -*- coding: utf-8 -*-import osfrom flask import Flask, jsonifyapp = Flask(__name__)@app.route("/")def index(): TOKEN = os.getenv(TOKEN, ) LANGUAGE = os.getenv(LANGUAGE, ) return jsonify(token=TOKEN, lang=LANGUAGE)if __name__ == __main__: app.run(host=0.0.0.0, port=5000)

現在我們就可以通過設置環境變數而不是直接去修改源碼來改變我們的配置了。在 Unix 系統(MacOS和Linux)下面,我們可以通過在終端中執行下面的命令來設置環境變數:

$ export TOKEN=abcdefg0000$ export LANGUAGE=English

對於 Windows 系統,我們就通過可以通過cmd命令進入終端,執行下面的命令來設置環境變數:

setx TOKEN "abcdefg0000"setx LANGUAGE "English"

另外我們也可以在啟動服務的時候設置環境變數,比如,對於我們的這個flask應用,我們可以這樣運行:

$ TOKEN=abcdefg0000 LANGUAGE=English python read-env-app.py * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)127.0.0.1 - - [10/Feb/2018 15:15:14] "GET / HTTP/1.1" 200 -

然後我們瀏覽器中打開地址127.0.0.1:5000/,可以看到下面的輸出信息:

{ "lang": "English", "token": "abcdefg0000"}

證明我們的環境變數設置生效了……

3. 使用 Docker 的環境變數

我們現在來使用容器運行我們的應用程序,我們就可以不依賴主機的環境變數了,每個容器都有自己的環境變數,所以保證容器的環境變數正確的配置就顯得尤為重要了。幸運的是,Docker 可以非常輕鬆的構建帶有環境變數的容器,在Dockerfile文件中,我們可以通過ENV指令來設置容器的環境變數。

FROM python:3.6.4# 設置工作目錄RUN mkdir -p /usr/src/appWORKDIR /usr/src/app# 安裝依賴RUN pip install flask# 添加應用ADD . /usr/src/app# 設置環境變數ENV TOKEN abcdefg0000ENV LANGUAGE English# 暴露埠EXPOSE 5000# 運行服務CMD python read-env-app.py

將上面的文件保存為Dockerfile,放置在read-env-app.py文件同目錄下面,然後我們可以構建鏡像:

$ docker build -t cnych/envtest .

構建成功後,我們可以使用上面的cnych/envtest鏡像啟動一個容器:

$ docker run --name envtest --rm -p 5000:5000 -it cnych/envtest* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

同樣的我們可以通過標記-e來覆蓋容器的環境變數:

$ docker run --name envtest --rm -e TOKEN=abcdefg88888 -e LANGUAGE=English -p 5000:5000 -it cnych/envtest * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

然後我們在瀏覽器中打開127.0.0.1:5000/可以看到下面的輸出信息證明我們的環境配置成功了:

{ "lang": "English", "token": "abcdefg88888"}

4. 使用 Kubernetes 的環境變數

當我們開始使用Kubernetes的時候,情況又不太一樣了,我們可能會在多個 Kubernetes Deployment 中使用相同的 Docker 鏡像,我們也可能希望對 Deployment 進行 A/B 測試,對不同的 Deployment 設置不同的配置信息。

和上面的 Dockerfile 一樣,我們可以在 Kubernetes Deployment 的YAML文件中指定環境變數,這樣我們就可以在不同的 Deployment 中設置不同的環境變數:(read-env.yaml

apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: envtest labels: name: envtestspec: replicas: 1 template: metadata: labels: name: envtest spec: containers: - name: envtest image: cnych/envtest ports: - containerPort: 5000 env: - name: TOKEN value: "abcd123456" - name: LANGUAGE value: "English"

注意上面的 POD 的 env 部分,我們可以在這裡指定我們想要設置的環境變數,比如這裡我設置了 TOKEN 和 LANGUAGE 兩個環境變數!

5. 使用 Kubernetes 的 Secrets 和 ConfigMap 進行配置

DockerKubernetes環境變數的不足之處在於它們是和容器的部署相關的,如果我們想要更改它們的話,就得重新構建容器或者修改 Deployment,更麻煩的是,如果想將變數用於多個容器或 Deployment 的話,就必須將配置複製過去。

幸運的是,Kubernetes中提供了Secrets(用於比較私密的數據)和ConfigMap(用於非私密的數據)兩種資源可以很好的解決我們上面的問題。

Secret 和 ConfigMap 之間最大的區別就是 Secret 的數據是用Base64編碼混淆過的,不過以後可能還會有其他的差異,對於比較機密的數據(如API密鑰)使用 Secret 是一個很好的做法,但是對於一些非私密的數據(比如數據目錄)用 ConfigMap 來保存就很好。

我們將 TOKEN 保存為 Secret:

$ kubectl create secret generic token --from-literal=TOKEN=abcd123456000

然後將 LANGUAGE 參數保存為 ConfigMap:

$ kubectl create configmap language --from-literal=LANGUAGE=English

然後我們可以通過下面的命令查看創建的 Secret 和 ConfigMap:

$ kubectl get secretNAME TYPE DATA AGEdefault-token-6s2bc kubernetes.io/service-account-token 3 42dtoken Opaque 1 1m$ kubectl get configmapNAME DATA AGElanguage 1 1m

現在,我們可以重新修改 Kubernetes Demployment 的 YAML 文件:(final-read-env.yaml

apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: envtest labels: name: envtestspec: replicas: 1 template: metadata: labels: name: envtest spec: containers: - name: envtest image: cnych/envtest ports: - containerPort: 5000 env: - name: TOKEN valueFrom: secretKeyRef: name: token key: TOKEN - name: LANGUAGE valueFrom: configMapKeyRef: name: language key: LANGUAGE

我們將之前的硬編碼 env 的值更改為從 secret 和 ConfigMap 中讀取。

6. 更新 Secret 和 ConfigMap

上面我們已經提到過,使用 Kubernetes 來管理我們的環境變數後,意味這我們不必更改代碼或者重新構建鏡像來改變環境變數的值了。由於 POD 在啟動的時候會緩存環境變數的值,所以如果我們要更改環境變數的值的話,需要以下兩個步驟:

首先,更新 Secret 或者 ConfigMap:

$ kubectl create configmap language --from-literal=LANGUAGE=Chinese -o yaml --dry-run | kubectl replace -f -configmap "language" replaced$ kubectl create secret generic token --from-literal=TOKEN=bbbbb123456 -o yaml --dry-run | kubectl replace -f -secret "token" replaced

然後,重啟相關的 PODS,重啟 POD 有很多方法,比如我們可以直接更新 Deployment,最簡單的方法是我們可以直接手動刪除 POD,然後 Deployemnt 會自動重建一個 POD起來的。

$ kubectl delete pod -l name=envtestpod "envtest-55d6ff7675-zqb75" deleted

然後我們可以通過查看 POD 確定新的 POD 啟動起來了:

$ kubectl get pod -l name=envtestNAME READY STATUS RESTARTS AGEenvtest-55d6ff7675-pkwpj 1/1 Running 0 36s

我們可以進入到上面的 POD 容器內部列印下環境變數是否設置成功:

$ kubectl exec envtest-55d6ff7675-pkwpj -it -- /bin/bashroot@envtest-55d6ff7675-pkwpj:/usr/src/app# echo $TOKENbbbbb123456root@envtest-55d6ff7675-pkwpj:/usr/src/app# echo $LANGUAGEChinese

我們可以看到我們的環境變數已經更新成功了。

當然對於特別複雜的應用,單純的只用 Secret 和 ConfigMap 來管理配置信息,可能也是不太現實的,這就需要引入配置中心的概念來進行統一管理我們的配置了,關於配置中心我們以後再進行相關的討論吧。

參考資料

  • kubernetes.io/docs/task
  • kubernetes.io/docs/conc

微信搜索k8s技術圈關注我們的微信公眾帳號,在微信公眾帳號中回復 kube100.com 即可加入到我們的 kubernetes 討論群裡面共同學習。

推薦閱讀:

實踐 | 一個案例思考容器落地的山高路遠坑深
如何學習、了解kubernetes?
Docker 17.06-1發布,兩個重要的BUG修復
Docker Swarm Mode
來自滬江、滴滴、蘑菇街、扇貝架構師的 Docker 實踐分享

TAG:Kubernetes | Docker | 教程 |