ข้ามไปยังเนื้อหา

ConfigMaps และ Secrets

ConfigMap และ Secret แยก configuration ออกจาก container image ทำให้ image เดียวรันได้หลาย environment และ Secret โดย default แค่ base64-encode เท่านั้น ไม่ได้ encrypt

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: v1
kind: ConfigMap
metadata:
name: app-config
data:
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: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
password: cGFzc3dvcmQxMjM=
Terminal window
# Anyone with read access can trivially reverse the encoding — this is not encryption
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode

ความปลอดภัยที่แท้จริงต้องเพิ่มมาตรการอื่นทับ Secret object เอง

  • เปิด encryption at rest ให้ etcd เพื่อให้ byte ที่เก็บบน disk ถูก encrypt จริง
  • RBAC ที่จำกัดแน่นหนาว่าใครหรืออะไร get/list Secret ได้บ้าง
  • 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 registry
  • kubernetes.io/tls — คู่ TLS certificate กับ private key มักถูกใช้โดย Ingress controller
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
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
ข้อมูล ConfigMap กับ Secret เข้าถึง Pod ผ่าน env var และผ่าน volume mount
base64 encoding ใน Kubernetes Secret ถือเป็น encryption หรือไม่
วิธีใช้ข้อมูลแบบไหนที่รับ update ของ ConfigMap หรือ Secret ได้โดยไม่ต้อง restart container
ทำไมถึงอยากตั้ง ConfigMap เป็น immutable
Secret type ไหนที่ built-in ไว้ใช้เก็บ credential สำหรับ pull image จาก private registry