k8s中secret解析
概覽
Secret是用來保存小片敏感數據的k8s資源,例如密碼,token,或者秘鑰。這類數據當然也可以存放在Pod或者鏡像中,但是放在Secret中是為了更方便的控制如何使用數據,並減少暴露的風險。
用戶可以創建自己的secret,系統也會有自己的secret。
Pod需要先引用才能使用某個secret,Pod有2種方式來使用secret:作為volume的一個域被一個或多個容器掛載;在拉取鏡像的時候被kubelet引用。
內建的Secrets
由ServiceAccount創建的API證書附加的秘鑰
k8s自動生成的用來訪問apiserver的Secret,所有Pod會默認使用這個Secret與apiserver通信
創建自己的Secret
使用kubectl create secret命令創建Secret
假如mougePod要訪問數據庫,需要用戶名密碼,分別存放在2個文件中:username.txt,password.txt
# Create files needed for rest of example. $ echo -n ‘admin‘ > ./username.txt $ echo -n ‘1f2d1e2e67df‘ > ./password.txt
kubectl create secret
指令將用戶名密碼寫到secret中,並在apiserver創建Secret
$ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt secret "db-user-pass" created
查看創建結果:
$ kubectl get secrets NAME TYPE DATA AGE db-user-pass Opaque 2 51s
$ kubectl describe secrets/db-user-pass Name: db-user-pass Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password.txt: 12 bytes username.txt: 5 bytes
get或describe指令都不會展示secret的實際內容,這是出於對數據的保護的考慮,如果想查看實際內容請繼續往下看。
手動創建Secret
創建一個secret.yaml文件,內容用base64編碼
$ echo -n ‘admin‘ | base64 YWRtaW4= $ echo -n ‘1f2d1e2e67df‘ | base64 MWYyZDFlMmU2N2Rm
yaml文件內容:
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
創建:
$ kubectl create -f ./secret.yaml secret "mysecret" created
解析Secret中內容
$ kubectl get secret mysecret -o yaml apiVersion: v1 data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm kind: Secret metadata: creationTimestamp: 2016-01-22T18:41:56Z name: mysecret namespace: default resourceVersion: "164619" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: cfee02d6-c137-11e5-8d73-42010af00002 type: Opaque
base64解碼:
$ echo ‘MWYyZDFlMmU2N2Rm‘ | base64 --decode 1f2d1e2e67df
使用Secret
secret可以作為數據卷掛載或者作為環境變量暴露給Pod中的容器使用,也可以被系統中的其他資源使用。比如可以用secret導入與外部系統交互需要的證書文件等。
在Pod中以文件的形式使用secret
- 創建一個Secret,多個Pod可以引用同一個Secret
- 修改Pod的定義,在
spec.volumes[]
加一個volume,給這個volume起個名字,spec.volumes[].secret.secretName
記錄的是要引用的Secret名字 - 在每個需要使用Secret的容器中添加一項
spec.containers[].volumeMounts[]
,指定spec.containers[].volumeMounts[].readOnly = true
,spec.containers[].volumeMounts[].mountPath
要指向一個未被使用的系統路徑。 - 修改鏡像或者命令行使系統可以找到上一步指定的路徑。此時Secret中
data
字段的每一個key都是指定路徑下面的一個文件名
下面是一個Pod中引用Secret的列子:
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret
每一個被引用的Secret都要在spec.volumes
中定義
如果Pod中的多個容器都要引用這個Secret那麽每一個容器定義中都要指定自己的volumeMounts
,但是Pod定義中聲明一次spec.volumes
就好了。
映射secret key到指定的路徑
可以控制secret key被映射到容器內的路徑,利用spec.volumes[].secret.items
來修改被映射的具體路徑
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username
發生了什麽呢?
- username被映射到了文件
/etc/foo/my-group/my-username
而不是/etc/foo/username
- password沒有變
Secret文件權限
可以指定secret文件的權限,類似linux系統文件權限,如果不指定默認權限是0644
,等同於linux文件的-rw-r--r--
權限
設置默認權限位
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret defaultMode: 256
上述文件表示將secret掛載到容器的/etc/foo
路徑,每一個key衍生出的文件,權限位都將是0400
由於JSON不支持八進制數字,因此用十進制數256表示0400,如果用yaml格式的文件那麽就很自然的使用八進制了
同理可以單獨指定某個key的權限
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username mode: 511
從volume中讀取secret的值
值得註意的一點是,以文件的形式掛載到容器中的secret,他們的值已經是經過base64解碼的了,可以直接讀出來使用。
$ ls /etc/foo/ username password $ cat /etc/foo/username admin $ cat /etc/foo/password 1f2d1e2e67df
被掛載的secret內容自動更新
也就是如果修改一個Secret的內容,那麽掛載了該Secret的容器中也將會取到更新後的值,但是這個時間間隔是由kubelet的同步時間決定的。最長的時間將是一個同步周期加上緩存生命周期(period+ttl)
特例:以subPath形式掛載到容器中的secret將不會自動更新
以環境變量的形式使用Secret
- 創建一個Secret,多個Pod可以引用同一個Secret
- 修改pod的定義,定義環境變量並使用
env[].valueFrom.secretKeyRef
指定secret和相應的key - 修改鏡像或命令行,讓它們可以讀到環境變量
apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: mycontainer image: redis env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password restartPolicy: Never
容器中讀取環境變量,已經是base64解碼後的值了:
$ echo $SECRET_USERNAME admin $ echo $SECRET_PASSWORD 1f2d1e2e67df
使用imagePullSecrets
創建一個專門用來訪問鏡像倉庫的secret,當創建Pod的時候由kubelet訪問鏡像倉庫並拉取鏡像,具體描述文檔在 這裏
設置自動導入的imagePullSecrets
可以手動創建一個,然後在serviceAccount中引用它。所有經過這個serviceAccount創建的Pod都會默認使用關聯的imagePullSecrets來拉取鏡像,參考文檔
自動掛載手動創建的Secret
參考文檔
詳情
限制
需要被掛載到Pod中的secret需要提前創建,否則會導致Pod創建失敗
secret是有命名空間屬性的,只有在相同namespace的Pod才能引用它
單個Secret容量限制的1Mb,這麽做是為了防止創建超大的Secret導致apiserver或kubelet的內存耗盡。但是創建過多的小容量secret同樣也會耗盡內存,這個問題在將來可能會有方案解決
kubelet只支持由API server創建出來的Pod中引用secret,使用特殊方式創建出來的Pod是不支持引用secret的,比如通過kubelet的--manifest-url
參數創建的pod,或者--config
參數創建的,或者REST API創建的。
通過secretKeyRef
引用一個不存在你secret key會導致pod創建失敗
用例
Pod中的ssh keys
創建一個包含ssh keys的secret
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
創建一個Pod,其中的容器可以用volume的形式使用ssh keys
kind: Pod apiVersion: v1 metadata: name: secret-test-pod labels: name: secret-test spec: volumes: - name: secret-volume secret: secretName: ssh-key-secret containers: - name: ssh-test-container image: mySshImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume"
Pod中區分生產和測試證書
創建2種不同的證書,分別用在生產和測試環境
$ kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11 secret "prod-db-secret" created $ kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests secret "test-db-secret" created
再創建2個不同的Pod
apiVersion: v1 kind: List items: - kind: Pod apiVersion: v1 metadata: name: prod-db-client-pod labels: name: prod-db-client spec: volumes: - name: secret-volume secret: secretName: prod-db-secret containers: - name: db-client-container image: myClientImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" - kind: Pod apiVersion: v1 metadata: name: test-db-client-pod labels: name: test-db-client spec: volumes: - name: secret-volume secret: secretName: test-db-secret containers: - name: db-client-container image: myClientImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume"
兩個容器中都會有下列的文件
/etc/secret-volume/username
/etc/secret-volume/password
以“.”開頭的key可以產生隱藏文件
kind: Secret apiVersion: v1 metadata: name: dotfile-secret data: .secret-file: dmFsdWUtMg0KDQo= --- kind: Pod apiVersion: v1 metadata: name: secret-dotfiles-pod spec: volumes: - name: secret-volume secret: secretName: dotfile-secret containers: - name: dotfile-test-container image: k8s.gcr.io/busybox command: - ls - "-l" - "/etc/secret-volume" volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume"
會在掛載目錄下產生一個隱藏文件,/etc/secret-volume/.secret-file
最佳實踐
風險
k8s中secret解析