Volume และ Volume Type
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Volume คือ directory ที่ container ใน Pod เข้าถึงได้ และ lifecycle ของตัวเองผูกกับ Pod ไม่ใช่ผูกกับ container ตัวใดตัวหนึ่ง
ทำไม filesystem ของ container เองถึงไม่พอ
หัวข้อที่มีชื่อว่า “ทำไม filesystem ของ container เองถึงไม่พอ”container แต่ละตัวมี writable layer เป็นของตัวเอง แต่ layer นั้นถูกทิ้งทันทีที่ container restart ไม่ว่าจะเพราะ crash, image update หรือ liveness probe fail ก็ตาม ซึ่งไม่มีปัญหาสำหรับ application code แบบ stateless แต่พังทันทีถ้าอะไรบางอย่างต้องอยู่รอดข้าม restart หรือต้อง share กันระหว่าง container ใน Pod เดียวกัน Volume แก้ทั้งสองปัญหานี้ เพราะถูก declare ครั้งเดียวที่ระดับ Pod และคงอยู่ตราบใดที่ Pod นั้นยังอยู่ ไม่ว่า container ตัวใดใน Pod จะ restart กี่รอบก็ตาม
# The container restarts, but the Pod (and its volumes) does not disappearkubectl get pod log-processor -o jsonpath='{.status.containerStatuses[*].restartCount}'Volume type ที่ใช้บ่อย
หัวข้อที่มีชื่อว่า “Volume type ที่ใช้บ่อย”emptyDir— directory เปล่าที่ถูกสร้างตอน Pod เริ่มทำงาน เป็น scratch space เหมาะกับ cache หรือ share file ระหว่าง container หลักกับ sidecaremptyDirถูกลบถาวรตอน Pod ถูกลบ ไม่ใช่แค่ตอน container ตัวใดตัวหนึ่ง restarthostPath— mount path จาก filesystem ของ node จริงเข้ามาใน Pod ตรง ๆ ทรงพลังแต่เสี่ยง เพราะผูก Pod เข้ากับข้อมูลที่อยู่บน node เฉพาะตัวนั้น และอาจเปิดให้ container เข้าถึงส่วนที่ sensitive ของ filesystem ของ node ได้ ใช้เท่าที่จำเป็นจริง ๆ เท่านั้น (เช่น log collector หรือ agent ระดับ node)configMap/secretvolume — project ข้อมูล key-value จาก ConfigMap หรือ Secret เข้าไปใน Pod เป็นไฟล์ หนึ่ง key ต่อหนึ่งไฟล์ รายละเอียดอยู่ในบทถัดไปเรื่อง ConfigMap และ Secret- projected volume — รวมหลาย source (ConfigMap, Secret, downwardAPI, serviceAccountToken) เข้าเป็น directory เดียว container เลยต้องใช้ mount point เดียวก็เห็นข้อมูลที่มาจากหลาย object ได้
นี่คือ Pod ที่ container สองตัว share emptyDir volume เดียวกัน ที่เป็น pattern ที่พบบ่อยเวลามี container หลักคู่กับ sidecar ที่คอย ship log
apiVersion: v1kind: Podmetadata: name: log-processorspec: volumes: - name: shared-logs emptyDir: {} containers: - name: app image: my-app:1.0 volumeMounts: - name: shared-logs mountPath: /var/log/app - name: log-shipper image: fluent-bit:2.2 volumeMounts: - name: shared-logs mountPath: /var/log/app readOnly: trueและตัวอย่าง hostPath ที่ mount directory /proc ของ node แบบ read-only เข้า monitoring agent
apiVersion: v1kind: Podmetadata: name: node-exporterspec: volumes: - name: proc hostPath: path: /proc type: Directory containers: - name: node-exporter image: prom/node-exporter:v1.7.0 volumeMounts: - name: proc mountPath: /host/proc readOnly: trueการ declare volume มีสองขั้นตอนเสมอ
หัวข้อที่มีชื่อว่า “การ declare volume มีสองขั้นตอนเสมอ”แค่ตั้งชื่อ volume ยังใช้งานไม่ได้ volume ระดับ Pod ทุกตัวต้องมีสองส่วนที่ตรงกัน
spec.volumes[]— declare volume และบอกว่าข้อมูลมาจากไหน (emptyDir,hostPath,configMap,persistentVolumeClaimฯลฯ) ส่วนนี้อยู่ในระดับ Podspec.containers[].volumeMounts[]ของแต่ละ container — เอา volume ที่ declare ไว้แล้วมา mount เข้า filesystem ของ container ตัวนั้น ที่mountPathที่กำหนด
volume ที่ declare ไว้ใน spec.volumes แต่ไม่มี container ไหน reference ผ่าน volumeMounts เลย จะไม่มีผลอะไรกับ container นั้น คือแค่ไม่ถูก mount เข้าไป นี่คือวิธีที่ container สองตัวใน Pod เดียวกัน share ข้อมูลกันได้ด้วย ทั้งสองตัวใส่ name ของ volume เดียวกันใน volumeMounts ของตัวเอง ชี้ไปที่ directory เดียวกันที่ declare ไว้ครั้งเดียวใน spec.volumes
# Confirm both containers see the same file through the shared emptyDirkubectl exec log-processor -c log-shipper -- ls /var/log/appflowchart LR
subgraph pod["Pod: log-processor"]
a["Container: app"] -->|writes| vol[("emptyDir volume: shared-logs")]
vol -->|reads| b["Container: log-shipper"]
end