มารู้จัก Docker กันเถอะ

Docker เครื่องมือสำหรับนักพัฒนา แบบครบจบในที่เดียว

มารู้จัก Docker กันเถอะ

ก่อนที่ Docker จะเข้ามามีบทบาท นักพัฒนาซอฟต์แวร์มักจะเจอกับปัญหาสุดคลาสสิกที่เรียกว่า "It works on my machine!" หรือ "มันก็ใช้ได้ดีนี่นาบนเครื่องของฉัน!" ปัญหานี้เกิดขึ้นบ่อยครั้งเมื่อนักพัฒนาเขียนโค้ดบนเครื่องของตัวเองแล้วทุกอย่างทำงานได้อย่างสมบูรณ์แบบ แต่เมื่อนำโค้ดนั้นไปรันบนเครื่องของคนอื่น ไม่ว่าจะเป็นเครื่องของเพื่อนร่วมทีม, เครื่องทดสอบ (staging server) หรือเครื่องเซิร์ฟเวอร์จริง (production server) กลับเกิดข้อผิดพลาดขึ้น

Docker meme

Docker คือ platform ที่ใช้เพื่อจำลอง environment แยกออกมาเป็น container เพื่อให้เราสามารถที่จะพัฒนาโปรแกรม และสร้างโปรแกรม โดยจะรวมทั้ง code , lib , run time และ os เพื่อให้เราจัดการโปรแกรมได้ดียิ่งขึ้น

จุดเริ่มต้นของปัญหา: "มันก็ใช้ได้ดีนี่นาบนเครื่องของฉัน!"

สาเหตุหลักของปัญหานี้คือความแตกต่างของ Environment ของอุปกรณ์ เช่น:

  • เวอร์ชันของไลบรารีและ Dependencies: โปรแกรมหนึ่งอาจต้องการ Python เวอร์ชัน 3.8 แต่บนเซิร์ฟเวอร์อาจเป็น 3.6 หรืออีกโปรแกรมอาจต้องการไลบรารีบางตัวในเวอร์ชันที่ต่างกัน ทำให้เกิดความขัดแย้ง
  • การตั้งค่าระบบปฏิบัติการ: ความแตกต่างในการตั้งค่า เช่น PATH environment variable, firewall rules หรือการกำหนดสิทธิ์การเข้าถึงไฟล์
  • โครงสร้างพื้นฐานที่ต่างกัน: การรันแอปพลิเคชันบนฮาร์ดแวร์หรือระบบปฏิบัติการที่ต่างกันก็อาจทำให้เกิดปัญหาได้

ปัญหานี้ทำให้กระบวนการพัฒนาซอฟต์แวร์ยุ่งยากและเสียเวลามาก นักพัฒนาต้องใช้เวลาไปกับการแก้ไขปัญหาที่เกิดจากความไม่เข้ากันของสภาพแวดล้อมมากกว่าการพัฒนาฟีเจอร์ใหม่ ๆ

เมื่อก่อนผมมักจะมีปัญหาลักษณะแบบนี้ค่อนข้างมาก โดยเฉพาะงานที่ต้องส่งให้ทีมอื่น ๆ คล้ายกับการส่งไฟล์ .rar เข้าไปที่ MacOs นั่นแหละครับ เราสามารถเห็นไฟล์ได้แต่ไม่สามารถทำอะไรตรง ๆ กับไฟล์นั้นได้ ต้องมาแก้ Config หรือลงแอพเพิ่มเติมจึงจะสามารถใช้งานได้


ก่อน Container: ยุคทองของ Virtual Machine (VM)

ก่อนที่เทคโนโลยี Container โดยเฉพาะอย่างยิ่ง Docker จะเข้ามาพลิกโฉมวงการในปี 2013 โลกของการพัฒนาและดูแลระบบซอฟต์แวร์นั้นพึ่งพา Virtual Machine (VM) เป็นหลัก ในยุคนั้น VM ถือเป็นนวัตกรรมที่เข้ามาช่วยแก้ปัญหาใหญ่ ๆ ที่องค์กรและนักพัฒนาต้องเผชิญในยุคที่ยังไม่มี VM

ปัญหาหลักในยุคก่อน VM

ก่อนการมาของ VM การรันแอปพลิเคชันบนเซิร์ฟเวอร์นั้นเต็มไปด้วยข้อจำกัดและปัญหามากมาย

  1. การใช้ทรัพยากรที่ไม่มีประสิทธิภาพ (Inefficient Resource Utilization)
    • เซิร์ฟเวอร์ (Physical Server) มักจะถูกติดตั้งเพียงแอปพลิเคชันเดียวหรือสองแอปพลิเคชันเท่านั้น เพื่อหลีกเลี่ยงความขัดแย้งและการใช้ทรัพยากรที่มากเกินไป
    • ส่งผลให้ CPU, RAM และ Storage ของเซิร์ฟเวอร์ส่วนใหญ่ถูกใช้งานต่ำกว่าศักยภาพที่แท้จริง (มักจะใช้แค่ 10-20% ของความสามารถทั้งหมด) ทำให้เป็นการลงทุนที่สิ้นเปลือง
    • การซื้อเซิร์ฟเวอร์ใหม่ทุกครั้งที่ต้องการรันแอปพลิเคชันใหม่เป็นเรื่องปกติ ซึ่งมีค่าใช้จ่ายสูงมากทั้งในส่วนของฮาร์ดแวร์ ค่าไฟ และค่าบำรุงรักษา
  2. ความซับซ้อนในการจัดการฮาร์ดแวร์ (Hardware Management Complexity)
    • การต้องจัดการและบำรุงรักษาเซิร์ฟเวอร์จำนวนมากเป็นภาระหนักสำหรับทีม IT
    • เมื่อฮาร์ดแวร์ล้มเหลว การกู้คืนระบบและแอปพลิเคชันทำได้ยากและใช้เวลานาน ทำให้เกิด Downtime
  3. ปัญหาความเข้ากันได้ของซอฟต์แวร์ (Software Compatibility Issues)
    • แอปพลิเคชันที่ต่างกันอาจต้องการระบบปฏิบัติการ (OS) เวอร์ชันที่ต่างกัน หรือไลบรารีที่ไม่เข้ากัน
    • การพยายามติดตั้งหลายแอปพลิเคชันบน OS เดียวกันมักนำไปสู่ความขัดแย้งที่แก้ได้ยาก ทำให้ระบบไม่เสถียร
  4. การ Deploy และทดสอบที่ยุ่งยาก (Difficult Deployment and Testing)
    • การสร้างสภาพแวดล้อมสำหรับพัฒนา ทดสอบ และ Production ให้เหมือนกันทำได้ยาก
    • ปัญหา "It works on my machine!" ที่กล่าวถึงก่อนหน้านี้ เกิดขึ้นบ่อยครั้งเนื่องจากความแตกต่างของสภาพแวดล้อม
    • การนำแอปพลิเคชันใหม่ขึ้นสู่ Production เป็นเรื่องที่ต้องวางแผนอย่างรอบคอบและมีความเสี่ยงสูง


การมาของ Virtual Machine (VM): จุดเปลี่ยนที่ยิ่งใหญ่

ในช่วงปลายทศวรรษ 1990 และต้นทศวรรษ 2000 เทคโนโลยี Virtual Machine (VM) ได้ถือกำเนิดขึ้นและกลายเป็นพระเอกในการแก้ปัญหาเหล่านี้ โดยมีบริษัทอย่าง VMware เป็นผู้บุกเบิกและเป็นผู้นำตลาด

แนวคิดหลักของ VM คือการจำลองฮาร์ดแวร์เสมือนขึ้นมาบนเซิร์ฟเวอร์เดียว ทำให้สามารถรันระบบปฏิบัติการได้หลายระบบพร้อมกันบนเซิร์ฟเวอร์เครื่องเดียวได้ VM ได้นำการเปลี่ยนแปลงที่สำคัญมาสู่โลก IT

  1. การใช้ทรัพยากรที่มีประสิทธิภาพมากขึ้น (Improved Resource Utilization):
    • ด้วย VM ทำให้เซิร์ฟเวอร์หนึ่งเครื่องสามารถรัน "เซิร์ฟเวอร์เสมือน" ได้หลายสิบหรือหลายร้อยเครื่อง
    • แต่ละ VM มี Guest OS ของตัวเอง ทำให้แอปพลิเคชันแต่ละตัวทำงานในสภาพแวดล้อมที่แยกจากกันโดยสมบูรณ์
    • ช่วยให้ใช้ทรัพยากร CPU และ RAM ของฮาร์ดแวร์จริงได้อย่างคุ้มค่ามากขึ้น จากที่เคยใช้แค่ 10-20% อาจเพิ่มเป็น 60-80%
  2. การรวมเซิร์ฟเวอร์ (Server Consolidation):
    • องค์กรสามารถลดจำนวนเซิร์ฟเวอร์ลงได้อย่างมหาศาล ซึ่งช่วยลดค่าใช้จ่ายในการจัดซื้อฮาร์ดแวร์ ค่าไฟ และพื้นที่ใน Data Center ได้อย่างมีนัยสำคัญ
  3. ความยืดหยุ่นและการแยกส่วน (Flexibility and Isolation):
    • VM แต่ละตัวมีระบบปฏิบัติการและทรัพยากรที่แยกจากกันอย่างสมบูรณ์ (isolated) ถ้า VM หนึ่งล่ม ก็ไม่ส่งผลกระทบต่อ VM อื่น ๆ บนเซิร์ฟเวอร์เดียวกัน
    • สามารถรัน OS ที่แตกต่างกัน (เช่น Windows Server, หลายๆ Linux Distro) บนเซิร์ฟเวอร์เครื่องเดียวได้
  4. ความง่ายในการจัดการและกู้คืน (Easier Management and Disaster Recovery):
    • สามารถสร้าง VM image ที่เป็นแม่แบบได้ ทำให้การสร้างเซิร์ฟเวอร์ใหม่รวดเร็วขึ้น
    • การสำรองข้อมูล (Backup) และกู้คืน (Recovery) VM ทำได้ง่ายกว่าการกู้คืนเซิร์ฟเวอร์ปกติ
    • ความสามารถในการย้าย VM ระหว่างเซิร์ฟเวอร์(Live Migration) โดยไม่ต้องหยุดการทำงานของแอปพลิเคชัน ทำให้การบำรุงรักษาฮาร์ดแวร์ทำได้ง่ายขึ้น

การทำงานของ VM ดังต่อไปนี้


การทำงานของ Virtual Machine มี Layer ดังนี้ (จากล่างขึ้นบน)

  1. Hardware (ฮาร์ดแวร์จริง): CPU, RAM, Storage, Network Card ที่จับต้องได้
  2. Host Operating System (Host OS - ไม่เสมอไป / สำหรับ Type 2 Hypervisor): บางครั้งอาจมีระบบปฏิบัติการหลัก (เช่น Windows, Linux) ติดตั้งอยู่บนฮาร์ดแวร์ก่อน แล้วค่อยติดตั้ง Hypervisor ทับลงไป (อันนี้เรียกว่า Type 2 Hypervisor)
  3. Hypervisor:
    • Type 1 (Bare-metal): ติดตั้งโดยตรงบนฮาร์ดแวร์ (เช่น VMware ESXi, Microsoft Hyper-V) ทำหน้าที่จัดสรรทรัพยากรฮาร์ดแวร์ให้ VM แต่ละตัวโดยตรง
    • Type 2 (Hosted): ติดตั้งบน Host OS อีกที (เช่น VirtualBox, VMware Workstation) ทำหน้าที่เหมือนโปรแกรมหนึ่งที่จำลองฮาร์ดแวร์ให้ VM
    • หน้าที่หลักของ Hypervisor: สร้างและบริหารจัดการทรัพยากรเสมือน (virtual CPU, virtual RAM, virtual disk) ให้กับ VM แต่ละตัว
  4. Guest Operating System (Guest OS): ระบบปฏิบัติการเต็มรูปแบบ (เช่น Windows, Ubuntu, CentOS) ที่ติดตั้งอยู่ภายใน VM แต่ละตัว มี Kernel และส่วนประกอบ OS ทั้งหมดแยกจากกัน
  5. Applications & Libraries: แอปพลิเคชันพร้อม Dependencies และไลบรารีต่าง ๆ ที่ทำงานอยู่บน Guest OS ของแต่ละ VM

สรุปการทำงาน VM (แบบสั้นๆ)

  • Hypervisor ทำหน้าที่ จำลองฮาร์ดแวร์ ขึ้นมา ทำให้สามารถติดตั้ง Guest OS ได้หลายตัวบนเซิร์ฟเวอร์เครื่องเดียว แต่ละ Guest OS มี OS ของตัวเองแยกกัน ทำให้กินทรัพยากรเยอะ และบูตช้า

ข้อจำกัดของ VM ที่นำไปสู่ Container

แม้ว่า VM จะเป็นนวัตกรรมที่ยอดเยี่ยมและแก้ปัญหาได้มากมาย แต่ก็ยังมีข้อจำกัดที่ทำให้เกิดแนวคิดของ Container ตามมาในภายหลัง

  1. การใช้ทรัพยากรยังไม่ถึงที่สุด: แม้จะดีกว่ายุคก่อน VM แต่ละ VM ยังคงต้องมี Guest OS ของตัวเอง ซึ่งกินทรัพยากร CPU และ RAM ไปส่วนหนึ่ง (Overhead) แม้ว่าแอปพลิเคชันภายในจะไม่ได้ใช้งานอะไรมากก็ตาม
  2. ขนาดไฟล์และ Boot Time: VM มีขนาดไฟล์ใหญ่ (หลาย GB) และใช้เวลาในการบูตระบบปฏิบัติการนาน (หลายนาที) ซึ่งไม่เหมาะกับการ Scale up/down อย่างรวดเร็ว หรือการพัฒนาที่ต้องสร้าง/ลบสภาพแวดล้อมบ่อย ๆ
  3. การจัดการ OS หลายตัว: การต้องแพตช์ (patch) และอัปเดต Guest OS ของ VM ทุกตัวเป็นภาระงานที่ต้องทำอย่างต่อเนื่องและใช้เวลา

Containerization: ทางออกของปัญหา

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

การเข้ามาของ Containerization engine

ในปี 2013 Docker ได้เปิดตัวขึ้นและนำแนวคิด Containerization มาทำให้ใช้งานง่ายและเข้าถึงได้จริงสำหรับนักพัฒนา ด้วยเทคโนโลยีนี้ Docker ได้เปลี่ยนวิธีการสร้าง, รัน, และจัดส่งแอปพลิเคชันไปโดยสิ้นเชิง

Docker Components

ก่อนจะรู้จักว่ามันทำงานยังไง จะต้องเข้าใจว่า Docker มีองค์ประกอบหลักๆ ดังนี้ครับ

  • Docker Image: คือไฟล์แม่แบบ (Template) ที่อ่านอย่างเดียว (Read-only) บรรจุโค้ดแอปพลิเคชัน, Runtime, Libraries และ Dependencies ทั้งหมดที่จำเป็นในการรันแอปพลิเคชันนั้นๆ เปรียบเสมือน "พิมพ์เขียว" หรือ "แพ็คเกจ" ของแอปพลิเคชันที่พร้อมใช้งาน
  • Docker Container: คือ Instance ที่ถูกรันขึ้นมาจาก Docker Image เปรียบเสมือน "แอปพลิเคชันที่กำลังทำงาน" ที่ถูกสร้างขึ้นจากพิมพ์เขียว (Image) สามารถเริ่มต้น, หยุด, ย้าย, หรือลบได้โดยไม่กระทบส่วนอื่น
  • Dockerfile: คือไฟล์ข้อความที่ใช้เขียนคำสั่ง (Instructions) เพื่อบอก Docker ว่าจะสร้าง Docker Image ได้อย่างไร เช่น ใช้ Base Image อะไร, ติดตั้ง Package อะไร, Copy ไฟล์ไหนเข้าไป, รันคำสั่งอะไรเมื่อ Container เริ่มต้น
  • Docker Hub: คือ Repository สาธารณะสำหรับจัดเก็บและแชร์ Docker Image เปรียบเสมือน "คลังเก็บ Image" ที่คุณสามารถดึง Image ที่คนอื่นสร้างไว้มาใช้ หรือ Push Image ของคุณขึ้นไปเก็บได้
  • Docker Compose: คือเครื่องมือที่ช่วยให้คุณสามารถกำหนดและรันแอปพลิเคชันที่มีหลาย Container ที่ทำงานร่วมกันได้ด้วยไฟล์ YAML เพียงไฟล์เดียว ทำให้การจัดการแอปพลิเคชันแบบ Multi-Container ง่ายขึ้นมาก
  • Docker Engine: คือ Core Component ที่ติดตั้งบน Host OS ทำหน้าที่สร้าง, รัน, และจัดการ Docker Container

องค์ประกอบเหล่านี้ทำงานร่วมกันเพื่อให้การพัฒนา, การทดสอบ, และการ Deploy แอปพลิเคชันเป็นไปอย่างมีประสิทธิภาพและสอดคล้องกันครับ

ผมอาจจะไม่ได้ลงรายเอียดสำหรับการใช้สคริปต์ Docker มากนักในบทความนี้ แต่ทุกคนสามารถไปอ่านวิธีการใช้งานเพิ่มเติมได้ตามตัวอย่างด้านล่างนี้ ในบางครั้งผมก็อาจจะค้น Google เพื่อพิมพ์บางคำสั่งด้วยซ้ำ สำหรับกรณีที่ต้องมีการ Custom หรือเขียน Docker Compose หลาย Containers

แนะนำโลกของ Docker | Mikelopster docs
docker-for-development

นอกจากนี้เรายังสามารถ Download Docker สำหรับคนที่ยังไม่คล่องการใช้ Terminal / Shells สามารถดาวน์โหลดได้ที่ https://www.docker.com/products/docker-desktop/

การทำงานของ Docker

การทำงานของ Docker มี Layer ดังนี้ (จากล่างขึ้นบน)

  1. Hardware (ฮาร์ดแวร์จริง): CPU, RAM, Storage, Network Card ที่จับต้องได้
  2. Host Operating System (Host OS): ระบบปฏิบัติการหลักที่ติดตั้งอยู่บนฮาร์ดแวร์ (เช่น Linux, Windows Server) Container จะใช้ Kernel ของ Host OS นี้ร่วมกัน
  3. Docker Engine (Container Runtime): เป็นซอฟต์แวร์ที่ติดตั้งอยู่บน Host OS ทำหน้าที่:
    • จัดการและรัน Container
    • สร้างและจัดการ Image
    • แยก Process ของแต่ละ Container ออกจากกัน
    • จัดสรรทรัพยากรให้แต่ละ Container
  4. Container:
    • Application & Libraries: เฉพาะแอปพลิเคชันและ Dependencies ที่จำเป็นเท่านั้น
    • ไม่มี Guest OS เต็มรูปแบบ: แต่จะใช้ Kernel ของ Host OS ร่วมกัน และมีเพียงส่วนของระบบไฟล์และไลบรารีที่จำเป็นสำหรับแอปพลิเคชันนั้นๆ

สรุปการทำงาน Docker (แบบสั้นๆ):

  • Docker Engine ทำหน้าที่รัน Container ซึ่งแชร์ Host OS Kernel ร่วมกัน ทำให้แต่ละ Container มีเพียงแอปพลิเคชันและ Dependencies ที่จำเป็น ไม่มี Guest OS เป็นของตัวเอง จึงมีขนาดเล็ก และบูตเร็วมาก

ทำไม Developer ถึงนิยมใช้ Docker?

Docker กลายเป็นเครื่องมือที่ได้รับความนิยมอย่างล้นหลามในหมู่นักพัฒนาด้วยเหตุผลหลัก ๆ ดังนี้

  1. ขจัดปัญหา "It works on my machine!": Docker ช่วยให้นักพัฒนาสามารถแพ็คเกจแอปพลิเคชัน, ไลบรารี, Dependencies และการตั้งค่าต่าง ๆ ทั้งหมดไว้ใน "Docker image" เพียงไฟล์เดียว Docker image นี้สามารถนำไปรันบนเครื่องใดก็ได้ที่มี Docker ติดตั้งอยู่ และรับประกันว่าแอปพลิเคชันจะทำงานในลักษณะเดียวกัน ไม่ว่าจะเป็นบนเครื่องของนักพัฒนาเอง, เครื่องทดสอบ, หรือเซิร์ฟเวอร์จริง ทำให้ปัญหาความไม่เข้ากันของสภาพแวดล้อมหมดไป
  2. ความสอดคล้องของสภาพแวดล้อม (Environmental Consistency): Docker สร้างสภาพแวดล้อมการทำงานที่แยกออกมาจากระบบปฏิบัติการหลัก (isolated environment) ทำให้แอปพลิเคชันแต่ละตัวทำงานในคอนเทนเนอร์ของตัวเองโดยไม่รบกวนซึ่งกันและกัน สภาพแวดล้อมนี้จะเหมือนกันทุกที่ ทำให้การทำงานร่วมกันเป็นทีมง่ายขึ้นมาก
  3. การปรับขนาดที่ง่าย (Scalability): เมื่อแอปพลิเคชันอยู่ในรูปแบบคอนเทนเนอร์ การเพิ่มจำนวนอินสแตนซ์ (instance) ของแอปพลิเคชันเพื่อรองรับผู้ใช้งานที่เพิ่มขึ้นก็เป็นเรื่องง่าย Docker ช่วยให้การปรับขนาดทำได้รวดเร็วและมีประสิทธิภาพ
  4. การติดตั้งและตั้งค่าที่รวดเร็ว (Fast Setup and Deployment): Docker ทำให้การติดตั้งสภาพแวดล้อมการพัฒนาใหม่ ๆ รวดเร็วขึ้นอย่างมาก นักพัฒนาไม่จำเป็นต้องเสียเวลาติดตั้งซอฟต์แวร์และ Dependencies ทีละตัวอีกต่อไป เพียงแค่ดึง Docker image ที่ต้องการมาและรันคอนเทนเนอร์ได้ทันที การ Deployment หรือการนำแอปพลิเคชันขึ้นสู่ Production ก็ทำได้ง่ายและรวดเร็วเช่นกัน
  5. การจัดการ Dependencies ที่มีประสิทธิภาพ (Efficient Dependency Management): Docker ช่วยให้การจัดการไลบรารีและ Dependencies ของแต่ละโปรเจกต์เป็นระเบียบ ไม่เกิดความขัดแย้งกันระหว่างโปรเจกต์ต่าง ๆ ที่อาจใช้ Dependencies คนละเวอร์ชัน
  6. ระบบนิเวศขนาดใหญ่ (Vibrant Ecosystem): Docker มีชุมชนผู้ใช้งานขนาดใหญ่ มี Docker Hub ที่เป็นเหมือนคลังเก็บ Docker image สำหรับแอปพลิเคชันและบริการต่าง ๆ ที่พร้อมให้ใช้งานได้ทันที ทำให้นักพัฒนาสามารถนำ image เหล่านี้มาใช้หรือปรับแต่งได้อย่างรวดเร็ว

ข้อจำกัดของ Docker

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

  1. การพึ่งพา Linux Kernel:
    • Docker ถูกออกแบบมาให้ใช้ Linux Kernel ร่วมกับ Host OS นั่นหมายความว่า หากรัน Docker บน Host OS ที่เป็น Linux, Container ที่รันอยู่ข้างในก็จะใช้ Linux Kernel ของ Host OS นั้น
    • ข้อจำกัดคือ: ไม่สามารถรัน Container ที่มีระบบปฏิบัติการ Guest OS เป็น Windows บน Host OS ที่เป็น Linux ได้โดยตรง (หรือกลับกัน) ถ้าต้องการรัน Container Windows ต้องรัน Docker บน Windows Server หรือบน VM ที่เป็น Windows
    • ในทางปฏิบัติ หากใช้ Docker บน Windows หรือ macOS (ผ่าน Docker Desktop) ตัว Docker Engine จะรันอยู่ภายใน Linux VM ขนาดเล็กที่สร้างขึ้นมาโดยอัตโนมัติ เพื่อให้สามารถใช้งานฟีเจอร์ของ Linux Kernel ได้
  2. ความปลอดภัยในการแยกส่วน (Isolation Security):
    • ถึงแม้ Docker Container จะมีการแยกส่วนที่ดีในระดับ Process และ Filesystem แต่ก็ยัง แชร์ Linux Kernel เดียวกันกับ Host OS
    • นี่หมายความว่า หากมีช่องโหว่ร้ายแรงใน Linux Kernel หรือมีการโจมตีที่สามารถ "หลุดออกมาจาก Container" (Container Escape) ได้ ก็อาจส่งผลกระทบต่อ Container อื่น ๆ หรือแม้กระทั่ง Host OS ได้
    • เมื่อเทียบกับ VM ที่มีการแยกส่วนระดับ Hardware (ผ่าน Hypervisor) ทำให้ VM มีความปลอดภัยในการแยกส่วนที่สูงกว่ามาก เหมาะสำหรับแอปพลิเคชันที่ต้องการ Security Isolation ระดับสูงสุด
  3. การจัดการ State (State Management):
    • โดยธรรมชาติ Container ถูกออกแบบมาให้เป็นแบบ Stateless (ไม่มีสถานะถาวร) คือข้อมูลภายใน Container จะหายไปเมื่อ Container ถูกลบหรือหยุดทำงาน
    • การจัดการข้อมูลถาวร (Persistent Data) เช่น ฐานข้อมูล หรือไฟล์ที่ต้องเก็บรักษาไว้ จำเป็นต้องใช้วิธีการที่ซับซ้อนขึ้น เช่น
      • Volumes: การ Mount พื้นที่จัดเก็บข้อมูลจาก Host OS เข้าไปใน Container
      • Bind Mounts: การผูก Directory ของ Host OS เข้ากับ Directory ใน Container
      • การใช้บริการ External Storage หรือ Network Storage
    • การจัดการข้อมูลเหล่านี้อาจเพิ่มความซับซ้อนในการ Deploy และการกู้คืนระบบ
  4. ความซับซ้อนในการจัดการ Container จำนวนมาก (Orchestration Complexity):
    • สำหรับแอปพลิเคชันขนาดเล็ก หรือการพัฒนาบนเครื่องเดียว Docker มีความเรียบง่ายและใช้งานง่าย
    • แต่เมื่อแอปพลิเคชันเติบโตขึ้น มีหลาย Microservices หรือต้องรัน Container จำนวนมากบนหลาย ๆ เซิร์ฟเวอร์ (Clustering) การจัดการด้วย Docker เพียงอย่างเดียวจะทำได้ยากและใช้เวลานาน
    • นี่คือเหตุผลที่ต้องใช้เครื่องมือ Container Orchestration อย่าง Kubernetes (K8s) เข้ามาช่วยจัดการเรื่องการ Deploy, Scaling, Network, Load Balancing และ Self-healing ซึ่งเป็นการเพิ่ม Layer ของความซับซ้อนในการเรียนรู้และการจัดการ
  5. ประสิทธิภาพ I/O บนบางแพลตฟอร์ม (I/O Performance on some Platforms):
    • ในบางกรณี การใช้งาน Docker Volumes หรือ Bind Mounts บนระบบปฏิบัติการบางตัว โดยเฉพาะอย่างยิ่ง Docker Desktop บน macOS หรือ Windows (ซึ่งรันผ่าน Linux VM อีกชั้นหนึ่ง) อาจมีปัญหาเรื่องประสิทธิภาพ I/O (การอ่าน/เขียนข้อมูล) ที่ช้ากว่าการรันบน Linux Host OS โดยตรง
    • ปัญหานี้มักจะเห็นได้ชัดเจนสำหรับแอปพลิเคชันที่ต้องมีการเข้าถึงไฟล์จำนวนมาก หรือมีการเขียนข้อมูลลงดิสก์บ่อยๆ
  6. ขนาดของ Image ที่อาจจะยังใหญ่เกินไป:
    • แม้ว่า Container Image จะเล็กกว่า VM Image มาก แต่บางครั้ง Image ที่สร้างขึ้นมาก็อาจจะยังมีขนาดใหญ่กว่าที่ควรจะเป็น หากนักพัฒนาไม่ได้ระมัดระวังในการเลือก Base Image ที่เหมาะสม หรือไม่ได้ลบ Dependencies ที่ไม่จำเป็นออกไป
    • Image ที่มีขนาดใหญ่จะส่งผลต่อเวลาในการดาวน์โหลดและ Deploy
  7. การ Debugging ที่อาจซับซ้อนขึ้น:
    • การ Debugging แอปพลิเคชันภายใน Container อาจซับซ้อนกว่าการ Debugging บน Host OS โดยตรง เนื่องจากแอปพลิเคชันถูกห่อหุ้มอยู่ในสภาพแวดล้อมที่แยกออกมา
    • ต้องมีความเข้าใจในการใช้คำสั่ง docker exec หรือการแนบ Debugger เข้ากับ Process ภายใน Container

แม้จะมีข้อจำกัดเหล่านี้ แต่ Docker ก็ยังคงเป็นเครื่องมือที่ทรงพลังและเป็นมาตรฐานอุตสาหกรรมสำหรับการสร้างและรัน Container และเมื่อทำงานร่วมกับ Orchestration Tools อย่าง Kubernetes ข้อจำกัดหลายอย่างก็จะถูกลดทอนลงไป ทำให้ Docker ยังคงเป็นตัวเลือกที่ดีที่สุดสำหรับ Use Case ส่วนใหญ่ในปัจจุบัน

ความแตกต่างของ VM vs Docker

Docker กับ Virtual Machine (VM) เป็นสองเทคโนโลยีที่ใช้สำหรับการรันแอปพลิเคชันแบบแยกส่วน (isolated environments) แต่มีสถาปัตยกรรมและวิธีการทำงานที่แตกต่างกันอย่างมาก ซึ่งส่งผลต่อประสิทธิภาพ, การจัดการทรัพยากร, และความยืดหยุ่นในการใช้งาน

Architecture Virtual Machine vs Containers

ผมลองเปรียบเทียบคุณสมบัติพื้นฐานของ VM , Docker เรื่องความเหมาะสมด้านการใช้งานไว้แล้วตามนี้ครับ

คุณสมบัติ Virtual Machine (VM) Docker (Container)
สถาปัตยกรรม Guest OS เต็มรูปแบบ + Hypervisor แชร์ Host OS Kernel + Docker Engine
ขนาด ใหญ่ (GB) เล็ก (MB)
การใช้ทรัพยากร สิ้นเปลือง (สำรองทรัพยากรให้ Guest OS) ประหยัด (ใช้เท่าที่จำเป็น, แชร์ Kernel)
Boot Time นาน (นาที) เร็ว (วินาที/มิลลิวินาที)
Isolation สูงมาก (Hardware-level isolation) ปานกลาง (Process-level isolation)
Portability ปานกลาง (ต้องเข้ากันกับ Hypervisor) สูงมาก ("Build once, Run anywhere")
การจัดการ ซับซ้อนกว่า (ต้องจัดการหลาย OS) ง่ายกว่า (จัดการแอปพลิเคชันและ Dependencies)
ความเหมาะสม ต้องการรันหลาย OS, Isolation สูง, จำลองเซิร์ฟเวอร์ การพัฒนา/Deployment แอปพลิเคชัน, Microservices, CI/CD


Orchestrate Container

สำหรับงานของ Docker ถ้าเราใช้สำหรับโปรเจกต์เล็ก ๆ โดยมีขั้นตอนไม่ซับซ้อน และไม่ได้มีงานหลายๆ ส่วนอยู่ ก็เพียงพอต่อการใช้งานทั่วไปครับ แต่สำหรับโปรเจกต์ใหญ่ เรามันจะต้องใช้ Kubernetes หรือที่คนนิยมเรียกกันว่า K8s ที่ถูกออกแบบมาเพื่อจัดการ Container โดยเฉพาะ

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

ส่วน K8s จะเปรียบเสมือนท่าเรือ หรือ ศูนย์กลางการจัดการตู้บรรทุกสินค้าขนาดใหญ่ ซึ่งมีหน้าที่ต่อไปนี้

  • จัดเรียงและจัดวาง: K8s ตัดสินใจว่าจะนำ Container (หรือกลุ่มของ Container ที่เรียกว่า Pod ใน K8s) ไปรันบนเซิร์ฟเวอร์ (Node) เครื่องไหนใน Cluster เพื่อให้ใช้ทรัพยากรอย่างมีประสิทธิภาพ
  • ควบคุมการเข้าออก: K8s จัดการเรื่อง Network เพื่อให้ Container สามารถสื่อสารกันเองได้ และให้ผู้ใช้งานภายนอกเข้าถึงแอปพลิเคชันใน Container ได้
  • ดูแลความเรียบร้อย: K8s ตรวจสอบสถานะของ Container อย่างต่อเนื่อง หาก Container ล่มหรือไม่ตอบสนอง K8s จะทำการสร้าง Container ใหม่ขึ้นมาแทนโดยอัตโนมัติ (Self-healing)
  • ปรับขนาด (Scaling): เมื่อมีผู้ใช้งานเพิ่มขึ้น K8s สามารถเพิ่มจำนวน Container ของแอปพลิเคชันนั้นโดยอัตโนมัติ และลดจำนวนลงเมื่อความต้องการลดลง เพื่อประหยัดทรัพยากร
  • อัปเดตและ Rollback: K8s ช่วยให้การ Deployment เวอร์ชั่นใหม่ของแอปพลิเคชันเป็นไปอย่างราบรื่นโดยไม่กระทบการทำงาน (Zero-downtime deployment) และสามารถ Rollback กลับไปเวอร์ชั่นเก่าได้ง่ายหากเกิดปัญหา

เราสามารถใช้ Docker สร้าง Container ได้ และเมื่อเราต้องการนำแอพไปใช้กับหลาย ๆ ส่วนที่มีผู้ใช้งานระบบเป็นจำนวนมาก มีส่วนประกอบหลายชิ้น และมีเงื่อนไขว่าการทำงานต้องสม่ำเสมอ เราจะใช้ Kubernetes ในการเข้ามาช่วยจัดการ Container เหล่านั้นให้มีประสิทธิภาพ

ปัจจุบันเรามีหลายทางเลือกในการใช้งานมากขึ้น ล่าสุดผมเพิ่งดูรีวิวเกี่ยว Apple container Framework จากงาน WWDC25 แล้วค่อนข้างมีความน่าสนใจ นอกจาก framework นี้จะช่วยในด้านประสิทธิภาพแล้ว ยังมีเรื่องระบบความปลอดภัยในการ mount directory เพื่อป้องกันการโจมตีในกรณีที่เรามีความผิดพลาดจาก container ตัวใดตัวนึง จะเป็นการยากที่ส่งผลกระทบต่อระบบทั้งหมดครับ

วิดีโออธิบาย Apple Container Framework

การใช้ Docker แม้ว่าจะต้องใช้การเรียนรู้ที่สูงในช่วงแรก แต่ก็คุ้มค่าที่จะเรียนรู้ นอกจากนี้แล้วเรายังสามารถใช้ AI ในการช่วยเขียนไว้ Yaml ของ Docker compose และจัดการ key ต่าง ๆ ให้อยู่ในมาตรฐานของความปลอดภัยได้ดี