LFS258 [8/15]: Kubernetes Volumes and Data
Sawit M.
Posted on March 23, 2020
TL;DR
- Overview: Volume มองง่ายๆ มันคือ hard disk ของ container มีทั้งแบบที่ถ้า Pods ตาย แล้ว data หาย (Ephemeral) และ data ไม่หาย (Persistent) โดย Kubernetes นั้น support storage หลายชนิด ที่เป็นแบบนั้นเพราะ มี FlexVolume และ CSI ซึ่งทำให้ vendor ต่างๆ สามารถพัฒนา library ที่ support product ของตนเองแล้วนำมาต่อกับ Kubernetes ได้เลย
-
Introducing Volumes: Kubernetes mount Volume ในระดับ Pods แสดงว่า container ต่างๆ ที่อยู่ใน Pods จะเห็น Volume เดียวกัน ขึ้นอยู่กับ container จะเลือกเอาอะไรไปใช้ การ access volume ขึ้นอยู่กับ Permission ที่เรากำหนดไว้ โดย Permission จะเป็นระดับ Node เช่น
ReadWriteOnce
,ReadOnlyMany
และReadWriteMany
-
Volume Types: Kubernetes support backend storage ได้หลากหลายชนิด เรียกชนิดเหล่านั้นว่า
StorageClass
โดยStorageClass
นี้เองจะเป็นคนไปคุยกับ backend storage เพื่อทำงานต่างๆ ให้ -
Volume Spec: แสดงการใช้งาน
emptyDir
ซึ่งเป็นStorageClass
ที่เป็น Ephemeral Storage - Share Volumes: แสดงการใช้ 2 containers ที่อยู่ใน Pod เดียวกัน mount Volume เดียวกัน โดยจะเห็นว่าทั้ง 2 containers เห็น data เดียวกัน
- Persistence Volumes and Volume Claims: ในการใช้งาน Persistent Volume ต้องสร้าง PV ก่อน จากนั้นสร้าง PVC เพื่อจองพื้นที่จาก PV มาใช้ จากนั้นจึงจะสามารถ attach ไปให้ Pods ใช้งานได้
- Secrets: เป็นวิธีช่วยให้ Pods การเข้าถึง data ได้ โดย Secret จะถูก encode แบบ base64 โดย Pods สามารถใช้งาน Secret ได้ทั้งแบบ Volume และ Environment Variable
- ConfigMap: เป็นวิธีช่วยให้ Pods การเข้าถึง data ได้ โดย ConfigMap เป็น plain textโดย Pods สามารถใช้งาน ConfigMap ได้ทั้งแบบ Volume และ Environment Variable
Overview
Volume ก็คือ storage นั่นเอง มองง่ายๆ มันคือ hard disk ของ container โดยทั่วไป ถ้า container ตายหรือ restart data ในนั้นก็จะหายไป ใน Kubernetes volume จะผูกอยู่กับ Pods ไม่ใช้ container ภายใน โดยถ้า Pods มี หลาย container อยู่ภายใน container นะเห็น volume และ data เดียวกัน
ถ้าไม่อยากให้ data หายสามารถใช้ Persistent Volumes ได้ ซึ่ง Pods จะต้องทำการ claim volume ผ่านทาง Persistent Volumes Claim
การเขียนหรืออ่านอะไรใน volume จะต้องทำผ่าน backend storage ซึ่งใน v1.17 รองรับถึง 28 ชนิด ไม่ว่าจะเป็น Ceph, NFS หรือ storage ของ cloud providers เจ้าต่างฟๆ ซึ่งอาจมีการ setting ต่างกันตามชนิดของ backend
Backend storage ของ kubernetes มี 2 แบบ คือ
- in-tree: ส่วนใหญ่เป็นมากับ kubernetes มาอย่างยาวนาน โดย code จะถูก built, linked, compiled และ shipped มากับ core ของ Kubernetes เลย Backend ประเภทนี้ค่อยๆ ถูก migrate ไปเป็น out-of-tree
- out-of-tree: หลังจากการมาของ FlexVolume ใน v1.2 และ Container Storage Interface (CSI) ใน v1.9 ทำให้ code ของ backend storage ไม่ต้องรวมอยู่ใน core ของ Kubernetes อีกต่อไป โดย เจ้าของ product สามารถ develop เองแล้วค่อยมาต่อกับ kubernetes ด้วยวิธีนี้ทำให้ core ของ kubernetes เบาขึ้น และ เจ้าของ product สามารถ fix bug หรือ เพิ่ม feature ได้เองโดยไม่ต้องรอมาก merge code กับ kubernets การทำงานก็จะเร็วขึ้น
นอกจากการใช้ volume ที่กล่าวข้างต้น ยังมีอีก 2 ทางในการนำ data เข้าไปยัง Pods นั่นคือ
- Secret: เป็นการส่งข้อมูลที่ถูก encode แล้ว เข้าไปใน pods เช่น SSH Key หรือ HTTPS certification
- ConfigMap: เป็นการส่งข้อมูลที่ไม่ถูก encode เข้าไปใน pods เช่น configuration file ต่างๆ
Introducing Volumes
ใน 1 Pods สามารถ mount ได้หลาย volume และสามารถคละ backend type ได้ และเนื่องจาก volume ถูก present ในระดับ Pod ดังนั้น ต่าง container กันสามารถเห็น volume เดียวกันได้ อาจใช้ท่านี้เพื่อใช้ในการทำ container-to-container communition
นอกจากนี้ Volume ยังสามารถถูกเข้าถึงได้จากหลายๆ pods พร้อมๆ กัน และสามารถให้สิทธิ์ที่แตกต่างกันได้ แต่เนื่องจาก volume ไม่มี concurrent checking ดังนั้น อาจเกิดปัญหา file corrupt หรือ locking ได้
ในการกำหนด access mode ต้องทำ 2 ทางคือ ที่ volume เอง และ ตอนที่ Pods request claim เข้ามา โดย request จะต้องไม่มากกว่าสิทธิ์ที่ volume กำหนดไว้ โดย access mode มี ดังนี้
-
ReadWriteOnce: อนุญาติให้อ่านและเขียนได้จาก node เดียวเท่านั้น (2 pods ที่อยู่ node เดียวกัน สามารถ read/write ได้พร้อมกัน แต่ pods ที่อยู่ต่าง node read/write ไม่ได้ จะได้ error
FailedAttachVolume
) - ReadOnlyMany: อนุญาติให้อ่านอย่างเดียว จากหลายๆ node พร้อมๆ กัน
- ReadWriteMany: อนุญาติให้อ่านและเขียนได้จากหลายๆ node พร้อมๆ กัน
kubernetes จะจัดกลุ่มของ volume ที่มี permission เหมือนกันไว้ และ sort volume size จาก น้อยไปหามาก เมื่อมี request เข้ามาก็จะเลือก volume เหมาะสมที่สุดให้ไป
เมื่อมีการ request volume
- API server ของ kubernetes จะ request ขอ storage ไปยัง
StorageClass
plugin และStorageClass
plugin จะเป็นคนไปคุยกับ backend storage เอง - kubelet จะ map raw devices กับ mount point ใน container แลัวทำเป็น symbolic link บน host node filesystem เพื่อให้ container ใช้งาน storage ได้
ถ้าเราไม่ระบุ StorageClass
Kubernetes จะเลือก Storage อะไรก็ได้ที่เหมาะสมกับ size และ access mode ที่เราส่งไปกลับมาให้เรา
Volume Types
Kubernetes รองรับ Backend Storage ได้หลากหลาย บางอย่างใช้แค่ใน local บางอย่างต้องใช้งานผ่าน network ซึ่งแต่ละชนิดก็มีข้อดี ข้อเสีย แต่ต่างกันออกไป เรามาดูตัวอย่างกันซักหน่อย
- GCEpersistentDisk: เป็นการ mount disk GCE บน Google Cloud Platform เข้าไปใน Pods
- awselasticblockstore: เป็นการ mount disk EBS บน AWS เข้าไปใน Pods
- emptyDir: เป็นการ mount empty directory ให้กับ Pods โดย volume จะเกิดและตายไปพร้อมกับ Pod
-
hostPath: เป็นการ mount resource จาก host เช่น file หรือ directoty ให้กับ Pods ซึ่งต้องมี resource ดังกล่าวอยู่ที่ host ก่อน ยกเว้น ถ้าเราใช้ option
DirectoryOrCreate
หรือFileOrCreate
ที่จะสร้าง resource ให้ Pods หากไม่มี - nfs: เป็นการ mount NFS (Network File System) ให้กับ Pods (เหมาะกับ multiple readers)
- iscsi: เป็นการ mount iSCSI (SCSI over IP) ให้กับ Pods (เหมาะกับ multiple readers)
- rbd: เป็นการ mount Rados Block Device ให้กับ Pods (เหมาะกับ multiple writers)
- cephfs: เป็นการ mount CephFS volume ให้กับ Pods (เหมาะกับ multiple writers)
- glusterfs: เป็นการ mount Glusterfs (an open source networked filesystem) ให้กับ Pods (เหมาะกับ multiple writers)
สามารถดู Backend Storage เพิ่มเติมได้จาก Official Doc
Volume Spec
ตัวอย่างการ mount volume ง่ายๆ คือ emptyDir โดย emptyDir
คือ การสร้าง directory ภายใน container นั่นเอง ไม่ได้ไป mount ที่ไหน การเขียน data จะอยู่ใน shared container space ทำให้ไม่ persistence และ หายไปพร้อมกับการตายของ Pod
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox
name: busy
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /scratch
name: scratch-volume
volumes:
- name: scratch-volume
emptyDir: {}
YAML file ข้างต้น เป็นการสร้าง Pod ที่มี 1 container และมี volume ชื่อ scratch-volume mount ไว้ที /scratch ภายใน container
สามารถทดสอบได้ดังนี้
$ cat > busybox-emptyDir.yml << EOF
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox
name: busy
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /scratch
name: scratch-volume
volumes:
- name: scratch-volume
emptyDir: {}
EOF
$ kubectl apply -f busybox-emptyDir.yml
pod/busybox created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 9s
$ kubectl exec -it busybox -- sh
/ # df -h /scratch
Filesystem Size Used Available Use% Mounted on
/dev/mapper/rhel-var 60.0G 2.5G 57.5G 4% /scratch
/ # exit
$ kubectl delete -f busybox-emptyDir.yml
pod "busybox" deleted
Share Volumes
จากที่เรารู้แล้วว่า container ใน pod เดียวกัน จะ share volume กัน หัวข้อนี้จะมาลงมือทำกันดู ก่อนอื่นมาดู YAML กันก่อน
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox
name: busy
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /busy
name: test
- image: busybox
name: box
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /box
name: test
volumes:
- name: test
emptyDir: {}
จาก YAML file ข้างต้น ใน pod มี 2 containers ชื่อ busy และ box โดย แต่ละ container mount volume ชื่อ test ไว้ที่ path /busy และ /box ตามลำดับ เดี๋ยวเราจะลองเขียนอ่าน file ดูกันว่ามันเห็นเหมือนกันไหม
$ cat > busybox-sharedVolume.yml << EOF
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox
name: busy
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /busy
name: test
- image: busybox
name: box
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /box
name: test
volumes:
- name: test
emptyDir: {}
EOF
$ kubectl apply -f busybox-sharedVolume.yml
pod/busybox created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox 2/2 Running 0 16s
$ kubectl exec -it busybox -c box -- touch /box/foobar
$ kubectl exec -it busybox -c busy -- ls /busy/
foobar
$ kubectl delete -f busybox-sharedVolume.yml
pod "busybox" deleted
Persistence Volumes and Volume Claims
Persistence Volumes (pv) คือ storage ที่ ถ้า pods ตาย data ก็ยังอยู่ การ assign volume ประเภทนี้ ให้กับ Pods จะต้อง define PersistentVolume
ก่อน จากนี้ สร้าง persistenctVolumeClaim
(pvc) เพื่อขอเฉือน volume มาให้ pod ใช้ จากนั้นจึงค่อย attach persistent volume ก้อนที่ claim มาให้กับ Pod
Lifecycle ของ volume และ claim มีดังนี้
-
Provisioning: เป็นการสร้าง PV โดยทำได้ทั้ง Static และ Dynamic
- Statics: admin create PV ทิ้งไว้ รอให้ users มา claim ไปใช้
-
Dynamic: ถ้าไม่มี PV ไหน match กับ PVC เลย kubernetes จะ create PV ให้อันโนมัติ โดย cluster ต้องระบุ
DefaultStorageClass
ด้วย
-
Binding: อาจเป็นช่วงที่กำลัง match PVC กับ PV หรือ PV รอให้
StorageClass
provision PV ขึ้นมา - Using: เป็นช่วงที่ Pods mount volume ไปใช้แล้ว
-
Releasing: เป็นช่วงที่ Pods ส่งคำสั่งไปเลิกใช้ PVC และทำการ delete PVC เมื่อ PVC ถูกลบไปแล้ว data อาจยังอยู่หรือถูกลบขึ้นอยู่กับ
persistentVolumeReclaimPolicy
-
Reclaiming: เป็นช่วงหลังจากที่ PVC ถูก delete เรียบร้อย โดย มี 3 options คือ
- Retain: ยังเก็บ PV ไว้อยู่ ให้ admin เอาไปจัดการเอง
- Delete: ลบ PV และ Backend Storage ที่ allocate ให้ไปด้วยเลย
-
Recycle: (deprecated แล้ว) ลบข้อมูลใน PVC ด้วย (
rm -rf /thevolume/*
) จากนั้นก็พร้อมสำหรับการถูก claim ใหม่
PV นั้นไม่ได้อยู่ใน namespace ใด namespace หนึ่ง แต่ PVC จะอยู่ได้แค่ namespace เดียวเท่านั้น
ตัวอย่างการ create PV ชื่อ task-pv-volume ซึ่งแบบ hostPath ขนาด 10 GB ใน mode ReadWriteOnce
$ cat > pv-volume.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
EOF
$ kubectl apply -f pv-volume.yaml
persistentvolume/task-pv-volume created
$ kubectl get pv task-pv-volume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Available manual 14s
ตัวอย่างการ create PVC ชื่อ task-pv-claim ซึ่ง เฉือนมาใช้ 3 GB ใน mode ReadWriteOnce
$ cat > pv-claim.yaml << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
EOF
$ kubectl apply -f pv-claim.yaml
persistentvolumeclaim/task-pv-claim created
$ kubectl get pv task-pv-volume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Bound default/task-pv-claim manual 6m23s
$ kubectl get pvc task-pv-claim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
task-pv-claim Bound task-pv-volume 10Gi RWO manual 75s
จะเห็นว่า PVC เปลี่ยนจาก status "Available" เป็น "Bound"
ตัวอย่างการ attch PVC ไปยัง Pods
# ที่ Master Node
$ cat > pv-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
EOF
$ kubectl apply -f pv-pod.yaml
pod/task-pv-pod created
$ kubectl get pod task-pv-pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
task-pv-pod 1/1 Running 0 71s 192.168.32.51 kube-0003.novalocal <none> <none>
# login เข้าไปยังเครื่องที่ pods run อยู่ ในที่นี้คือ kube-0003.novalocal
$ sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html"
$ cat /mnt/data/index.html
Hello from Kubernetes storage
# กลับไปยัง Master Node
kubectl exec -it task-pv-pod -- /bin/bash
root@task-pv-pod:/# apt update
root@task-pv-pod:/# apt install curl
root@task-pv-pod:/# curl http://localhost/
Hello from Kubernetes storage
root@task-pv-pod:/# exit
# Clean up
$ kubectl delete pod task-pv-pod
pod "task-pv-pod" deleted
$ kubectl delete pvc task-pv-claim
persistentvolumeclaim "task-pv-claim" deleted
$ kubectl delete pv task-pv-volume
persistentvolume "task-pv-volume" deleted
# Login ไปยังเครื่องที่ สร้าง file ไว้ (kube-0003.novalocal)
$ sudo rm /mnt/data/index.html
$ sudo rmdir /mnt/data
Secrets
ถึงแม้ Pods จะสามารถเข้าถึง data ต่างๆ ด้วย volume แต่ก็มีบาง data ที่เราไม่อยากให้เห็นได้ด้วยตาเปล่า เช่น password หรือ certification จึงมี Secret เกิดขึ้นมา โดย Secret จะถูกเก็บในรูปแบบ base64-encoded (default)
แต่เราสามารถ configure ให้มัน encrypt ได้โดยการสร้าง EncryptionConfiguration
ด้วย key และ identity ที่เหมาะสม จากนั้นทำการเพิ่ม flag --encryption-provider-config
ที่ใช่ระบุวิธีการ encrypt เช่น "aescdc" หรือ "ksm" ให้กับkube-apiserver
แล้วทำการ recreate Secret ใหม่ทั้งหมด
ในการเปลี่ยน key ต้องสร้าง key ใหม่ก่อน แล้วจึง restart kube-apiserver
ทุกตัว จากนั้นจึง recreate Secret ใหม่ทั้งหมด
kubernetes ไม่ได้จำกับจำนวน Secret แต่ Secret ไม่ควรมีขนาดเกิน 1 MB โดย Secret จะถูกเก็บใน tmpfs
ซึ่งเป็น memory ดังนั้นถ้ามีเยอะกินไปก็จะเปลือง memory ของ host
เราสามารถ create Secret ได้ดังนี้
-
Create ด้วย
kubectl create secret
command จาก file
$ echo -n 'admin' > username.txt $ echo -n '1f2d1e2e67df' > password.txt $ 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 10s $ kubectl describe secret/db-user-pass Name: db-user-pass Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password.txt: 12 bytes username.txt: 5 bytes
-
Create ด้วย
kubectl create secret
command จากการ pass argument
$ kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb' secret/dev-db-secret created $ kubectl get secrets NAME TYPE DATA AGE dev-db-secret Opaque 2 4s $ kubectl describe secret/dev-db-secret Name: dev-db-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 11 bytes username: 7 bytes
-
Create ด้วย YAML file แบบ manual
$ echo -n 'admin' | base64 YWRtaW4= $ echo -n '1f2d1e2e67df' | base64 MWYyZDFlMmU2N2Rm $ cat > secret.yaml << EOF apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm EOF $ kubectl apply -f secret.yaml secret/mysecret created $ kubectl get secrets NAME TYPE DATA AGE mysecret Opaque 2 15s $ kubectl describe secret/mysecret Name: mysecret Namespace: default Labels: <none> Annotations: Type: Opaque Data ==== password: 12 bytes username: 5 bytes
-
Create ด้วย YAML file โดยใช้
stringData
$ cat > secret_config.yaml << EOF apiVersion: v1 kind: Secret metadata: name: config.yaml type: Opaque stringData: config.yaml: |- apiUrl: "https://my.api.com/api/v1" username: "user" password: "password" EOF $ kubectl apply -f secret_config.yaml secret/config.yaml created $ kubectl get secrets NAME TYPE DATA AGE config.yaml Opaque 1 8s $ kubectl describe secret/config.yaml Name: config.yaml Namespace: default Labels: <none> Annotations: Type: Opaque Data ==== config.yaml: 73 bytes
เราสามารถใช้งาน Secret ได้ดังนี้
-
ใช้ในรูปแบบของ file ใน Pods
$ kubectl describe secret/mysecret Name: mysecret Namespace: default Labels: <none> Annotations: Type: Opaque Data ==== password: 12 bytes username: 5 bytes $ cat > pod-with-mysecret-01.yaml << EOF 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 EOF $ kubectl apply -f pod-with-mysecret-01.yaml pod/mypod created $ kubectl exec -it mypod -- bash root@mypod:/data# ls -l /etc/foo total 0 lrwxrwxrwx. 1 root root 15 Mar 20 15:30 password -> ..data/password lrwxrwxrwx. 1 root root 15 Mar 20 15:30 username -> ..data/username root@mypod:/data# cat /etc/foo/username admin root@mypod:/data# cat /etc/foo/password 1f2d1e2e67df root@mypod:/data# exit $ kubectl delete -f pod-with-mysecret-01.yaml pod "mypod" deleted
เราสามารถเลือก mount บาง file แล้วเปลี่ยนที่วาง และ ชื่อ file ด้วยได้ดังนี้
$ cat > pod-with-mysecret-02.yaml << EOF 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 EOF $ kubectl apply -f pod-with-mysecret-02.yaml pod/mypod created $ kubectl exec -it mypod -- bash root@mypod:/data# ls -l /etc/foo/my-group/my-username -rw-r--r--. 1 root root 5 Mar 20 15:35 /etc/foo/my-group/my-username root@mypod:/data# cat /etc/foo/my-group/my-username admin root@mypod:/data# exit $ kubectl delete -f pod-with-mysecret-02.yaml pod "mypod" deleted
ใช้ในรูปแบบของ Environment Variable ใน Pods
$ cat > pod-with-mysecret-03.yaml << EOF
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
EOF
$ kubectl apply -f pod-with-mysecret-03.yaml
pod/secret-env-pod created
$ kubectl exec -it secret-env-pod -- bash
root@secret-env-pod:/data# echo $SECRET_USERNAME
admin
root@secret-env-pod:/data# echo $SECRET_PASSWORD
1f2d1e2e67df
root@secret-env-pod:/data# exit
exit
$ kubectl delete -f pod-with-mysecret-03.yaml
pod "secret-env-pod" deleted
ConfigMap
ConfigMap เหมือน Secret เลย ยกเว้นแค่ไม่ได้ encode เราใช้ ConfigMap เพื่อแยก configuration file ออกมาจาก image ของ container เพื่อเวลาที่ต้องการแก้ไข configuration file จะได้ไม่ต้อง build image ใหม่ ซึ่งจะทำให้ version ของ image เพิ่มขึ้น โดยไม่ได้มีการแก้ไข code ด้วย
ConfigMap สามารถเก็บข้อมูลได้ทั้งในรูปแบบ key-value pair หรือ plain configuration file
เราสามารถสร้าง ConfigMap ได้ดังนี้
-
สร้างจาก Directory
$ mkdir -p configure-pod-container/configmap/ $ wget https://kubernetes.io/examples/configmap/game.properties -O configure-pod-container/configmap/game.properties $ wget https://kubernetes.io/examples/configmap/ui.properties -O configure-pod-container/configmap/ui.properties $ ls -l configure-pod-container/configmap/ total 8 -rw-rw-r--. 1 admin admin 157 Mar 20 22:57 game.properties -rw-rw-r--. 1 admin admin 83 Mar 20 22:57 ui.properties $ kubectl create configmap game-config --from-file=configure-pod-container/configmap/ configmap/game-config created $ kubectl describe configmaps game-config Name: game-config Namespace: default Labels: <none> Annotations: <none> Data ==== 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 Events: <none> $ kubectl get configmaps game-config -o yaml apiVersion: v1 data: 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 kind: ConfigMap metadata: creationTimestamp: "2020-03-20T15:58:20Z" name: game-config namespace: default resourceVersion: "13587110" selfLink: /api/v1/namespaces/default/configmaps/game-config uid: efe9fc42-4c19-495f-a0f1-91b59dc77431
-
สร้างจาก File
$ kubectl create configmap game-config-3 --from-file=game-special-key=configure-pod-container/configmap/game.properties configmap/game-config-3 created $ kubectl describe configmaps game-config-3 Name: game-config-3 Namespace: default Labels: <none> Annotations: <none> Data ==== game-special-key: ---- enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 Events: <none> $ kubectl get configmaps game-config-3 -o yaml apiVersion: v1 data: game-special-key: |- enemies=aliens lives=3 enemies.cheat=true enemies.cheat.level=noGoodRotten secret.code.passphrase=UUDDLRLRBABAS secret.code.allowed=true secret.code.lives=30 kind: ConfigMap metadata: creationTimestamp: "2020-03-20T16:00:46Z" name: game-config-3 namespace: default resourceVersion: "13587478" selfLink: /api/v1/namespaces/default/configmaps/game-config-3 uid: 03e19330-0498-43f3-baf6-fce9501398df
-
สร้างจาก literal values
$ kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm configmap/special-config created $ kubectl describe configmaps special-config Name: special-config Namespace: default Labels: <none> Annotations: <none> Data ==== special.how: ---- very special.type: ---- charm Events: <none> $ kubectl get configmaps special-config -o yaml apiVersion: v1 data: special.how: very special.type: charm kind: ConfigMap metadata: creationTimestamp: "2020-03-20T16:03:16Z" name: special-config namespace: default resourceVersion: "13587836" selfLink: /api/v1/namespaces/default/configmaps/special-config uid: 7d5636bb-a677-46dd-8f49-a60f35999357
เราสามารถใช้งาน ConfigMap ได้ดังนี้
-
Assign ConfigMap ไปยัง Environment Variable
$ kubectl create configmap special-config --from-literal=special.how=very configmap/special-config created $ cat > pod-single-configmap-env-variable.yaml << EOF apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: redis env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how restartPolicy: Never EOF $ kubectl create -f pod-single-configmap-env-variable.yaml pod/dapi-test-pod created $ kubectl exec -it dapi-test-pod -- bash root@dapi-test-pod:/data# echo $SPECIAL_LEVEL_KEY very root@dapi-test-pod:/data# exit exit $ kubectl delete -f pod-single-configmap-env-variable.yaml pod "dapi-test-pod" deleted
-
add ConfigMap เป็น data ใน Volume
$ cat > configmap-multikeys.yaml << EOF apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: SPECIAL_LEVEL: very SPECIAL_TYPE: charm EOF $ kubectl create -f configmap-multikeys.yaml configmap/special-config created $ cat > pod-configmap-volume.yaml << EOF apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: redis volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config restartPolicy: Never EOF $ kubectl create -f pod-configmap-volume.yaml pod/dapi-test-pod created $ kubectl exec -it dapi-test-pod -- bash root@dapi-test-pod:/data# ls -l /etc/config/ total 0 lrwxrwxrwx. 1 root root 20 Mar 20 16:25 SPECIAL_LEVEL -> ..data/SPECIAL_LEVEL lrwxrwxrwx. 1 root root 19 Mar 20 16:25 SPECIAL_TYPE -> ..data/SPECIAL_TYPE root@dapi-test-pod:/data# cat /etc/config/SPECIAL_LEVEL very root@dapi-test-pod:/data# cat /etc/config/SPECIAL_TYPE charm root@dapi-test-pod:/data# exit $ kubectl delete -f pod-configmap-volume.yaml pod "dapi-test-pod" deleted $ kubectl delete -f configmap-multikeys.yaml configmap "special-config" deleted
Posted on March 23, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 24, 2023