Hike News
Hike News

Basic Kubernetes Part 3 - Service คืออะไรมีไว้ใช้ทำอะไร

Basic Kubernetes Part 3 - Service คืออะไรมีไว้ใช้ทำอะไร

เราจะยิง Request ไปหา Pod ยังไง

ในตอนที่แล้วเราได้รู้แล้วว่า pod เปรียบเสมือนเครื่องที่ลง docker ที่สามารถ Run container ได้ ซึ่งก็แปลว่าเรามีเครื่องที่ Run Application ที่เราต้องการแล้ว แต่ปัญหาคือเราไม่สามารถยิง Request ไปหา pod แต่ละเครื่องได้แบบง่ายๆ เพราะ pod ถูกกำหนด IP ให้แบบอัตโนมัติ อีกทั้งเมื่อเรา Scale out pod ให้มีหลายตัวก็แปลว่า pod ที่ Run application ของเรามีหลายเครื่องที่ Run เหมือนกัน เราจะรู้ได้อย่างไรว่าเครื่องไหนเป็นที่เราอยากจะยิง Request ไปหาดังปัญหาในภาพ

จากภาพจะเห็นว่ามี Pod 6 ตัวคือ

  • client : 1 ตัว
  • nginx : 3 ตัว
  • whoami : 2 ตัว

ถ้า client ต้องการติดต่อไปหา nginx ตัว client จะรู้ได้ยังไงว่าจะยิงไป IP ไหน ( อย่าลืมว่า client ไม่รู้อะไรเลยนะครับ ) ถ้าเราคิดง่ายๆก็ให้ client describe pod ทุกตัวใน k8s เพื่อดูว่า pod ไหนเป็น pod nginx จากนั้นก็เอาไปเก็บไว้ในตารางของ client หลังจากนี้เวลาจะยิงก็ไปดูจากตาราง

ดูแล้วก็เป็นวิธีที่เป็นไปได้แต่ปัญหาคือ

  1. ถ้า k8s ทำการเพิ่มลดจำนวน pod เช่นสั่งเพิ่ม pod เป็น 10 ตัว หรือ ลดเหลือ 1 ตัว ตาราง ip ที่ client มีก็ไม่ใช่ตาราง ip ที่ถูกต้อง ทางแก้ก็คืออาจจะยิงไปถามบ่อยๆเอาก็ได้ แต่ต้องบ่อยแค่ไหนล่ะ บ่อยไปก็สร้างปัญหาให้ k8s ไม่บ่อยข้อมูล ip ก็ไม่ถูกต้อง

  2. การที่ทำแบบนี้แปลว่า k8s ต้องเปิดให้ client สามารถยิงมาถามว่า pod ใน k8s มีอะไรบ้าง และ เป็น pod อะไร การที่เปิดให้ทำอะไรแบบนี้ก็ดูเสี่ยงเพราะกลายเป็นว่า client สามารถรู้ได้ว่ามี pod อะไรบ้างใน k8s ถ้าสัก pod ใน k8s โดนโจมตี เขาก็สามารถโจมตีทุก pod ใน k8s ได้เลย

ในอีกแนวทางนึงถ้าเรามีอะไรสักอย่างโผล่ขึ้นมาแล้วเรายิงไปที่นั่นแล้วอะไรสักอย่างนั้นสามารถยิงไปหา pod ที่ถูกต้องได้ (จริงๆก็คือ Reverse proxy นั่นเอง) ถ้าเรามีอะไรสักอย่างนั้นเราก็แค่ยิงไปที่นั่นแล้วที่นั่นก็จะส่ง Request ไปหา nginx ต่อให้ ซึ่งมันจะทำงานดังภาพ

สิ่งนั้น ที่นั่น ไอนั่น ที่ผมพูดถึงก็คือ Service ของ k8s นั่นเอง

Service คืออะไร

คุณสามารถดู Configuration file ทั้งหมดได้ที่ :Link

ถ้าพูดกันแบบง่ายๆ Service คือตัวที่ทำให้เราสามารถ Request เข้าไปหา Pod ได้ โดยไม่ไปคอย Describe ดู IP ของ Pod ตัวนั้นว่าอยู่ที่ไหน แค่ยิงไปหา Service เดี๋ยว Service จะพา Request ไปหา Pod ให้เอง (นิยามที่ผมพูดไม่ถูกต้อง 100% ถ้าถูกต้องจริงๆดูได้ที่ https://kubernetes.io/docs/concepts/services-networking/service/)

สร้าง Service กัน

ก่อนที่จะทดลองสร้าง Service ผมอยากขอให้ทุกท่านลบ Deployment และ Pod ที่มีอยู่ทิ้งก่อน เพื่อให้การทดลองไม่มีผลกระทบจาก Pod ตัวก่อนๆที่เคยสร้าง โดยใช้คำสั่งด้านล่างเพื่อทำการลบ

1
2
kubectl delete deployment --all
kubectl delete pod --all

จากนั้นสร้างไฟล์ test-server-pod.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
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
owner: wasinee
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: whoami-pod
labels:
app: whoami
owner: wasinee
spec:
containers:
- name: whoami-container
image: traefik/whoami
ports:
- containerPort: 80

โดยไฟล์นี้จะทำการสร้าง Pod ขึ้นมา 2 ตัวคือ nginx กับ whoami จากนั้นสั่งคำสั่งด้านล่างเพื่อสร้าง pod ขึ้นมา

1
kubectl apply -f test-server-pod.yml

จากนั้นสร้างไฟล์ client-pod.yml

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: client-pod
labels:
app: busybox
spec:
containers:
- name: busybox
image: yauritux/busybox-curl
command: ["tail", "-f", "/dev/null"]

จากนั้นสั่งสร้าง pod ด้วยคำสั่ง

1
kubectl apply -f client-pod.yml

ซึ่งเมื่อสั่งเสร็จเราจะมีข้อ Pod ทั้งหมด 3 ตัวดังภาพ

จากนั้นทำการสั่ง Describe nginx-pod กับ whoami-pod เพื่อเอา IP ของ Pod ทั้ง 2 ซึ่งของผมคือ

  • nginx-pod : 10.42.0.103
  • whoami-pod : 10.42.0.104

จากนั้นทำการสั่ง

1
kubectl exec -ti client-pod sh

อธิบายคำสั่ง

  • kubectl exec เป็นการสั่งคำสั่งที่อยู่ใน pod
  • -ti คือการเชื่อมต่อ terminal ของ pod เข้ากับ terminal ของเรา
  • kubectl exec -ti client-pod sh รวมแล้วคือสั่งคำสั่ง sh ในเครื่อง pod และเชื่อมต่อ terminal ของเราเข้ากับ pod

ดังนั้นจะกลายเป็นว่าเราได้คุมคำสั่ง sh ในเครื่อง pod ซึ่งแปลว่าเราสามารถสั่ง command ต่างๆในเครื่อง pod ได้ทั้งหมด ซึ่งการที่เราทำอย่างนี้ก็เพื่อจำลองว่าเราเป็นเครื่อง client ที่จะยิงไปหา pod เครื่องอื่นๆ

ลองยิงไปหา pod เครื่องอื่นๆด้วยคำสั่งด้านล่าง

1
2
curl 10.42.0.103
curl 10.42.0.104

จากภาพจะเห็นว่าเราสามารถยิงไปหา nginx และ whoami ได้ จากนั้นลองสั่ง exit เพื่อออกจาก pod client-pod

สร้าง Service มาทดลองใช้งาน

ทำการสร้างไฟล์ service-1-basic.yml จากนั้นทำการสั่ง kubectl apply เพื่อสร้าง service

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP

จากนั้นสั่ง get service และทำการ describe service nginx-service เพื่อดูรายละเอียด

1
2
kubectl get service
kubectl describe service nginx-service

คราวนี้เราลองเข้าไปในเครื่องของ client-pod ด้วยคำสั่ง kubectl exec และสั่ง curl ไปที่ IP ของ Service ซึ่งของผมคือ 10.43.19.168 (ของคุณอาจไม่เหมือนกับของผม)

1
curl 10.43.19.168

จะเห็นว่าเราได้ Response เหมือนกับที่ยิงไปที่ 10.42.0.103 (nginx-pod) เลย ซึ่งนั่นก็แปลว่า เราสามารถยิงไปหา pod ผ่านการยิงไปที่ service ได้

คราวนี้เราลองมาดูรายละเอียดของ Service อีกรอบ

สังเกตตรงค่า Endpoint ของ Service Endpoint นี้คือ Endpoint ที่ Service นี้ชี้ไปหา ซึ่งค่าของผมมันคือ 10.42.0.103 ซึ่งมันคือ IP ของ nginx-pod นั่นก็แปลว่า Service นี้ชี้ไปหา nginx-pod ดังนั้น การยิงไปหา Service นี้ก็คือการยิงไปหา nginx-pod นั้นเอง

คราวนี้คุณอาจสงสัยว่า Service รู้ได้อย่างไรว่า Pod ไหนเป็น Pod nginx ขอให้ดูตรง Selector ครับ จะเห็นว่าค่าคือ

1
app=nginx 

คุ้นๆค่านี้ไหมครับ ลองดูที่ label ของ nginx-pod ดูครับว่ามันคือค่าอะไร จะเห็นว่ามันคือค่าเดียวกันครับ ดังนั้น Service ใช้ตัว selector ไปหา label ของ pod ที่ตรงกับค่า selector มาเป็น Endpoint ของ Service นี้ครับ

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

ส่วนของ apiVersion , kind , metadata ผมไม่ขอพูดนะครับ เนื่องจากเหมือนกับของ pod และ deployment จะขออธิบายในส่วนของ spec เป็นต้นไป

selector

ค่านี้เป็นค่าที่กำหนดว่าจะเอา pod ตัวไหนมาเป็น endpoint ที่ service ชี้ไปบ้าง โดยเอาไปหา pod ที่มีค่า label ตรงกับ selector

จากตัวอย่างไฟล์ service-1-basic.yml มีค่า selector คือ app=nginx ดังนั้น pod ทุกตัวที่มีค่า label app=nginx ซึ่งมี pod ที่มีค่า label app=nginx คือ nginx-pod

ports

ส่วนนี้เป็นการบอกว่า Service นี้เปิดที่ Port อะไรบ้าง สังเกตุว่า port มี s ดังนั้นสามารถกำหนดได้หลาย port

  • protocol ส่วนนี้เป็นการบอกว่า protocol ของ port นี้คืออะไร
  • port: port ของ service ที่เปิดคืออะไร
  • targetPort: port ของ endpoint เป้าหมายคือ port อะไร

type

อันนี้เป็นชนิดของ Service ส่วนนี้ขออธิบายทีหลัง

ทดลอง Selector เพื่อความเข้าใจ

สร้างไฟล์ service-2-test-selector.yml ด้วยข้อมูลข้างล่างจากนั้นสั่ง apply และ describe ตัว service

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: wasinee-service
spec:
selector:
owner: wasinee
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP

จากภาพเราจะเห็นว่า selector ของเราคือ owner=wasinee ซึ่ง pod ที่มี label ดังกล่าวมี 2 pod คือ nginx-pod กับ whoami-pod ซึ่งนั่นทำให้ตัว Endpoint ของ Service มี 2 IP คือ 10.42.0.103 (nginx-pod) , 10.42.0.104(whoami-pod)

ซึ่งถ้าเรา kubectl exec เข้า client-pod แล้วลองยิงไปที่ wasinee-service ( 10.43.123.125 IP ของคุณไม่เหมือนของผม ) บางครั้งเราจะได้ response ของ nginx-pod บางครั้งเราจะได้ response ของ whoami-pod ดังภาพ

ดังนั้น seletor ของ Service และ label ของ pod เป็นสิ่งสำคัญมาก ดังนั้นตอนสร้าง Pod ก็ควรเตรียม label ไว้ให้ Service ชี้มา และ selector ก็ควรกำหนดให้ตรงกับ pod ที่ต้องการจริงๆ

ทดลอง ports เพื่อความเข้าใจ

สร้างไฟล์ service-3-test-port.yml จากนั้น apply และ describe ตัว test-port-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Service
metadata:
name: test-port-service
spec:
selector:
app: nginx
ports:
- name: port-a
protocol: TCP
port: 80
targetPort: 80
- name: port-b
protocol: TCP
port: 81
targetPort: 80
- name: port-c
protocol: TCP
port: 82
targetPort: 81

type: ClusterIP

ในการ test นี้เราสร้าง service ที่เปิด port 3 port คือ 80, 81, 82 จากนั้นเรามาลองยิงไปที่ service ตาม port เหล่านี้แล้วมาดูผลลัพธ์กันว่าเป็นอย่างไร ทำไมถึงเป็นอย่างนั้น

ยิงไป port 80

การยิงไปที่ port 80 ของ service จากนั้นตัว service ส่งต่อ request ไปที่ pod ที่ port 80 (targetPort) ซึ่งการยิงแบบนี้ทำให้ได้ response จาก nginx-pod กลับมา

ยิงไป port 81

การยิงไปที่ port 81 ของ service จากนั้นตัว service ส่งต่อ request ไปที่ pod ที่ port 80 (targetPort) ซึ่งการยิงแบบนี้ทำให้ได้ response จาก nginx-pod กลับมา

ยิงไป port 82

การยิงไปที่ port 82 ของ service จากนั้นตัว service ส่งต่อ request ไปที่ pod ที่ port 81 (targetPort) ซึ่งเมื่อยิงไปที่เครื่อง nginx-pod แล้วตัว nginx-pod ไม่ได้เปิด port 81 ดังนั้น ยิงไปจึงได้รับ Error เหมือนดังภาพ

จากการทดลองในส่วนนี้น่าจะทำให้เราเข้าใจเรื่อง port ของ service นะครับว่า port คืออะไร targetPort คืออะไร ตั้งค่ายังไงให้เหมาะสมกับงานที่เจอ

เรียก service ด้วยชื่อ

หลังจากทดลองกันมานานคุณอาจจะสงสัยว่า แล้วมันต่างจากการหา ip ผ่าน pod ยังไงวะ เพราะสุดท้ายก็ต้องไป describe service เอา ip มาอยู่ดี ที่ผมทำไปทั้งหมดเนี่ยเพื่อให้ง่ายในการทดลอง ในความเป็นจริงนั้นเวลาเราจะเรียก service นั้นเราจะเรียกด้วยชื่อครับ โดยชื่อ service นั้นมีหลักการตั้งแบบนี้

1
<serviceName>.<namespaceName>.svc.cluster.local
  • serviceName คือชื่อ service ที่เราต้องการยิงไปหา
  • namespaceName คือชื่อ namespace ที่ service นั้นอยู่ ( ตอนนี้เรายังไม่ได้กำหนด namespace ดังนั้น namespace ของเราคือ default )

สมมติถ้าเราจะยิงไป service : nginx-service เราจะต้องยิงไปที่

1
nginx-service.default.svc.cluster.local

ด้วยวิธีนี้เราก็ไม่จำเป็นต้องไปหา IP จากการ describe service เพื่อดู ip แค่เรารู้ชื่อ service กับ namespace เพียงเท่านี้ก็สามารถยิงไปหา service ที่ต้องการได้แล้ว

เครื่อง Client ที่อยู่ข้างนอกจะยิงเข้ามายังไง

ที่เราทดลองไปทั้งหมดนั้น client ที่เราทดลองอยู่ภายใน k8s แต่ถ้า client อยู่นอก k8s แล้ว เราจะยิงเข้าไปได้ยังไง

เราลองจำลองตัวเองเป็น client ที่อยู่นอก k8s สิ่งที่ client รู้เกี่ยวกับ app มีเพียงอย่างเดียวคือ เครื่องที่ run k8s อยู่มี IP อะไร ( ของผมคือ 192.168.156.101 )

คราวนี้โดยปกติแล้วเวลา 2 เครื่องติดต่อกันในระดับ Application layer นั้นจะต้องระบุ IP และ Port ดังนั้น ถ้าเราสามารถ Map port ไปหา Service ของ k8s ได้ เราก็สามารถยิงจากด้านนอก k8s เข้าไป pod ที่อยู่ใน k8s ได้ ซึ่ง k8s ก็ได้เปิดช่องทางนี้ให้กับเราโดยสร้าง Service type ชนิด NodePort ขึ้นมาให้กับเรา

ลองสร้าง ServiceType : NodePort

สร้างไฟล์ : service-4-test-node-port.yml จากนั้นสั่ง kubeclt apply และสั่ง kubectl describe ดูรายละเอียด

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: test-node-port-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 32010
type: NodePort

จะเห็นว่ามีกันเพื่ม NodePort 32010 ตาม config nodePort ที่เพิ่มเข้ามา และ Type เปลี่ยนเป็น NodePort ตามใน Config ไฟล์

คราวนี้ให้เราเปิด Browser ขึ้นมาและเปิดไปที่ url : http://<ip เครื่องที่ run k8s>:32010 โดยของผมเครื่อง run คือ 192.168.156.101 url จึงเป็น http://192.168.56.101:32010

ซึ่งเมื่อลองยิงไปจะเห็นว่าเราจะได้รับ Response กลับมาจาก nginx-pod ซึ่งนั่นทำให้เห็นว่าเราสามารถเชื่อมต่อกับ nginx-pod ผ่าน Service ที่เราพึ่งทำขึ้นมาได้ โดยเชื่อมต่อผ่าน Port : 32010

Service type

จากส่วนที่ผ่านมาเราจะเห็นว่าเรามีความต้องการใช้ Service ในหลายความต้องการ เช่น เรียกด้วยชื่อ service , เรียกจาก client ที่อยู่นอก cluster ดังนั้นเพื่อตอบสนองความต้องการที่หลากหลายจึงมีการกำหนด Service type เพื่อให้ระบุว่า Service นี้ต้องทำงานแบบไหน

จริงๆมีคนเขียนเกี่ยวกับ Service type ไว้ดีมากพร้อมมีภาพที่ทำให้เข้าใจง่าย ซึ่งผมเชื่อว่าดีกว่าผมอธิบาย คุณสามารถอ่านได้ที่ : https://thanwa.medium.com/kubernetes-%E0%B8%A7%E0%B9%88%E0%B8%B2%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2%E0%B9%80%E0%B8%A3%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B8%87-services-%E0%B9%81%E0%B8%95%E0%B9%88%E0%B8%A5%E0%B8%B0%E0%B8%9B%E0%B8%A3%E0%B8%B0%E0%B9%80%E0%B8%A0%E0%B8%97-25bade6d4725

โดยในบทความนี้จะพูดถึงแค่ 2 Service type คือ ClusterIP กับ NodePort เท่านั้น ส่วน Service type อื่นๆผมไม่ขอพูดถึงเพราะว่า เราไม่สามารถทดลองใช้งานได้ง่ายๆและน่าจะยังไม่มีตัวอย่างที่เหมาะสมในการแสดงให้ดู

ClusterIP

Service Type ประเภทนี้จะอนุญาตให้เข้าถึงได้เฉพาะสิ่งที่อยู่ใน Cluster ของ k8s เท่านั้น จะเห็นว่าตอนเราสร้าง Service Type ด้วย ClusterIP เราจะต้องใช้ client-pod ที่อยู่ใน cluster ของ k8s ในการทดลองยิง request หากเราลองเปิด browser ของเครื่องเรา แล้วใส่ IP ของ Service แล้วยิงไปจะเห็นว่าไม่สามารถยิงไปหาได้

NodePort

Service Type ประเภทนี้จะอนุญาตให้เครื่องที่อยู่ภายนอกสามารถเชื่อมต่อกับ Pod (ผ่าน Service) ผ่าน IP ของเครื่องที่ Run k8s และ Port จากในตัวอย่างที่เราจะเห็นว่าเราสามารถใช้งาน nginx-pod ผ่าน browser โดยยิงไปที่ url : http://<ip เครื่องที่ run k8s>:32010

  • ข้อควรระวัง : ตัว NodePort มีข้อจำกัดที่จะใช้ Port ได้แค่ Port : 30000-32767 เท่านั้น

สรุป

สำหรับตอนนี้เราได้เรียนรู้เกี่ยวกับ Service รู้ที่มาที่ไปของมัน วิธีการสร้าง Service ผ่าน Configuration file ความแตกต่างระหว่าง Service type : ClusterIP กับ NodePort ส่วนในตอนต่อไปเราจะมีเรียนรู้เกี่ยวกับ Volume กัน

Basic Kubernetes Part 2 - Pod คืออะไรและวิธีการใช้งานผ่าน Deployment

Basic Kubernetes Part 2 - Pod คืออะไรและวิธีการใช้งานผ่าน Deployment

Pod คืออะไร

ถ้ามองง่ายๆแบบไม่อิงนิยามที่ถูกต้องแบบเป๊ะๆ Pod ก็คือเครื่อง 1 เครื่อง โดยที่เครื่องเครื่องนั้นลง Docker และสามารถ Run container ได้ (บอกไว้ตรงนี้เลยว่านิยามนี้ไม่ถูก แต่อยากอธิบายเพื่อให้เข้าใจง่าย นิยามที่ถูกต้องดูได้จาก : https://kubernetes.io/docs/concepts/workloads/pods/) และในเมื่อมันเป็นเครื่อง ดังนั้นมันจึงมี Volume ซึ่งเราสามารถ Mount volume เข้าไปที่เครื่อง แล้วให้ทุก Container สามารถเข้าถึง Volume นั้นได้ ภาพของ Pod จะเป็นดังตัวอย่างด้านล่าง

ภาพจาก : https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-intro/

จากภาพจะเห็นว่า Pod นั้น Run หลาย Contianer และใน 1 Pod มีหลาย Volume อยู่ และ 1 Pod นั้นจะถูกกำหนด IP ให้สามารถเรียกเข้าไปใช้งานได้

สำหรับ k8s นั้นตัว Pod จะถือเป็นหน่วยที่ถูก Deploy ซึ่งอาจจะถูก Deploy ด้วย Deployment , DaemonSet , StatefulSets ซี่งเดี๋ยวเราค่อยมาทำความเข้าใจกันทีหลัง รู้แค่ว่าตัว Pod จะต้องถูก Deploy

สร้าง Pod ยังไง

ในการสร้าง Pod นั้นสามารถสั่งได้ด้วยคำสั่ง kubectl โดยสามารถสร้างได้ด้วย cmd ตรงๆเลย หรือจะสร้างผ่าน File ที่ทำการบอกว่า pod นั้นมีต้องมีอะไรบ้าง (ไฟล์ที่เราทดลองทำกันใน Part 1) โดยที่เราจะเรียนรู้กันนี่จะเป็นแบบสร้างแบบ File (เขามีภาษาทางการว่า Declarative Configuration Files) เพราะในงานจริงที่เราทำกันนั้น เราไม่สั่ง cmd เป็นคำสั่งๆเพื่อให้ตัว k8s ทำงานให้เพราะมันยากที่จะตรวจสอบว่าสั่งอะไรไปแล้วบ้าง และส่วนใหญ่เราก็ลืมว่าสั่งอะไรไป แต่ถ้าเราทำเป็น Configuration Files นั้นเราจะรู้ว่าเราสั่งสร้างมันยังไง อีกทั้งเมื่อทำเป็น File เราสามารถ Save ลง Git เพื่อเอาไปใช้งานได้อย่างหลากหลายเพิ่มขึ้น ไม่ว่าจะเอาไปทำ CD (Continuous deployment) ทำการตรวจสอบหาการเปลี่ยนแปลงที่ทำให้เกิดปัญหา เป็นต้น

เริ่มสร้าง Pod พื้นฐาน

เนื่องจากต้องเขียน Configuration Files เป็นจำนวนมาก ผมเลยทำ Git ไว้เก็บตัวไฟล์ เผื่อใครขี้เกียจ Copy หรือ พิมพ์ (ผมแนะนำให้ลองพิมพ์เองดูสักหน่อยเพื่อให้เกิดความคุ้นเคยว่ามันต้องมี keyword อะไรบ้างในไฟล์) สามารถไป Clone Repository ได้ที่ Git ตามลิ้งนี้ https://github.com/surapong-taotiamton/training-k8s

สร้างไฟล์ : pod-1.yml จากนั้นใส่เนื้อไฟล์ข้างล่าง

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80

จากนั้นสั่งคำสั่งด้านล่างเพื่อทำการสร้าง Pod

1
kubectl apply -f pod-1.yml

จากนั้นสั่งคำสั่งด้านล่างเพื่อทำการดูข้อมูล Pod ที่เราสร้างขึ้นมา

1
kubectl get pods

จากภาพจะเห็นว่าเราสามารถสร้าง Pod ขึ้นมาได้แล้ว

คราวนี้เราลองมาดูรายละเอียดของ Pod กัน โดยเราจะสั่งโดยใช้คำสั่ง (คำสั่งเหมือนเยอะ แต่ที่ใช้บ่อยๆมันมีแค่ get , apply ,delete , describe logs ซึ่งพอใช้ไปจะรู้เองว่า ถ้าอยากดูให้คำสั่งนี้ อยากสร้าง อยากลบ ใช้คำสั่งนี้ )

1
kubectl describe pod nginx-pod 

ขอให้ดูตรงที่ผมครอบแดงไว้ อย่างแรกคือ IP Pods ตัวนี้จะถูก Assign IP ให้เป็น 10.42.0.59 ซึ่ง IP นี้ตัว k8s จะเป็นคนกำหนดให้ ดังนั้นเครื่องคุณกับเครื่องผมจะไม่เหมือนกัน จากนั้นให้คุณลองสั่ง

1
2
3
4
5
curl <ip ของ pod>
# ของผมคือ 10.42.0.59 เลยได้เป็น

curl 10.42.0.59

ซึ่งจะเห็นว่าสามารถยิง http request ไปหา Pod ที่เราสร้างได้

ส่วนต่อมาที่อยากให้ดูคือ Image เราจะเห็นว่า Image ของ Pod นี้คือ nginx:1.25.1 ซึ่งจะตรงกับที่เรากำหนดไว้ในไฟล์ Configuration

จากทั้งหมดจะเห็นว่าเราสามารถสร้าง Pod ขึ้นมาได้ผ่านไฟล์ pod-1.yml

อธิบายไฟล์ pod-1.yml

ในไฟล์ pod-1.yml จะประกอบด้วย keyword ที่ใช้กำหนด pod ว่ามีต้องมีอะไรบ้าง โดยเราจะค่อยๆอธิบายทีละ keyword เพื่อให้เกิดความเข้าใจ

apiVersion

keyword นี้เป็นตัวบอกว่าตัวการประกาศนี้ต่อจากนี้นั้นใช้ version อะไร โดยที่เขาทำแบบนี้เนี่ยเพราะตัว k8s นั้นต้องรองรับการ deploy ผ่านหลาย version ไม่ใช่พอ Update k8s เป็น version ใหม่ปุ๊ปจะไม่ Support ตัวการประกาศ version เก่าแล้ว ดังนั้นเลยต้องมี apiVersion เพื่อบอกให้ k8s ทราบ

kind

เป็นตัวบอกว่าสิ่งที่สั่งให้ k8s สร้างคืออะไร ซึ่งในไฟล์นี้คือ Pod ก็แปลว่าสั่งให้สร้าง pod

metadata

ส่วนนี้จะเป็นการบอกค่าคุณลักษณะของ pod ตัวนี้ เช่น กำหนดชื่อ กำหนดว่ามันจัดอยู่ในกลุ่มอะไร โดยคุณลักษณะพวกนี้ไม่ได้เกี่ยวกับว่าใช้ Image อะไร ใช้ Ram เท่าไหร่ แต่เป็นคุณลักษณะที่เราอยากเพิ่มเข้าไปเพื่อใช้ในจุดประสงค์เรื่องการจัดการ หรือ ทำอย่างอื่น โดยคุณลักษณะที่เรา set ในตอนนี้คือ

1. name

อันนี้เป็นการบอกว่า pod นี้จะให้เรียกชื่อว่าอะไร

2. labels

ส่วนนี้เป็นเหมือนการแปะ label (ฉลาก) ให้กับ pod นี้ว่ามีค่าอะไรบ้าง ถ้านึกภาพไม่ออกให้นึกว่าคุณอยู่โกดังคัดแยกสินค้า คุณเป็นเจ้าหน้าที่ตรวจสอบสินค้าเพื่อบอกคุณลักษณะของสินคาว่ามีคุณลักษณะอะไร เช่น บอบบาง เป็นอาหาร มีประกัน ส่งด่วน เพื่อให้คนที่รับงานต่อจากคุณสามารถดำเนินการต่อได้ถูก คุณจะทำยังไงให้ง่าย วิธีง่ายๆที่คิดคือคุณสร้าง ฉลากขึ้นมาและแปะไปกับตัวสินค้าเพื่อให้คนที่รับหน้าที่ไปดำเนินการต่อรู้ว่ามันมีคุณลักษณะอะไร เช่น ถ้าส่งด่วน คุณอาจแปะฉลากสีแดงเขียนว่าด่วนไปกับสินค้า คนที่มีหน้าจัดการต่อพอเห็นฉลากนั้นก็จะทราบทันทีว่าส่งด่วน หรือ ถ้าแปะฉลากสีน้ำเงินว่ามีประกัน คนที่รับหน้าที่ดูแลต่อจะทราบว่ามีประกัน ต้องไปทำเอกสารเกี่ยวกับประกัน โดยตัวฉลากนั้นไม่จำเป็นต้องมีอันเดียว เราสามารถแปะได้หลากหลาย เช่น แปะฉลากสีแดง และ สีน้ำเงิน ทำให้รู้ว่า ส่งด่วนและมีประกัน

จากตัวอย่างเราแปะ label ชื่อ app แล้วมีค่าว่า nginx ให้กับ pod นี้

spec

ส่วนนี้จะเป็นการบอกให้ k8s ทราบว่า pod ที่เราสร้างเนี่ยจะใช้ container อะไรบ้าง ใช้ Image อะไร เปิด port อะไร

1. containers

ตรงนี้เป็นการบอกว่า container ใน pod มีอะไรบ้าง (จะเห็นว่าเขาเติม s) จากตัวอย่าง

1
2
3
4
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80

ตรงนี้จะเป็นการบอกว่า container ใน pod ชื่อ nginx-container ใช้ Image nginx:1.25.1 และเปิด port : 80

สร้าง Pod แบบมีหลาย Container

สร้างไฟล์ : pod-2-multiple-container.yml โดยเราจะเห็นว่า pod นี้ต่างจาก pod ที่แล้วคือ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
name: pod-multiple-container
labels:
app: test-multiple-conatainer
site: test
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
- name: whoami-container
image: traefik/whoami
ports:
- containerPort: 81
env:
- name: WHOAMI_PORT_NUMBER
value: "81"
  1. เรากำหนด label มากกว่า 1 ตัวคือมี label
1
2
app: test-multiple-conatainer
site: test
  1. มีการสร้าง Container มากกว่า 1 ตัวให้ Pod ตัวนี้คือ nginx-container , whoami-container

  2. ตัว whoami-container มีการกำหนด Environment โดยกำหนด Environment ชื่อ WHOAMI_PORT_NUMBER ให้กับ whoami-container โดยสั่งผ่าน

1
2
3
env:
- name: WHOAMI_PORT_NUMBER
value: "81"

จากนั้นสั่ง

1
2
kubectl apply -f pod-2-multiple-container.yml
kubectl describe pod pod-2-multiple-container.yml

ลอง curl ไปเช็ค pod ที่คุณสร้างขึ้นมาใหม่ (อย่าลืม IP ของเราไม่เหมือนกันนะครับ) โดยยิงไปที่ port 80 เพื่อดูว่า nginx ทำงานไหม ยิงไปที่ port 81 เพื่อดูว่า whoami ทำงานไหม

1
2
3
4
# ของผม ip ของ pod คือ 10.42.0.62

curl 10.42.0.62:80
curl 10.42.0.62:81

ซึ่งจากภาพเราจะเห็นว่าทั้ง 2 container บน pod เดียวกันสามารถทำงานได้

อยากสร้าง Pod หลายๆตัวที่หน้าตาเหมือนกันทำยังไง

ตอนนี้เราสามารถ pod : nginx เพื่อรองรับ request ได้แล้ว ทุกอย่างดูเหมือนเรียบร้อยแต่ทุกปัญหาก็เกิดขึ้นเมื่อ pod ที่คุณสร้างนี่รับ request ที่มาจากลูกค้าไม่ไหว วิธีที่เราจะจัดการปัญหานี้ในเบื้องต้นคือการ scale out หรือถ้าเทียบกับความเป็นจริงคือเพิ่มสิ่งที่เหมือนกันเข้าไปเพื่อช่วยทำงาน เช่น ถ้าคุณไปซื้อของในห้างสรรพสินค้าแล้วในห้างมีคนรอคิวจ่ายเงินเยอะมากๆ สิ่งที่ห้างจะแก้ปัญหาคนที่รอคิวนานๆได้คือ เปิดช่องจ่ายเงินเพิ่มอีก 1 ช่อง เพื่อให้คนไปจ่ายเงินช่องใหม่ เวลาในการรอคิวก็ลดลง

ด้วยความรู้ที่คุณมีอยู่ตอนนี้คืออยาก scale out ให้มี nginx 3 ตัวคุณจะทำยังไง วิธีการไม่ยากครับคุณสามารถทำได้โดยทำแบบนี้คือ Copy วางแล้วเปลี่ยนชื่อ pod เอาดังด้านล่าง

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
apiVersion: v1
kind: Pod
metadata:
name: scale-nginx-pod-1
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: scale-nginx-pod-2
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: scale-nginx-pod-3
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
---

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

1
2
kubectl delete scale-nginx-pod-3
kubectl delete scale-nginx-pod-2

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

Deployment

Deployment คือตัวที่ทำหน้าที่บริหารจัดการสร้าง pod ให้ได้ในลักษณะที่ต้องการ เช่น มีจำนวน pod เท่าไหร่ (นิยามของผมไม่ถูกต้อง 100% นิยามแบบถูกต้อง 100% ดูที่ https://kubernetes.io/docs/concepts/workloads/controllers/deployment/ )

มาลองสร้าง Deployment กันโดยทำการสร้างไฟล์ : deployment-1-basic.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 5
selector:
matchLabels:
app: nginx
template:
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80

จากนั้นสั่งคำสั่งด้านล่างเพื่อสร้าง deployment

1
kubectl apply -f deployment-1-basic.yml

ลองสั่ง get และ describe deployment เพื่อดูว่าเราสามารถสร้างได้สำเร็จหรือไม่และ deployment มีรายละเอียดอะไร

1
2
kubectl get deployment
kubectl describe deployment nginx-deployment

จากภาพจะเห็นว่าเมื่อสั่ง get มี deployment ชื่อ nginx-deployment และมี pod ที่ต้องสร้างภายใต้ deployment นี้คือ 5 ตัว และเมื่อดูรายละเอียดด้วย describe จะพบรายละเอียดว่า deployment นี่สร้าง pod แบบไหน มีจำนวนที่ต้องสร้างเท่าไหร่ และอื่นๆ (จริงๆมีรายละเอียดการสร้างว่าจะค่อยๆ update แบบไหน ค่อยๆสร้างแล้วเปลี่ยน หรือ สร้างหมดแล้วค่อยเปลี่ยน หรือแบบไหน แต่ตอนนี้เรายังไม่ลงรายละเอียด)

คราวนี้เราลองสั่ง

1
kubectl get pods

จะเห็นว่ามี pod ที่ชื่อขึ้นต้นด้วย nginx-deployment โผล่ขึ้นมา 5 ตัว ซึ่ง pod เหล่านี้คือ pod ที่ถูกสร้างขึ้นมาจาก deployment

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

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

spec

ตรงนี้คือการบอกว่าเราอยากได้ Deployment แบบไหน

1. replicas

ตรงนี้เป็นการบอกว่าจำนวนสร้าง pod จำนวนกี่ตัว ตรงนี้กำหนดไว้ 5 คือต้องการมี pod 5 ตัว

2. matchLabels

ตรงนี้เป็นการบอกว่าจะจัดการ pod นั้นโดยการ search หาด้วย label อะไร ซึ่งในที่นี้คือ app: nginx ซึ่งค่านี้จะต้องสัมพันธ์กันกับค่า label ในส่วนของ template

3. template

ตรงส่วนนี้เป็นการบอกว่า pod ของเรามีหน้าตาที่จะให้สร้างอย่างไร หากสังเกตดีๆค่าที่อยู่ใน template นั้นคือการ copy ค่าจากส่วน metadata จนถึง spec ใน pod มาใส่ ไม่เชื่อลองไปดูไฟล์ pod-1.yml

1
2
3
4
5
6
7
8
9
10
11
# ส่วน metadata ไปจนถึง spec
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80

ลองลบ Pod เล่นดูว่าจะเกิดอะไรขึ้น

เรามาลองลบ pod ที่เกิดจากการที่ Deployment สร้างดูว่าจะเกิดอะไรขึ้น

1
2
# kubectl delete pod [ชื่อ pod] ที่ได้มาจากการ get
kubectl delete pod nginx-deployment-59ccdc84fc-rtq7b

จากภาพเราจะเห็นว่าเมื่อเราลบ pod ทิ้งไป แต่เมื่อเราสั่ง get pods มาดูใหม่จะเห็นว่า pod ที่เราลบหายไป (ตัวสีแดง) แต่จะมีตัวใหม่เพิ่มเข้ามา (ตัวสีเขียว) ดังนั้นจะเห็นว่าไม่ว่า pod ของเราจะหายไปด้วยเหตุผลอะไร ตัว deployment จะพยายามสร้างตัว pod ขึ้นมาให้ได้เท่าเดิม

ลองเพิ่มลดจำนวน replicas ดูว่ามีอะไรเปลี่ยนแปลง

ลองเปลี่ยนค่า replicas เป็น 10 จากนั้นสั่ง apply ดูแล้วดูว่าเกิดอะไรขึ้น

จากภาพจะเห็นว่า replicas เพิ่มขึ้นเป็น 10

ลองเปลี่ยน replicas เป็น 1 แล้วลอง apply ดู ซึ่งเมื่อสั่งแล้วจะเห็นว่าตัว pod นั้นเหลือแค่ 1 ตัวแล้ว

สรุป

สำหรับตอนนี้เราได้เรียนรู้ว่า Pod ใน k8s นั้นคืออะไร มีวิธีสร้างผ่าน file ยังไง ลองสร้าง pod ในรูปแบบที่มีหลาย container เรียนรู้ keyword ต่างๆของ pod (ซึ่งบอกเลยว่ายังเหลืออีกหลายอันที่ต้องไปศึกษาเพิ่ม) ไปจนเห็นปัญหาการในการใช้งานจริงว่าถ้าต้อง scale out แล้วมีแค่ pod อย่างเดียวจะเกิดปัญหาอะไร ซึ่งทำให้เราไปใช้ Deployment เพื่อแก้ปัญหานี้

หวังว่าตอนนี้จะทำให้คนที่อ่านได้เข้าใจเกี่ยวกับ Pod และ Deployment ในตอนต่อไปเราจะมาใช้งานตัว Service กัน

Basic Kubernetes Part 1 - Kubernetes คืออะไร และ เตรียมเครื่องเพื่อใช้งาน

Basic Kubernetes Part 1 - Kubernetes คืออะไร และ เตรียมเครื่องเพื่อใช้งาน

เกริ่นก่อนเข้าเรื่อง

ก่อนจะเริ่มเนื้อหาผมขอออกตัวก่อนเลยว่าผมไม่ใช่ผู้เชี่ยวชาญด้านการใช้งาน Kubernetes ผมเป็นแค่ Developer ที่ต้องใช้งาน Kubernetes ซึ่งกว่าจะใช้งานเป็นนั้นใช้เวลาานานมาก Video ที่สอนก็เป็นภาษาต่างประเทศฟังก็ไม่ค่อยเข้าใจ พอจะเรียนของไทยคอร์สเรียนก็มีราคา ซึ่งเป็นไปได้ยากที่บริษัทเล็กๆนั้นจะต้องแบกภาระส่ง Developer ไปเรียน ผมจึงเห็นว่ามันคงดีถ้ามีคนสอน Kubernetes แบบพื้นฐานให้ ดังนั้นผมก็เลยทำเองเลยละกัน สอนมันแบบไร้ใบ Certificate นี่แหละ สอนตามที่ทดลองที่เคยทำรู้เท่าไหร่ก็สอนเท่านั้น ดังนั้นผมจึงไม่การันตีว่าเนื้อหาทั้งหมดจะถูกต้องตามหลักการและเป็น Best practice

Kubernetes คืออะไร

Kubernetes หรือเรียกสั้นๆว่า k8s ( ตัว k เป็นตัวแรกและอีก 8 ตัวตามมาก่อนลงท้ายด้วย s ) เป็นตัวโปรแกรมที่มีไว้จัดการตัว Container ศัพท์ทางการเรียกว่า Container Orchestration โดยมันมีหน้าที่ทำการสร้าง container ตามที่เราต้องการ เช่น มีค่า config อะไร ใช้ storage ที่ไหน เชื่อมต่อกับ network อะไร จะให้ contianer ไปอยู่ที่เครื่องไหน container นั้นต้องใช้ RAM , CPU เท่าไหร่ ถ้าเกิดเกินจะต้อง scale out แบบไหน นี่เป็นสิ่งที่ Container Orchestration สามารถทำได้ ซึ่งตัว Container Orchestration นั้นไม่ได้มีแค่ Kubernetes จริงๆยังมี Docker Swarm , Mesos แต่ผู้ที่ได้รับความนิยมในการใช้มากที่สุดคือ Kubernetes (การที่มีคนนิยมใช้มากที่สุดไม่ได้แปลว่ามันดีที่สุด ตัว Docker Swarm , Mesos ก็มีข้อดีของมันซึ่งเหนือกว่า Kubernetes ) จนตอนนี้กลายเป็นเรื่องพื้นฐานที่ Developer ควรจะต้องรู้เหมือน docker

Install Kubernetes

เราสามารถ Install Kubernetes ใช้งานเองได้โดยสามารถดูได้ตาม OS ที่เราใช้

แต่หากใครไม่สะดวกจะลงเองหรือคิดว่าลงแล้วจะติดผมก็มีตัว VM ที่ลง k8s (จริงๆคือ k3s) และโปรแกรมที่จำเป็นต้องใช้งานไว้เรียบร้อยแล้ว คุณสามารถลงตัว Virtualbox แล้ว config network ก็สามารถนำมาใช้งานได้เลย

ใช้งาน Kubernetes ผ่าน VM

  1. ทำการ Install VirtualBox โดยเข้าไปที่ Link นี้ : https://www.virtualbox.org/wiki/Downloads แล้วเลือกตาม OS ที่ใช้

  2. ทำการ Download ตัว VM ตาม Link

  3. ทำการแตก zip แล้วกด double click ที่ไฟล์ training-rocky-k8s.vbox ตัว Virtual box จะเปิดขึ้นมา

  4. ทำการ Setting Network โดยทำการเลือกตามภาพและกดสร้าง

จากนั้นทำการเลือก Network adapter ที่ทำการสร้างขึ้นมา ของผมคือ VirtualBox Host-Only Ethernet Adapter #3(ของคุณอาจไม่เหมือนกับของผม) ให้เป็นดังภาพ

จากนั้นให้ทำการเลือกที่ VM : training-rocky-k8s แล้วเลือก Setting แล้วเลือก Network ดังภาพ

เลือก Adapter 1 เลือกชนิดของ Attach เป็น Host Only Adapter แล้วเลือก Adapter ที่คุณสร้างโดยของผมคือ VirtualBox Host-Only Ethernet Adapter #3 (ของคุณอาจไม่เหมือนของผม)

เลือก Adapter 2 เลือกชนิดของ Attach เป็น NAT

  1. ทำการ Start ตัว VM ขึ้นมา เปิด Browser ขึ้นมาจากนั้นเปิดไปที่ URL : http://192.168.156.101:8080/ ซึ่งถ้าไม่มีอะไรผิดพลาดจะได้ดังภาพ

ซึ่งที่คุณเปิดขึ้นมามันคือ Visual Code ที่ใช้งานบน Server ดังนั้นคุณสามารถสร้างไฟล์ แก้ไขไฟล์ สามารถสั่งใช้งานคำสั่งต่างๆผ่าน Terminal ใน Visual code ได้เลย

ทดสอบการใช้งาน Kubernetes

ส่วนนี้เรามาลองทดสอบว่าที่เราทำการ Install Kubernetes นั้นสำเร็จหรือไม่ โดยมีขั้นตอนดังต่อไปนี้

  1. สร้างไฟล์ test-deploy.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
34
35
36
37
apiVersion: v1
kind: Service
metadata:
name: my-nginx-svc
labels:
app: nginx
spec:
type: NodePort
ports:
- port: 31040
targetPort: 80
nodePort: 31040
name: nodered-port
selector:
app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
  1. ทำการสั่งให้ Kubernetes ทำการสร้าง container ในรูปแบบที่เราต้องการ
1
kubectl apply -f test-deploy.yml

  1. ทำการสั่ง Command ด้านล่างเพื่อเช็คว่า Kubernetes ทำการสร้าง container ให้เราตามที่ต้องการหรือไม่
1
2
kubectl get pods
kubectl get service

  1. เปิด Web browser จากนั้นไปที่ URL : http://<ip ของเครื่องที่ลง Kubernetes>:31040 โดยของผม ip ของเครื่องที่ลง Kubernetes คือ 192.168.156.101 ดังนั้น URL ของผมจึงเป็น http://192.168.156.101:31040

อธิบายว่าเราทำอะไรไป

ไฟล์ test-deploy.yml นั้นเป็นไฟล์ที่ใส่ข้อมูลที่จะสั่งให้ Kubernetes นั้นทำการสร้าง container แบบไหน เปิด Port อะไร และให้เข้ามาจาก Port อะไร โดยไฟล์ test-deploy.yml เป็นการสั่งให้สร้าง container nginx ขึ้นมาโดยกำหนดว่าให้มี 3 container คอยช่วยงานกัน (เห็นได้จากสั่ง kubectl get pods แล้วเห็นว่ามี 3 ตัวที่ run อยู่) จากนั้นทำการบอกว่าให้สามารถเข้าถึง container นี้ได้จาก Port 31040 จากเครื่องที่ลง ( เห็นได้จากสั่ง kubectl get service แล้วเห็นว่ามีการ Bind Port 31040 กับ service nginx ) ซึ่งเมื่อทดลองเปิด Browser แล้วเข้าไปที่ http://<ip ของเครื่องที่ลง Kubernetes>:31040 ก็จะเห็นว่าเราสามารถเข้าไปใช้งาน nginx ที่เราสร้างได้

ลบสิ่งที่สร้าง

ทำการสั่งคำสั่งด้านล่างเพื่อลบ container ที่ทำการสร้าง

1
kubectl delete -f test-deploy.yml

สรุป

ตอนนี้เราได้รู้กันแล้วว่า Kubernetes คืออะไรใช้ทำอะไร จากนั้นเราก็ได้ทำการ Install Kubernetes เพื่อจะนำมาทำการใช้งาน โดยมีวิธีให้ลงทั้งแบบลงบน OS เลย หรือจะใช้ผ่าน VM ที่ผมสร้างขึ้นมาให้ และสุดท้ายเราได้ลองสั่งใช้งานตัว Kubernetes ให้ทำการสร้าง Container nginx เพื่อให้เรามาทดลองใช้งาน

สำหรับตอนต่อไปเราจะมาทำความรู้จักเกี่ยวกับ Pod ว่ามันคืออะไร เวลาจะสร้างมันจะทำอย่างไร

Basic Database System Part 7 - ออกแบบ Database ด้วย ER Model ตอนที่ 2

Basic Database System Part 7 - ออกแบบ Database ด้วย ER Model ตอนที่ 2

ตอนที่แล้วเราได้รู้วิธีการเขียน ER Model กันมาแล้ว ตอนนี้เราจะมาเปลี่ยน ER Model ให้กลายเป็น Relational Database Model เพื่อเอาไปเป็นตัว Table ตั้งต้นก่อนจะนำไปทำ Normalization Process

เปลี่ยน ER Model ไปเป็น Relational Database Model

เราจะเอา ER Model จากตัวอย่างโจทย์การประชุมวิชาการทางศาสนามาเป็นตัวอย่างการแปลง ER Model ไปเป็น Relational Database Model

1. แปลงเป็น Entity ให้กลายเป็น Relation Table ตาม Attribute ที่มี โดยให้ Key Attribute มาเป็น Primary key

จากตัวอย่างเราจะแปลง Entity : SPEAKER , RELIGION , TEXT ไปเป็น Relation Table ซึ่งจะแปลงเป็น Table ได้ดังภาพ

2. แปลง Relationship แบบ M to M หรือ Relationship ที่เกิดจาก Entity ที่มากกว่า 2 ไปเป็น Relation Table โดยเอา Primary key ของแต่ละ Entity ไปเป็น Primary key ของ Table ใหม่ (และเป็น FK ด้วย) และเอา Attribute ของ Relationship ไปเป็น Attribute ในตาราง

จากตัวอย่างเราจะเห็น Relationship ที่เป็น M to M อยู่คือ SPEAK กับ READ ซึ่งจะแปลงเป็น Table ได้ดังภาพ

3. ดูที่ Relationship แบบ 1 To M จากนั้นนำ Primary key ของฝั่ง M ไปเป็น Foreign key ของ Table ฝั่ง 1

จากตัวอย่างเราจะเห็น Relationship : HAS ที่เกิดจาก Entity : RELIGION กับ Entity : TEXT เป็น 1 to M ซึ่งจะทำให้เราแก้ไข Table : TEXT เป็น

4. ดูที่ Relationship แบบ 1 to 1 จากนั้นตัดสินใจเลือกฝั่งใดฝั่งหนึ่งเป็น Table หลักจากนั้นให้ Table รองเอา Primary key ของ Table หลักมาเป็น Foreign key

เนื่องจากความสัมพันธ์นี้ไม่มีตัวอย่างการประชุมวิชาการเลยขอเอาความสัมพันธ์ พนักงาน กับ แผนก มาเป็นตัวอย่าง

ซึ่งเราจะเห็นว่ามี Entity พนักงาน กับ แผนก โดยเราตัดสินใจว่าจะเอา Entity พนักงานเป็น Table หลัก ซึ่งเราจะได้ Table ออกมาดังภาพ

ผลลัพธ์การเปลี่ยน ER Model เป็น Relational Table

ผลลัพธ์จากการเปลี่ยน ER Model การประชุมวิชาการทางศาสนา ไปเป็น Relational Model จะเป็นดังภาพ

Associative Entity

อันนี้เป็น Type พิเศษของ Relationship ซึ่งเกิดจาก Relationship แบบ M to M ที่อยากจะมีแบบหลายครั้ง พูดแบบนี้คุณอาจคิดไม่ออก ลองนึกภาพการยืมหนังสือในห้องสมุด โดยมี Entity คือ สมาชิก กับ หนังสือ เราจะเขียน ER Model ได้ดังภาพ

จากนั้นเราเปลี่ยน ER Model เป็น Relational Table ได้ดังภาพ

ดูแล้วเหมือนไม่มีปัญหาอะไร แต่ปัญหามันจะเกิดขึ้นถ้าเราอยากเก็บประวัติการยืมหนังสือ ตัวอย่างช่น ถ้าหากสมาชิก M001 ต้องการยืมหนังสือชื่อ “Database Design” ข้อมูลที่จะ Save ลง Table : ยืม จะเป็นดังภาพ

คราวนี้ถ้าเราอยากเก็บว่า M001 ยืมหนังสือชื่อ “Database Design” ไปแล้วกี่ครั้งมันจะทำไม่ได้เพราะ Primary key จะซ้ำ ตรงนี้บางท่านบอกว่างั้นก็เพิ่ม Attribute วันที่ยืมเข้าไปสิ ซึ่งก็ทำได้ครับ พอทำแล้วสัญลักษณ์จะเปลี่ยนไปเป็นแบบภาพด้านล่าง เพื่อให้เข้าใจว่า Relationship แบบนี้เกิดซ้ำได้

ซึ่ง Table ที่ได้จะเป็น

แต่สมมติว่า Associative Entity นี้จะต้องไปมี Relationship กับ Entity อื่น ซึ่งอาจจะทำให้เกิดการเอา Primary key ของ Associative Entity ไปเป็น Foreign key ซึ่งจากตัวอย่างนี้ต้องใช้ถึง 3 Attribute ซึ่งบางกลุ่มเห็นว่ามันยุ่งยากจึงมีการสร้าง Surrogate key (key แทน ) มาใช้แทน Primary key 3 attribute ซึ่งสามารถทำได้ดังภาพ

ซึ่งสามารถเปลี่ยนเป็น Table ได้ดังภาพ

โดย Primary key จะกลายเป็น ID_การยืม ส่วน Primary key เก่า 3 Attribute จะกลายเป็น Unique Key แทน

สรุป

ตอนนี้เราได้เห็นวิธีการเปลี่ยน ER Model ให้กลายเป็น Relational Database Model เพื่อนำไปตั้งต้นในการทำ Normalization Process ต่อ ซึ่งจะเห็นได้ว่าไม่ยากเลย และตอนนี้ได้แนะนำ Entity อีก Type นึงซึ่งก็คือ Associative Entity ว่ามันคืออะไร เอาไปใช้ตอนไหน

สำหรับตอนนี้น่าจะเป็นตอนสุดท้ายในชุด Basic Database System ก็หวังว่าทุกตอนน่าจะสร้างประโยชน์ให้กับผู้ที่เข้ามาอ่านไม่มากก็น้อย และสุดท้ายผมอยากจะฝากไว้ว่า “พื้นฐานเป็นเรื่องสำคัญ” ถ้าคุณเข้าใจพื้นฐานคุณจะสามารถต่อยอดความรู้อื่นๆได้ไม่ยาก แล้วพบกันใหม่ในหัวข้ออื่นๆสวัสดีครับ

Basic Database System Part 6 - ออกแบบ Database ด้วย ER Model ตอนที่ 1

Basic Database System Part 6 - ออกแบบ Database ด้วย ER Model ตอนที่ 1

สำหรับ 2 ตอนที่แล้วเราได้เรียนรู้การทำ Normalization ให้ถึงระดับ 5NF ไปแล้ว แต่ผมเชื่อว่าคงมีหลายคนสงสัยว่า เราจะได้ตารางเริ่มต้นแล้วเอามาทำ Normalization ยังไง อยู่ดีๆมันขึ้นมาเองเลยเหรอ จริงๆมันมีวิธีให้ได้มาซึ่งตารางนั้น

  1. เอามาจาก Report ใช่ครับ คุณฟังไม่ผิดครับ ปกติงานส่วนใหญ่จะรู้ว่าต้องเก็บข้อมูลอะไรส่วนหนึ่งจะรู้จาก Report ครับ เพราะสุดท้ายแล้วเราจะเอา Report ออกมาดูว่ามีอะไรในระบบ มีอะไรทำสำเร็จเท่าไหร่ ไม่สำเร็จเท่าไหร่ ซึ่ง Report ส่วนใหญ่เป็นตาราง

  2. ใช้วิธีการออกแบบที่มีคนคิดขึ้นมาแล้ว โดยแต่ละวิธีเมื่อออกแบบเสร็จเราจะได้เป็น Conceptual model ตามแต่ละวิธี โดย Conceptual model คือ model ที่ทำให้เราเข้าใจว่าสิ่งที่เราสนใจนั้นเกี่ยวข้องกันอย่างไร มีโครงสร้างอย่างไร ซึ่งตัว Conceptual model นี้ไม่ขึ้นตรงกับ DBMS เจ้าไหน และ Database model อะไร เมื่อคุณได้ตัว Conceptual model ออกมาคุณสามารถแปลงมันไปเป็น Database model ใดก็ได้ โดย Conceptual model ที่เป็นที่นิยมมากๆมี 2 ชนิดคือ ER Model กับ ORM โดยที่ผมจะเขียนนั้นคือ ER Model เพราะคนส่วนใหญ่ใช้ (คนส่วนใหญ่ใช้ไม่จำเป็นต้องดีที่สุดนะครับ ลองไปดูการออกแบบด้วย ORM ดูเพิ่มเติมครับ บางทีอาจจะสนุกกว่าการใช้ ER )

Entity Relationship Model ( ER Model )

Entity Relationship Model คือ Model ที่พูดถึง Entity ซึ่งหมายถึงสิ่งที่เราสนใจในระบบ อาจจะหมายถึงหนังสือ คน การโอน การยืม ตามแต่สิ่งที่ระบบนั้นสนใจ กับ Relationship ซึ่งหมายถึงความสัมพันธ์ ( เตือนไว้ตรงนี้เลยว่าไม่เกี่ยวกับ Relation ในเนื้อหา Relational database ) รวมแล้ว Entity Relationship model คือ Model ที่แสดงความสัมพันธ์ระหว่าง Entity ในระบบ

สัญลักษณ์ของ Entity Relationship Model

ถ้าสมมุติเราทำระบบเก็บข้อมูลพนักงานในบริษัท Entity ที่เราน่าจะต้องสนใจคือ “พนักงาน” ซึ่งพนักงานก็น่าจะมีข้อมูลเกี่ยวกับ รหัสพนักงาน , ชื่อ-นามสกุล , วันที่เริ่มทำงาน , เพศ ซึ่งด้วยข้อมูลทั้งหมดนี้เราสามารถเขียนมันให้อยู่ในสัญลักษณ์ดังภาพด้านล่าง

โดยเราจะไล่ทีละสัญลักษณ์

Entity

นี่คือสัญลักษณ์แทน Entity โดย Entity เราจะตั้งชื่อว่าอะไรก็ใส่เข้าไป โดยในตัวอย่างแรก Entity พนักงาน เราก็ใส่พนักงานเข้าไปแทน Entity

Key attribute

นี่คือสัญลักษณ์แทน Key attribute โดย Key attribute คือค่าที่ใช้อ้างอิงถึง Entity ได้เพียง Entity เดียว (มองง่ายๆว่าคือ Primary) ในตัวอย่างแรกของเราคือรหัสพนักงาน ซึ่งรหัสพนักงานสามารถใช้เป็นตัวอ้างอิงถึง Entity ได้เพียง Entity เดียว โดย Key attribute อาจมีหลายตัวได้

Attribute

นี่คือสัญลักษณ์แทน Attribute ซึ่งคือค่าต่างๆที่ Entity นั้นจากตัวอย่างของเราคือ ชื่อ-นามสกุล (ไม่เป็น Key attirbute เพราะอาจจะมีคนชื่อนามสกุลซ้ำกันก็ได้ เช่น บุญชู บ้านโข้ง อาจจะมีหลายคน) เพศ วันที่เริ่มทำงาน

ความสัมพันธ์ระหว่าง Entity

ระบบที่เรากำลังออกแบบคือระบบเก็บข้อมูลพนักงานในบริษัท Entity ของเราไม่ได้มีเพียงแค่ Entity พนักงานอย่างเดียว มันน่าจะมี Entity อื่นด้วย ตัวอย่างเช่น Entity แผนกดังภาพ

แล้วความสัมพันธ์ระหว่าง Entity พนักงานกับ Entity แผนกเกี่ยวข้องกันยังไง อันนี้แล้วแต่ระบบที่คุณสนใจว่ามันเกี่ยวข้องกันยังไงตาม อย่างระบบของผมที่สมมติขึ้น Entity พนักงานกับ Entity แผนก เกี่ยวข้องกันคือ พนักงานอยู่ภายใต้แผนก ซึ่งจากความสัมพันธ์นี้จะเขียน Model ได้ดังภาพ

โดยเราสามารถอ่านความสัมพันธ์ระหว่าง Entity พนักงาน กับ Entity แผนกได้แบบนี้

  • 1 Entity พนักงาน อยู่ภายใต้ Entity แผนก ได้เพียง 1 Entity แผนก (ภาษามนุษย์คือ พนักงาน 1 คน อยู่ภายใต้ แผนก ได้แผนกเดียว)

  • 1 Enttiy แผนก มี Entity พนักงาน อยู่ภายใต้ได้ “หลาย” Entity พนักงาน ( ภาษามนุษย์คือ แผนก 1 แผนก มีพนักงาน อยู่ภายใต้ ได้หลายคน)

Relationship

Relationship ใช้สัญลักษณ์ สี่เหลี่ยมขนมเปียกปูนในการบอกความสัมพันธ์ว่าคือความสัมพันธ์มีตัวอักษร 1 , M , N เป็นจำนวนความสัมพันธ์

คราวนี้คุณอาจจะสงสัยว่าตัวอักษร 1 , M อะไรพวกนี้มันเขียนยังไง จะรู้ได้ไงว่ามันเป็น 1 หรือ M เอาอะไรตัดสินใจ เดี๋ยวเราค่อยๆมาทำความเข้าใจกันครับ

ลากเส้นความสัมพันธ์

ถ้าเราอยากรู้ว่า Entity มีความเกี่ยวข้องกันยังไงให้ลองยกตัวอย่างขึ้นมา เช่น พนักงาน E001 , E002 , E003 , E004 เนี่ยมันอยู่ภายใต้ แผนก DEV , QA ยังไง ถ้าคิดไม่ออกลองดูภาพด้านล่างครับ

จากภาพคุณจะเห็นว่าความสัมพันธ์ว่าพนักงานแต่ละคนอยู่ภายใต้แผนกอะไร หากคุณสังเกตดีๆ คุณจะเห็นว่า 1 พนักงานนั้นอยู่ได้ 1 แผนก ส่วนถ้ามองฝั่งแผนกจะเห็นว่า 1 แผนกนั้นมีพนักงานอยู่ได้หลายคน ( M ) ดังนั้นเวลาไปเขียนสัญลักษณ์ตัวเลขจะเป็นฝั่ง Entity พนักงานจะเป็นตัว M เพื่อบอกว่า 1 Entity แผนกมีหลาย Entity พนักงาน ส่วนฝั่งแผนกจะใส่เลข 1 เพื่อบอกว่า 1 Entity พนักงานอยู่ได้ 1 Entity แผนก

แล้วมีความสัมพันธ์แบบอะไรบ้าง

ตัวอย่างความสัมพันธ์อยู่ภายใต้เป็นความสัมพันธ์แบบ 1 to M แต่คุณก็น่าจะเกิดคำถามว่ามันมีความสัมพันธ์แบบอื่นไหม เช่น แบบ 1 to 1 หรือ M to M อะไรแบบนี้ ซึ่งผมก็ขอบอกเลยครับว่ามีครับ เรามาลองดูตัวอย่างกัน

1 to 1

จริงๆความสัมพันธ์ระหว่าง พนักงาน กับ แผนก ไม่ได้มีความสัมพันธ์แค่ “อยู่ภายใต้” จริงๆยังมีความสัมพันธ์อื่นอีก เช่น ความสัมพันธ์ เป็นหัวหน้าแผนก ซึ่งความสัมพันธ์แบบเป็นหัวหน้าแผนกนี้เป็นความสัมพันธ์แบบ 1 to 1 คือ พนักงาน 1 คนเป็นหัวหน้าแผนกได้เพียง 1 แผนก ส่วน แผนก 1 แผนก เป็นมีหัวหน้าได้ 1 คน โดยเราสามารถทดสอบด้วยการลากเส้นความสัมพันธ์ดังรูปด้านล่าง (ขอละการเขียน Attriubte นะครับ)

M to M

คราวนี้มาลองคิดดูว่าบริษัทเนี่ยน่าจะมีโปรเจคที่บริษัทรับมาทำซึ่งแน่นอนว่าพนักงานน่าจะต้องถูกจัดให้ทำโปรเจคนั้น โดยพนักงานหนึ่งคนอาจจะถูกให้ทำหลายโปรเจค ดังนั้นความสัมพันธ์มันน่าจะเป็นแบบ M to M ซึ่งเราสามารถทดสอบเขียนความสัมพันธ์ดังรูปด้านล่าง (ขอละการเขียน Attriubte นะครับ)

Relationship มี Attribute ได้ไหม

คุณอาจจะมีคำถามว่า Relationship มันมี Attribute ได้ไหม คำตอบคือมันมีได้ครับ ยกตัวอย่างเช่นความสัมพันธ์ ที่ พนักงานต้องรับผิดชอบโปรเจคนั้นอาจมีค่าที่เกี่ยวข้องกับความสัมพันธ์นั้น เช่น จำนวนชั่วโมงที่ต้องรับผิดชอบต่อสัปดาห์ ซึ่งเป็นเรื่องที่เกิดขึ้นได้กับทุกความสัมพันธ์ ดังนั้นเราสามารถเขียน Attribute ของความสัมพันธ์ได้ดังภาพ

ความสัมพันธ์เกิดจาก Entity มากกว่า 2 Entity ได้หรือไม่

ตัวอย่างมาทั้งหมดนั้นเป็นความสัมพันธ์ที่เกิดจาก Entity 2 ตัวมีความสัมพันธ์กัน แล้วถ้า Entity มากกว่า 2 ตัวสามารถมีความสัมพันธ์กันได้ไหม คำตอบคือได้ครับ ตัวอย่างความสัมพันธ์ การขายของ โดยมี Entity ที่เกี่ยวข้องคือ บริษัทที่ขาย ของที่ขาย โปรเจค โดยเขียนความสัมพันธ์ดังภาพ (ขอละการเขียน Attribute ของ Entity ครับ)

Entity สามารถมีความสัมพันธ์กับตัวเองได้หรือไม่

คำตอบคือได้ครับ ยกตัวอย่าง พนักงานสามารถถูกกำหนดหน้าที่ให้ดูแลพนักงานคนอื่น เหมือนเป็นพี่เลี้ยง ซึ่งพนักงานหนึ่งคนสามารถดูแลพนักงานได้หลายคนและพนักงานสามารถถูกดูแลจากพนักงานหลายคนได้ ซึ่งเราสามารถเขียนความสัมพันธ์ได้ดังภาพ (ขอละการเขียน Attribute ของ Entity ครับ)

มาลองเขียน ER Model จากโจทย์กัน

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

หน้าที่ของเราคือทำ ER Model จากข้อมูลทั้งหมดที่มี

วิเคราะห์โจทย์

จากข้อมูลที่ได้เราพอเห็นคร่าวๆว่าน่าจะมี 2 Entity แน่นอนคือ RELIGION กับ SPEAKER แน่นอน และทั้งสองน่าจะมีความสัมพันธ์ด้วยความสัมพันธ์ SPEAK คือ SPEAKER SPEAK RELIGION ซึ่งจะได้ ER Model เบื้องต้นออกมาดังภาพ

ซึ่งข้อมูลที่เหลือที่ยังไม่อยู่ใน ER Model คือข้อมูลเกี่ยวกับ Text ซึ่งถ้าเราสังเกตดีๆจะเห็นว่า TEXT น้ันมีความสัมพันธ์กับ RELIGION และ SPEAKER ดังนั้นเราน่าจะให้ตัว TEXT เป็น Entity ด้วย ซึ่งจะได้ ER Model ออกมาดังภาพ

สรุป

สำหรับตอนนี้เราได้เรียนรู้เกี่ยวกับการเขียน Entity Relationship Model ว่ามีเขียนยังไง มีสัญลักษณ์อะไรบ้าง ไปจนถึงทดลองเขียน Entity Relationship Model จากโจทย์งานประชุมวิชาการศาสนา ซึ่งทั้งหมดที่เราเรียนรู้ไปเป็นเพียงบางส่วนของ Entity Relationship Model ซึ่งผมคิดว่าใช้บ่อยในการทำงาน จริงๆยังมีอีกหลายสัญลักษณ์ที่ผมไม่ได้พูดถึงตัวอย่างเช่น Weak Entity เป็นต้น ซึ่งถ้าใครสนใจสามารถไปหาอ่านเพิ่มได้ สำหรับตอนหน้าเราจะมาเปลี่ยน Entity Relationship Model นี้ให้กลายเป็น Relational Database Model เพื่อเอาไปใช้งานกัน

Ref

ตัวอย่างโจทย์เอามาจากหนังสือ Relational database systems : language, conceptual modeling and design for engineers

500 ล้านปีของความรัก วิทยาศาสตร์ของอารมณ์ ความรัก และความเกลียดชัง

500 ล้านปีของความรัก วิทยาศาสตร์ของอารมณ์ ความรัก และความเกลียดชัง

500 ล้านปีของความรัก วิทยาศาสตร์ของอารมณ์ ความรัก และความเกลียดชัง

ผมได้รู้จักหนังสือเล่มนี้จาก Page : Tactschool ที่แนะนำหนังสือน่าอ่านเมื่อหลายปีที่แล้ว แต่ตอนนั้นไม่ได้อ่านเพราะไปอ่านหนังสือเกี่ยวกับปรัชญาและการลงทุน จนเมื่อไม่นานมานี้ผมไปเห็นหนังสือชุดนี้ใน TK Park ก็เลยยืมมาอ่าน ซึ่งพอได้อ่านแล้วก็เข้าใจเลยทำไมหลายคนถึงแนะนำว่าเป็นหนังสือดี

หนังสือเล่มนี้พาเราไปเรียนรู้เกี่ยวกับ “มนุษย์” ซึ่งก็คือตัวเราผ่านคำถามที่เราอาจจะเคยสงสัยหรือบางเรื่องที่ธรรมดามากๆจนเราไม่เคยนึกสงสัย ตัวอย่างเช่น ทำไมเราชอบกินของหวานของมันทั้งๆที่รู้ว่ามันอันตรายต่อสุขภาพ ทำไมเราต้องฆ่ากันเพียงเพราะพูดหยามกันเล็กๆน้อยๆ ทำไมคู่รักมักจะทำอะไรแบบเด็กๆ อารมณ์มาจากไหน ยาแก้ปวดทำไมสามารถช่วยให้เจ็บจากการอกหกน้อยลง ทำไมคนเราถึงฆ่าล้างเผ่าพันธุ์กันได้ ทุกคำถามที่ว่ามาหนังสือเล่มนี้มีคำตอบให้เราทั้งหมด แถมเป็นคำตอบที่ไม่ได้มโนขึ้นมาแต่ผ่านการศึกษาจากการทดลอง การแสกนสมอง ด้วยวิธีการทางวิทยาศาสตร์ ดังนั้นมันจึงน่าเชื่อถือและสอดคล้องกัน และที่เด่นที่สุดของหนังสือเล่มนี้คือการเขียนเป็นเรื่องเล่าให้เราอ่านได้อย่างได้เรื่อยๆสบายๆไม่เครียด เหมือนเรามีเพื่อนผู้รู้มาเล่าเรื่องให้เล่าฟังด้วยศัพท์ง่ายๆสนุกและเห็นภาพ ดังนั้นคุณจะอ่านมันได้แบบเพลินๆเลยทีเดียว

ในส่วนต่อไปผมจะเขียนเล่าสิ่งที่ผมได้จากหนังสือซึ่งถือเป็นส่วนน้อยมากๆจากในหนังสือ

ทำไมทหารถึงเดินสวนสนาม

เคยสงสัยไหมว่าทำไมเราซึ่งจะไปเป็นทหารทำไมเราต้องมาฝึกเดินสวนสนาม ต้องมาวิ่งจัดแถวตอนหน้ากระดาน 10 คน 20 คน วิ่งวนไปวนมา ฝึกแบบนี้ซ้ำไปซ้ำมา ในสมัยก่อนอาจจะจำเป็นในรูปแบบการจัดทัพสู้กันในระยะประชิด แต่ในปัจจุบันเราใช้ปืนยิงไกลห่างกันเป็น 100 - 200 เมตร ก็ยิงถึง แล้วถ้าเคลื่อนที่แบบสวนสนามรับรองเลยโดนยิงตายง่ายๆแน่ ด้วยการทำที่ไม่สมเหตุสมผลนี้ทำให้นักวิทยาศาสตร์ท่านหนึ่ง (ผมจำชื่อไม่ได้แล้ว) ที่ได้ไปเป็นทหารเกิดความสงสัย ว่าทำไปทำไม แต่จริงๆมันมีอีกคำถามหนึ่งซึ่งเขาก็รู้สึกได้และสงสัยคือตอนเป็นทหารเขาเริ่มรู้สึกแปลกๆหลังจากฝึกสวนสนามไปนานๆ เขารู้สึกว่าเขาเริ่มรู้สึกถึงความเป็นกลุ่ม รู้สึกฮึกเหิมไปกับกลุ่ม เริ่มรู้สึกอยากทำอะไรให้กับกลุ่ม ความรู้สึกถึงความเป็นตัวเองเริ่มลดลง ตอนแรกเขาคิดว่าเขาเป็นเพียงคนเดียวแต่เมื่อถามหลายๆคนที่ฝึกด้วยกันก็ได้คำตอบว่ารู้สึกแบบเดียวกัน

อาการแบบนี้ไม่ได้เกิดแค่การเดินสวนสนาม อาการเหล่านี้สามารถเกิดได้กับการร้องหรือการเต้นที่ทำเป็นหมู่คณะ ตัวอย่างง่ายๆถ้าคุณได้ยินและร้องเพลงชาติตอนทีมชาติของเราได้เหรียญทองในงานแข่งขันระดับใหญ่ๆเช่น เอเชียนเกมส์ หรือ ซีเกมส์ คุณจะรู้สึกถึงความฮึกเฮิม ความรักชาติ รู้สึกเราเป็นหนึ่งเดียวกันอย่างแปลกประหลาด (ทั้งๆที่เราฟังเพลงชาติที่อื่นเราไม่ได้อิน บางคนถึงกับบอกว่าไร้สาระ)

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

การแลกเปลี่ยน เรื่องธรรมดาของมนุษย์แต่ไม่ธรรมดาในทางธรรมชาติ

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

แต่การแลกเปลี่ยนเหล่านี้หายากในสิ่งมีชีวิตอื่นบนโลกมาก ลิงชิมแปนซีญาติของเรานั้นก็ไม่สามารถทำได้ในระดับเดียวกับเรา พวกมันสามารถแลกเปลี่ยนของที่มันเห็นคุณค่ามากกับน้อยได้ เช่น แลกองุ่นกับแครอท (หวานมาก กับ ไม่หวานเลย) แต่มันไม่สามารถแลกเปลี่ยนของที่มีค่ามากๆกับมากรองลงมาได้ เช่น แลกองุ่นกับแอปเปิ้ล (หวานมากสุด กับ หวานรองลงมา) แตกต่างจากมนุษย์ที่สามารถแลกเปลี่ยนได้เกือบทุกอย่างภายใต้ความพอใจของตนเอง เราสามารถแลกของมีค่าจำนวนมากเพื่อได้กระเป๋าโง่ๆที่ใช้ประโยชน์อะไรไม่ได้เยอะ

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

จริงๆผลกระทบที่ตามมาจากการแลกเปลี่ยนมีมากกว่านี้แต่ผมอยากคุณไปอ่านจากหนังสือจะดีกว่าเพราะมันเยอะจริงๆแถมสนุกมากด้วย

อ่านแล้วได้อะไร

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

การอ่านหนังสือเล่มนี้เหมือนผมกลับไปอ่านหนังสือ “พวกฉัน พวกมัน พวกเรา” “วิทยาศาสตร์แห่งความสุข” “MISBEHAVING” แต่เป็นในมุมมองภาพรวมซึ่งมีการเชื่อมโยงส่วนต่างๆให้เราเห็นด้วย โดยส่วนตัวผมยกให้เป็นหนังสือที่ควรไปหามาอ่านเพราะมันจะทำให้คุณเข้าใจ ยอมรับ ความเป็นมนุษย์ของเรา และหาทางจัดการกับความเป็นมนุษย์ของเราครับ

Hedwig and The Angry Inch

Hedwig and The Angry Inch

ออกตัวก่อนเลยว่าผมไม่ใช่ผู้มีความรู้ความเชี่ยวชาญในด้านการแสดง การกำกับ ไม่ใช่ศิษย์เก่าของที่ไหน ผมเป็นแค่พนักงานบริษัทเอกชนย่านใจกลางเมืองที่ดันรู้ว่ามีการจัดแสดงละครเวทีซึ่งสามารถไปดูฟรีได้ (คุณอ่านไม่ผิดครับ มันฟรี และ ผมชอบดูของฟรี) ผมก็เลยไปดูละครเวทีเรื่องนี้แบบไม่รู้เรื่องย่อใดๆ ไม่รู้ทั้งผู้กำกับ นักแสดง ไปรู้กันเลยว่าเรื่องที่ดูมันเป็นยังไง ซึ่งหลายครั้งที่เจอเรื่องดีๆ หลายครั้งเหมือนกันที่เหมือนกูไปเสียเวลาไปดูอะไรก็ไม่รู้ ดังนั้นการวิจารณ์ หรือ Review แล้วแต่จะพูดเป็นสิ่งที่มาจากคนไม่รู้ศาสตร์และศิลป์ ซึ่งอาจจะเข้าไม่ถึงสิ่งที่เรียกว่าสุดยอดการแสดงดังนั้นถ้ามันไปขัดใจใครก็ขออภัย

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

เพลงกับเนื้อเรื่อง

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

บท

สำหรับผมบทเรื่องนี้ดูไม่มีอะไรมาก ไม่ได้ทำให้น่าติดตามอยากรู้ทุกวินาที ทั้งหมดคือการเล่าเรื่องชีวิตของคุณ Hedwig นั่นแหละว่าเขาคือใคร ผ่านอะไรมา อาจจะมีน่าสนใจนิดหน่อยก็ตรงปมที่คุณ Hedwig เกี่ยวอะไรกับคุณ Tommy และการใช้เรื่องทฤษฎีความรักของเพลโตที่บอกว่ามนุษย์ในอดีตมี 2 คนในหนึ่งร่างซึ่งอาจจะเป็นชายชาย หญิงหญิง ชายหญิง แต่ถูกพระเจ้าจับแยกออกจากกันเพราะเกรงกลัวความสามารถของมนุษย์ ซึ่งนั่นทำให้มนุษย์โหยหาอีกครึ่งเพื่อมาเติมเต็มซึ่งนั่นสามารถอธิบายได้ว่าทำไมคนบางคนถึงชอบเพศเดียวกัน (ดูเป็นทางออกที่ดีในการอธิบาย) ซึ่งจากการใช้ทฤษฎีนี้อธิบายทำให้เราเข้าใจคุณ Hedwig ว่าทำไมเขาถึงฝังใจกับคุณ Tommy ขนาดนั้น

การแสดง

สิ่งที่ทำให้ผมคิดว่าเรื่องนี้มีคุณค่าคุ้มกับเวลาที่เสียไปคงจะเป็นการแสดงของนักแสดง นักแสดงที่เล่นเป็นคุณ Hedwig นี่เขาเล่นได้สุดยอดจริงๆแบบที่เราดูแล้วเชื่อว่าเขาเป็นคุณ Hedwig จริงๆไม่ได้กำลังแสดง ส่งอารมณ์สุขเศร้าตลกมาให้เราได้อย่างเต็มที่ (ผมเคยเห็นนักแสดงคนนี้เล่นเรื่องอื่นมาแล้ว (ถ้าจำไม่ผิด แต่คงไม่ผิดหรอก) เรื่องแวมไพร์อะไรสักอย่าง แต่เรื่องนั้นไม่น่าประทับใจเท่าไหร่ อาจจะเพราะบทไม่ได้ส่งให้เขาเล่นได้เต็มที่เหมือนเรื่องนี้) แถมเขามีการเล่นนอกบทที่เล่นได้ดีรู้ว่าควรเล่นตอนไหน เล่นกับใคร บริบทไหนที่ควรเล่น (ปกติถ้าเจอคนเล่นนอกบทที่ไม่เก่งมาก เล่นแล้วคนจะไม่เข้าใจ เงียบ หรือไปต่อไม่ได้ )

สรุป

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

วิทยาศาสตร์แห่งความสุข - สำรวจความสุขและความหมายของชีวิตด้วยวิทยาศาสตร์

วิทยาศาสตร์แห่งความสุข - สำรวจความสุขและความหมายของชีวิตด้วยวิทยาศาสตร์

วิทยาศาสตร์แห่งความสุข - สำรวจความสุขและความหมายของชีวิตด้วยวิทยาศาสตร์

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

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

ถ้าคุณไปหาหนังสือเกี่ยวกับความสุขผมรับรองเลยว่าน่าจะมีเป็นร้อยเล่ม อาจจะเกี่ยวกับศาสนา ความเชื่อ วิธีปฏิบัติตัว การมองโลกให้มีความสุข ซึ่งผมก็อ่านมาหลายเล่มทั้งแบบที่พูดถึงความสุขโดยตรง หรือนิยายปรัชญาที่พูดถึงความสุขในแง่มุมต่างๆของตัวละคร แต่ส่วนใหญ่มันก็ดูจะตอบแบบครอบจักรวาลหรือออกแนวเพ้อเจ้อไปเลย (คุณอาจไม่พอใจผมแต่แน่นอนครับ “เรื่องของคุณ” ) จนได้มาเจอหนังสือ วิทยาศาสตร์แห่งความสุข - สำรวจความสุขและความหมายของชีวิตด้วยวิทยาศาสตร์ ที่อธิบายความสุขจากมุมมองทางวิทยาศาสตร์ทั้งในด้านการแพทย์ ทางจิตวิทยาที่ใช้การทดลองกับกลุ่มทดลอง อีกทั้งหนังสือยังรวบรวมแนวคิดเกี่ยวกับความสุขของอารยธรรมยิ่งใหญ่บนโลกทั้งฝั่งตะวันออกและตะวันตก ซึ่งบอกเลยถ้าคุณอ่านหนังสือเล่มนี้แล้วคุณจะเข้าใจความสุขในหลายๆแง่มุม

คนรวย ผู้มั่งคั่ง ทำไมยังมีความทุกข์

คุณคงได้ยินเรื่องเล่าของคนมั่งมีรวยล้นฟ้าแต่ชีวิตยังทุกข์ไหมครับ ผมเชื่อว่าคุณคงเคยได้ยิน ถ้าคุณเป็นคนนับถือศาสนาพุทธหรือเรียนโรงเรียนพุทธที่ใส่วิชาพระพุทธศาสนาในหลักสูตรคุณคงรู้จัก “เจ้าชายสิทธัตถะ” ที่มีทรัพย์สินเงินทอง ข้าทาสบริวาร แต่เขาก็ยังรู้สึกทุกข์จนสุดท้ายต้องออกบวชหาทางพ้นทุกข์ ซึ่งหลายคนก็จะพูดว่า “ถ้ากูรวยแบบนั้น กูไม่ออกบวชแม่งหรอก กูจะอยู่แบบนั้นใช้ชีวิตสบายไปจนตายเลย” หรือ ถ้ามองให้ใกล้ตัวขึ้นมาหน่อย หากคุณเป็นคนชั้นกลางที่มีเงินเดือนมากกว่าค่าแรงขั้นต่ำ คุณมีเงินเหลือจากค่าใช้จ่ายในชีวิตประจำวัน มีเงินเก็บ มีประกันสุขภาพ ถ้ามองแบบพื้นๆไม่ลงรายละเอียดคุณควรจะมีความสุข (กินอิ่ม นอนหลับ ไม่ต้องดิ้นรน) แต่ถ้าลองถามตัวเองดีๆคุณจะรู้สึกว่างเปล่า ชีวิตมันขาดอะไร น่าเบื่อ ไม่มี “ความสุข” เลย จนมันทำให้คุณขวนขวายหาสิ่งใหม่เข้ามาในชีวิต ไม่ว่าจะเป็นต้องมีบ้าน มีรถ มีเครื่องใช้ของใช้ใหม่ๆ ถ้ามีอยู่แล้วก็ต้องหาที่ดีกว่า ตำแหน่งงานที่มีอยู่แล้วยังไม่มากพอ ต้องมีมากกว่านี้

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

ความสุขหาได้เสมอเพราะเราปรับตัว

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

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

การศึกษาที่แล้วเจาะจงไปหาคนที่อยู่ดีๆก็ทุกข์แบบแสนสาหัสว่าจะสามารถมีความสุขได้ไหม ทีนี้มาถึงการศึกษาคนที่น่าจะทุกข์อยู่ตลอดเวลาว่าเขาสามารถมีความสุขได้หรือไม่ โดยเขาทำการศึกษาคนที่ทำอาชีพขายบริการ (ในบางประเทศคนขายบริการมีคุณภาพชีวิตไม่ค่อยดีเท่าไหร่) ซึ่งก่อนอ่านผมคิดว่าเขาคงทุกข์ทรมานต้องมาทำงานอะไรพวกนี้ เงินที่ได้ก็น้อยนิด มีปัญหาเรื่องการตีค่าเกี่ยวกับคุณค่าของชีวิต เขาคงจะต้องทุกข์ทรมานไม่มีวันความสุขได้แน่ แต่จากการศึกษาพบว่าคนเหล่านั้นสามารถมีความสุขได้ซึ่งก็ไม่ต่างจากการศึกษาที่แล้วคือเขามีความสุขจากสิ่งที่พวกเขาพอจะมีได้คือ การคุยกับเพื่อน คนรู้จัก

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

จากหลายๆการศึกษามันคงบอกคุณได้อย่างหนึ่งว่าคนเราสามารถหาความสุขให้ตัวเองได้เสมอดูได้จากทั้งคนที่กลายเป็นอัมพาตหรือคนที่มีอาชีพที่ไม่น่าจะหาความสุขได้ ดังนั้นถ้ามีใครสักคนพูดว่าความสุขสามารถหาได้เสมอเขาไม่ได้พูดเกินจริงนะครับ เช่นกัน ต่อให้คุณรวยล้นฟ้าล้นแผ่นดินคุณก็อาจจะไม่ได้มีความสุขมากขึ้นกว่าเดิมสักเท่าไหร่ครับ ทั้งหมดทั้งมวลนั้นเกิดจากการที่คนเรามีการปรับตัวให้เข้าสภาพปัจจุบัน

ทำเลยมีความสุขแน่นอน

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

  1. ลดระยะเวลาการเดินทาง จากการสำรวจพบว่าคนจำนวนมากไม่มีความสุขกับการเดินทางไกลซึ่งส่วนใหญ่จะเป็นการเดินทางไปทำงาน ซึ่งก็น่าจะเป็นเรื่องที่ทุกคนเจอกัน ไม่ว่าจะเป็นสภาพรถติด เพื่อนร่วมถนนชั้นเลว ดังนั้นหากคุณลดระยะเวลาการเดินทางลดได้คุณก็จะทุกข์กับเรื่องเหล่านี้น้อยลงมาก

  2. ที่พักไม่อยู่ในที่เสียงดัง จากการสำรวจพบว่าคนจำนวนหนึ่งทุกข์กับการอาศัยอยู่ในที่เสียงดังเพราะทำให้มีปัญหาเกี่ยวกับการนอนและสูญเสียสมาธิ ซึ่งเรื่องนี้เราก็เข้าใจได้เลยเพราะถ้าคุณมีบ้านอยู่ใกล้ผับหรือสถานที่ก่อสร้างคุณคงรำคาญเสียงรบกวนซึ่งมันแทบจะดังตลอดเวลาที่มีกิจกรรม

  3. จัดการรูปร่างของคุณให้ตรงตามความต้องการของคุณ เรื่องนี้ในหนังสืออธิบายว่าเรื่องนี้เป็นเรื่องความมั่นใจของคนนั้น เขายกตัวอย่างเพศหญิงที่มีปัญหาหน้าอกใหญ่ หรือ เล็กจนเกินไปทำให้ตัวเองขาดความมั่นใจ ไม่ค่อยรู้สึกมีความสุข แต่หลังจากพวกเธอเข้ารับการผ่าตัดให้หน้าอกมีขนาดตามที่ตัวเองต้องการแล้วพบว่าผู้หญิงเหล่านั้นมีความสุขมากขึ้นซึ่งนั่นมาจากการที่เธอมีความมั่นใจ

อ่านแล้วได้อะไร

จริงๆหนังสือเล่มนี้ยังมีอีกหลายเรื่องที่น่าสนใจไม่ว่าจะเป็นเรื่อง ช้างและควาญช้าง (ถ้าเข้าใจตรงนี้คุณจะเห็นภาพใหม่เกี่ยวกับความสุข) ทนายของตัวเอง กระจกสีกุหลาบ การตัดสินคนอื่น ที่มาของความสุข การถูกล็อตเตอร์รี่ความสุข สมการของความสุข (อันนี้มีสูตรจริงจัง) ซึ่งน่าสนใจมากกว่าที่ผมเขียนเล่าไปด้านบน (ก็ถ้าผมเล่าส่วนที่น่าสนใจสุดคุณก็ไม่ไปหามาอ่านน่ะสิ) ซึ่งผมรับรองเลยว่าอ่านแล้วคุ้มค่ากับเวลามากๆ มันจะทำให้คุณเข้าใจความสุขมากขึ้นซึ่งเมื่อคุณเข้าใจแล้วคุณก็สามารถนำมันมาปรับใช้กับชีวิตของคุณได้ ซึ่งมันดีกว่าการคุณไปอ่านหนังสือ How to ต่างๆนาๆแล้วเอามาทำตามเพื่อให้ได้ดั่งใจโดยไม่รู้ว่าเป้าหมายที่อยากทำนั้นคืออะไร

นายพลกับคืนหายนะ - An unpleasant predicament

นายพลกับคืนหายนะ - An unpleasant predicament

นายพลกับคืนหายนะ - An unpleasant predicament

หนังสือเล่มนี้เป็นหนังสือที่รวมเรื่องสั้นของ Fyodor Dostoevsky ซึ่งเขาคือนักเขียนชื่อดังของรัสเซีย ผมเคยอ่านงานเขียนของเขาที่ชื่อ “พี่น้องคารามาซอฟ” แล้วรู้สึกประทับใจในวิธีการเขียนและการสร้างตัวละครของเขา เนื้อเรื่องก็แหวกแนวไม่เหมือนตลาด แม้แต่ผมที่เป็นคนในยุคปัจจุบันได้อ่านก็รู้สึกว่ามันแหวกแนวดี ดังนั้นผมจึงไม่พลาดจะเอาหนังสือเล่มนี้มาอ่าน

ในหนังสือเล่มนี้ประกอบด้วยเรื่องสั้นดังต่อไป ต้นไม้สวรรค์, เขาชื่อมาเรย์, หัวขโมยใจซื่อ, นิยายในจดหมายเก้าฉบับ, โบบ็อก จากบันทึกของใครบางคน, ฮีโร่ใต้เตียง, นายพลกับคืนหายนะ และจระเข้ โดยผมจะเล่าความประทับใจแบบไม่สปอย 2 เรื่องคือ นิยายในจดหมายเก้าฉบับ กับ นายพลกับคืนหายนะ

นิยายในจดหมายเก้าฉบับ

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

นายพลกับคืนหายนะ

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

อ่านแล้วได้อะไร

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

Basic Database System Part 5 - Normalization Process 4NF , 5NF

Basic Database System Part 5 - Normalization Process 4NF , 5NF

ในตอนที่แล้วเราได้ทำ Normalization Process ไปถึงขั้น BCNF ซึ่งจากตัวอย่างที่เราได้ลองทำกันนั้นมันไม่มีปัญหา INSERT , DELETE , UPDATE ซึ่งนั่นทำให้เกิดความเข้าใจผิดๆว่าทำ Normalization Process ถึงขั้น BCNF ก็พอ ในตอนนี้เราจะมาดูว่า Table ที่เป็น BCNF ก็ยังเกิดปัญหา INSERT , DELETE , UPDATE ได้ จากนั้นเราจะมาทำ Normalization Process กันต่อจนถึงขั้น 5NF

Table : Course Teacher Text (CTX)

Course Teacher Text

นี่เป็นรายละเอียดเกี่ยวของ Course เรียนที่บอกว่าจะสอนวิชาอะไร ใครเป็นอาจารย์ และต้องใช้หนังสืออะไรบ้าง ซึ่งจากตัวอย่างเราจะเห็นว่า Course : Database มีอาจารย์สอน 2 คนคือ John , Smith ใช้หนังสือสำหรับสอนวิชานี้ 2 เล่มคือ Set theory , DBMS Technology ส่วน Course : Math มีอาจารย์สอน 1 คนคือ John ใช้หนังสือสำหรับสอนวิชานี้ 3 เล่มคือ Set theory , Algebra , Calculus

ถ้าเราอยากเก็บข้อมูลนี้ให้อยู่ใน Relational Database Model เราสามารถทำได้โดยใช้ Normalization Process ที่เราเรียนไปในตอนที่แล้ว ซึ่ง Table ที่ได้ออกมาจะมีหน้าตาแบบนี้

Table : Course Teacher Text (CTX) ที่เป็น BCNF

รายละเอียดของ Table

  1. Primary key : { COURSE , TEACHER , TEXT }
  2. Candidate key : { COURSE , TEACHER , TEXT }
  3. Nonkey attribute : ไม่มี
  4. FD ไม่มี

ตรวจสอบว่าเป็น BCNF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และแต่ละ Column มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น

Table เป็น BCNF แต่ปัญหา INSERT , DELETE , UPDATE

คุณจะเห็นว่า Table : Course Teacher Text (CTX) นั้นเป็น BCNF แล้ว ซึ่งถ้าคุณเชื่อตามความเชื่อผิดๆที่บอกไปตอนต้นว่า Table ที่เป็น BCNF (บางคนบอก 3NF) แล้วจะไม่มีปัญหาเรื่องการ INSERT , DELETE , UPDATE เรามาลองพิสูจน์กันครับว่าจริงไหม

ปัญหา Insert ข้อมูลเข้าไปไม่ได้

ถ้าผมต้องการอยากเพิ่ม COURSE : PROGRAMMING เข้าไปโดยวิชานี้ใช้ TEXT : BASIC C, C++ , JAVA Programming เข้าไป เราจะได้ข้อมูลที่จะ INSERT เป็น

ข้อมูลที่ต้องการ INSERT ลง TABLE : CTX

ซึ่งจะเห็นว่าเราไม่สามารถ INSERT ข้อมูลได้เพราะติดกฎเรื่อง Primary key must not be null

ปัญหา Delete

ถ้าสมมุติผมต้องการลบข้อมูล TEACHER : John ออกไป คุณจะเห็นว่าถ้าลบข้อมูล John ออกไป ข้อมูลของ TEXT ที่เกี่ยวกับ COURSE : Math ทั้งหมดจะหายไปด้วย ซึ่งนั่นไม่ใช่สิ่งที่เราต้องการ

ข้อมูลที่ต้องการ DELETE

ยังมีปัญหาอยู่

คุณจะเห็นแล้วว่าแม้ Table จะเป็น BCNF แต่มันก็ยังเกิดปัญหา INSERT , UPDATE , DELETE อยู่ ดังนั้นถ้าใครบอกว่า Normalization Process ทำแค่ BCNF (3NF) ก็พอ ผมแนะนำให้เอา Table : Course Teacher Text (CTX) ให้เขาดูครับ เขาจะได้เข้าใจว่ามันไม่ใช่อย่างที่เขาคิด

ความสัมพันธ์ระหว่าง Column (Attriubte) ของ Table : CTX

ใน Table : CTX นั้นไม่มีความสัมพันธ์แบบ FD เลย แต่ถ้าคุณดูความสัมพันธ์ตามงานจริงคุณจะรู้ว่าตัว COURSE นั้นจะเป็นตัวบอกว่ามี TEACHER คนไหนบ้างที่สอนวิชานี้ และ COURSE นั้นเป็นตัวบอกว่ามี TEXT เล่มไหนถูกใช้ในการสอนบ้าง และ TEXT กับ TEACHER นั้นไม่ได้มีความเกี่ยวข้องกันเลย (TEACHER ไม่ได้เป็นตัวบอกว่าใช้ TEXT เล่มไหน และ TEXT ก็ไม่บอกว่า TEACHER คนไหนต้องใช้ ) เราจะเรียกความสัมพันธ์ใน Table นี้ว่า Multivalued dependence ( MVD )

Multivalued dependence ( MVD )

Given a relational table structure T , With attributes X , Y and Z , the multivalued dependence (MVD) T.X ->-> T.Y holds in T if and only if the set of Z-values matching a given (X-value, Y-value) pair in T depends only on the X-Value and is independent of the Y-value. As usual , X, Y and Z may be composite.
Note that MVDs as defined can exist only if the relational table structure T has at least three attributes.

ผมแนะนำให้อ่านนิยามภาษาอังกฤษนะครับ สำหรับภาษาไทยที่ผมจะแปลผมไม่รู้จะแปลยังไงให้ถูกดี ลองอ่านดูละกันครับ

กำหนดให้ Table T มี Column (Attribute) X , Y และ Z โดย Multivalued dependence (MVD) T.X ->-> T.Y จะอยู่ใน Table T ก็ต่อเมื่อ มี Set ของค่าใน Column Z ตรงกับคู่ของค่า ( X-value , Y-value ) โดยค่า Z นั้นจะขึ้นอยู่กับ X เท่านั้น และค่า Z เป็นอิสระต่อ Y โดยปกติแล้ว X Y และ Y นั้นจะอยู่ด้วยกัน ดังนั้น MVD จะเกิดขึ้นได้ก็ต่อเมื่อ Table นั้นมีอย่างน้อย 3 Attribute

อ่านแล้วอาจจะงงเรามาดูตัวอย่างของ Table : CTX

Table : CTX มี 3 Column (Attribute) คือ COURSE , TEACHER , TEXT

  1. MVD COURSE ->-> TEACHER

    X คือ COURSE , Y คือ TEACHER , Z คือ TEXT

    ตรงนี้เราพอเห็นแล้วว่า COURSE ->-> TEACHER น่าจะเป็น MVD เพราะค่า COURSE นั้นกำหนดกลุ่มของค่า TEACHER แต่เราต้องตรวจสอบด้วยนิยามคือดูค่า TEXT ว่าตรงตามเงื่อนไขไหม

    ตรวจสอบทีละเงื่อนไขว่าเป็น MVD หรือไม่

    • Z ขึ้นอยู่กับ X เท่านั้น ดูได้จากค่า Text = { Set theory , DBMS Technology } เมื่อ Course = Database และ Text = { Set theory , Algebra , Calculus } เมื่อ Course = Math

    • Z เป็นอิสระจาก Y ดูได้จาก TEXT สามารถเปลี่ยนค่าได้โดยไม่เกี่ยวข้องกับ TEACHER เลย

    • Z (Text) = { Set theory , DBMS Technology } นั้นสามารถ Map ได้กับคู่ของ COURSE , TEACHER ซึ่งก็คือ {Database , John} , { Database , Smith } ได้

      MVD COURSE ->-> TEACHER

  1. MVD COURSE ->-> TEXT

    X คือ COURSE , Y คือ TEXT , Z คือ TEACHER

    ตรวจสอบทีละเงื่อนไขว่าเป็น MVD หรือไม่

    • Z ขึ้นอยู่ X เท่านั้น ดูได้จากค่า TEACHER = { John , Smith } เมื่อ Course = Database และ TEACHER = { John } เมื่อ Course = Math

    • Z เป็นอิสระจาก Y ดูได้จาก TEACHER สามารถเปลี่ยนค่าได้โดยไม่เกี่ยวกับ TEXT เลย

    • TEACHER = { John , smith } นั้นสามารถ Map ได้กับคู่ของค่า COURSE , TEXT ซึ่งก็คือ { Database , Set theory } , { Database , DBMS Technology }

      MVD COURSE ->-> TEXT

ข้อสังเกตเกี่ยวกับ MVD

  • เวลาเกิด MVD นั้นจะเกิดเป็นคู่อย่างในตัวอย่างเมื่อเกิด MVD COURSE ->-> TEACHER ก็จะเกิด MVD COURSE ->-> TEXT เกิดขึ้นเสมอ ดังนั้นเขาจะเขียน MVD เป็น MVD COURSE ->-> TEACHER | TEXT

  • ความสัมพันธ์แบบ MVD นั้นเป็นความสัมพันธ์แบบ 1 to Many ตัวอย่างเช่น MVD COURSE ->-> TEXT นั้นค่า COURSE หนึ่งค่าสามารถกำหนดว่าค่า TEXT สามารถมีค่าเป็นอะไรได้หลายค่า เช่น ถ้า COURSE = Database ค่า TEXT ที่สามารถเป็นได้คือ { Set theory , DBMS Technology } คราวนี้ถ้าเราเกิดสมมุติว่าความสัมพันธ์แบบ MVD X ->-> Y แล้ว Set ของ Y มีค่า 1 ค่า มันก็จะกลายเป็น 1 to 1 ซึ่งนั่นก็หมายความว่ามันคือความสัมพันธ์แบบ FD ดังนั้นความสัมพันธ์แบบ FD ซึ่งเป็นกรณีพิเศษของ MVD

4NF

A relational table structure T is in th fourth normal form (4NF) if it is in BCNF and all MVDs in T are FDs Only.

ผมแนะนำให้อ่านนิยามภาษาอังกฤษเพราะเป็นนิยามที่ถูกต้องครับ แต่ถ้าไม่เข้าใจลองอ่านที่ผมแปล

Table จะเป็น 4NF เมื่อ Table นั้นเป็น BCNF และ MVD ทั้งหมดใน Table ต้องเป็น FD

ตรวจสอบ Table : CTX

MVD ของ Table : CTX

  • MVD COURSE ->-> TEACHER
  • MVD COURSE ->-> TEXT

Table นี้ไม่เป็น 4NF เพราะ MVD ไม่ได้เป็น FD ดูได้จากเพราะค่า COURSE –>–> TEACHER เป็น 1 to many ดังนั้น MVD นี้ไม่ได้เป็น FD

วิธีแก้ไข

ให้ทำการแยกความสัมพันธ์ MVD ออกไปเป็น Table ใหม่ Table : CTX มี 2 MVD ก็จะได้เป็น 2 Table คือ Table : Course Teacher (CT) กับ Table : Course Text (CX)

Table : Course Teacher (CT)

Table : Course Teacher (CT)

รายละเอียดของ Table

  1. Primary key : { COURSE , TEACHER }
  2. Candidate key : { COURSE , TEACHER }
  3. Nonkey attribute : ไม่มี
  4. FD : ไม่มี
  5. MVD : ไม่มี

ตรวจสอบว่าอยู่ใน 4NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม

Table : Course Text (CX)

Table : Course Text (CX)

รายละเอียดของ Table

  1. Primary key : { COURSE , TEXT }
  2. Candidate key : { COURSE , TEXT }
  3. Nonkey attribute : ไม่มี
  4. FD : ไม่มี
  5. MVD : ไม่มี

ตรวจสอบว่าอยู่ใน 4NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม

ตรวจสอบปัญหา Insert Update Delete

ปัญหา Insert ข้อมูลเข้าไปไม่ได้

ถ้าผมต้องการอยากเพิ่ม COURSE : PROGRAMMING เข้าไปโดยวิชานี้ใช้ TEXT : BASIC C, C++ , JAVA Programming เข้าไป เราจะได้ข้อมูลที่จะ INSERT เข้า Table : Course Text (CX) ได้เลย

INSERT ข้อมูลลง TABLE : CX

ซึ่งจะไม่ติดปัญหา Primary Must not be null

ปัญหา Delete

ถ้าสมมุติผมต้องการลบข้อมูล TEACHER : John ออกไป เราก็สามารถลบข้อมูลออกจาก Table : Course Teacher (CT) ได้เลยซึ่งข้อมูลเกี่ยวกับ Text จะไม่หายไปเหมือนกรณีที่แล้ว

DELETE ข้อมูล TABLE : CT

Table : Vendor Item Project (VIJ)

Table : Vendor Item Project (VIJ)

Table : Vendor Item Project (VIJ) Table นี้นั้นทำการเก็บข้อมูลว่า Vendor ไหน ส่ง Item อะไร ให้ Project ไหน ดูแล้วมันก็เหมือน Table ทั่วไปใช่ไหมครับ โดยตัว Table นี้เก็บข้อมูลเกี่ยวกับ Vendor ไหนมี Item อะไร Item อะไรถูกใช้กับ Project ไหน และ Project ไหนใช้งาน VENDOR เจ้าไหน ฟังแล้วมันก็เก็บข้อมูลทั่วๆไปใช่ไหมครับแต่ Table นี้มีความพิเศษครับ เรามาลองดูกันครับ

รายละเอียดของ Table

  1. Primary key : { VENDOR_ID , ITEM_ID , PROJECT_ID }
  2. Candidate key : { VENDOR_ID , ITEM_ID , PROJECT_ID }
  3. Nonkey attribute : ไม่มี
  4. FD : ไม่มี (ลองพิสูจน์ดูครับ)
  5. MVD : ไม่มี (ลองพิสูจน์ดูครับ)

ตรวจสอบว่าอยู่ใน 4NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม

Table เป็น 4NF แต่ยังมีปัญหา INSERT , DELETE , UPDATE

คุณอ่านไม่ผิดครับ Table นี้เป็น 4NF และยังมีปัญหา ปัญหา INSERT , DELETE , UPDATE อยู่ครับ (ใครบอก 3NF ไม่มีปัญหา นี่ 4NF แล้วยังมีปัญหาเลย) ลองมาดูปัญหากัน

ปัญหา Insert ข้อมูลไม่ได้

ถ้าผมอยาก Insert ข้อมูลว่า VENDOR : V2 ว่ามี ITEM : I2 ขาย เราจะทำการ INSERT ข้อมูลเข้าไปดังภาพ ซึ่งจะไม่สามารถทำได้เพราะติด Primary must not be null

ข้อมูลที่ต้องการ INSERT ลง TABLE : VIJ

ปัญหา Delete ข้อมูล

ถ้าผมอยากลบข้อมูลที่ VENDOR : V1 ขาย ITEM : I2 ซึ่งถ้าผมลบข้อมูลนี้ไปแล้ว ข้อมูลที่ว่า ITEM : I2 นั้นถูกใช้ใน PROJECT : 1 นั้นจะถูกลบไปด้วยครับ

5NF

A relational table structure T is in projection-join normal form (PJ/NF) or the fifth normal form (5NF) if and only if every join dependency in T is implied by the candidate keys of T.

แนะนำให้อ่านนิยามภาษาอังกฤษครับ หากอ่านไม่เข้าใจจริงๆค่อยมาอ่านที่ผลแปลไทย

Table จะเป็น 5NF ก็ต่อเมื่อทุก Join dependency ใน Table จะต้องมี candidate key ของ Table นั้น

ผมว่าอ่านแล้วน่าจะไม่เข้าใจเพราะติดศัพท์ Join dependency ดังนั้นเรามาทำความเข้าใจ Join dependency คืออะไร

Join dependency

เราทราบแล้วว่าตัว Table : CTX นั้นสามารถถูกแยกออกมาเป็น Table : CT , Table : CX ซึ่งการแยก Table ได้หรือไม่นั้นมาจากการแยก Table แล้วสามารถ Join กลับมาเป็นข้อมูลเดิมได้ ดังตัวอย่าง CTX ด้านล่างที่สามารถแยกและ Join กลับได้ข้อมูลดังเดิม

ตัวอย่างการแยก Table ที่สามารถ Join กลับได้

แต่ถ้าเราเปลี่ยนการแยก Table เป็น Table : CT กับ Table : TX นั้นจะไม่สามารถ JOIN กลับได้เหมือนเดิม

ตัวอย่างการแยก Table แล้วไม่สามารถ Join กลับได้

ดังนั้น Table : CTX นั้นมี Join dependency กับการแยก Table ด้วย ( COURSE , TEXT ) , (COURSE , TEACHER ) แต่ไม่มี Join dependency กับ ( COURSE , TEACHER ) ( TEACHER , TEXT )

โดยเวลาเราเขียนความสัมพันธ์แบบ Join dependency จะเขียนอยู่ในรูป

R(A,B,C) มีความสัมพันธ์แบบ JD * (AB , AC)

โดยถ้าจากตัวอย่างของเราจะเขียนอยู่ในรูป

CTX(COURSE , TEACHER , TEXT) มีความสัมพันธ์แบบ JD * ( (COURSE , TEACHER) , (COURSE , TEXT) )

JD ของ TABLE : VIJ

TABLE : VIJ มี Join dependency คือ

VIJ(VENDOR_ID , ITEM_ID , PROJECT_ID) มีความสัมพันธ์แบบ JD * ( (VENDOR_ID , ITEM_ID ) , ( ITEM_ID , PROJECT_ID ) , ( PROJECT_ID , VENDOR_ID ) )

ตัวอย่างการแยก TABLE : VIJ แล้วสามารถ JOIN กลับ

ตรวจสอบว่า TABLE : VIJ เป็น 5NF ไหม

  1. Primary key : { VENDOR_ID , ITEM_ID , PROJECT_ID }
  2. Candidate key : { VENDOR_ID , ITEM_ID , PROJECT_ID }
  3. JD : ( (VENDOR_ID , ITEM_ID ) , ( ITEM_ID , PROJECT_ID ) , ( PROJECT_ID , VENDOR_ID ) )

Table : VIJ ไม่เป็น 5NF เพราะ JD ไม่ได้มี Candidate key ในนั้น

วิธีแก้ไข

ทำการแยก Table ออกไปตาม JD ของ Table นั้น ซึ่งจากตัวอย่างเราจะแยก Table ออกเป็น 3 Table คือ

TABLE : VENDOR ITEM (VI)

TABLE :  VENDOR ITEM (VI)

รายละเอียด Table

  1. Primary key : { VENDOR_ID , ITEM_ID }
  2. Candidate key : { VENDOR_ID , ITEM_ID }
  3. Nonkey attribute : ไม่มี
  4. FD : ไม่มี (ลองพิสูจน์ดูครับ)
  5. MVD : ไม่มี (ลองพิสูจน์ดูครับ)
  6. JD : ไม่มี (ลองพิสูจน์ดูครับ)

ตรวจสอบว่าเป็น 5NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม
  • 5NF : เป็นเพราะ Table นี้ไม่มี JD ดังนั้นจึงไม่ต้องเช็คว่าทุก JD มี Candidate key อยู่ไหม

TABLE : ITEM_PROJECT (IJ)

TABLE :  ITEM_PROJECT (IJ)

รายละเอียด Table

  1. Primary key : { ITEM_ID , PROJECT_ID }
  2. Candidate key : { ITEM_ID , PROJECT_ID }
  3. Nonkey attribute : ไม่มี
  4. FD : ไม่มี (ลองพิสูจน์ดูครับ)
  5. MVD : ไม่มี (ลองพิสูจน์ดูครับ)
  6. JD : ไม่มี (ลองพิสูจน์ดูครับ)

ตรวจสอบว่าเป็น 5NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม
  • 5NF : เป็นเพราะ Table นี้ไม่มี JD ดังนั้นจึงไม่ต้องเช็คว่าทุก JD มี Candidate key อยู่ไหม

TABLE : PROJECT_VENDOR (JV)

TABLE : PROJECT_VENDOR (JV)

รายละเอียด Table

  1. Primary key : { PROJECT_ID , VENDOR_ID }
  2. Candidate key : { PROJECT_ID , VENDOR_ID }
  3. Nonkey attribute : ไม่มี
  4. FD : ไม่มี (ลองพิสูจน์ดูครับ)
  5. MVD : ไม่มี (ลองพิสูจน์ดูครับ)
  6. JD : ไม่มี (ลองพิสูจน์ดูครับ)

ตรวจสอบว่าเป็น 5NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table นี้ไม่มี Nonkey attribute และ ไม่มี FD ดังนั้นจึงผ่านเรื่อง Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี Nonkey attriubte จึงไม่มี FD ระหว่าง Nonkey attribute
  • BCNF : เป็นเพราะ Table นี้ไม่มี FD ดังน้ันจึงไม่ต้องเช็คว่า Determinant ของ FD ใน Table นี้ต้องเป็น Candidate key เท่านั้น
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม
  • 5NF : เป็นเพราะ Table นี้ไม่มี JD ดังนั้นจึงไม่ต้องเช็คว่าทุก JD มี Candidate key อยู่ไหม

ตรวจสอบว่ามีปัญหา INSERT UPDATE DELETE ไหม

ปัญหา INSERT ข้อมูลไม่ได้

ถ้าผมอยาก Insert ข้อมูลว่า VENDOR : V2 ว่ามี ITEM : I2 ขาย เราสามารถทำการ INSERT ข้อมูลลง Table : VENDOR_ITEM ได้เลย โดยไม่ติดปัญหา Primary key must not be null

ปัญหา DELETE ข้อมูล

ถ้าผมอยากลบข้อมูลที่ VENDOR : V1 ขาย ITEM : I2 ก็สามารถลบข้อมูลได้เลยไม่ติดอะไร

การทำ 5NF ต้องทำให้ Table เหลือแค่ 2 Column (Attribute)

เรื่องนี้เป็นความเข้าใจผิดว่าเวลาจะทำให้ Table ให้เป็น 5NF ได้นั้นต้องแยก Table ให้เหลือ 2 Column ซึ่งนั่นทำให้เกิดปัญหาเรื่อง Join ข้อมูลทำให้เกิดปัญหาเรื่อง performance แต่นี่เป็นความเข้าใจที่ผิดครับ หากคุณอ่านนิยามของ 5NF ดีๆไม่มีตรงไหนบอกว่าต้องมี Column (Attribute) เหลือแค่ 2 Column เรามาดูตัวอย่าง Table ที่เป็น 5NF และมีมากกว่า 2 Column ดูกันครับ (คุณเคยเห็น Table นี้แล้ว)

TABLE : USER

รายละเอียด Table

  1. Primary key : USERNAME

  2. Candidate key :

    • USERNAME
    • EMAIL
  3. Nonkey attribute :

    • USER_TYPE
  4. FD :

    • USERNAME -> EMAIL
    • EMAIL -> USERNAME
    • USERNAME -> USER_TYPE
    • EMAIL -> USER_TYPE
  5. MVD : ไม่มี (ลองพิสูจน์ดูครับ)

  6. JD

    • ( (USERNAME , EMAIL ) , ( USERNAME , USER_TYPE ) )

    • ( (USERNAME , EMAIL ) , ( EMAIL , USER_TYPE ) )

    • ( (EMAIL , USERNAME) , ( USERNAME , USER_TYPE) )

    • ( (EMAIL , USERNAME ) , ( EMAIL , USER_TYPE ) )

      จะเห็นว่า 1 Table สามารถมีได้หลาย JD นะครับ ลองแยก Table แล้วกลับไป JOIN ดูครับมัน JOIN กลับได้ข้อมูลเดิมทั้งหมด

ตรวจสอบว่าเป็น 5NF ไหม

  • 1NF : เป็นเพราะทุก Column (Attribute) เป็น Atomic และ แต่ละ Column (Attribute ) มาจาก Domain เดียว
  • 2NF : เป็นเพราะ Table Primary key กับ Nonkey Attribute เป็น Full FD
  • 3NF : เป็นเพราะ Table นี้ไม่มี FD ระหว่าง Nonkey attribute
  • BCNF: เป็นเพราะ Determinant ของ FD ใน Table นี้เป็น Candidate key ทั้งหมด
  • 4NF : เป็นเพราะ Table นี้เป็น BCNF และ Table นี้ไม่มี MVD ดังนั้นจึงไม่ต้องเช็คว่าทุก MVD เป็น FD ไหม
  • 5NF : เป็นเพราะ ทุก JD มี Candidate key อยู่ด้วยทั้งหมด (ลองดูครับจะเห็นว่ามี USERNAME , EMAIL เป็นส่วนประกอบในทุกๆ JD )

จากตัวอย่างนี้คุณจะเห็นว่า Table ที่มี 3 Column ก็เป็น 5NF ได้นะครับ (จริงๆจะกี่ Column ก็ได้ครับ ตราบเท่าที่มันตรงนิยาม)

อย่า Split ถ้าไม่จำเป็น

อย่า Split ถ้าไม่จำเป็น เป็นคำพูดที่อาจารย์สอนวิชา Database ผมพูดเสมอ เพราะคนส่วนใหญ่มักจะเข้าใจผิดว่าการทำ Normalization นั้นคือการพยายามแยก Table ให้เล็กที่สุด แบบที่บางคนเข้าใจกันว่า 5NF ต้องมี 2 Column (Attribute) ซึ่งการทำ Normalization นั้นมีเป้าหมายเพื่อจัดการให้ Table ไม่มีปัญหา INSERT , DELETE , UPDATE ซึ่งเมื่อเกิดปัญหาก็ต้องแก้โครงสร้างของ Table นั้นคือการแยกตาราง ดังนั้นอย่าเข้าใจผิดนะครับ เราทำเพื่อแก้ปัญหา ไม่ใช่ทำเพื่อแยก Table

สรุป

ตอนนี้เราได้รู้ว่า 4NF , 5NF คืออะไร วิธีการแก้ไขให้ Table เป็น 4NF , 5NF และเราได้ทำลายความเชื่อและความเข้าใจผิดๆที่ว่าการทำ Normalization ทำถึง BCNF (3NF) ก็พอผ่านการแสดงให้เห็นปัญหา INSERT DELETE UPDATE ที่ยังคงมีอยู่

สุดท้ายผมหวังว่าเนื้อหาเกี่ยวกับ Normalization ทั้ง 3 ตอนที่ผมเขียนไปคงมีประโยชน์ไม่มากก็น้อย และที่สำคัญผมแนะนำให้ท่านไปหาหนังสือเกี่ยวกับวิชา Database จริงๆมาอ่านเพราะหนังสือเหล่านั้นจะอธิบายได้ดีกว่าที่ผมอธิบายเยอะมากๆครับหรือถ้ามีโอกาสควรไปเรียนกับอาจารย์ที่สอนวิชา Database ดูครับ คุณจะได้เห็นตัวอย่างที่ทำให้เข้าใจมากกว่านี้ครับ และหากข้อมูลตรงจุดไหนไม่ถูกต้อง ไม่ครบถ้วน สามารถแจ้งผมได้เลยนะครับ เดี๋ยวจะไปแก้ให้ถูกต้องให้ครับ

ในตอนถัดไปเราจะมาดูวิธีการ Design Database ด้วยการใช้ ER Diagram กันครับ

REF

  • กระบวนการ Normalization Process และนิยามที่เป็นภาษาอังกฤษทั้งหมด และตัวอย่าง Table : CTX , Table : VIJ เอามาจาก หนังสือ Relational database systems : language, conceptual modeling and design for engineers