iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 20
1
Kubernetes

在地端建置Angular+ASP.NET Core的DevOps環境系列 第 20

day20_k8s06_儲存篇_下集_Secrets,ConfigMap

前言

Secrets跟ConfigMap是非常像的Object,就放在一起囉
非常重要喔~

Secrets

官網文件:
https://kubernetes.io/docs/concepts/configuration/secret/

官網文件的最後面,因為覺得很重要,所以放到前面來
往往因為時程壓力,會用新工具就很厲害了,趕快交差了事
殊不知,離安全還有很長的路…

注意事項(如果有理解錯誤請指正)

  • 在API server secret是以明文的形式放在etcd,所以administrator要特別控管etcd的權限
  • json或yaml檔裡的base64,是編碼,不是加密喔
  • 目前任何NODE上具有root權限的人都可以通過模擬kubelet從apiserver中讀取secrets
    (所以不要什麼都放在secret,雖然它叫作secret,容易讓人覺得它很安全)
  • 加強資安:application拿到secret裡的資料,仍然要對裡面的機敏資料加密
  • 有權限建pod的user,可以看到secret裡的資料喔

即使api server設不允許,有權限建pod的user,可以透過建一個mount secret的pod來看到裡面的資料

secrets用來放一些機敏設定、帳號密碼之類的,
也可以餵serets吃檔案

# 產生範例檔
$ echo -n 'admin' > ./username.txt
$ echo -n '1f2d1e2e67df' > ./password.txt

# db-user-pass是object name
# 另外還可以設定:Namespace(預設是default)、Labels、Annotations
# 餵secrets吃檔案
$ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret "db-user-pass" created

# 加完後看一下
$ kubectl get secrets # 查secrets object
$ kubectl describe secrets/db-user-pass # 進一步列出db-user-pass放多少data

#也可以每次加1個
$ kubectl create secret generic mysql-pass --from-literal=password=YOUR_PASSWORD
# 轉成base64編碼
$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

# 換行字元
# macOS: 避免使用 -b 來拆行
# Linux: 應該加-w 0,或者試試 base64 | tr -d '\n'

# 寫一個secret object(yaml語法)
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

# 建Secret,這裡的mysecret是SECRET OBJECT NAME,就像上面的db-user-pass
$ kubectl create -f ./secret.yaml
secret "mysecret" created

Decoding a Secret(取回secret)

# 先輸出成yaml
$ 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進去,現在看到也是base64:MWYyZDFlMmU2N2Rm
# 轉碼
$ echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df

在Pod怎麼使用secrets呢?(yaml怎麼寫)

官網的說明我也看不懂,我們直接看範例吧:

  • 在volume裡面mount一個secret object
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: # 在volume裡面mount一個secret object
      secretName: mysecret # secret object name
  • 也可以設定path,如果沒設定,預設是在/etc/foo/username
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: # 掛載後,可在 .spec.volumes[].secret.items  中使用
      - key: username
        path: my-group/my-username # username 會存在/etc/foo/my-group/my-username
# 因為只有設定username,並沒有設定password,所以password就沒有(projected)
  • secret也能設權限喔
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 0777
# 也可以用8進位,但JSON不支援8進位表示法

預設是0644,可以設定整個secret volume的權限(中央),細項(地方)再另外設權限

權限小學堂:

代表10進位 USER(owner) GROUP OTHER USER
0 6 4 4
1:excute
2:write
3:write,excute
4:read
5:read,excute
6:read,write
7:read,write,excute

要背的話,就背 R W X

3個bit,例如:「6」 轉 2進位 => 1(可讀) 1(可寫) 0(不能執行)

Secrets也能用在環境變數

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:  # 用在環境變數
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef: # secret key reference,就可使用: env[].valueFrom.secretKeyRef
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

# 這樣環境變數就能用囉
$ echo $SECRET_USERNAME
$ echo $SECRET_PASSWORD

其他案例

  • ssh keys
# 注意:放在secret的話,cluster管理者可能都能拿到
$ 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

  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

# 然後,就可以把ssh key掛到pod
/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

備註:

  • 如果在--from-literal中,你的密碼的有特殊符號
    $,!,前面加\,變成\$、\!
    *,一樣前面加\,變成\*

  • 例如:S!B*d$zDsb,變成S\!B\*d\$zDsb

$ kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=S\\!B\\\*d\\$zDsb
  • 把檔案掛成.開頭的(例如隱藏檔)
kind: Secret
apiVersion: v1
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
...中間略...
  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"
# 這樣在container dotfile-test-container裡面的
# /etc/secret-volume/.secret-file 就有這個檔案

===

ConfigMap

參考文件:
https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/
https://k8smeetup.github.io/docs/tasks/configure-pod-container/configmap/#使用kubectl创建configmap

也是非常重要的主題,放在後面是怕你學完ConfigMap就懶得學Secrets了
(管他資料重不重要,時程壓力,全放ConfigMap就好了 = =)
利用ConfigMap讓config跟image解耦

Secrets VS ConfigMap 來比較一下

兩者很類似,pod跑起來後,就像mount volume

  • Secrets:機敏資料

  • ConfigMap:不是密秘的Configuration parameters

  • 特色:
    key-value pairs

參考書籍

  • Kubernetes建置與執行,第11章,ConfigMap和Secret
    雖然資訊的書不太需要買,由其是Kubernetes變化這麼快的東西
    平常主要還是以官網-kubernetes.io/docs為主,
    但是當有哪個主題不太清楚的時候還是可以翻書
    這一本書可以買喔,比較固定、基礎的元件都有(可以幫助初學者)

  • 基本指令:

$ kubectl create configmap <map-name> <data-source>(key-value)

官網的文件看不太懂,借課本範例:

先來看最簡單的建ConfigMap方式--from-literal

  • 1、假設有個組態檔
    my-config.txt
    parameter1=value1
    parameter2=value2
$ kubectl create configmap my-config # 你的config map name
# 餵單一檔案
	--from-file=my-config.txt # 餵檔案,檔案的形式是key-value
# 也可以給目錄,這個目錄底下的檔案都會餵進去,每個檔案對應到下面的data:
	--from-file=configmap/myconfigfile/ 
	--from-literal=extra-param=extra-value # 你也可以在指令加進去
  • 2、把剛剛建的configmap object,輸出相對應的yaml檔
kubectl get configmaps my-config -o yaml
# 輸出的yaml類似下面這樣
apiVersion: v1
data: # 想像成my-cofig/這個目錄裡面的檔案
  another-param: another-value # 前面的another-param是key,掛到pod後是一個檔案
  my-config.txt: | # 好像餵檔案的就會有| # key,掛到pod後是一個檔案 
    parameter1 = value1 # 檔案裡的content
    parameter2 = value2

kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T18:52:05Z
  name: my-config # 想像成一個目錄
  namespace: default
  resourceVersion: "516"
  selfLink: /api/v1/namespaces/default/configmaps/my-config
  uid: b4952dc3-d670-11e5-8cd0-68f712354985
  • 3、使用ConfigMap,mount到pod
    把ConfigMap裡的環境變數,寫到Pod sepcification
# busybox-config.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox-config
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command:
         - "/bin/sh"
         - "-c"
         - "env"  
      # 也可以寫成一行  command: [ "/bin/sh", "-c", "env" ]
      # 也可以在command直接用 $(ANOTHER-PARAM)
      env:
        - name: ANOTHER-PARAM
          valueFrom:
            configMapKeyRef:
              name: my-config # 你的ConfigMap Object Name
              key: another-param # 上面yaml的data:裡面的key,在container看會是一個檔案
      
      volumeMounts:
        - name: config-volume # 參考到下面volumes裡的資源
          mountPath: /config  # 掛到container裡的哪個目錄
  volumes:
    -name: config-volume
     configMap:
       name: my-config # 你的ConfigMap Object Name 
  restartPolicy: Never
  • 運行pod
$ kubectl apply -f busybox-config.yaml

來看官網的範例:

  • 1、建個configmap object
# 建個目錄
$ mkdir -p configure-pod-container/configmap/kubectl/

# 把檔案game.properties、ui.properties下載到configure-pod-container/configmap/kubectl/
# 可以想像成是原本config檔
$ wget https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/game.properties -O configure-pod-container/configmap/kubectl/game.properties
$ wget https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/ui.properties -O configure-pod-container/configmap/kubectl/ui.properties

# 建一個ConfigMap Object: game-config
# 把configure-pod-container/configmap/kubectl/裡的檔案餵進去
$ kubectl create configmap game-config --from-file=configure-pod-container/configmap/kubectl/
  • 看一下你建好的configmap
$ kubectl describe configmaps game-config
Name:           game-config
Namespace:      default
Labels:         <none>
Annotations:    <none>

Data # 在這裡
====
game.properties:        158 bytes # 這個叫key,內容沒列出來
ui.properties:          83 bytes
  • 2、把剛剛建的configmap object,輸出相對應的yaml檔
$ kubectl get configmaps game-config -o yaml
# yaml內容類似如下:
apiVersion: v1
data:
  game.properties: | # key
    enemies=aliens   # 內容 content
    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
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T18:52:05Z
  name: game-config # ConfigMap Object Name
  namespace: default
  resourceVersion: "516"
  selfLink: /api/v1/namespaces/default/configmaps/game-config # 用api方式的url
  uid: b4952dc3-d670-11e5-8cd0-68f728db1985
  • 補充說明,如果分次餵檔案也是可以的,結果同game-config
$ kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/kubectl/game.properties
$ kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/kubectl/ui.properties
  • 除了--from-file、--from-literal,還有一種「--from-env-file」
# 下載範例檔
$ wget https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/game-env-file.properties -O configure-pod-container/configmap/kubectl/game-env-file.properties
$ cat configure-pod-container/configmap/kubectl/game-env-file.properties
enemies=aliens # 也是key-value
lives=3
allowed="true"
$ kubectl create configmap game-config-env-file \
        --from-env-file=configure-pod-container/configmap/kubectl/game-env-file.properties

# 看看yaml檔
$ kubectl get configmap game-config-env-file -o yaml
apiVersion: v1
data: # game-env-file.properties裡面的資料就全進來了,而且在最上層
  allowed: '"true"' # string,外面用單引號'
  enemies: aliens
  lives: "3" # 數字也視為string ?
kind: ConfigMap
metadata:
  creationTimestamp: 2017-12-27T18:36:28Z
  name: game-config-env-file # ConfigMap Object Name
  namespace: default
  resourceVersion: "809965"
  selfLink: /api/v1/namespaces/default/configmaps/game-config-env-file
  uid: d9d1ca5b-eb34-11e7-887b-42010a8002b8
  • 一個kubectl create configmap指令裡,--from-env-file可以下多個
$ wget https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/ui-env-file.properties -O configure-pod-container/configmap/kubectl/ui-env-file.properties
$ kubectl create configmap config-multi-env-files \
        --from-env-file=configure-pod-container/configmap/kubectl/game-env-file.properties \
        --from-env-file=configure-pod-container/configmap/kubectl/ui-env-file.properties

最後,看跟上面無關的Pod yaml檔

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
# envFrom參考進來後,就能直接用環境變數囉,$(SPECIAL_LEVEL_KEY)
      command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
# 也可以用envFrom
      envFrom:
      - configMapRef: # 參考整個Config Map Object
          name: special-config

# 假設要參考到2個以上的ConfigMap Object,而且是指定到key(不是整個object都參考)
      env: 
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef: # 顧名思義,就是參考 Config Map Object的某個key的item
              name: special-config # 第1個object
              key: special.how 
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config # 第1個object
              key: log_level
  restartPolicy: Never

一回生、二回熟,勇者們,衝呀呀呀~~~

再來看另一篇官網的教學範例:

Configuring Redis using a ConfigMap

https://kubernetes.io/docs/tutorials/configuration/

  • 1、建立ConfigMap.
  • 2、在yaml檔裡使用ConfigMap.
  • 3、建立pod
  • 4、驗證結果

1、建立ConfigMap

  • pods/config/redis-config
# 先下載練習用的redis-config
$ curl -OL https://k8s.io/examples/pods/config/redis-config
# 從redis-config建立1個configmap
$ kubectl create configmap example-redis-config --from-file=redis-config
configmap/example-redis-config created
# 把example-redis-config輸出成yaml檔
$ kubectl get configmap example-redis-config -o yaml
  • example-redis-config
apiVersion: v1
data:
  redis-config: |
    maxmemory 2mb
    maxmemory-policy allkeys-lru
kind: ConfigMap
metadata:
  creationTimestamp: 2016-03-30T18:14:41Z
  name: example-redis-config
  namespace: default
  resourceVersion: "24686"
  selfLink: /api/v1/namespaces/default/configmaps/example-redis-config
  uid: 460a2b6e-f6a3-11e5-8ae5-42010af00002

2、在yaml檔裡使用ConfigMap.

  • pods/config/redis-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: kubernetes/redis:v1
    env:
    - name: MASTER
      value: "true"
    ports:
    - containerPort: 6379
    resources:
      limits:
        cpu: "0.1"
    volumeMounts:
    - mountPath: /redis-master-data # 空目錄
      name: data
    - mountPath: /redis-master # config volume 掛載目錄
      name: config
  volumes:
    - name: data
      emptyDir: {}
    - name: config
      configMap: # configmap
        name: example-redis-config # 上面產生的
        items:
        - key: redis-config # example-redis-config裡面的其中一個data:redis-config
          path: redis.conf

最後在container裡面會有這個檔案:

  • /redis-master/redis.conf

3、建立pod

$ kubectl create -f https://k8s.io/examples/pods/config/redis-pod.yaml

4、驗證結果

  • 進redis,用redis-cli,看設定有沒有設成功
$ kubectl exec -it redis redis-cli
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "2097152"
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "allkeys-lru"

呼,終於看懂了@@~
因為時間太短,文章都很亂,真的很不好意思
英文程度好的大大們,就大概了解後直接看官網文件囉


上一篇
day19_k8s05_儲存篇_上集_Volumes(Storage)
下一篇
day21_k8s07_Pod Preset,RBAC,helm,ingress
系列文
在地端建置Angular+ASP.NET Core的DevOps環境31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言