Docker ฉบับรวบรัด 2020
Tags: docker
Published: 2020-08-04 08:32:03

Docker เป็นเครื่องมือที่มาเปลี่ยนวิธีการติดตั้งโปรแกรม จากเดิมที่จะทำการติดตั้งบน Virtual Machines หลายๆ เครื่อง ก็เปลี่ยนมาติดตั้งบน Containers แทน แถมยังใช้งานง่าย สร้าง template (Docker image)ไว้ครั้งเดียว ก็จะสามารถติดตั้งโปรแกรมที่ทำงานเหมือนเดิมได้ตลอด และยังสามารถแจกจ่าย Docker image นี้ได้โดยใช้ docker registry อย่างเช่น [Docker Hub](https://hub.docker.com/) ซึ่งก็มี Docker image ทั้งจากทาง official และที่คนอื่นๆ ทำเอาไว้แล้ว เราสามารถเอามาใช้งานต่อได้เลย ถือว่าสะดวกมากๆ

---

##หัวข้อ
1. ทำความรู้จักกับ Docker
2. การติดตั้ง Docker
3. รัน Docker Container
4. สร้าง Docker Image
5. ทำความเข้าใจ Docker Network
ุ6. Docker Volume คืออะไร
7. ทุกอย่างจะง่ายขึ้นด้วย Docker Compose

---

##1. ทำความรู้จักกับ Docker
ในบทความนี้อยากให้ทำความเข้าใจอยู่ 2 เรื่อง

- **เรื่องแรกคือ Docker ต่างจาก Virtual Machines อย่างไร**

![Picture from https://docs.docker.com/get-started/](https://dev-to-uploads.s3.amazonaws.com/i/7b8n6qqoankuletqarog.png)

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

###ตัวอย่างถ้าต้องติดตั้งโปรแกรมตามนี้

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3nizndtskl0yejza5bx3.jpg)

ถ้าติดตั้งบน VM จะสร้าง VM ขึ้นมาถึง 7 ตัว

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dip6of9cqwpxg06fjrct.jpg)

แต่ถ้าติดตั้งบน Docker ทุกอย่างถูกรันเป็น container อยู่บนเครื่องเดียวกัน

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zxj050428dtzuqyv7xyd.jpg)

- **เรื่องที่สอง การทำงานของ Docker** นั้นมี architecture เป็นแบบ client-server โดยที่ client (cli) จะส่งคำสั่งผ่าน Rest Api ไปยัง server (Docker daemon) ว่าต้องการทำอะไร

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5qmc9ylfpdu0xrpd5pmc.png)

ซึ่งจะเห็นว่า Docker นั้นจะมีองค์ประกอบอยู่ด้วยกัน 4 ส่วนคือ
1. Container
2. Image
3. Network
4. Volume

และเมื่อมีการสั่งรันจะมี workflow คือ Docker daemon จะดูที่เครื่องตัวเองก่อนว่ามี image นั้นแล้วหรือยัง ถ้ายังไม่มีจะไปดึงมาจาก Registry มาเก็บไว้ก่อน แล้วค่อยรัน image นั้น เป็น container

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rwufrjj4fnux1l3eerag.jpg)

##2. การติดตั้ง Docker

Docker นั้นรองรับการติดตั้งทั้ง Linux, Mac และ Windows 10 โดยสามารถดูได้จาก [Get Docker](https://docs.docker.com/get-docker/)

> Windows 10 Home ก็สามารถติดตั้ง Docker Desktop ได้ผ่านทาง WSL2 

หรือจะลองเล่นผ่าน [Play with Docker](https://labs.play-with-docker.com/
) ก็ได้

##3. รัน Docker Container
เริ่มจาก [Docker Hub](https://hub.docker.com/) กันก่อน ซึ่งก็คือ Registry ที่เก็บรวบรวม images ต่างๆ เอาไว้ และเราสามารถดึงมาใช้งานได้ทันที โดยใช้คำสั่ง `docker container COMMAND` ผ่านทาง cli 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/g0eajqn4v0i0aidhmy9a.PNG)

###วิธีรัน Docker Container
ใช้คำสั่ง `docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]`

```bash
$ docker container run --rm alpine echo "Hello World"

Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
df20fa9351a1: Pull complete
Digest: sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321
Status: Downloaded newer image for alpine:latest
Hello World
```
**มาทำความเข้าใจ**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4wrjhkgm3ewidvuk6e7l.png)

> `--rm` คือ options ที่เอาไว้บอกว่าให้ลบ container นี้ทันทีที่จบการทำงาน เนื่องจากโดยปกติ
> `alpine` คือ ชื่อของ image ที่ต้องการจะรัน โดยถ้าไม่ระบุเลขเวอร์ชัน ระบบจะเอาตัวล่าสุด (latest) มาให้เสมอ ซึ่งวิธีการระบุคือ `alpine:3.12` แบบนี้คือบอกว่าให้ใช้ image เวอร์ชัน 3.12
> `echo` คือ คำสั่งที่จะรันเมื่อ container นั้นๆ เริ่มการทำงาน ซึ่งจะไม่ระบุ ก็จะใช้ คำสั่ง default ที่ถูระบุเอาไว้ใน image ตัวนั้นๆ แทน
> `"Hello World"` คือ argument ของคำสั่งที่ระบุก่อนหน้านี้

ซึ่งจะสังเกตได้ว่าเมื่อรันคำสั่งนี้ครั้งแรก ในเครื่องจะยังไม่มี image ชื่อ alpine เวอร์ชัน latest ตัว Docker จะทำการดึง (pull) image มาจาก Registry มาเก็บไว้ในเครื่องก่อนที่จะรัน แต่ถ้าลองรันคำสั่งนี้อีกครั้งจะพบว่าสามารถทำงานได้เลย

```bash
$ docker container run --rm alpine echo "Hello World"
Hello World
```

###โหมดการทำงาน
คำสั่ง `docker container run` นั้น โดยปกติ เมื่อทำงานเสร็จก็จบหยุดการทำงานไปเลย แต่จริงๆ แล้วยังมีโหมดการทำงานอีก 2 โหมด คือ

- Interactive mode คือ เราสามารถเข้าไปใช้งานภายใน container ได้ โดยใช้ option `-it` เช่น ถ้าต้องการจากเข้าไปสร้างไฟล์ชื่อ `hello.txt`

```bash
$ docker container run -it alpine sh
/ # ls
bin etc lib mnt root sbin sys usr dev home media proc run srv tmp var
/ # touch hello.txt
/ # ls
bin etc home media proc run srv tmp var dev hello.txt lib mnt root sbin sys usr
/ # exit
```

แต่ถ้าลองรันคำสั่งเดิมอีกครั้ง จะพบว่าไม่มีไฟล์ `hello.txt` นั่นก็เพราะว่าทุกครั้งที่สั่ง `docker container run` จะได้ container ใหม่เสมอ

```bash
$ docker container run -it alpine sh
/ # ls
bin etc lib mnt root sbin sys usr dev home media proc run srv tmp var
/ # exit
```

> วิธีดูว่ามี container อะไรที่กำลังรันอยู่บ้างใช้คำสั่ง `docker container ls` หรือถ้าต้องการดูทั้งหมดรวมกับที่หยุดทำงานไปแล้วใช้คำสั่ง `docker container ls -a`

```bash
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
d4e4e2d06bc1        alpine              "/bin/sh"           30 seconds ago      Exited (0) 27 seconds ago                       lucid_blackburn
6b161b84dd2f        alpine              "/bin/sh"           33 seconds ago      Exited (0) 30 seconds ago                       magical_swartz
```

> จะเห็นว่าชื่อของ container นั้นจะถูกสุ่มตั้งชื่อมาให้ ถ้าต้องการที่จะกำหนดชื่อให้ใช้ option `--name`

```bash
$ docker container run --name my_container_name alpine echo "Hello World"
Hello World
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
e7f5de85e4ff        alpine              "echo 'Hello World'"    14 seconds ago      Exited (0) 13 seconds ago                       my_container_name
d4e4e2d06bc1        alpine              "/bin/sh"           30 seconds ago      Exited (0) 27 seconds ago                       lucid_blackburn
6b161b84dd2f        alpine              "/bin/sh"           33 seconds ago      Exited (0) 30 seconds ago                       magical_swartz
```

> วิธีลบ container ที่ไม่ได้ใช้งาน ใช้คำสั่ง `docker container rm` ตามด้วย id หรือ name และสามารถลบได้ครั้งละมากกว่า 1 ตัว เช่น

```bash
$ docker container rm d4e4e2d06bc1 my_container_name
d4e4e2d06bc1
my_container_name
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
6b161b84dd2f        alpine              "/bin/sh"           33 seconds ago      Exited (0) 30 seconds ago                       magical_swartz
```

> ถ้าต้องการลบทุก container ที่หยุดทำงานแล้วใช้คำสั่ง `docker container prune`

- Background mode คือ การสั่งให้ container นั้นๆ ยังคงทำงานอยู่เบื้องหลังเมื่อสั่งรัน โดยใช้ option `-d`

```bash
$ docker container run -d --name alpine alpine sleep 6000
f0f9ee6d40b4f596612d93f73d26a8cc88484514d743b1950998a23710e3ec80
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
f0f9ee6d40b4        alpine              "sleep 6000"        55 seconds ago      Up 54 seconds                           alpine
```

จะเห็นว่าสถานะยังเป็น Up คือ ยังทำงานอยู่นั้นเอง และเมื่อทำงานอยู่ในโหมดนี้ ถ้าต้องการรันคำสั่งใน container ทำได้โดยใช้คำสั่ง `docker container exec [OPTIONS] CONTAINER COMMAND [ARG...]`

```bash
$ docker container exec alpine ls
bin etc lib mnt root sbin sys usr dev home media proc run srv tmp var
```

> การใช้ร่วมกับ interactive mode

```bash
$ docker container exec -it alpine sh
/ # ls
bin etc lib mnt root sbin sys usr dev home media proc run srv tmp var
/ # exit
```

> ถ้าต้องการหยุดการทำงาน ใช้คำสั่ง `docker container stop CONTAINER`
> ถ้าต้องการใช้งานใหม่อีกครั้ง (จะทำงานตามคำสั่งที่รันไว้ครั้งแรก) ใช้คำสั่ง `docker container start CONTAINER`

```bash
$ docker container stop alpine
alpine
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
f0f9ee6d40b4        alpine              "sleep 6000"        14 minutes ago      Exited (137) 13 seconds ago                       alpine
$ docker container start alpine
alpine
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
f0f9ee6d40b4        alpine              "sleep 6000"        14 minutes ago      Up 2 seconds                            alpine
```

###Bind mount a volume
โดยปกติถ้าเราสามารถสร้างไฟล์ภายใน container ได้ แต่ทุกครั้งที่จะแก้ไขจะต้องเข้าไปแก้ไขข้างใน container หรือถ้าลบ container นั้นทิ้งไปแล้วไฟล์นั้นๆ ก็จะหายไปด้วย ซึ่งสามารถแก้ไขได้โดยการสร้างไฟล์ที่เครื่องของเรา แล้วค่อยทำการ binding volume เข้าไปใน container ก็จะแก้ปัญหานี้ได้

```bash
# Step 1: สร้างไฟล์ host/hello.txt ไว้ที่เครื่องของเราเอง
$ mkdir host
$ echo "Hello from host" >> host/hello.txt

# Step 2: รัน container และ binding volume จาก host ไปที่ container
$ docker container run -d -v $(pwd)/host:/container --name alpine2 alpine sleep 600

# Step 3: เข้าไปของใน container และแก้ไข container/hello.txt
$ docker container exec -it alpine3 sh
/ # cd container/
/container # ls
hello.txt
/container # cat hello.txt
Hello from host
/container # echo "Edit from container" >> hello.txt
/container # exit

# Step 4: ลองดูที่ไฟล์ host/hello.txt จะพบว่าได้ถูกแก้ไขไปด้วย
$ cat host/hello.txt
Hello from host
Edit from container

# Step 5: ให้แก้ไขที่ไฟล์ host/hello.txt
$ echo "Edit from host" >> host/hello.txt 

# Step 6: ให้เข้าไปใน container อีกครั้ง แล้วดูที่ไฟล์ ก็จะถูกแก้ไขด้วยเหมือนกัน
$ docker container exec -it alpine3 cat container/hello.txt
Hello from host
Edit from container
Edit from host
```

ทั้งหมดนี้ค่อนข้างครอบคลุมการใช้งานทั่วไปแล้วในส่วนของ Container แล้ว ถัดไปจะเป็นการสร้าง Image ขึ้นมาใช้งานเอง

##4. สร้าง Docker Image

##5. ทำความเข้าใจ Docker Network

ุ##6. Docker Volume คืออะไร

##7. ทุกอย่างจะง่ายขึ้นด้วย Docker Compose