k8s env、configmap、secret外部資料載入配置

iqsing發表於2022-01-07

K8s提供了多種外部資料注入容器的方式,今天我們主要學習環境變數、ConfigMap以及Secret的使用和配置。

環境變數


在docker專案中,對一個容器新增環境變數可以在容器建立時通過-e ENV=name方式載入。而k8s在建立 Pod 時,也提供了其下容器環境變數配置的能力。

我們可以通過配置清單中的 envenvFrom(來自外部配置) 欄位來設定環境變數。

比如如下的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

image-20220105234011959注意: 環境變數將覆蓋容器映象中指定的所有環境變數。

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中的envDEMO_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 getkubectl 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

相關文章