Docker tutorial for beginners : Part 2

ภาพจาก dev.to

บทความโดย ผศ.ดร.ณัฐโชติ พรหมฤทธิ์
ภาควิชาคอมพิวเตอร์
คณะวิทยาศาสตร์
มหาวิทยาลัยศิลปากร

ใน Part นี้เราจะมารู้จักคำสั่งของ Dockerfile สำหรับการสร้าง Image และการใช้ Docker-compose เพื่อจัดการกับ Container รวมทั้งการจัดการ Container แบบง่ายๆ ด้วย Web Based GUI ครับ

Dockerfile

Dockerfile คือหัวใจสำคัญของ Docker โดย Dockerfile จะบอกให้ Docker สร้าง Image ขึ้นมาทีละ Layer แบบ Read only ด้วยคำสั่ง docker build

ซึ่งแต่ละ Layer ของ Image จะเป็นเพียงไฟล์ที่เก็บความเปลี่ยนแปลงของ Layer ก่อนหน้า คล้ายกับการ Commit Version ของ Source Code บน Version Control System โดยเราเรียก Layer แรกของ Image ว่า Base image

คำสั่งของ Dockerfile จะขึ้นต้นด้วยอักษรตัวใหญ่ตามด้วย Argument ซึ่งจะถูกประมวลผลขณะที่มีการสร้าง Image จากบรรทัดบนจนถึงบรรทัดล่างสุด

ตัวอย่างคำสั่งของ Dockerfile เช่น

FROM python:3.7.2-alpine3.8
COPY . /app
ENTRYPOINT [“python”, “./app/hello.py”, “my_var”]

คำสั่งหลักๆ ของ Dockerfile 12 คำสั่ง ได้แก่

FROM กำหนด Base image
LABEL ใช้กำหนด Metadata เช่น ชื่อ Version หรือเจ้าของ Image
ENV กำหนด Environment variable ภายใน Container
RUN ใช้สำหรับติดตั้ง Packagesให้ Container
COPY สำหรับ Copy file และ Folder ไปยัง Container
ADD สำหรับ Copy file และ Folder ไปยัง Container โดยสามารถแตกไฟล์ .tar รวมทั้ง Copy file จาก Host ภายนอกได้
CMD สำหรับรันคำสั่งที่ต้องการขณะรัน Container
WORKDIR กำหนด Working directory ของ Container
ARG กำหนด Variable ขณะสร้าง Image
ENTRYPOINT สำหรับรันคำสั่งที่ต้องการขณะรัน Container
EXPOSE กำหนด Port ที่เปิดให้ Container อื่นติดต่อเข้ามา
VOLUME สร้าง Folder เก็บข้อมูลแบบถาวรให้ Container

ตัวอย่างของ Dockerfile

# Example Dockerfile
FROM python:3.7.2-alpine3.8

LABEL maintainer="nuttachot@hotmail.com"

RUN apk add --update git

WORKDIR /myapp

COPY . .

ARG my_var=my_default_value

ENTRYPOINT ["python", "./app/hello.py", "my_var"]

EXPOSE 8000

VOLUME /my_volume

จากตัวอย่างด้านบนเราจะติดตั้ง Packages บน Alpine image ด้วยคำสั่ง RUN apk ขณะที่การติดตั้ง Packages บน Ubuntu image จะใช้คำสั่ง RUN apt-get ครับ

RUN apt-get update && apt-get install my_package

Build a smaller image

Layer ของ Docker image นั้นทำให้เราสร้างและเคลื่อนย้าย Image ได้รวดเร็ว และประหยัดเนื้อที่ของ Harddisk ถ้ามี Image หลายตัวแชร์ Layer ด้วยกัน นอกจากนี้เมื่อมีการ Pull image จาก Docker hub หรือ Registry แล้ว Docker จะ Download เฉพาะ Layer ที่ไม่มีใน Cache บน Host Machine เท่านั้น

แต่ยิ่งมีจำนวน Layer มาก Image ที่ได้ก็จะใหญ่ขึ้น ทำให้สิ้นเปลือง Memory เมื่อมีการรัน Container ดังนั้นเพื่อให้เกิดความสมดุลระหว่างการประหยัดเนื้อที่ของ Harddisk กับการประหยัด Memory ของ Server ขณะรัน Container เราอาจลดจำนวน Layer ของ Image ลง โดยการลดจำนวนคำสั่งใน Dockerfile ครับ

  • Remote Login ไปยัง Cloud Server โดยใช้ ssh
ssh nc-user@labxx.cpsudevops.com
  • สร้าง Project ใหม่ภายใน Folder small_image ซึ่งประกอบด้วย ไฟล์ "Dockerfile"
.
|__ Dockerfile
  • แก้ไข Dockerfile โดยเพิ่มคำสั่งตามตัวอย่างด้านล่าง
FROM debian:stable

WORKDIR /var/www

RUN apt-get update
RUN apt-get -y --no-install-recommends install curl
RUN apt-get -y --no-install-recommends install ca-certificates

RUN curl https://raw.githubusercontent.com/gadiener/docker-images-size-benchmark/master/main.go -o main.go

RUN apt-get purge -y curl
RUN apt-get purge -y ca-certificates
RUN apt-get autoremove -y
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
  • สร้าง Docker image
docker build -t debiandock .
  • ดูจำนวน Layer ของ Image
docker history debiandock
  • ดูขนาดของ Images
docker images

จะเห็นว่า debiandock image มีขนาด 150MB

  • แก้ไขไฟล์ Docker ตามตัวอย่างด้านล่าง
FROM debian:stable

WORKDIR /var/www

RUN apt-get update && \
    apt-get -y --no-install-recommends install curl \
        ca-certificates && \
    curl https://raw.githubusercontent.com/gadiener/docker-images-size-benchmark/master/main.go -o main.go && \
    apt-get purge -y curl \
        ca-certificates && \
    apt-get autoremove -y && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
  • สร้าง Docker image ใหม่
docker build -t small_debiandock .
  • ดูจำนวน Layer ของ Image
  • ดูขนาดของ Images
docker images

ซึ่งจะเห็นว่าขนาดของ Image ลดลงจาก 150MB เป็น 116MB

Docker Compose

ข้อดีหนึ่งของ Docker คือเราสามารถแบ่ง Application ออกเป็นส่วนๆ ตามหน้าที่ และแยกไปเก็บในแต่ละ Container

โดย Docker ได้เตรียม Docker-compose เพื่อจัดการกับ Container หลายๆ ตัวพร้อมกัน ซึ่งการ Config Docker-compose มีขั้นตอนดังนี้

1) แบ่ง Application เป็น Component
2) สร้าง หรือ Pull image
3) Configure environment variables (ถ้าต้องใช้งาน)
4) Configure networking
5) กำหนด Volumes
6) สร้างและรัน Image

เราจะเริ่ม Config Docker-compose เพื่อสร้าง Website แบบ Static ที่มี Server component 1 Component กันครับ

  • สร้าง Project ใหม่ภายใน Folder web_dock ซึ่งประกอบด้วย ไฟล์ดังต่อไปนี้
.
|__ docker-compose.yml
|__ server/
   |__Dockerfile
   |__index.html
   |__server.py
  • แก้ไข Dockerfile ตามตัวอย่างด้านล่าง
FROM python:3.7.2-alpine3.8

WORKDIR /html/

ADD server.py .
ADD index.html .
  • แก้ไขไฟล์ index.html
Hello Docker-compose!
  • แก้ไขไฟล์ server.py
#!/usr/bin/env python3


import http.server
import socketserver

handler = http.server.SimpleHTTPRequestHandler


with socketserver.TCPServer(("", 80), handler) as httpd:

    httpd.serve_forever()
  • กลับมาที่ docker-compose.yml แก้ไขไฟล์ตามตัวอย่างด้านล่าง
version: "3"

services:
  server:
    build: server/
    command: python ./server.py
    
    networks:
      - frontend
    ports:
      - 80:80
      
    volumes:
      - server_volume:/html

volumes: 
  server_volume:

networks:
  frontend:
    external:
      name: web_network
  • สร้าง Image ด้วย docker-compose
docker-compose build 
  • ดู Image
  • ดู Layer ของ Image
  • สร้าง Bridge network โดยตั้งชื่อเป็น web_network
docker network create web_network
  •  ดู Network ทั้งหมด
  • รัน Container ด้วย docker-compose และกลับไปที่ Terminal โดยใช้ parameter -d
docker-compose up -d
  • ดู Containers ที่รันทั้งหมด ตามที่ docker-compose.yml ดูแล
  • เปิดดู Website ที่รันบน Container โดยระบุ URL เป็นชื่อ Server ของเราเอง เช่น http://lab20.cpsudevops.com
  • ดู Volume ที่สร้างขึ้นมาทั้งหมด
docker volume ls
  • ตรวจสอบ web_dock_server_volume
docker volume inspect web_dock_server_volume

จะเห็นว่า Volume web_dock_server_volume เกิดจากการที่ Docker ได้ Mount  Folder html กับ path /var/lib/docker/volumes/web_dock_server_volume/_dat ของ Host Machine

เราสามารถเข้าถึงไฟล์ภายใน Container ผ่าน Folder นี้

sudo ls /var/lib/docker/volumes/web_dock_server_volume/_data
  • Remote ไปยัง sh shell ของ web_dock_server_1 Container ด้วยคำสั่ง docker exec -it แล้วดูไฟล์ใน Working Directory
docker exec -it web_dock_server_1 sh
  • Stop/Delete Container ที่ docker-compose.yml ดูแล ด้วยคำสั่ง docker-compose down และลบ image ทั้งหมดด้วย parameter --rmi all
docker-compose down --rmi all

แต่เราจะยังเห็น web_dock_server_volume อยู่ เพราะ Volume เป็นที่เก็บข้อมูลถาวร ซึ่งจะไม่ถูกลบเมื่อมีการ Stop container

  • ลบ web_dock_server_volume
docker volume rm web_dock_server_volume
  • ลบ web_network
docker network rm web_network
  • ดูเนื้อที่ที่ Docker ใช้
docker system df

Web Based Container Management

เพื่อจะจัดการกับ Container แบบง่ายๆ เราจะ Config Docker-compose ให้ใช้งาน Web Based Container Management ที่ชื่อว่า "portainer" ซึ่งเป็น Open source platform กันครับ

  • สร้าง Network แบบ Bridge ชื่อ webproxy
docker network create webproxy
  • สร้าง Project ใหม่ภายใน Folder port_dock ซึ่งประกอบด้วย ไฟล์ดังต่อไปนี้
.
|__ docker-compose.yml
|__ .env
  • แก้ไข docker-compose.yml ตามตัวอย่างด้านล่าง
version: '3'

services:
  portainer:
    container_name: ${CONTAINER_NAME}
    restart: unless-stopped
    image: portainer/portainer
    volumes:
      - portainer_data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      VIRTUAL_HOST: ${DOMAINS}

    ports:
      - "80:9000"
volumes: 
  portainer_data:

networks:
    default:
       external:
         name: ${NETWORK}
  • แก้ไขไฟล์ .env โดยกำหนด Domains ตามชื่อเครื่องของตัวเอง
CONTAINER_NAME=portainer

DOMAINS=labxx.cpsudevops.com

NETWORK=webproxy
  • รัน Container
docker-compose up
  • เปิดดู portainer ผ่าน Web Browser โดยใส่ URL เป็นชื่อ Server ของตัวเอง เช่น http://lab20.cpsudevops.com แล้วตั้งรหัสผ่านใหม่
  • เลือก Local Manage... แล้วกด Connect
  • กดเมนู Containers เพื่อดู Container ทั้งหมด
  • กดที่ปุ่ม Stats เพื่อดูการใช้งานทรัพยาการของ Container ครับ
ขอขอบคุณ Nipa.Cloud ที่ให้การสนับสนุน Environment ในการเรียนการสอน
รายวิชา Dev-Ops and Cloud Engineering 101