สวัสดีครับ ในบทความก่อนหน้านี้ผมได้อธิบายว่า docker คืออะไร และ วิธีติดตั้ง Docker engine ไปแล้ว ถ้าใครยังไม่ได้อ่านก็กลับไปอ่านก่อนนะครับ ในบทความนี้จะเกี่ยวข้องกับการสร้าง Docker Image ก่อนอื่นมาทําความเข้าใจอีกซักนิดเกี่ยวกับ Docker Image กันก่อน
Docker Image คืออะไร
Docker Image คือ Environment ของ เครื่อง server เครื่องนึง ที่เราสร้างขึ้นมา โดยเราสามารถติดตั้งโปรแกรมต่างๆ(Nginx, apache, mysql ฯลฯ) ได้ตามต้องการ รวมไปถึง App ที่เราเขียนขึ้นมาด้วย มันจะถูกหอรวมกันเป็น Docker Image คอนเซ็ปต์ของ Docker คือ จะเอา Application ที่เราเขียนขึ้น มาสร้างเป็น Docker Image ตาม Environment ที่ Application นั้นๆต้องการ เช่น ถ้าเราต้องการสร้างเว็บ Hello world ขึ้นมา เราก็จะต้องนําเว็บ Hello world มาสร้างเป็น Docker Image โดยในขั้นตอนของการสร้าง Docker Image จะต้องระบุว่า Image ตัวนี้ จะต้องติดตั้งโปรแกรมอะไรบ้าง(apache, Nginx, mysql, ฯลฯ) และต้อง Config ตรงไหนบ้าง เมื่อระบุเรียบร้อย เราก็จะได้ Docker Image (เว็บ Hello world) ซึ่งเราจะนํา Docker Image ตัวนี้ไปรันบนเครื่องใดก็ได้
ภาพรวมขั้นตอนการสร้าง Docker Image
ภาพรวมขั้นตอนการสร้าง Docker Image คือ อันดับแรกเราจะต้องสร้างไฟล์ชื่อว่า dockerfile โดยภายในไฟล์นี้จะเป็น code ที่เราเขียนขึ้นมาเพื่อใช้บอก Docker ว่า เราจะสร้าง Docker Image จาก Docker Image base อะไร เราจะลงโปรแกรมอะไรใน Docker Image นี้บ้าง เมื่อเราเขียนไฟล์ dockerfile เรียบร้อย ก็สั่งให้ Docker build ได้เลย เท่านี้ก็จะได้ Docker Image มาอยู่ในเครื่องเราแล้ว
ในการสร้าง Docker Image จะต้องสร้างจาก Docker Image base ที่มีอยู่แล้ว ซึ่งอาจจะใช้จากคนอื่นที่สร้างขึ้นมา หรือใช้จาก official Docker Image ก็ได้
คําสั่ง Docker พื้นฐาน
คําสั่งพื้นฐานของ Docker ที่เราจะใช้ในบทความนี้
- docker images เป็นคําสั่งที่ใช้ดูว่าในเครื่องของเรามี Docker Image อะไรอยู่บ้าง
- docker ps เป็นคําสั่งที่ใช้ดูว่า ตอนนี้มี Container อะไรที่กําลัง run อยู่บ้าง
- docker run เป็นคําสั่งที่ใช้ run Docker Image
- docker stop เป็นคําสั่ง stop Container
- docker rm เป็นคําสั่งที่ใช้ลบ Container ที่สร้างขึ้นมา
- docker rmi เป็นคําสั่งที่ใช้ลบ Docker Image ในเครื่องเรา
- docker build เป็นคําสั่งที่ใช้สร้าง Docker Image
ผมจะยกตัวอย่างการสร้าง Docker Image สองตัวอย่างนะครับ คือ ในฝั่งของ Raspberry Pi และ ฝั่งของ PC หรือ Server ทั่วไป
กรณีศึกษา สร้าง Docker Image ของ nodejs express example
ในตัวอย่างนี้เราจะมาสร้าง Docker Image ของ nodejs ที่จะใช้ express เพื่อสร้างเว็บแสดงคําว่า “Hello world!” ตามนี้
ไฟล์ที่เกี่ยวข้องมีดังนี้ครับ
- app.js คือไฟล์ Source code ของ nodejs ซึ่งเราจะเขียนให้แสดง “Hello world!”
- package.json คือไฟล์ config สําหรับ npm
- dockerfile คือไฟล์ Script สําหรับสร้าง Docker Image
app.js และ package.json คือ Application ของ nodejs ถ้าไม่เข้าใจให้ศึกษาการเขียน nodejs เพิ่มเติม ในบทความนี้จะไม่ลงลึกในส่วนนี้
ไฟล์ app.js
const express = require('express')
const app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
ไฟล์ package.json
{
"name": "docker-image-example",
"version": "1.0.0",
"description": "Docker image example",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Thiti Yamsung ([email protected])",
"license": "ISC",
"dependencies": {
"express": "^4.15.3"
}
}
ไฟล์ dockerfile เป็นส่วนสําคัญในการสร้าง Docker Image เพราะเป็นส่วนที่จะบอกว่า Image ของเราจะออกมาเป็นอย่างไร
FROM node:8.2.1-alpine
MAINTAINER Thiti Yamsung ([email protected])
COPY . /app
WORKDIR /app
RUN npm install
EXPOSE 3000
ENTRYPOINT npm run start
อธิบายคําสั่ง
- บรรทัดที่ 1 คือบอก Docekr ว่าเราจะใช้ Docker Image อะไรเป็น Docker Image ต้นแบบ
- บรรทัดที่ 2 คือบอกชื่อของคนสร้าง Docker Image นี้
- บรรทัดที่ 4 คือ ให้ Copy ไฟล์ทั้งหมดที่อยู่ใน Current path ไปไว้ใน /app บน Docker Image
- บรรทัดที่ 5 คือ กําหนดให้ ทํางานอยู่ที่ path ไหน
- บรรทัดที่ 6 คือ สั่งให้รันคําสั่ง npm install เพื่อโหลด Depencies ของ nodejs
- บรรทัดที่ 8 คือ บอกว่าเราจะใช้ port ไหน
- บรรทัดที่ 10 คือ สั่งให้ Docker Image ตัวนี้ทํางานอะไร ในตัวอย่างคือสั่งให้รัน npm run start เพื่อให้ Application nodejs ของเราทํางาน
เมื่อเขียน dockerfile ได้ตามต้องการแล้วต่อไปสั่ง build ด้วยคําสั่ง
docker build -t myimage .
- -t blink คือการระบุชื่อและ tag ของ Docker Image ในตัวอย่างนี้ใช้ชื่อว่า blin
- . คือ path ของ dockerfile
จบขั้นตอนการสร้าง Image ถ้าต้องการที่จะลบ Docker Image ให้ใช้คําสั่ง
docker rmi image_name
image_name คือชื่อของ image ที่ต้องการลบ
Run Docker Image ที่สร้างขึ้น
ในหัวข้อนี้จะเป็นการ run Docker Image ที่เราสร้างไว้เมื่อกี้ มีขั้นตอนดังนี้ ใช้คําสั่ง
docker run -d -p 3000:3000 myimage
- -d คือให้ run แบบ background
- myimage คือ ชื่อของ Docker Image ที่ต้องการจะ run
ถ้าเราต้องการจะดูว่ามี Container ตัวไหน รันอยู่บ้างให้ใช้คําสั่ง
docker ps
ถ้าต้องการให้ show Container ที่ถูก stop แล้วขึ้นมาด้วย ให้ใส่ -a ต่อท้าย ps ถ้าต้องการ stop Container ที่กําลัง run อยู่ใช้คําสั่ง
docker stop container_name
container_name คือชื่อของ Container ที่ต้องการลบ ตัวอย่าง Source code: github.com/mrthiti/docker-image-example
กรณีศึกษา สร้าง Docker Image ของ โปรแกรมไฟกระพริบ Blink
เพื่อให้เข้าใจมากขึ้น ในบทความนี้ผมจะยกตัวอย่างวิธีการทํา Docker Image ของโปแกรมไฟกระพริบ ใน Raspberry Pi โดยจะใช้ขา GPIO17 เป็นขา Output และจะใช้ Library wiringpi ในการ Control LED แบบง่ายๆ
การต่อวงจรก็ประมาณนี้
ลงมือสร้าง Docker Image บน Raspberry Pi
ติดตั้ง Docker engine ใน Raspberry Pi ให้เรียบร้อย สร้าง folder blink ใน /home/pi/ (จะสร้างไว้ที่ไหนก็ได้แล้วแต่ชอบ)
mkdir /home/pi/blink
สร้างไฟล์โปรแกรม blink.c แล้วเพิ่ม code นี้เข้าไป
#include <wiringPi.h>
int main (void)
{
wiringPiSetup () ;
pinMode (0, OUTPUT) ;
for (;;)
{
digitalWrite (0, HIGH) ; delay (500) ;
digitalWrite (0, LOW) ; delay (500) ;
}
return 0 ;
}
จาก code นี้เป็น โปรแกรมไฟกระพริบธรรมดาครับ สร้าง dockerfile ไว้ที่เดียวกับ blink.c (/home/pi/blink) แล้วใส่ code นี้เข้าไป
# Pull base image
FROM resin/rpi-raspbian:jessie
MAINTAINER Thiti Yamsung <[email protected]m>
# Install dependencies
RUN apt-get update && apt-get install -y \
git-core \
build-essential \
gcc
# Load and install Libraries wiringPi
RUN git clone git://git.drogon.net/wiringPi
RUN cd wiringPi && ./build
# Copy and complie blink program to this image
COPY ./blink.c /data/
RUN cd /data && gcc -Wall -o blink blink.c -lwiringPi
# Define working directory
WORKDIR /data
# Define default command
CMD ["./blink"]
ผมจะอธิบาย code ให้ฟังนะครับ
- บรรทัดที่ 2 คือบอก Docekr ว่าเราจะใช้ Docker Image อะไรเป็น Docker Image ต้นแบบ
- บรรทัดที่ 3 คือบอกว่าใครเป็นคนสร้าง Image นี้ขึ้นมา
- บรรทัดที่ 6-9 คือบอก Docekr ว่าให้ run command(เป็น command ทั่วๆไป) อะไรให้เรา ในที่นี่คือ ติดตั้งโปรแกรมต่างๆที่เราจะใช้
- บรรทัดที่ 12-13 คือ โหลด Libraries wiringpi และติดตั้งด้วย
- บรรทัดที่ 16-17 คือ สั่งให้ Docker copy ไฟล์โปรแกรมที่เราเขียนขึ้นมาไปไว้ใน Docker Image และ Compile ด้วย
- บรรทัดที่ 20 คือ บอก Docker ว่าเมื่อ Run container จาก Image นี้ขึ้นมาแล้ว dir ปัจจุบันจะอยู่ path ไหน
- บรรทัดที่ 23 คือ บอก Docker ว่าเมื่อ Run container จาก Image นี้ขึ้นมาแล้ว ให้ทําการ run คําสั่งอะไรเป็นคําสั่ง default ในที่นี้คือ ผมสั่งให้มันรันโปรแกรม ไฟกระพิบเลย
เมื่อเขียน dockerfile ได้ตามต้องการแล้วต่อไปสั่ง build ด้วยคําสั่ง
docker build -t blink /home/pi/blink/
- -t blink คือการระบุชื่อและ tag ของ Docker Image ในตัวอย่างนี้ใช้ชื่อว่า blink
- /home/pi/blink/ คือ path ของ dockerfile
นั่งรอ… เมื่อทุกอย่างเรียบร้อย ไม่ขึ้น error อะไร เราจะได้ Docker Image ขึ้นมา 1 อัน ชื่อว่า blink สามารถตรวจสอบได้ด้วยคําสั่ง
docker images
จะขึ้นมาประมาณนี้
จบขั้นตอนการสร้าง Image ถ้าต้องการที่จะลบ Docker Image ให้ใช้คําสั่ง
docker rmi image_name
image_name คือชื่อของ image ที่ต้องการลบ
Run Docker Image ที่สร้างขึ้น
ในหัวข้อนี้จะเป็นการ run Docker Image ที่เราสร้างไว้เมื่อกี้ มีขั้นตอนดังนี้ ใช้คําสั่ง
docker run -d --cap-add SYS_RAWIO --device /dev/mem blink
- -d คือให้ run แบบ background
- —cap-add SYS_RAWIO —device /dev/mem คือ โปรแกรมที่เราเขียนขึ้นมีการเรียกใช้ hardware I/O จะต้องระบุ option นี้เข้าไป เพื่อให้ container สามารถใช้งาน I/O ได้
- blink คือ ชื่อของ Docker Image ที่ต้องการจะ run ในบทความนี้คือ blink
หลังจากที่รันคําสั่งนี้ไปแล้วคุณจะเห็นไฟกระพริบขึ้นมาแบบนี้
ถ้าเราต้องการจะดูว่ามี Container ตัวไหน รันอยู่บ้างให้ใช้คําสั่ง
docker ps
ถ้าต้องการให้ show Container ที่ถูก stop แล้วขึ้นมาด้วย ให้ใส่ -a ต่อท้าย ps จะได้ประมาณนี้
ถ้าต้องการ stop Container ที่กําลัง run อยู่ใช้คําสั่ง
docker stop container_name
container_name คือชื่อของ Container ที่ต้องการลบ