ConfigMaps และ Secrets
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”ConfigMap และ Secret แยก configuration ออกจาก container image ทำให้ image เดียวรันได้หลาย environment และ Secret โดย default แค่ base64-encode เท่านั้น ไม่ได้ encrypt
ConfigMap: configuration ที่ไม่ confidential
หัวข้อที่มีชื่อว่า “ConfigMap: configuration ที่ไม่ confidential”ConfigMap เก็บข้อมูล configuration แบบ key-value เช่น feature flag, log level, URL ซึ่ง Pod เอาไปใช้ได้สามแบบ คือเป็น environment variable, เป็น command-line argument หรือเป็นไฟล์ที่ mount เข้ามา ถ้าฝัง configuration ไว้ใน image ต้อง build ใหม่ทุก environment แต่ ConfigMap ทำให้ image เดียวอ่าน configuration ต่างกันได้ตามว่าชี้ไปที่ ConfigMap ตัวไหน
apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: LOG_LEVEL: "info" FEATURE_FLAG_NEW_UI: "true"immutable: trueการตั้ง immutable: true บอก Kubernetes ว่าข้อมูลใน ConfigMap นี้จะไม่เปลี่ยนอีกแล้ว นี่คือ optimization จริงบน cluster ขนาดใหญ่ kubelet ไม่ต้อง watch object นี้เพื่อรอ update อีกต่อไป ลด load ที่ API server และยังกันการแก้ผิดพลาดโดยไม่ตั้งใจได้ด้วย
Secret: โครงสร้างเดียวกัน แต่ intent ต่างกัน พร้อมข้อควรระวังสำคัญ
หัวข้อที่มีชื่อว่า “Secret: โครงสร้างเดียวกัน แต่ intent ต่างกัน พร้อมข้อควรระวังสำคัญ”Secret มีโครงสร้าง object เหมือน ConfigMap ทุกอย่าง แต่ตั้งใจไว้สำหรับค่าที่ sensitive เช่น password, token, certificate ข้อเท็จจริงที่สำคัญที่สุดคือ ค่าใน data ของ Secret แค่ base64-encode เท่านั้น ไม่ได้ encrypt base64 คือ encoding ไม่ใช่ cipher ใครก็ตามที่มีสิทธิ์อ่าน Secret ผ่าน API หรือมีสิทธิ์อ่าน etcd โดยตรง สามารถ decode ออกมาได้ด้วยคำสั่งเดียว อย่ามองว่า Secret ปลอดภัยด้วยตัวเองเฉย ๆ
apiVersion: v1kind: Secretmetadata: name: db-credentialstype: Opaquedata: password: cGFzc3dvcmQxMjM=# Anyone with read access can trivially reverse the encoding — this is not encryptionkubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decodeความปลอดภัยที่แท้จริงต้องเพิ่มมาตรการอื่นทับ Secret object เอง
- เปิด encryption at rest ให้ etcd เพื่อให้ byte ที่เก็บบน disk ถูก encrypt จริง
- RBAC ที่จำกัดแน่นหนาว่าใครหรืออะไร
get/listSecret ได้บ้าง - external secret manager (เช่น secret store ที่อิง cloud KMS) เชื่อมผ่าน CSI driver หรือ operator แทนที่จะพึ่ง Secret object ดิบ ๆ ของ Kubernetes เป็น source of truth
Kubernetes มี built-in Secret type มาให้ใช้กับ case ที่พบบ่อย
Opaque— default ทั่วไป ข้อมูล key-value อะไรก็ได้kubernetes.io/dockerconfigjson— credential สำหรับ registry ใช้กับimagePullSecretsตอน pull image จาก private registrykubernetes.io/tls— คู่ TLS certificate กับ private key มักถูกใช้โดย Ingress controller
ใช้ ConfigMap กับ Secret ใน Pod
หัวข้อที่มีชื่อว่า “ใช้ ConfigMap กับ Secret ใน Pod”apiVersion: v1kind: Podmetadata: name: webspec: containers: - name: web image: my-web-app:2.4 envFrom: - configMapRef: name: app-config env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: app-configสองวิธีนี้พฤติกรรมต่างกันตอน object ต้นทางเปลี่ยนหลัง Pod รันไปแล้ว
- environment variable ถูก resolve ครั้งเดียวตอน container เริ่ม การ update ConfigMap หรือ Secret ทีหลังไม่มีผลกับ container ที่รันอยู่แล้ว container จะยังใช้ค่าเดิมจนกว่าจะถูก restart
- volume ที่ mount ถูก kubelet sync เป็นระยะ พอ ConfigMap หรือ Secret ต้นทางเปลี่ยน เนื้อหาไฟล์บน disk จะถูก update ในที่สุด แต่ application เองก็ยังต้องคอยสังเกตว่าไฟล์เปลี่ยนแล้ว reload เอง Kubernetes ไม่ restart container ให้อัตโนมัติ
flowchart LR cm["ConfigMap: app-config"] -->|envFrom| pod["Pod container"] cm -->|volume mount, kubelet syncs on change| pod sec["Secret: db-credentials"] -->|env valueFrom, fixed at start| pod sec -->|volume mount, kubelet syncs on change| pod