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

แนะนำการใช้งาน Docker แบบพื้นฐาน

Docker นั้นพัฒนาไปเร็วมาก
ทำให้มี ecosystem ขนาดใหญ่
แต่การศึกษาทั้งหมดเป็นเรื่องที่ไม่ง่าย และ ใช้เวลานานมาก
ดังนั้นใน blog นี้จะเน้นเรื่อง
การใช้งาน Docker command line

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

1. การจัดการ image

Image คือต้นฉบับสำหรับการสร้าง container ต่าง ๆ
โดยที่เราสามารถทำการจัดการได้ดังนี้ เลือกคำสั่งที่ใช้บ่อย ๆ

  • สร้าง image ใหม่ขึ้นมา กำหนดขั้นตอนการสร้างจาก Dockerfile
  • ทำการลบ image
  • ทำการดึง หรือ pull image มาจาก repository เช่น Docker Hub มายังเครื่องเราได้
  • ทำการ push image ไปยัง repository

เริ่มต้นจากการเลือก image จาก Docker Hub หรือจาก Official ของสิ่งที่เราต้องการ
ยกตัวอย่างเช่น Java ก็มีให้เลือกทั้ง OpenJDK และ Oracle Java
อีกทั้งยังมีจาก community อีกนะ
เลือกได้ตามรสนิยมกันไปเลย
ทั้ง OS เช่น Ubuntu, Alpine เป็นต้น
โดยเราจะเรียกว่า Base Image นั่นเอง
แสดงดังรูป

เมื่อได้ image ที่ต้องการแล้ว ก็นำมาใช้ได้เลย
สามารถทำการ download image ลงมาด้วยคำสั่ง

$docker image pull  <ชื่อ image>:<ชื่อ tag>

ยกตัวอย่างเช่นการใช้งาน image openjdk
ซึ่งมีให้เลือกมากมายทั้ง OS, JDK และ JRE
เราจะใช้งาน openjdk ด้วย tag 9-jdk ดังนี้

$docker image pull  openjdk:9-jdk

จากนั้นทำการดูว่า image ถูก download มายังเครื่องของเราหรือไม่
ด้วยคำสั่ง

$docker image ls

ถ้าต้องการลบ image ให้ใช้คำสั่ง

$docker image rm <ชื่อ image>

หรือถ้าต้องการลบ image ที่ไม่ถูกใช้งานเลย
สามารถใช้คำสั่งใหม่คือ prune ดังนี้

$docker image prune

นี่แค่เริ่มต้นคำสั่งจัดการ image เบื้องต้นเท่านั้น
แต่เพียงพอสำหรับผู้เริ่มต้น

เมื่อได้ image หรือ base image ที่ต้องการแล้ว

ถ้าเราไม่พอใจ สามารถทำการแก้ไขและสร้าง image ใหม่ขึ้นมา
เพื่อให้เหมาะสมกับระบบที่เราต้องการได้
สามารถกำหนดขั้นตอนการสร้าง image ผ่านไฟล์ Dockerfile

ตัวอย่างง่าย ๆ
ต้องการสร้าง image สำหรับการ compile และ run
โปรแกรมที่พัฒนาด้วยภาษา Java คือ javac และ java
มีขั้นตอนดังนี้

  • เลือก Base image ซึ่งจะใช้ openjdk:9-jdk
  • ทำการสร้าง working directory สำหรับจัดเก็บ source code
  • กำหนด endpoint ให้ทำการ run bash shell ขึ้นมา

สร้าง image จาก Dockerfile ด้วยคำสั่ง

$docker image build -t <ชื่อ image>:<ชื่อ tag> .

จากตัวอย่างถ้าต้องการสร้าง image ชื่อว่า hello_java ทำได้ดังนี้

$docker image build -t hello_java .

ส่วนการใช้งานต้องสร้าง container ขึ้นมาจาก image นี้ต่อไป
ยกตัวอย่างเช่น

$docker container run --name java_compile  -v $(pwd)/src:/my_project —rm  hello_java  javac Hello.java
$docker container run --name java_run  -v $(pwd)/src:/my_project —rm  hello_java  java Hello

ชีวิตน่าจะดีขึ้น
ลองคิดดูสิว่า ถ้าเราต้องการ run java มากกว่า 1 version
เราจะต้องทำอย่างไร ?

ตัวอย่างที่ซับซ้อนขึ้น
ต้องการสร้าง image สำหรับระบบงานที่พัฒนาด้วยภาษา Java
และต้องทำการ compile และ build ด้วย Apache Maven
ดังนั้นเราต้องกำหนดขั้นตอนการสร้าง image ของเราดังนี้

  • เลือก Base image ซึ่งจะใช้ openjdk:9-jdk
  • ทำการติดตั้ง package ต่าง ๆ ที่จำเป็นนั่นคือ Apache Maven และ Vim เอาไว้แก้ไข code
  • ทำการสร้าง working directory สำหรับจัดเก็บ source code
  • กำหนด endpoint ให้ทำการ run bash shell ขึ้นมา

สามารถเขียนใน Dockerfile ได้ดังนี้

ทำการสร้าง image ชื่อว่า my_image ใหม่จาก Dockerfile ด้วยคำสั่ง

$docker image build -t my_image  .

โดยการสร้าง image จะใช้เวลานานนิดหน่อย
ถ้าไม่ใสชื่อ tag เข้าไปจะมีค่า default เป็น latest นะ
เพียงเท่านี้เราก็ได้ image ใหม่มาใช้งานกันแล้ว

ในแต่ละบรรทัดของ Dockerfile นั่นหมายถึงจำนวน layer หรือชั้นของ image เราด้วย
สามารถดูได้ว่าแต่ละ image มี layer อะไรบ้างด้วยคำสั่ง

$docker image history <ชื่อ image>:<ชื่อ tag>

เมื่อเราได้ image ที่ต้องการแล้ว
สามารถนำ image นี้ไปเก็บยัง repository ของ Docker ได้
ไม่ว่าจะเป็น Docker hub หรือ registry ของเราเอง ด้วยคำสั่ง

$docker image push <ชื่อ image>:<ชื่อ tag>

มาถึงตรงนี้น่าจะพอทำให้เข้าใจเกี่ยวกับการจัดการ image ขั้นพื้นฐานบ้างแล้ว
แน่นอนว่ายังมีอีกเยอะนะ
แต่อยากขอแนะนำเพิ่มเติมเพื่อให้ศึกษาต่อคือ

2. การจัดการ container

หลังจากที่ได้ image ที่ต้องการแล้ว
สิ่งต่อมาที่จะต้องทำคือ การสร้าง container จาก image เหล่านั้น
เทียบง่าย ๆ คือ การสร้าง instance/object จาก class ใน OOP นั่นเอง
โดยทาง docker ได้เตรียม command line สำหรับการจัดการ container มาให้เพียบ
แต่เรามาใช้งานคำสั่งที่ใช้บ่อย ๆ กัน

เริ่มด้วยการสร้าง container จาก image

$docker container run<options> <ชื่อ image>  <command> <arguments>

จะเห็นได้ว่ามีให้ใส่ option ซึ่งมีเยอะมาก ๆ
ดังนั้นมาดูตัวอย่างกัน ใช้ image จากข้างต้น

  • สร้าง container ชื่อ demo01
  • สร้างจาก image ชื่อว่า my_image
  • map path จากเครื่องของเราหรือเรียกว่า host ไปยัง container
  • Run container แบบ foreground mode (แสดงว่ายังมี mode อื่นอีกเช่น interactive และ background เป็นต้น)
  • ลบ container ทิ้งทันทีหลังจากทำงานเสร็จสิ้น
  • Run คำสั่งของ Apache Maven เพื่อ compile และ build ระบบงาน

สร้าง container ด้วยคำสั่ง

$docker container run --name demo01  -v $(pwd)/src:/my_project --rm  my_image  mvn clean package

เพียงเท่านี้ก็สร้าง container ได้แล้ว
และทำงานตาม command ของ Apache Maven ที่ต้องการ run
มันง่ายและสะดวกมาก ๆ สำหรับนักพัฒนา

ลองคิดดูว่า
ถ้าทุกคนในทีมพัฒนาใช้งาน image เดียวกัน
ถ้าทุกคนในทีมพัฒนาทำการสร้าง container เหมือนกัน
นั่นคือทุกคนมีขั้นตอนการทำงานเหมือนกัน
ที่สำคัญถ้า image นี้เหมือนกับสิ่งที่จะ deploy บน production server
ก็น่าจะลดปัญหาต่าง ๆ ได้มากมาย จริงไหม ?

ถ้าต้องการดูว่ามี container อะไรบ้างก็ใช้คำสั่ง
ตัวอย่างดู container ทั้งหมด ทุกสถานะ

$docker container ls -a

ยังมีคำสั่งอื่น ๆ ที่ใช้จัดการ container ทั้ง stop/start/restart/rm

$docker container stop <ชื่อ container>
$docker container start <ชื่อ container>
$docker container restart <ชื่อ container>
$docker container rm <ชื่อ container>

ถ้าต้องการลบ container ที่ไม่ทำงานสามารถใช้คำสั่ง prune ได้

$docker container prune

ถ้าต้องการลบ container ทั้งหมด ทุกสถานะ
สามารถใช้คำสั่งนี้ได้เลย ใช้บ่อย

$docker container stop $(docker container ls -a -q)
$docker container rm $(docker container ls -a -q)

ยังไม่พอนะ ยังมีชุดคำสั่งสำหรับดู stat/log การทำงานของแต่ละ container ได้อีกด้วย

$docker container stats <ชื่อ container>
$docker container top <ชื่อ container>
$docker container logs <ชื่อ container>

เพียงเท่านี้ก็น่าจะเพียงพอสำหรับการเริ่มต้นจัดการ container นะ

3. การกำหนดจำนวน resource ต่าง ๆ ทั้ง CPU และ Memory ให้ container

$docker container update<options> container

สำหรับชาว Java นั้นเรื่องนี้สำคัญมาก ๆ
เนื่องจากถ้ากำหนดไม่ดี และไม่เข้าใจการทำงานของ JVM แล้ว
ก็อาจเกิดข้อผิดพลาดได้
ยกตัวอย่างเช่นการใช้ resource เกินจำนวนที่กำหนด
สามารถดูปัญหาและการแก้ไขได้จาก JVM is Docker-aware

คำสั่งสุดท้ายคือ การลบข้อมูลที่ไม่ใช้งานทิ้งไป

ลบทั้ง image, container, volume และ network
รวมไปถึง layer ของจากการสร้าง image อีกด้วย
ซึ่งทำให้เราได้พื้นที่ disk กลับคืนมาอย่างมากมาย

$docker system prune<options>

แต่คำสั่งนี้ไม่ลบพวก container volume นะ
ถ้าอยากจะลบต้องเพิ่ม option -volume เข้าไปด้วย

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

ยกตัวอย่างเรื่องที่ต้องศึกษาต่อเช่น

ถามว่าชุดคำสั่งเยอะไหม ?
ตอบเลยว่ามาก
ถามว่าต้องจำหมดไหม ?
ตอบเลยว่าไม่
ดังนั้นสิ่งที่สำคัญสุด ๆ ความเข้าใจ และ keyword ที่ต้องการ

ขอให้สนุกกับการ coding ครับ