ลองรัน Docker และพื้นฐาน Image / Container
ลอง run docker hello world
ก่อน run docker รู้จักกับ docker image และ container กันก่อน
- image คือ stand-alone package ที่ทำการรวมคำสั่งทั้งหมดที่จำเป็นเอาไว้ในนี้สำหรับ run software ตัวนั้น (ไม่ว่าจะเป็นคำสั่ง system, code, runtime, library, environment, config) = เปรียบได้กับการแพ็คของทั้งหมดรวมกันไว้ในแผ่นดิส (image) อันเดียว (เปรียบได้เป็น blueprint ของ software)
- container คือ การนำ image นั้นมา run เป็น instance (เครื่องๆหนึ ่ง) ให้สามารถใช้งานได้ ซึ่งก็จะเป็นการ run โดยใช้ memory ของ host ของ OS นั้นขึ้นมา
ตัวอย่างภาพด้านล่าง เรามี image อยู่ 1 อัน ซึ่ง เราสามารถทำการ run image มาเป็น Container หลายๆอันได้ (เปรียบได้เป็นการเปิด 1 instance ที่ run image นั้นขึ้นมา) ซึ่งทั้ง 3 Container ก็จะมีการทำงานที่เหมือนๆกัน (เนื่องจากมาจาก image เดียวกัน)
เราจะเริ่มต้นอย่างนี้ก่อน
- เราจะทำการ pull image จาก docker hub (สถานที่ฝาก docker ไว้บน cloud)
- แล้วเราจะนำ image ที่ pull (จาก server มายังเครื่องเรา) ทำการ run เป็น container ขึ้นมา
ที่อยู่ของ image ใน docker hub https://hub.docker.com/_/hello-world
# pull image มาลงเครื่อง
# command
docker pull <url docker หรือชื่อ ถ้าอยู่ใน docker hub>
# example (สำหรับ hello world)
docker pull hello-world
# run image นั้นออกมาเป็น 1 container
# command
docker run <ชื่อ image ที่จะ run>
# example (run image hello-world)
docker run hello-world
คำสั่ง
docker pull
คือคำสั่งสำหรับการนำ image จาก Server ดึงมา load ไว้ใน local- หลังจาก pull แล้ว สามารถใช้คำสั่ง
docker images
เพื่อทำการเช็คได้ว่า ตอนนี้ image อยู่ที่เครื่องเราแล้วหรือไม่ ? (เช็คได้จากชื่อ hello-world ** ภาพของเครื่องผม คือมีหลาย images อยู่ในเครื่อง ให้สังเกตแค่ image ที่เรา pull มาก็พอ)
docker run
คือคำสั่งสำหรับการ run image (ที่อยู่ในเครื่อง) ออกมาเป็น Container 1 เครื่อง
เมื่อได้หน้าตาแบบนี้ออกมาถือว่า docker เราได้ทำการ run image เรียบร้อยแล้ว (และก็จบ process ลงแล้วเช่นกัน เนื่องจากเป็นแค่การพ่นข้อความออกมาแค่นั้น)
ลองเขียน Dockerfile เพื่อ custom module ขึ้นมา
ทีนี้เราจะมาลองกับเคสที่มันสมจริงขึ้นหน่อย เราจะลองสร้าง Image ของ node ของเราเองขึ้นมา (จะไม่ใช่การ pull มาจาก internet) รวมถึงจะให้เป็นการ run Container แบบ http server ขึ้นมาได้ เหมือนกับเวลาเรา run ด้วยคำสั่ง node ออกมา
เริ่มต้นเราจะ
- สร้าง
package.json
(จาก npm init) และลง package express เอาไว้ (npm install express)
package.json จะหน้าตาออกมาเป็นแบบนี้
{
"name": "project-name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2"
}
}
- สร้าง
index.js
ที่ทำการ run http server เอาไว้ และมี API 1 อันที่แสดงคำว่า hello world ออกมา
ไฟล์ index.js
const express = require('express')
const app = express()
const port = 8000
app.get('/hello-world', (req, res) => {
res.send('hello world')
})
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`)
})
- และที่ไฟล์นั้นจะทำการเปิด http server เอาไว้ที่ port 8000 เพื่อให้สามารถยิง
localhost:8000/hello-world
แล้วได้ข้อความ "hello world" ออกมาได้ - ลองทดสอบโดยการ run
node index.js
ได้ผลลัพธ์ออกมาประมาณนี้
เราลองเรียบเรียง step ตอนลง project ก่อนว่า สมมุติ เราส่ง project นี้ให้เพื่อไป run สิ่งที่เราจะทำคือ
npm install
เพื่อทำการลง package ทั้งหมดnode index.js
เพื่อทำการ run server
Note
- เราไม่จำเป็นต้อง npm init ใหม่ เนื่องจากมี package.json อยู่แล้ว
- และไม่ต้องส่ง node_modules ไป เนื่องจากเป็น folder ที่เกิดขึ้นหลังจาก npm install ซึ่งแต่ละ environment อาจจะมีการ map path ไม่เหมือนกัน = จำเป็นต้องลงใหม่เสมอเมื่อไปเครื่องใหม่
ok เมื่อเราเรียบเรียงเรียบร้อย เราจะลองมาสร้าง Dockerfile
กัน
Dockerfile คือ file ที่รวมคำสั่งเอาไว้เพื่อทำการ build ออกมาเป็น Docker Image
- คำสั่ง
docker build
ของ docker จะสามารถสร้าง Docker image จาก Dockerfile ออกมาได้ โดยจะทำตามลำดับคำสั่งใน Dockerfile - ผลลัพธ์หลังคำสั่ง
docker build
เราจะได้ Docker image ที่สามารถใช้คำสั่งdocker run
ทำการ run ออกมาเป็น Container ได้
Step จะเป็นตามภาพนี้
เมื่อเรานำคำสั่งที่เราเรียบเรียงเมื่อสักครู่มาเรียงเป็น Dockerfile เราจะได้ Dockerfile หน้าตาประมาณนี้ออกมา
# ทำการเลือก base image (จาก docker hub) มาเป็นตัว runtime เริ่มต้น เพื่อให้สามารถ run project ได้
# ในทีนี้เราทำการเลือก node image version 18 ออกมา
FROM node:18
# กำหนด directory เริ่มต้นใน container (ตอน run ขึ้นมา)
WORKDIR /usr/src/app
# ทำการ copy file package.json จากเครื่อง local เข้ามาใน container
COPY package.json ./
# ทำการลง dependency node
RUN npm install
# copy file index.js เข้ามาใน container
COPY index.js ./
# ทำการปล่อย port 8000 ออกมาให้ access ได้
EXPOSE 8000
# กำหนด command สำหรับเริ่มต้น run application (ตอน run container)
CMD ["node", "index.js"]
เรามาลองทำการ build image
# command
docker build -t <ชื่อ image ที่ต้องการตั้ง> -f <ตำแหน่ง dockerfile> <path เริ่มต้นที่จะใช้ run docker file>
# example ที่เราจะ run
docker build -t node-server -f ./Dockerfile .
# ถ้าเกิดเป็น Dockerfile อยู่แล้ว (ไม่ใช่ชื่ออื่น) สามารถย่อเหลือเพียงแค่
docker build -t node-server .
เมื่อลอง run ด้วยคำสั่ง docker build
ดู docker ก็จะเริ่ม process build ตามคำสั่งที่อยู่ใน Dockerfile ไปก็จะได้ผลลัพธ์ออกมาประมาณนี้
เมื่อจบคำสั่ง = build เรียบร้อยแล้ว ลองใช้คำสั่ง docker images
ทำการ recheck image ดูว่า bulid เรียบร้อยแล้วหรือไม่ ? ถ้าถูกต้องเราจะ เจอ image ชื่อเดียวกับที่เราตั้งจาก -t ของคำสั่งออกมาได้
ลองทำการ run ออกมาด้วยคำสั่ง docker run
แต่คราวนี้จะต้องมีการ map port ออกมา เพื่อให้สามารถ access ไปยัง port ภายในที่ run node อยู่ได้ ด้วย -p
# command
docker run -p <port ภายนอก>:<port ภายใน> <ชื่อ image>
# example สำหรับ run เคสนี้
docker run -p 8000:8000 node-server
เมื่อทำการ run จะได้หน้าตาประมาณนี้ออกมา (เหมือนกับเวลาเรา run node index.js
)
เราจะได้ผลลัพธ์ออกมาเหมือนกันได้
ทีนี้คำสั่ง docker run
อาจจะไม่สะดวก เวลาที่อนาคตเราจะต้อง run container หลายตัว เนื่องจากตอนใช้คำสั่ง มันจะเป็นการ run แบบ foreground (เราจะอยู่หน้าจอ process นั้นไว้ด้วย) เราจะเปลี่ยนให้ docker run
มา run แบบ background แทน
ทีนี้เราลอง ctrl+c ออกมาก่อน (ทำการ quit process) หากใคร quit ไม่ได้ ให้เปิด terminal (หรือ cmd ใหม่) แล้วใช้คำสั่งนี้
docker rm -f $(docker ps -a -q)
ถ้าถูกต้อง จะต้องยิง API localhost:8000 ไม่ได้แล้ว เปลี่ยนมาใช้คำสั่งนี้สำหรับการ run แทน
# เพิ่ม -d เพื่อให้สามารถ run background ได้
docker run -d -p 8000:8000 node-server
หลังจาก run จะขึ้นเป็น hash ตัวนึงออกมา (ไม่ขึ้นเป็นคำว่า run at localhost แบบเดิม)
เมื่อเราลองใช้คำสั่ง docker ps
จะสามารถดู container ทั้งหมดที่กำลัง run อยู่ได้ และจะเจอ container ที่ run โดยใช้ image node-server ขึ้นมา
- คำสั่ง
docker ps
ใช้สำหรับเช็ค container ที่กำลัง run อยู่ตอนนี้ได้
ก็จะเจอว่า มี container node-server run อยู่และสามารถยิง localhost:8000/hello-world ได้เหมือนเดิม
สำหรับ log ของ node (ที่พิมพ์คำว่า localhost) สามารถตรวจสอบได้จากการใช้คำสั่ง docker logs
# command
docker logs <ชื่อ container>
# example (ดูตามชื่อข้างหลังใน docker ps)
docker logs charming_ishizaka
เมื่อลองใช้คำสั่ง docker logs
ก็จะเจอ log ตามภาพนี้ได้
FYI: จริงๆเราสามารถตั้งชื่อ Container name ได้เช่นกัน ด้วยการใส่ --name ได้
# เพิ่ม name มา
docker run -d -p 8000:8000 --name my-container node-server
# ตอน log (ดูเพิ่มเติมจากตอน docker ps ได้)
docker log my-container