K8s提供了多種外部資料注入容器的方式,今天我們主要學習環境變數、ConfigMap以及Secret的使用和配置。
環境變數
在docker專案中,對一個容器新增環境變數可以在容器建立時通過-e ENV=name
方式載入。而k8s在建立 Pod 時,也提供了其下容器環境變數配置的能力。
我們可以通過配置清單中的 env
及 envFrom(來自外部配置)
欄位來設定環境變數。
比如如下的yaml
#busybox-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
spec:
selector:
matchLabels:
app: busybox
replicas: 1
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
resources:
limits:
memory: 20Mi
env:
- name: DEMO_VERSION
value: demov1
- name: DEMO_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: DEMO_CONT_MEM
valueFrom:
resourceFieldRef:
containerName: busybox
resource: limits.memory
command: ['top']
在清單中我們配置了三個環境變數:
DEMO_VERSION
:直接新增變數值demov1
DEMO_POD_NAME
:結合valueFrom中fieldRef獲取pod名稱欄位metadata.name
DEMO_CONT_MEM
:結合valueFrom中resourceFieldRef獲取容器資源欄位limits.memory
此時我們建立pod進入容器後通過printenv命令可以檢視到環境變數已經被載入:
#kubectl exec busybox-deployment-5bb768546c-jbsmz -- printenv
DEMO_POD_NAME=busybox-deployment-5bb768546c-jbsmz
DEMO_CONT_MEM=20971520
DEMO_VERSION=demov1
valueFrom
中其他欄位如下待會我們會用到,需要時可參考官方API文件:envvar-v1-core
注意: 環境變數將覆蓋容器映象中指定的所有環境變數。
ConfigMap
ConfigMap 是一種 API 物件,用來將非機密性的資料儲存到鍵值對中。使用時, Pods可以將其用作環境變數、命令列引數或者儲存卷中的配置檔案。
1、用於環境變數
Configmap 用於配置環境變數的好處是可以將環境配置資訊和容器映象解耦,便於應用配置的修改。
我們可以快速的建立出一個configmap如下:
#busybox-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: busybox-configmap
data:
DEMO_VERSION: "demov2"
configmap使用 data
(UTF-8位元組序列) 和 binaryData
(二進位制資料base64 編碼的字串) 欄位建立鍵值對做資料儲存。
接著使用調整我們deployment中的env
DEMO_VERSION的欄位如下:
- name: DEMO_VERSION
valueFrom:
configMapKeyRef:
name: busybox-configmap
key: DEMO_VERSION
configMapKeyRef
如API所說的選擇一個configmap
同樣建立後檢視
# kubectl exec pod/busybox-deployment-64c678977f-zjnhb -- printenv
DEMO_VERSION=demov2
...
這樣我們只需要維護這個configmap即可,不過通過環境變數引用configmap時也是不支援熱更新,環境變數只在容器建立時載入,所以你需要觸發一次deployment的滾動更新。
2、掛載配置資訊
顯然從名字上可以看出configmap並不是為環境變數而生。我們可以將configmap中key作文檔案掛載到容器中,我們建立如下清單:
apiVersion: v1
kind: ConfigMap
metadata:
name: busybox-configmap
data:
DEMO_VERSION: "demov3"
game.properties: |
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
相當於此時我們獲得三個key檔案,接下來我們就可以通過volume掛載了。
...
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: busybox-configmap
...
在volume中configmap欄位指定我們的busybox-configmap,建立後檢視/etc/config
$ kubectl exec busybox-deployment-87b6c7bd7-ljcfr -- ls /etc/config/
DEMO_VERSION
game.properties
ui.properties
當卷中使用的 ConfigMap 被更新時,所投射的鍵最終也會被更新。 kubelet 元件會在每次週期性同步時檢查所掛載的 ConfigMap 是否為最新。即k8s的watch機制。
Secret
與ConfigMap類似,k8s提供了另一種API物件Secret用於儲存機密資訊,我們可以使用Secret物件儲存敏感資訊例如密碼、令牌或金鑰,這樣在應用程式程式碼中解耦機密資料。
建立一個Sercet
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: cGFzc3dk
stringData:
username: k8s
data
欄位用來儲存 base64 編碼的任意資料,我們可以通過base64命令生成編碼。
stringData
則允許 Secret 使用未編碼的字串,只用於寫,無法直接讀取明文欄位。
$ kubectl get secret mysecret -o yaml
apiVersion: v1
data:
password: cGFzc3dk
username: azhz
...
$ kubectl describe secret mysecret
...
Data
====
password: 6 bytes
username: 3 bytes
這樣在kubectl get
和 kubectl describe
中預設不顯示 Secret
的內容。 這是為了防止 Secret
意外地暴露給旁觀者或者儲存在終端日誌中。
Kubernetes 提供若干種內建的Secret型別,用於一些常見的使用場景。 針對這些型別,Kubernetes 所執行的合法性檢查操作以及對其所實施的限制各不相同。
內建型別 | 用法 |
---|---|
Opaque |
使用者定義的任意資料 |
kubernetes.io/service-account-token |
服務賬號令牌 |
kubernetes.io/dockercfg |
~/.dockercfg 檔案的序列化形式 |
kubernetes.io/dockerconfigjson |
~/.docker/config.json 檔案的序列化形式 |
kubernetes.io/basic-auth |
用於基本身份認證的憑據 |
kubernetes.io/ssh-auth |
用於 SSH 身份認證的憑據 |
kubernetes.io/tls |
用於 TLS 客戶端或者伺服器端的資料 |
bootstrap.kubernetes.io/token |
啟動引導令牌資料 |
型別說明可參考官方文件:secret,當然也可以通過Opaque
自定義的實現內建型別。
這裡我們以型別kubernetes.io/ssh-auth
為例嘗試使用Secret,kubernetes.io/ssh-auth
用來存放 SSH 身份認證中 所需要的憑據。使用這種 Secret 型別時,我們必須在其 data
(或 stringData
) 欄位中提供一個 ssh-privatekey
鍵值對,作為要使用的 SSH 憑據。
建立如下的yaml:
apiVersion: v1
kind: Secret
metadata:
name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
ssh-privatekey: |
PRIVATEKEY_STINGS.. #base64編碼資料
建立後可以檢視到型別和key名稱。
$ kubectl describe secret/secret-ssh-auth
Name: secret-ssh-auth
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/ssh-auth
Data
====
ssh-privatekey: 2626 bytes
接著建立用於載入secret的pod
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
spec:
containers:
- name: pod-secret
image: nginx
volumeMounts:
- name: secret-volume
mountPath: "/etc/ssh/"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: secret-ssh-auth
此時容器中已載入到secretName中的ssh-privatekey
項
$ kubectl exec pod/pod-secret -- ls /etc/ssh
ssh-privatekey
這樣我們可以通過此key來做ssh相關的認證。
和configmap一樣,secret也可用於環境變數配置。通過secretRef欄位引入secret
...
envFrom:
- secretRef:
name: mysecret
...
以上secret使用僅做學習,生產中請排查以下安全問題,更多secret內容參考官方文件:Secret
安全問題:
-
當部署與 Secret API 互動的應用程式時,應使用 鑑權策略, 例如 RBAC,來限制訪問。
-
API 伺服器上的 Secret 資料以純文字的方式儲存在 etcd 中,因此:
- 管理員應該為叢集資料開啟靜態加密(要求 v1.13 或者更高版本)。
- 管理員應該限制只有 admin 使用者能訪問 etcd;
- API 伺服器中的 Secret 資料位於 etcd 使用的磁碟上,不再使用secret應該被刪除。
- 如果 etcd 執行在叢集內,管理員應該確保 etcd 之間的通訊使用 SSL/TLS 進行加密。
-
如果將 Secret 資料編碼為 base64 的清單(JSON 或 YAML)檔案,共享該檔案或將其檢入程式碼庫,該密碼將會被洩露。 Base64 編碼不是一種加密方式,應該視同純文字。
-
應用程式在從卷中讀取 Secret 後仍然需要保護 Secret 的值,例如不會意外將其寫入日誌或傳送給不信任方。
-
可以建立使用 Secret 的 Pod 的使用者也可以看到該 Secret 的值。即使 API 伺服器策略不允許使用者讀取 Secret 物件,使用者也可以執行 Pod 導致 Secret 暴露。
希望小作文對你有些許幫助,如果內容有誤請指正。
您可以隨意轉載、修改、釋出本文,無需經過本人同意。 通過部落格閱讀:iqsing.github.io