Basic Kubernetes Part 4 - สร้าง Volume ให้ Pod ใช้

Basic Kubernetes Part 4 - สร้าง Volume ให้ Pod ใช้

3 ตอนที่ผ่านมาเราได้เรียนรู้การสร้าง Pod สร้าง Network เข้าไปหา Pod สิ่งที่เรายังขาดคือการ Disk หรือ Volume ที่ให้ตัว Pod (Application) ของเราใช้งานในการเขียนไฟล์ อ่านไฟล์ วางไฟล์ config ตัวอย่างของ Pod (Application) ที่จำเป็นต้องใช้ Database ที่ต้องใช้ Disk หรือ Volume ในการเก็บข้อมูล

ตัวอย่างไฟล์ Configuration ทั้งหมดในตอนนี้อยู่ที่ Link

Persistent Volume : PV

Persistent Volume หรือเรียกสั้นๆว่า PV นั้นคือ Volume ที่เราแจ้งให้ k8s ทราบว่า k8s สามารถเอาไปใช้งานได้ เหมือนกับเราต้องการเอา Hard disk (สมัยนี้อาจจะเป็น SSD หมดแล้ว) มาใช้งาน เราก็ต้องแจ้งกับ OS ว่า Hard disk ของเราต่ออยู่กับช่องไหน

ลองสร้าง Persistent Volume กัน

ทำการสร้าง Folder ชื่อ nginx-volume จากนั้นสร้างไฟล์ index.html ขึ้นมา

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HELLO WORLD</title>
</head>
<body>
<h1>HELLO WORLD</h1>
</body>
</html>

สร้างเสร็จแล้วจะได้โครงสร้าง Folder แบบนี้

1
2
3
.
├── nginx-volume
└── index.html

ออกมาจาก Folder nginx-volume จากนั้นสร้างไฟล์ basic-pv.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
storageClassName: nginx-volume
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
hostPath:
path: <"Path ไป Folder nginx-volume">

# ตัวอย่าง
# ของผมว่าง nginx volme อยู่ที่ /root/test-pod/part4/nginx-volume ข้อมูลเลยเป้นด้านล่าง
# path: /root/test-pod/part4/nginx-volume

จากนั้นสั่ง apply ตัวไฟล์ basic-pv.yml จากนั้นสั่งด้านล่างเพื่อดูรายละเอียดของ Persistent Volume

1
2
kubectl get pv
kubectl descrbie pv nginx-volume

อธิบายไฟล์ basic-pv.yml

ในส่วนของ apiVersion , kind , metadata ขอไม่อธิบายนะครับ เพราะเหมือนกับ pod , service , deployment จะขออธิบายในส่วนของ spec

storageClassName

ส่วนนี้เป็นการบอกว่า volume นี้อยู่ในประเภทไหน โดย storageClassName จะถูกเอาไปใช้ใน map เพื่อเอา volume ไปใช้งาน ซึ่งค่านี้สามารถตั้งเป็นอะไรก็ได้ ผมตั้งชื่อมันว่า nginx-volume

capacity

เป็นการบอกว่า Volume นี้มีขนาดเท่าไหร่ จากตัวอย่างของผมประกาศไว้ว่ามีขนาด 2 GB

accessModes

ส่วนนี้เป็นการบอกว่า Volume นี้สามารถเข้าถึงจาก Node ใน k8s cluster ( k8s สามารถทำให้มีหลายๆเครื่องทำงานร่วมกันได้ ดังนั้น 1 cluster สามารถมีหลายเครื่องทำงานร่วมกันได้ โดยเราจะเรียกเครื่องใน k8s cluster ว่า Node ) รูปใดบ้าง โดยตอนนี้มี 3 แบบ

  • ReadWriteOnce : สามารถอ่านเขียนได้จาก Node เดียวใน k8s cluster

  • ReadOnlyMany : สามารถอ่านได้อย่างเดียวจากทุก Node ใน k8s cluster

  • ReadWriteMany : สามารถอ่านเขียนได้จากทุก Node ใน k8s cluster

ตัวอย่างเรา Set เป็น ReadWriteOnce

hostPath

ส่วนนี้เป็นการบอกว่า Volume นี้เป็นแบบใช้ไฟล์บนเครื่อง Node นั้นโดยบอกว่า path บนเครื่องคือ path ไหน โดยในตัวอย่างของผมคือ /root/test-pod/part4/nginx-volume

Persistent Volume Cliam : PVC

ในส่วนของ Persistent Volume นั้นเป็นประกาศให้ k8s ทราบว่ามี Volume พร้อมให้ใช้งาน แต่คงมีปัญหาแน่นอนถ้ามีหลายที่พยายามใช้ Volume เดียวกัน เช่น Volume นี้ถูกใช้งานจาก MYSQL และ MongoDB ดังนั้นเพื่อให้ Volume ถูกเอาไปใช้งานและไม่เกิดแย่งกันจึงต้องมีการประกาศสิ่งที่เรียกว่า Persistent Volume Cliam เพื่อบอกว่า Volume ตรงนี้ชั้นจองไว้นะ

ลองสร้าง Persistent Volume Cliam : PVC

ทำการสร้างไฟล์ basic-pvc.yml

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: nginx-volume
resources:
requests:
storage: 2Gi

จากนั้นสั่ง apply ไฟล์ basic-pvc.yml และสั่งดูรายละเอียดของ Persistent Volume claim

1
2
kubectl get pvc
kubectl describe pvc nginx-pvc

ลองสังเกตที่วงแดงไว้จะเห็นว่าตัว PVC นี้อยู่ในสถานะ Bound และส่วนตรง Volume มีค่าเป็น nginx-pv ซึ่งสองค่านี้เป็นการบอกว่า PVC ตัวนี้ทำการผูกกับตัว PV แล้ว

หากคุณลองสั่ง Describe ตัว nginx-pv จะเห็นว่ามีการเปลี่ยนไป โดยลองดูภาพด้านล่าง ซึ่งจะเป็นไปในทางเดียวกันคือ nginx-pv ถูก nginx-pvc ทำการเอาไปใช้แล้ว

อธิบายไฟล์ basic-pvc.yml

accessModes

ส่วนนี้เหมือนกับ PV เลย แต่แค่เปลี่ยนจาก PV เป็น PVC ซึ่งตัว PVC จะเอา accessModes นี้ไปหา PV ที่มี accessModes รองรับความต้องการ PVC

storageClassName

ส่วนนี้เป็นการบอกว่า PVC นี้จะใช้ storageClass ไหน

resources

ส่วนนี้เป็นการบอกว่า PVC ต้องการ Resource อะไรบ้าง จากตัวอย่างบอกว่าต้องการ Storage size 2Gi

ลองสร้าง PVC ที่หา PV ไม่ได้

ทำการสร้างไฟล์ unmatch-pvc.yml

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: unmatch-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: wasinee-volume
resources:
requests:
storage: 1Gi

จากนั้นทำการ apply file และ describe ดูตัว unmatch-pvc

จะเห็นว่าตัว unmatch-pvc นั้นไม่สามารถ Match ได้กับ PV ไหนได้ โดยด้านล่างจะมี message บอกว่าไม่สามารถหา storageClassName wasinee-volume ได้ ซึ่งก็แน่นอนอยู่แล้วเพราะเราไม่ได้สร้าง PV ที่มี storageClassName นี้ขึ้นมา

สร้าง Pod ที่ใช้งาน PVC

สร้างไฟล์ nginx-use-pvc.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nginx-webpage
volumes:
- name: nginx-webpage
persistentVolumeClaim:
claimName: nginx-pvc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 32010
type: NodePort

สั่ง apply ไฟล์ nginx-use-pvc.yml จากนั้นลองเปิด browser แล้วไปที่ url : http://<ip เครื่องที่ run k8s>:32010 โดยของผมจะเป็น http://192.168.156.101:32010

จะเห็นว่า Browser แสดงหน้า Html ออกมาเป็นเหมือนไฟล์ index.html ที่เราสร้างในตอนต้น จากนั้นให้เราลองแก้ไฟล์ index.html ของเราเป็นอย่างอื่นดู อย่างของผมเปลี่ยนจาก HELLO WORLD เป็น HELLO WASINEE แล้วไปเปิด Browser ใหม่จะเห็นว่าจะแสดงผลเปลียนไป

จากการทดลองจะเห็นว่าตัว pod nginx ที่เราสร้างนั้นมีการใช้ไฟล์ index.html ที่เรา mount เข้าไปแล้ว ซึ่งเท่ากับว่าเราสามารถ Mount ตัว Volume ให้กับ Pod ได้เรียบร้อยแล้ว

อธิบายไฟล์ nginx-use-pvc.yml

ในส่วนของ service และ pod ที่ใช้ keyword เก่าจะไม่ขอพูดถึงนะครับ จะพูดถึงส่วนใหม่ที่เพิ่มเข้ามา

volumes

1
2
3
4
5
6
spec:
containers: ....
volumes:
- name: nginx-webpage
persistentVolumeClaim:
claimName: nginx-pvc

ตรง volumes นี้จะเป็นการบอกว่าตัว Pod นี้มี Volumes อะไรบ้าง (มีได้หลาย Volume) และ Volume แต่ละตัวนั้นไปเอามาจากไหน จากในตัวอย่างคือมี volume ชื่อ nginx-webpage โดย Volume นี้ไปเอามาจาก persistentVolumeClaim โดยมีชื่อว่า nginx-pvc

VolumeMounts

1
2
3
4
5
6
7
8
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nginx-webpage

ตรง VolumeMounts ตรงนี้จะเป็นการบอกว่า container นี้จะทำการ Mount volumes อะไรบ้าง (ใส่ได้หลาย mount ) โดยจากตัวอย่างนั้นเราจะบอกว่าเราจะทำการ Mount volume เข้าไปใน container ที่ mountPath : /usr/share/nginx/html (Path นี้คือ Path ที่เอาไว้เอาไฟล์จำพวก web มาใส่เพื่อให้ตัว nginx ทำการแสดงผล) โดย map กับ Volume ของ Pod ที่ชื่อ nginx-webpage ซึ่ง nginx-webpage ไปเอา volume มาจาก pvc ชื่อ nginx-pvc ซึ่ง nginx-pvc นั้นคือ folder nginx-volume ที่เราสร้างกันตอนแรก

ลองลบ PVC กับ PV กัน

เมื่อสร้างแล้วก็ต้องลบได้ ดังนั้นเรามาลบ PVC กับ PV กัน โดยเราลองลบ PV กันก่อนโดยสั่ง

1
kubectl delete pv nginx-pv

โดยเมื่อกดสั่งจะเห็นว่ามันค้างไปและเมื่อ get pv มาดูจะพบว่ามันอยู่ในสถานะ Terminating (กำลังปิด) ซึ่งจะค้างอยู่อย่างนั้นไปเรื่อยๆ ซึ่งทำไมล่ะ ก็ง่ายๆครับเพราะ PV นี้มีคนใช้อยู่ ซึ่งนั่นก็คือ PVC : nginx-pvc นั่นเอง ก็เลยค้างอยู่อย่างนั้น

ดังนั้นเราก็ต้องไปลบ nginx-pvc ก่อน

1
kubectl delete pvc nginx-pvc

ซึ่งพอเราลบก็จะพบว่ามันค้างและเมื่อกดออกมาและมา get pvc ดูจะพบว่าติดสถานะ Terminating เหมือนกัน ซึ่งก็น่าจะเดากันได้ที่ติดอยู่ก็เพราะมี Pod สัก Pod กำลังใช้งาน pvc อยู่ ซึ่งนั่นก็คือ Pod : nginx-pod ดังนั้นถ้าเราจะลบ PVC ได้ก็ต้องลบ Pod ทิ้งก่อน

1
kubectl delete -f nginx-use-pvc.yml

ซึ่งเมื่อเราสั่งลบตัว nginx-pod ทิ้งไปแล้วไป get pv กับ pvc จะพบว่า nginx-pv และ nginx-pvc หายไป

ลองสร้าง Database : MariaDB ผ่าน Kube มาใช้งานกัน

เพื่อทดสอบความเข้าใจและดูว่ามันเอาไปใช้งานจริงยังไง เรามาลองสร้าง Database บน k8s กัน

สร้าง PV กับ PVC กัน

ในขั้นแรกให้สร้าง Folder ขั้นมาสัก Folder เพื่อเอาไว้ใช้เก็บข้อมูลของ database จากนั้นสร้าง PV กับ PVC ขั้นมา ลองทำเองกันดูครับ ของผมที่ทำจะได้แบบข้างล่าง

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: v1
kind: PersistentVolume
metadata:
name: database-pv
spec:
storageClassName: database-volume
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
hostPath:
# อันนี้แล้วแต่ว่าคุณจะเอา mount ข้อมูลไว้ที่ไหน ของผม Mount ไว้ตามนี้
# อาจจะติดเรื่องสิทธิ์ ผมแนะนำเปลี่ยนเป็นให้สิทธิ์หมดก่อน เพื่อใช้ในการทดลอง
path: /root/k8s-data/database-volume
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: database-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: database-volume
resources:
requests:
storage: 2Gi

สร้าง MariaDB เพื่อใช้งาน

ขั้นต่อมาคือการทำการสร้างตัว Pod MariaDB ขึ้นมาและสร้าง Service ขึ้นมา ผมแนะนำให้ลองสร้างกันเองดูก่อน ส่วนของผมสร้างได้แบบข้างล่าง โดยผมใช้การสร้าง Pod ผ่าน Deployment (ในการใช้งานจริงเราจะสร้าง Pod ผ่าน Deployment กัน ที่ผมสร้าง Pod ตรงๆเพราะอยากให้เข้าใจง่าย) ในส่วนของการ Mount volume นั้น เรา Mount ที่ Path : /var/lib/mysql ซึ่งเป็น Path ที่ Mariadb ใช้ในการเก็บข้อมูล Database

สุดท้ายคือส่วนของ Service อันนี้ก็คือทำการเปิดช่องทาง Client สามารถติดต่อกับ Port ได้โดยยิงเข้ามาผ่าน IP เครื่อง + Port ที่กำหนดไว้ (ของผม IP : 192.168.156.101 Port : 32110)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
apiVersion: apps/v1
kind: Deployment
metadata:
name: database-pod
labels:
app: database-maria
spec:
selector:
matchLabels:
app: database-maria
template:
metadata:
labels:
app: database-maria
spec:
containers:
- name: database-maria
imagePullPolicy: Always
image: mariadb:10.10
env:
- name: MARIADB_ROOT_PASSWORD
value: root
ports:
- containerPort: 3306
name: database-port
volumeMounts:
- mountPath: /var/lib/mysql
name: database-volume
volumes:
- name: database-volume
persistentVolumeClaim:
claimName: database-pvc
---
apiVersion: v1
kind: Service
metadata:
name: database-service
spec:
selector:
app: database-maria
ports:
- protocol: TCP
port: 3306
targetPort: 3306
nodePort: 32110
type: NodePort

ลองใช้งานกัน

ให้ลองใช้ตัว SQL Query Browser ต่อเข้า Database ที่พึ่งสร้าง ของผมใช้ตัว dbeaver เพื่อทดสอบว่าสามารถเชื่อมต่อเข้ากับ Database ได้หรือไม่ ซึ่งผมสามารถติดต่อได้ดังภาพ

ลองไปดูที่ Path ที่ Mount volume

ลองไปดูที่ path ที่ Mount volume กันว่ามีไฟล์อะไรโผล่ขึ้นมาไหม ซึ่ง Path ที่ Mount volume ของผมคือ /root/k8s-data/database-volume ซึ่งเมื่อเข้าไปดูก็พบไฟล์มากมายที่เกิดจากตัว MariaDB

เราต้องสร้าง PV เองตลอดเลยไหม

ตัวอย่างที่ผมได้แสดงให้ดูเนี่ยจะเห็นว่าเราต้องสร้าง PV เองเสมอทุกครั้ง แต่ในโลกความเป็นจริงนั้น จะมีสิ่งที่เรียกว่า Dynamic Volume Provisioning คอยหา Volume มา Map กับ PVC ให้โดยอัตโนมัติ (ในการทดลองเราไม่มีเพราะต้อง Config เอง ยุ่งยากมาก) โดยเราแค่สร้างตัว PVC แล้วกำหนด storageClassName ให้ตรงกับที่ Dynamic Volume Provisioning กำหนดให้ พอสร้าง PVC ตัว Dynamic Volume Provisioning จะสร้าง Volume แล้ว Map ให้เอง

ตัวอย่างของ Dynamic Volume Provisioning

เรื่องที่ควรไปอ่านเพิ่ม

เนื่องจากบทความนี้เป็นบทความสอนใช้งานเบื้องต้น ถ้าลงรายละเอียดเยอะจะทำให้ยุ่งยากและคิดว่ามันเป็นเรื่องจากที่จะใช้ ดังนั้นผมจึงอธิบายแค่เรื่องง่ายๆกับทำยังไงให้สร้างสิ่งที่ต้องการและใช้งานได้ ซึ่งแน่นอนว่าในการใช้งานจริงระดับ Production ยังมีเรื่องที่ควรจะต้องเข้าไปอ่านทำความเข้าใจเพิ่ม โดยเรื่องที่แนะนำให้ไปอ่านเพิ่มคือ

  • Lifecycle of a volume and claim : ส่วนนี้จะพูดถึงสถานะต่างๆของ volume และ cliam ว่ามีอะไรบ้าง ถ้าลบ Cliam แล้วอยากใช้ Volume ต่อต้องทำอย่างไร มีแบบไหนบ้าง เป็นต้น

  • Dynamic Provisioning : อันนี้พูดถึงเรื่อง Dynamic Provisioning ว่าทำงานอย่างไร

  • Types of Persistent Volumes : ส่วนนี้เป็นชนิดของ Persistent Volumes ในตัวอย่างที่เราทำกันเราใช้ pv type : hostPart แต่จริงๆแล้วยังมี type อื่นอีกมากมายที่เราสามารถใช้ได้

สรุป

สำหรับตอนนี้เราได้รู้วิธีจะ Mount volume เข้าไปใน Pod ได้ยังไง ได้รู้ว่า Persistent Volume (PV) และ Persistent Volume (PVC) คืออะไร ได้ลองสร้าง Database : MariaDB ขึ้นมาใช้งาน ซึ่งถ้าคุณเข้าใจและได้ลองทำ คุณก็สามารถ Deploy Appplication ขึ้นไปใช้งานบน k8s ได้แล้ว ในตอนต่อไปเราจะมาพูดถึง ConfigMap และ Secret ของ k8s กันว่าคืออะไร