Docker: แนะนำการใช้งาน

Somprasong Damyos
9 min readFeb 13, 2017

--

Docker คืออะไร

Docker มันคืออะไร ทำไมคนรอบๆ ตัว หลายถึงเริ่มใช้มันเยอะขึ้น ซึ่งจริงๆ แล้ว Docker มันก็คือ Virtualization อย่างนึงที่รันอยู่บน LXC ที่อยู่บน Linux โดยมันจะสร้าง Containers เพื่อจำลองสภาพแวดล้อมการทำงานอยู่บนระดับ OS เลย ซึ่งจะต่างกับ VMs ที่ต้องจำลอง Guest OS โดยใช้ Hypervisor (VMware, Virtual Box) ขึ้นมาก่อน

https://www.docker.com/what-docker

ข้อดีของ Docker เมื่อเทียบกับ VMs

1. Docker ไม่ต้องเสียเวลาในสร้าง OS ใหม่ และการ config แต่ละ OS เลย
2. Docker เบาและเร็วกว่ามาก ไม่ว่าจะเป็น start stop และ restart เพราะมันใช้ OS, CPU และ RAM ร่วมกันกับ Host OS
3. Docker สามารถรัน container ได้มากกว่า VMs ในเครื่องที่มีทรัพยากรที่เท่ากัน
4. Docker มีระบบ Registry ทำให้สามารถเคลื่อนย้าย หรือติดตั้ง Container ได้สะดวก และรวดเร็วกว่ามาก
5. Containers มันรันอยู่บน Docker Engine ทำให้ไม่ต้องสนใจว่า Infrastructure หรือ Host OS ว่าจะเป็นอะไรยังไง ทำให้หมดปัญหาว่าเครื่อง Dev รันได้ แต่เครื่อง Production มันรันไม่ได้บ้าง หรือเครื่อง Dev แต่ละคนติดตั้งเครื่องมือคนละเวอร์ชันกัน เราก็ build container เป็น image แล้วส่งในคนในทีมใช้ ก็หมดปัญหาแล้ว

Docker Client-Server Architecture

Docker นั่นใช้ client-server architecture โดย Docker client จะติดต่อกับ Docker server เพื่อสั่งให้ build, run และ distribute Docker containers ของเรา ผ่าน REST API บน UNIX sockets หรือ network interface

https://docs.docker.com/engine/understanding-docker/

Docker Client

Docker Client นั้นเป็น user interface ไว้รับ commands และ configuration flags จากผู้ใช้งานและส่งไปยัง Docker Daemon ซึ่งมี client อยู่ 2 แบบ คือ CLI และ Kitematic

Docker CLI & Kitematic

Docker Daemon

Docker Daemon (หรือเรียกว่า Docker Engine/Docker Server ก็ได้)เป็น persistence process ที่เอาไว้ building, running และ distributing Docker containers โดยจะได้รับคำสั่งมาจาก Docker client

ซึ่ง Docker Client และ Docker Daemon สามารถรันอยู่ Docker Host เดียวกันได้ หรือจะใช้ Docker Client ติดต่อผ่าน remote Docker Daemon ก็ได้ แต่ที่สำคัญเลยคือ Docker Daemon ต้องรันอยู่บน Linux เท่านั้น ดังนั้นบน Windows และ Mac OS X Docker Daemon จะรันอยู่ Docker Machine ซึ่ง Linux VM ตัวเล็กๆ ที่ทำขึ้นมาเพื่อรัน Docker Daemon โดยเฉพาะ

การติดตั้ง Docker

Docker สามารถติดตั้งได้ทั้ง Linux, Mac (OS X 10.11 or newer macOS)และ Windows (Windows 10 Pro 64bit) ซึ่งสำหรับ Mac และ Windows รุ่นต่ำกว่านั้นต้องติดตั้งโดยใช้ Docker Toolbox แทน

โดยในบทความนี้ ได้เลือกติดตั้งแบบ Docker Toolbox เนื่องจาก Docker for Windows นั้นต้องเปิดใช้งาน Microsoft Hyper-V ซึ่งจะทำให้ไม่สามารถใช้งานโปรแกรม VirtualBox และ VMWare ได้

Docker Toolbox จะประกอบด้วย

  • Docker CLI client ไว้สำหรับรันคำสั่ง Docker Engine (docker) เพื่อสร้าง images และ containers
  • Docker Machine (docker-machine) เพื่อสร้าง Linux VM ตัวเล็กๆ ที่มี Docker Engine รันอยู่
  • Docker Compose สำหรับรันคำสั่ง docker-compose
  • Kitematic ที่เป็น Docker GUI
  • Docker QuickStart shell ที่มีการทำ preconfigured สำหรับ Docker command-line environment ไว้แล้ว
  • Oracle VM VirtualBox

Docker Concepts

ก่อนจะไปลองเล่น Docker กัน เรามาทำความรู้จัก concepts ของ Docker กันก่อนดีกว่า

Images

  • images จะเป็น read only templates ที่เอาไว้สร้าง containers
  • images จะสร้างโดยใช้คำสั่ง docker build ซึ่งใครๆ ก็สามารถสร้างได้
  • images นึงๆ จะประกอบด้วยชั้น (layer) ของ images อื่นๆ
  • images จะถูกเก็บไว้ที่ Docker registry

Containers

  • ถ้าเปรียบ images เป็น class แล้ว containers ก็จะเป็น instance ของ class นั้นๆ (runtime object)
  • containers จะถูกสร้างมาจาก images ซึ่งภายใน container จะมี binaries และ dependencies ทั้งหมดที่จำเป็นในการรันโปรแกรมที่ต้องการ

Registries and Repositories

  • registry คือที่ที่เอาไว้เก็บ images
  • เราสามารถสร้าง registry เองก็ได้ หรือจะใช้ public registry ของ Docker ที่ชื่อ DockerHub ก็ได้
  • ภายใน registry จะมี repositories ที่เอาไว้จัดเก็บ images แต่ละตัว
  • Docker repository แต่ละอันคือที่รวบรวม images ตัวเดียวกัน แต่ต่างเวอร์ชัน แยกโดยใช้ tags

ลองสร้าง Hello World Docker Container

เริ่มจากการสร้าง container ที่ทำงานง่ายๆ โดยการสั่งพิมพ์คำว่า Hello World ออกมาทางหน้าจอ โดยจะใช้ image ที่ชื่อ busybox

  • ให้เปิดเบราว์เซอร์ไปที่ https://hub.docker.com/ และค้นหา busybox
  • เลือก repository ของ busybox ที่เป็น offical ข้างในจะแสดงรายละเอียดวิธีการใช้งาน และแทบ tags แสดงเลขเวอร์ชัน ณ ตอนนี้เวอร์ชันล่าสุด คือ 1.26.2
  • เปิด Docker Quickstart Terminal (ถ้าใช้ Linux, Docker for Mac หรือ Docker for Windows สามารถเปิดใช้งานได้จาก Terminal ปกติได้เลย)
  • พิมพ์คำสั่ง docker images เพื่อดูรายการ images ทั้งหมดที่เรามี จะพบว่าตอนนี้ยังไม่ images ใดๆ
  • พิมพ์คำสั่ง docker run busybox:1.26.2 echo "hello world"คำสั่งนี้เอาไว้สร้าง container จาก image ชื่อ busybox ที่ระบุ tag เป็น 1.26.2 (แต่ถ้าไม่ระบุ tag จะใส่ tag เป็น latest เสมอ) ซึ่งถ้าเรายังไม่มี image ตัวนี้ Docker จะทำการ pull มาจาก registry ให้ ตามด้วยคำสั่งที่จะใช้งานคือให้พิมพ์คำว่า hello world ออกมา รูปแบบคำสั่ง docker run repository:tag command [arguments] รายละเอียดเพิ่ม ที่นี่
  • ลองพิมพ์คำสั่ง docker images อีกครั้ง คราวนี้จะมี image ของ busybox ขึ้นมาแล้ว
  • ลองพิมพ์คำสั่ง docker run busybox:1.26.2 echo "hello world" อีกครั้ง คราวนี้จะแสดงคำว่า “hello world” ขึ้นมาเลยเนื่องจากเรามี image busybox:1.26.2 แล้วไม่ต้องไป pull มาใหม่
  • การรัน Docker Container ใน interactive mode ทำได้โดยการใช้ -i flag และใช้ -t เพื่อสร้าง pseudo-TTY เพื่อแสดง input และ output ตัวอย่าง docker run -i -t busybox:1.26.2 และลองสร้างไฟล์ a.txt ถ้าต้องการออกจากโหมดนี้ใช้คำสั่ง exit Docker จะ shutdown container นี้ไป
  • ถ้าลองรัน docker run -i -t busybox:1.26.2 อีกรอบ และ ls ดูจะพบว่าไม่มีไฟล์ a.txt เนื่องจากทุกครั้งที่ใช้ docker run จะเป็นการสร้าง container ใหม่เสมอ

ลงลึกการใช้งาน Docker Container

รัน container ใน detached mode
การใช้คำสั่ง docker ps
การกำหนดชื่อ container
การใช้คำสั่ง docker inspect
การใช้งาน port
การใช้คำสั่ง docker logs

Detached Mode

ความแตกต่างระหว่างการรัน container แบบ foreground (default mode) กับแบบ background หรือ detached mode (-d option) คือ เมื่อรันแบบ foreground หน้าจอ console จะแสดง process ของ container ตัวนั้นๆ อยู่ ไว้รับ input แสดง output รวมถึง standard error ดังนั้นจะใช้ console พิมพ์คำสั่งอื่นๆ ต่อไม่ได้ ซึ่งจะต่างจากแบบ background ซึ่งเมื่อรัน container แล้วจะแสดง container id ออกมาทางหน้าจอ console และสามารถพิมพ์คำสั่งอื่นได้ ส่วน process จะรันอยู่ใน background จนกว่าจะสั่งให้ container นั้นหยุดทำงาน

ถ้าใช้ -d option หน้า console จะสามารถรับคำสั่งอื่นได้ ต่างจาก default ที่ต้องรอจนกว่า container จะหยุดทำงาน

การใช้งานคำสั่ง docker ps

คำสั่ง docker ps เอาไว้แสดงรายการ container ที่กำลังทำงานอยู่ทั้งหมด

แสดง container id, ชื่อ image, คำสั่ง, เวลาที่เริ่มทำงาน, สถานะ, port (ถ้ามี) และชื่อของ container (จะ auto gen มาให้)

ซึ่งถ้าต้องการให้แสดง container ทั้งหมด รวม container ที่หยุดการทำงานไปแล้วด้วยต้องใช้ -a option docker ps -a

กรณีที่ต้องการสร้าง container และต้องการให้ลบทิ้งเมื่อทำงานเสร็จให้ใช้ --rm option ตัวอย่าง docker run --rm busybox:1.26.2 sleep 1

จะเห็นลอง docker ps -a แล้วไม่มี container id ที่รันคำสั่ง sleep 1 เลย

การกำหนดชื่อให้กับ Container

เนื่องจากการสร้าง container แบบปกติ Docker จะทำการระบุชื่อของ container ให้เองแบบไม่ซ้ำกัน แต่เราสามารถระบุชื่อเองได้โดยใช้ --name option ตัวอย่างdocker run --name hello_world busybox:1.26.2 echo "hello world"

จะได้ container ชื่อ hello_world ตามที่กำหนด

การดูรายละเอียดภายใน Container

ทำได้โดยใช้คำสั่ง docker inspect id(container/image) เพื่อแสดงข้อมูล low level ของ container หรือ image

สามารถใช้ 4 หลักแรก ของ container id นั้นๆ แทนได้

การทำ Docker Port Mapping

โดยปกติ ports ที่ถูกเปิดไว้ภายใน container นั้นจะไม่สามารถเข้าใช้งานได้จาก Docker Host ได้ ซึ่งการที่จะเข้าใช้งานได้นั้นต้องทำ publish port ที่ต้องการก่อน ตั้งแต่ขั้นตอนการสร้าง container ขึ้นมา (docker run) ซึ่งสามารถทำได้ 2 วิธี
1. ใช้ -p host_port:container_port เพื่อผูก port ของ container เข้ากับ host
2. ใช้ -P ถ้าต้องการผูกทุก ports ของ container ที่ expose* ไว้ให้กับ host โดยฝั่ง host จะเป็นการ random port
*expose คือ การเปิด port ใน container ใช้ option --expose โดยสามารถระบุเป็น port หรือ เป็นช่วงของ port ก็ได้

ทดลองสร้าง container ของ Tomcat 8.0 ซึ่งมี port เป็น 8080 และต้องการผูกกับ host port ที่ 8888 โดยใช้คำสั่งนี้ docker run -it --rm -p 8888:8080 tomcat:8.0 ทดสอบเข้า http://host-ip:8888 จากเบราว์เซอร์ ถ้าใช้ Docker for Linux/Mac/Windows ใช้ http://localhost:8888

Docker logs

ในกรณีที่รัน container ใน detached mode เราสามารถดู log ของ container ตัวนั้นๆ ได้จาก docker logs ซึ่งมีรูปแบบคำสั่ง คือdocker logs [options] container_id

ถ้าต้องการติดตาม log ให้ใช้ option -f หน้า console จะแสดง log แบบ realtime

Docker Images

Docker Image Layers
สร้าง Docker Image จากคำสั่ง docker commit
สร้าง Docker Image ด้วย Dockerfile
Dockerfile In-depth
วิธีเอา Docker Image ขึ้น Docker Hub

Docker Image Layers

Docker Image แต่ละตัวจริงๆ แล้วมันจะเป็นการอ้างอิงถึงรายการของ read-only layers ซึ่ง layers ที่ว่านี่ก็คือ stack image layer ที่ซ้อนต่อกันจาก base image

จากรูป image ตัวนี้ประกอบด้วย 3 stacked image layers

วิธีการดูว่า Docker Image ที่เราใช้นั้นประกอบด้วย image อะไรบ้าง ดูได้จากคำสั่ง docker history ตัวอย่าง docker history busybox:1.26.2

busybox:1.26.2 จะมี 2 layers คือ base image ที่ add file และ image ที่ 2 สั่งรัน bash

โดยเมื่อสั่งสร้าง container แล้ว มันจะสร้าง Thin R/W layer หรือเรียกว่า container layer ขึ้นมาไว้ข้างบน โดยสามารถมีได้หลายตัวต่อ 1 image ซึ่งการเปลี่ยนทั้งหมดเช่น เขียนไฟล์ใหม่ แก้ไขไฟล์เดิม หรือลบไฟล์ทิ้ง จะเกิดขึ้นที่ container layer ของแต่ละตัว ซึ่งถ้าลบ container ทิ้งไป container layer ก็จะถูกลบไปด้วย โดยไม่ไปแก้ไขอะไรที่ image เลย

การสร้าง Docker Image

มี 2 วิธีในการสร้าง Docker Image
1. commit สิ่งที่เราแก้ไขทั้งหมดใน Docker Container
2. สร้าง Dockerfile

สร้าง Docker Images จากคำสั่ง docker commit

ตัวอย่างถ้าต้องสร้าง image ของ debian ที่มี git client ติดตั้งมาให้พร้อมใช้งาน ทำได้โดย

  • รัน container ของ debian ตัวที่เราจะใช้เป็น base image ก่อน ใช้คำสั่งdocker run --it debian:jessie
  • ทดลองรัน $ git จะพบว่าไม่สามารถทำงานได้ ให้ติดตั้ง git ก่อน $ apt-get update && apt-get install -y git เมื่อติดตั้งเสร็จลองรัน $ git อีกครั้งจะแสดง help ของ git ขึ้นมาแสดงว่าพร้อมใช้งานแล้ว ให้ออกมาจาก container ได้เลย
  • ใช้คำสั่ง Docker commit เพื่อบันทึกการแก้ไขทั้งหมดใน container layers ไปเป็น image ตัวใหม่ ใช้คำสั่งdocker commit container_id repository:tag
จะได้ image ตัวใหม่ ที่มีขนาดใหญ่กว่าเดิม เพราะติดตั้ง git เพิ่มเข้าไป
  • ลองสร้าง container จะ image ใหม่นี่ดู และทดลองใช้คำสั่ง git พบว่ามี git ติดตั้งมาพร้อมใช้งานเลย docker run --it somprasongd/debian_git:1.0

สร้าง Docker Image ด้วย Dockerfile

Dockerfile คือ text document ที่ระบุวิธีการ และคำสั่งทั้งหมด ที่เอาไว้ประกอบร่าง Docker Image ขึ้นมา โดยใช้คำสั่ง docker build ซึ่งรูปแบบของ Dockerfile คือ

# Comment
INSTRUCTION arguments

โดย instruction นั้นไม่ใช่ case-sensitive แต่แนะนำให้เขียนเป็นตัวพิมพ์ใหญ่ทั้งหมด เพื่อให้เวลาอ่านจะได้แยกออกจาก arguments ได้ง่ายๆ โดย instruction ต้องเริ่มต้นด้วย ‘FROM’ เป็นตัวแรกเสมอเพื่อใช้ระบุ base image ที่จะนำมาใช้ ที่สำคัญอีกอย่างคือ แต่ละ instruction จะสร้าง image layer ใหม่ขึ้นมาเสมอ ลองสร้าง Dockerfile ดังนี้

FROM debian:jessie
RUN apt-get update
RUN apt-get install -y git
RUN apt-get install -y vim

และสร้าง image โดยใช้ Docker build ซึ่งมีรูปแบบคือdocker build [Options] PATH ตัวอย่าง docker build -t "somprasongd/debian_git:1.1 . ถ้ารัน docker build ที่เดียวกับ Dockerfile ใช้ ‘.’ แต่ถ้าอยู่ที่อื่นต้องระบุ PATH และใช้ -f option ซึ่งเป็นการกำหนด PATH ให้ build context

เมื่อเริ่มทำงาน Docker client จะเอาไฟล์ทั้งหมดใน build context ไปยัดใส่ใน tarball และส่งไปให้ Docker daemon และในแต่ละ instruction ตัวอย่างเช่น RUN จะทำงานบน Thin R/W layer ของ container เสมอ เสร็จจะ commit container นั้นเป็น image ตัวใหม่ ซึ่งถ้ามีคำสั่ง RUN ถัดไปอีก ก็จะเอา image ตัวใหม่ที่ได้มาไปใช้สร้าง container เพื่อ RUN อันถัดไป ดังนั้นแนะนำให้รวมคำสั่ง RUN ทำเป็น chain เพื่อลดจำนวน image layer ที่จะถูกสร้างขึ้นมา

Dockerfile Instructions

  • FROM ไว้สำหรับกำหนด Base Image
    FROM <image>:<tag> หรือ FROM <image>@<digest>
  • RUN ไว้สำหรับรันคำสั่งของ Linux มี 2 รูปแบบ
    RUN <command> ไว้รันจาก shell ( default is /bin/sh -c on Linux or cmd /S /C on Windows)
    RUN ["executable", "param1", "param2"] ไว้รันจาก exec
  • CMD เอาไว้กำหนด default command ซึ่งมีได้แค่อันเดียว ถ้ามีหลายอันจะใช้ตัวสุดท้าย ซึ่งเอาไว้รันคำสั่งเมื่อเริ่มรัน image มีวิธีใช้ 3 รูปแบบ
    CMD ["executable","param1","param2"] (exec form, preferred)
    CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
    CMD command param1 param2 (shell form)
  • LABEL เอาไว้เพิ่ม metadata ใน image กำหนดเป็น key-value LABLE <key>=<value> <key>=<value> <key>=<value> ...
  • EXPOSE เอาไว้เปิด port ใน container เท่านั้น ซึ่งต้องใช้ -p หรือ -P เพื่อผูกกับ host port ตอนรัน image
    EXPOSE <port> [<port> ...]
  • ENV เอาไว้กำหนด environtment variable
    ENV <key> <value>หรือ ENV <key>=<value> <key>=<value> ...
  • ADD ใช้สำหรับ copy ไฟล์, directories หรือ ดาวน์โหลดไฟล์จาก URLs
    ADD <src>... <dest>
    ADD ["<src>",... "<dest>"] ( required forpaths containing whitespace)
    กฎการใช้ ADD
    - ไฟล์ หรือ diretories ใหม่ที่ถูกสร้างจะมี UID, GID เป็น 0 (root/superusers)
    - ถ้าดาวน์โหลดไฟล์จาก URL จะมี permissions 600 (R&W)
    - <src> path ต้องอยู่ภายใน build context เท่านั้น ไม่สามารถใช้ ADD ../something /something ได้
    - <dest> ถ้าระบุ relativeDir/ จะ copy ไปไว้ที่ `WORKDIR`/relativeDir/ แต่ถ้าระบุเป็น /absoluteDir/ จะ copy ไปไว้ที่ /absoluteDir/ ซึ่งถ้ายังไม่มีจะสร้างให้
    - ถ้า <src> เป็น URL และ <dest> ไม่ได้ปิดด้วย ‘/’ ไฟล์จะถูกดาวน์โหลดให้เสร็จก่อน และค่อย copy ไปไว้ใน <dest>
    - ถ้า <src> เป็น URL และ <dest> ปิดด้วย ‘/’ ไฟล์จะถูกดาวน์โหลดไว้ใน <dest>/<filename>
    - ถ้า <src> เป็นไฟล์ tar archive (identity, gzip, bzip2 or xz) จะ unpacked as a directory แต่ถ้าเป็นไฟล์จาก URL จากไม่ unpack ให้
  • COPY เหมือนกับ ADD แต่ไม่สามารถดาวน์โหลดไฟล์จาก URLs ได้
    COPY <src>... <dest>
    COPY ["<src>",... "<dest>"] ( required forpaths containing whitespace)
  • ENTRYPOINT เอาไว้กำหนด default executable ซึ่งปกติจะเป็น ‘/bin/sh -c’ แล้วสามารถใช้ร่วมกับ CMD ได้ โดยให้ CMD เป็นตัวกำหนดค่าพารามิเตอร์ให้ entrypoint ที่กำหนด รายละเอียดเพิ่มเติม
    ENTRYPOINT ["executable","param1","param2"] (exec form, preferred)
    ENTRYPOINT command param1 param2 (shell form)
  • VOLUME ไว้สร้าง volume ใน container เพื่อเอาไว้เป็น mount point จาก host หรือ container อื่น
    VOLUME ["/data"]
  • USER เอาไว้กำหนด user name หรือ UID ที่จะใช้ตอนกำลังรัน image และใช้กับ RUN, CMDและ ENTRYPOINT ใน Dockerfile ด้วย ปกติจะเป็น root
  • WORKDIR เอาไว้กำหนด working directory สำหรับ RUN, CMD, ENTRYPOINT, COPY และ ADD instructions ใน Dockerfile
    WORKDIR /path/to/workdir

วิธีการเอา Docker Image ขึ้น Docker Hub

  • เริ่มต้นให้สมัครบัญชีผู้ใช้งานก่อนที่ https://hub.docker.com/
ostlab คือ docker_hub_id โดย Repository จะเป็น docker_hub_id/repository name
  • เปลี่ยนชื่อ Repository ของ image ให้ถูกต้อง docker tag source_image[:tag]target_name[:tag] เช่นdocker tag somprasongd/debian_git:1.0 ostlab/debian:1.0
  • ล็อกอินเข้า Docker Hub docker login --username=ostlab และใส่ password
  • push image เข้า Docker Hub docker push ostlab/debian:1.0
  • เมื่อกลับไปดูที่ https://hub.docker.com/ จะมี repository ostlab/debian เพิ่มขึ้นมาแล้ว

--

--