Docker 101: Shipping Application with Container

เคยเจอปัญหาดีพลอยแอพลิเคชันขึ้นบนเซิฟเวอร์แล้วเจอปัญหาในการรันหรือเปล่า เช่น version node ไม่ตรงกับที่ต้องการ , ไม่สามารถอัพเดตได้ เพราะมีแอพอื่นรันอยู่ หรือเทสบนเครื่องเสร็จแล้วใช้งานได้ปกติ แต่พอดีพลอยจริงแล้วเกิดปัญหาสารพัด ปัญหานี้จะหมดไปเพียงแค่เราใช้ Doggy เอ้ย Docker

It's Work

What is Docker?

Docker

Docker เป็น Container สำหรับใช้ในการดีพลอยเว็บเซอร์วิสต่างๆ ซึ่งจะช่วยเราในการ Bundle ทุกอย่างที่จำเป็นสำหรับการรันแอพลิเคชันของเราไปด้วยกัน

Docker Structure

โดย Container แต่ละตัวนั้นสามารถทำการแยก Library ที่ต้องการใช้ได้ และทำการรันบน Docker Engine อีกที ดังนั้นเราสามารถนำ Container นั้นไปรันบนเครื่องไหนก็ได้ที่ลง Docker ไว้ โดยการรันก็จะเป็น Environment เดียวกันหมดเพราะเป็น Config ชุดเดียวกัน อีกทั้งยังช่วยให้เราสามารถ Scale ได้ง่ายด้วย

Get Stated

Install Docker Engine

วิธีลงก็แสนง่ายดาย เดี๋ยวเราจะมาลองลงเล่นในเครื่องเราก่อนแล้วกันนะครับ ผมใช้ osx อยู่ วิธีการลงง่ายๆแค่ไปโหลดมา

https://www.docker.com/products/docker#/mac

ลงเสร็จก็เข้าไปที่ terminal เลยครับ แล้วรัน command

docker

ลองดูกันก่อนว่าใช้เวอร์ชันอะไร

docker --version

เท่านี้ก็มี docker ในเครื่องแล้วครับ ต่อไปก็จะไปลองเล่นกัน

Docker Images

บน Docker นั้นเราจะทำการ bundle ทุกอย่างที่จำเป็นในการรันแอพลิเคชันของเราด้วย docker ซึ่งพอเราแพคเสร็จ มันก็จะออกมาเป็น Docker Images เนี่ยแหละครับ แล้วก็สามารถเอา Images ตัวนี้ ไปรันที่เครื่องไหนก็ได้ที่ลง docker ไว้แล้ว

ซึ่งตัว Docker Images ก็จะมีตัวสำเร็จรูปที่คนอื่นทำไว้แล้วให้เราสามารถโหลดมาใช้และรันได้เลย ซึ่งจะถูกเก็บไว้บน Docker Hub ซึ่งเป็น Repository สำหรับการเก็บ Docker Images คล้ายๆเราใช้ git แล้วเราเก็บโค้ดไว้บน github หรือ bitbucket เลย ซึ่งปัจจุบันกำลังอัพเดตเป็นตัวใหม่ที่ชื่อ https://store.docker.com

เราสามารถดึง public docker images ลงมารันบนเครื่องเราได้ หรือจะสมัคร account แล้วใช้เก็บ docker images ที่เราสร้างขึ้นมาก็ได้เช่นกัน

Docker Hub - hub.docker.com

Pull Images

เราจะมาลองดึง Docker Images ที่มีอยู่บน Docker Hub มารันบนเครื่องกันก่อนนะครับ ในตัวอย่างผมจะลองไปดึง ubuntu images มาแล้วรัน hello world บน bash

ก่อนอื่นเริ่มจาก pull ลงมาก่อน ด้วยคำสั่ง docker pull [ชื่อ image]

docker pull ubuntu

มาดูกันว่ามันลงมาอยู่ที่เครื่องเราหรือยัง รันคำสั่ง

docker images

ก็จะเห็นรายชื่อ images ทั้งหมดที่มีอยู่บนเครื่องเรานะครับ นั่นไง ubuntu มาแล้ว

Ubuntu Image

หากเราต้องการลบอิมเมจตัวไหนทิ้ง ก็สามารถใช้คำสั่ง

docker rmi [image-name หรือ id]
docker rmi ubuntu

Run Images

เมื่อทำการ pull images มาแล้ว ก็สั่งรันได้เลย ด้วยคำสั่ง docker run [image] [cmd ....]

เราจะลองรัน ubuntu จากนั้นรัน echo “hello world” ใน container ตัวนั้นนะครับ

docker run ubuntu echo "hello world"

จะเห็นได้ว่ามันทำการรัน images ที่ชื่อ ubuntu จากนั้นรันคำสั่ง echo “hello world” ใน container จากนั้นก็ exit ออกมา เพราะจบการทำงานแล้ว

ทีนี้มาดูกันว่าเรารัน images อะไรไปแล้วบ้าง ด้วยคำสั่ง

docker ps -a

จะเห็นได้ว่า เรารัน image ubuntu ด้วย command “echo ‘hello world’” เราจะเห็น column STATUS ที่แสดงสถานะว่าแอพเรายังรันอยู่ดีหรือเปล่านะครับ

Run with Options

เรามาดู Options ที่เราต้องใช้บ่อยๆในการรัน Docker Images กันก่อน

  1. -d หรือ deamon mode นั่งเอง จะทำการรันแบบ background process ครับ
  2. --name [name] เป็นการตั่งชื่อให้กับ container ที่กำลังรันอยู่นั่นเอง ( แสดงผลในคอลัมสุดท้าย หากไม่ตั้ง ตัว docker จะทำการสุ่มชื่อมาให้เราอัตโนมัติ )
  3. -p [HOST_PORT:CONTAINER_PORT] หรือ port เป็นการ binding port ระหว่าง host และ container เดี๋ยวจะอธิบายในหัวข้อถัดไปนะครับ
  4. -v [HOST_PATH:CONTAINER_PATH] หรือ volume เป็นการ binding path ของไฟล์บน host และ container ก็เดี๋ยวจะอธิบายในหัวข้อถัดไปอีกทีครับ

ที่ต้องใช้กันบ่อยๆในช่วงเริ่มต้นก็ประมานนี้ครับ
 

Network Mode

บน Docker นั้น เราสามารถที่จะเปิด Port เพื่อให้เข้าถึงจากภายนอกได้ โดยจะมีอยู่ 3 แบบ

  1. Bridge เป็นรูปแบบ Default โดยเราจะต้องทำการ Mapping เองระหว่าง Port ของ Container และ Port ของ Host
  2. Host เป็นรูปแบบที่ Container จะใช้ Host Port ในการ Binding Network เลย
  3. None คือไม่มีการใช้ Network ของ Container

ยังมี Network ในรูปแบบของ User-defined Network หรือการ Custom ได้ ซึ่งสามารถดูรายละเอียดเพิ่มเติมได้ที่นี่
 

Example

ทีนี้เราจะมาลอง Options ของ Network แบบ Bridge ซึ่งเป็นแบบ Default ดูนะครับ โดยจะให้ไป pull images ที่ชื่อว่า tui2tone/helloworld-express ซึ่งเป็น Express App โง่ๆตัวนึง โดยรันอยู่ที่ port 3000

docker pull tui2tone/helloworld-express

พอเราได้ Images มาแล้วก็จะมารันด้วย Opts port ดู โดยเราต้องทำการ Mapping Port จาก HOST:CONTAINER ตามที่เราต้องการ ในทีนี้ก็จะใช้ port 3000 บน Host เป็น Port เดียวกับ Container และให้รันเป็น deamon นะครับ

docker run -d -p 3000:3000 tui2tone/helloworld-express

Run HelloWorld Image

เราก็จะเข้าเว็บได้ผ่าน http://localhost:3000 ลองเอา -p 3000:300 ออกดูนะครับ แล้วจะเข้าไม่ได้นะ เพราะไม่ได้ Mapping Port ไว้ของ Container เอาไว้

HelloWorld Url

Volume

การใช้งานไฟล์ใน Container นั้นเราสามารถเรียกใช้ไฟล์หรือเขียนไฟล์บน Container ได้ แต่ไฟล์เหล่านั้นจะเป็น Temp ไฟล์ หากเราทำการปิด Container ที่รันอยู่นั้นทิ้งไป ไฟล์นั้น็จะหายไปด้วย หรือในกรณีที่เราไม่ต้องการให้ config ไฟล์เข้าไปอยู่ใน Container เราก็สามารถเรียกใช้ config ไฟล์จาก Host ได้ โดย Docker จะเรียกสิ้งนี้ว่า Volume

เราสามารถทำการ Mapping Folder บน Host เราเข้ากับโฟลเดอร์ที่อยู่บน Container ได้ เมื่อมีการเรียกใช้ไฟล์ในโฟลเดอร์นั้นบน Container ก็จะมาเรียกใช้ไฟล์บน Host แทน

เช่น

-v /xxx/app:/app

หมายถึงในโฟลเดอร์ที่ /xxx/app บนเครื่อง Host แต่ในตัว Container จะสามารถเรียกใช้งานไฟล์ในโฟลเดอร์นั้นได้ผ่าน /app

Example

มาลอง Mount Volume กันด้วย Docker Images tui2tone/helloworld-express-vol กันครับ ซึ่งจะเหมือนกับอิมเมจข้อที่แล้วทุกประการ ยกเว้น เราจะไม่มีไฟล์ app.js พ่วงเข้าไปให้ในอิมเมจให้ด้วย ต้องสร้างกันเองบนโฮสและเรียกใช้ผ่าน Volume

docker pull tui2tone/helloworld-express-vol

โดยให้ทำการสร้าง folder ขึ้นมาแล้วและสร้างไฟล์ app.js โดยมี Content ดังนี้

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!')
})

จากนั้นก็รันเพื่อลง Dependencies ในโฟลเดอร์เดียวกัน

npm i express

แล้วทำการรัน Docker โดยใช้ Absolute Path ( ดูได้จากคำสั่ง pwd บน Linux และ OSX )

docker run -d -p 3000:3000 -v /Users/xxx/Documents/helloworld:/usr/app tui2tone/helloworld-express-vol

Run HelloWorld Image

แล้วก็จะเข้าเว็บได้ผ่าน http://localhost:3000 เช่นเดิม

HelloWorld Url

Stop Container

หากเราจะทำการหยุด container ที่รันอยู่ก็ใช้คำสั่ง docker stop

docker stop [container_name หรือ id]
docker stop tui2tone/helloworld-express

Start Container

เราสามารถใช้คำสั่ง docker start เพื่อรัน container ที่ stop อยู่ได้ โดยจะรันขึ้นมาใหม่ใน configuration เดิมที่เราใช้ตอน docker run ไป

docker start [container_name หรือ id]
docker start tui2tone/helloworld-express

Remove Container

หาก Container ไหนที่เราไม่ใช้แล้ว ก็สามารถลบออกไปจากระบบได้ โดยต้องเป็น container ที่ stop อยู่เท่านั้น

docker rm [container_name หรือ id]
docker rm tui2tone/helloworld-express

Dockerfile

Dockerfile เปรียบเสมือนการชุดคำสั่งในการสร้าง Docker Images ซึ่งเราจะใช้หลักการคล้ายๆการดีพลอยในวิธีปกติ มาเขียนเป็นชุดคำสั่ง เช่น

  1. ใช้ images ubuntu
  2. git clone repo มาลง
  3. รันแอพ

ซึ่ง command คร่าวๆก็จะมีประมาน 5 คำสั่ง

มาดูรายละเอียดของแต่ละคำสั่งกัน

FROM ubuntu
RUN apt-get install nodejs
RUN npm i express
ADD app.js app.js

คือการนำไฟล์ app.js ไปไว้บน ~/app.js ของอิมเมจนั้น

ADD app /user/app

คือการนำโฟลเดอร์ app ไปไว้เป็นโฟลเดอร์ /user/app ของอิมเมจ

WORKDIR /user/app
CMD node app.js

process ที่เราจะรันใน cmd คอมมานสุดท้ายนั้นจะต้องเป็น foreground process เนื่องจาก docker จำเป็นต้อง track การรันที่ foreground process หากรันเป็น background docker จะไม่สามารถ track ได้และจะหยุดทำงานในทันที

เราจะมาลอง build Node.js(Express) app ง่ายๆด้วย Docker กันครับ

Node.JS App Example

ก่อนอื่นลง express กันก่อน npm i express จากนั้นก็สร้างไฟล์ขึ้นมาไฟล์นึง ผมใช้ชื่อว่า app.js ก็แล้วกันนะครับ โดยมีโค้ดไว้รัน server helloworld แบบง่ายๆเลย

var express = require('express')
var app = express()

app.get('/', function (req, res) {
  res.send('Hello World!')
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
});

จากนั้นทำการสร้าง Dockerfile ขึ้นมา โดยมีเนื้อหาดังนี้

FROM mhart/alpine-node
WORKDIR /usr/app
RUN npm i express
COPY app.js /usr/app/app.js

CMD node app.js

อธิบายคร่าวๆคือ

Build Images

คำสั่งในการรัน build docker images จาก Dockerfile เราสามารถรันได้ด้วย

docker build -t [name] [path to dockerfile]

เราก็จะรันคำสั่งสร้างอิมเมจของเราจาก Dockerfile จากหัวข้อที่แล้ว

docker build -t tui2tone/helloworld-express-build .

เราก็จะได้อิมเมจมาแล้ว ลองรันดูสิทีนี้

docker run -d -p 3000:3000 tui2tone/helloworld-express-build

Docker Image Build

ก็สามารถเข้าเว็บแอพเราได้ผ่าน http://localhost:3000 แล้ว

HelloWorld Url

Push to Docker Hub

หากเราต้องการอัพโหลดอิมเมจของเราขึ้นไปบน Docker Hub เราสามารถเข้าไปสมัครสมาชิค จากนั้นทำการ Login ด้วยคำสั่ง

docker login

เมื่อ Login เสร็จแล้วเราหากเราจะทำการ push images ขึ้นไปบน account ของเรานั้น images ที่เราจะ build จะต้องมี prefix เป็นชื่อ account ของเราเช่น ผมใช้ชื่อ tui2tone ก็จะเป็น tui2tone/image-name เป็นต้น

จากนั้นก็ใช้คำสั่ง push ขึ้นไป

docker push tui2tone/helloworld-express-build

Docker Hub Push

มันก็จะขึ้นอยู่บน hub.docker.com เรียบร้อยแล้ว เราก็สามารถเข้าไปดูรายละเอียดอื่นๆได้ในเว็บนะครับ

Docker Hub Image

Docker Compose

docker compose ใช้สำหรับเก็บ command และ config ที่เราจะรันตัว docker images ซึ่งหากเราทำการรันแล้วครั้งหนึ่ง เราจะไม่สามารถแก้คำสั่งรันได้ เช่น

docker run -d -p 3000:3000 --name helloworld tui2tone/helloworld-express

เมื่อเราทำการรันไปครั้งนึงแล้ว จะมี container ตัวนึงรันอยู่ หากเราต้องการทำจะ mapping port เพิ่มละ ทำยังไงดี ตอนนี้มีวิธีเดียวเท่านั้น คือ หยุดและ รันใหม่ และ พิมคำสั่งใหม่หมด ซึ่งก็จะเป็นความยากลำบากของเราในการดีพลอยแอพใหม่หากมีการเปลี่ยน config ต่างๆในการรัน แต่ docker ก็ไม่ใจร้ายขนาดนั้น สร้าง docker-compose มาให้เราเก็บไฟล์ config ในการรัน และช่วยในการรีรัน container ในการณีที่มีการเปลี่ยนแปลง config นั่นเอง

Configuration

ไฟล์ config นั้นจะใช้ในชื่อ docker-compose.yml โดยมีส่วนประกอบหลักคือ คำสั่งที่เราใช้ในการรันนั่นเอง

version: '3.1'
services:
	web:
		image: tui2tone/helloworld-express-build
		container_name: helloworld
		ports:
			- 3000:3000

เราสามารถ config ได้ตามคำสั่ง option ของการ run ปกติ เช่น -v ใช้ volumes / -p ใช้ ports สามารถดู config อื่นๆได้ตามนี้ Docker Compose Reference

Runup Docker Compose

จากนั้นก็รันด้วยคำสั่งของ docker-compose up เพื่อสั่งให้รัน container ตาม config นี้ และใส่ -d เพื่อรัน deamon

docker-compose up -d

Docker Hub Image

สามารถ stop ได้ผ่านคำสั่ง

docker-compose down

References

Author

Anonymous

Web Developer

Nextzy Technology Co,. LTD.