Skip to main content

Remote Build Docker ด้วย Docker context CLI

Kongvut Sangkla
Tags:

Intro

สวัสดีครับ บทความนี้เป็นเนื้อหาที่เกี่ยวกับ Docker เหมาะสำหรับผู้ที่มีพื้นฐานเรื่อง Docker มาก่อน ซึ่งจะอธิบายถึงวิธีการใช้งาน Docker context

บทความนี้เป็นการสรุปไว้สำหรับทบทวน และมีตัวอย่างเล็กน้อยจากเนื้อหาอ้างอิง ซึ่งจะไม่ได้อธิบายเนื้อหามาก

TL; DR;

Docker context คือการสร้าง context ใด ๆ สำหรับการจัดการ Docker (daemon) เครื่องอื่น ๆ ในเครือข่ายผ่านวิธีการ Remote เช่น SSH หรือ ผ่าน TCP

บทความนี้จะแสดงวิธีใช้ Docker context เพื่อกำหนดเป้าหมายสภาพแวดล้อมที่แตกต่างกันสำหรับการปรับใช้ และสลับไปมาระหว่างกันได้อย่างง่ายดาย

Imgur

ตัวอย่าง Lab

สำหรับตัวอย่างไฟล์ Compose ประกอบไปด้วย 2 Services คือ frontend และ backend

  • frontend ประกอบไปด้วย nginx proxy ซึ่งจะ forward HTTP requests ไปที่ Go app server อีกที
  • backend ประกอบไปด้วย Go app server อย่างง่าย

Download ไฟล์ตัวอย่าง

มาลองทำแลปเล่น ๆ เพื่อทำความเข้าใจเรื่อง Context

สามารถ Download ไฟล์ตัวอย่างได้ที่นี่ https://github.com/dockersamples/nginx-gohttp หรือใช้ไฟล์ Compose จากอื่นแทนได้

Project structure
nginx-gohttp
├── README.md
├── backend
│ ├── Dockerfile
│ └── main.go
├── docker-compose.yml
└── frontend
├── Dockerfile
└── nginx.conf

2 directories, 6 files

โครงสร้างภายในไฟล์ docker-compose.yml

docker-compose.yml
version: "3.6"
services:
frontend:
build: frontend
ports:
- 8080:80
depends_on:
- backend
backend:
build: backend

1. ตัวอย่าง Deploy บน localhost

ตามปกติถ้าจะ Deploy app ก็จะไปที่ Directory จากนั้นก็รันคำสั่ง docker compose up -d

$ cd nginx-gohttp
$ docker compose up -d

Imgur

จากนั้นสามารถตรวจสอบ containers ที่รันอยู่ ด้วยคำสั่ง docker ps จะเห็นว่า port 80 ของ frontend นั้น mapped กับ port 8080 ของ localhost

$ docker ps

Imgur

ถ้าลองใช้คำสั่ง curl เรียก Web service port 8080 ก็จะได้รับข้อความดังนี้จาก Backend

$ curl localhost:8080

## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/


Hello from Docker!

2. ตัวอย่าง Deploy บน remote host

ตัวอย่างของ Application ที่สามารถ deploy บน Remote host ได้หลายวิธีโดยสมมุติถ้าเรามี SSH สำหรับ remote Docker host ด้วยวิธี key-based authentication (พยายามอย่าใช้วิธีแบบป้อน Password เมื่อ Deploying Application)

นี่คือ 3 วิธีสำหรับการ Deploy บน Remote host:

วิธีที่ 1 Manual deployment

วิธีแบบเบสิค ๆ ที่ใคร ๆ ก็คงเคยทำกัน 😅

  • Copy project files
  • สั่ง Run

ข้อเสีย ของวิธีนี้คือ ทุกการเปลี่ยนแปลงของ Project files คุณต้องทำการ Copy ไฟล์นั้น ๆ ไปที่เครื่องเป้าหมายแล้ว Re-run ใหม่ในทุก ๆ ครั้ง

วิธีที่ 1
$ scp -r hello-docker user@remotehost:/path/to/src 
$ #หรือ ใช้ git commit แล้ว git push จากนั้น git pull
$ ssh user@remotehost
$ cd /path/to/src/hello-docker
$ docker compose up -d

วิธีที่ 2 ใช้ DOCKER_HOST environment variable

วิธีนี้เป็นการกำหนดตัวแปรชั่วคราว DOCKER_HOST สำหรับกำหนด Docker engine ปลายทาง

เป็นวิธีที่ดี กว่าวิธีแรก แต่บางทีมันก็ค่อนข้างน่ารำคาญเพราะต้องการ set DOCKER_HOST ในทุก ๆ ครั้งหากต้องการเปลี่ยน Remote host

วิธีที่ 2
$ cd /path/to/src/hello-docker
$ DOCKER_HOST="ssh://user@remotehost" docker compose up -d

วิธีที่ 3 ใช้ Docker contexts

วิธีใช้ Docker context แต่ต้องสร้าง Context ขึ้นมาใช้งานเฉพาะ เป็นวิธีที่จะพูดถึงในบทความนี้

Docker Contexts เป็นวิธีที่ดีในการสลับไปมาระหว่าง Deployment targets โดยจะอธิบายในหัวข้อถัดไปในการใช้งานและความเข้าใจกับความง่ายและความรวดเร็วในการ Deployment

$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
...
remote ssh://user@remotemachine
$ cd hello-docker
$ docker --context remote compose up -d

Docker context

Contexts ใน Docker CLI นั้นมีความสามารถในการควบคุมและจัดการ Docker ได้หลาย endpoints คุณสามารถตั้งค่า Context สำหรับแต่ละ Hosts และสามารถสลับ Host ไปมาระหว่างกันได้

เมื่อ Context ได้ Activated แล้ว Docker จะสามารถ Remote กับ Host ได้โดยตรง ซึ่งบางครั้งเราอาจจะมีความจำเป็นต้องจัดการ Container ใน Production และด้วยคุณสมบัติ Context นี้สามารถทำให้คุณได้

สร้าง context สำหรับ Remote host

ก่อนจะใช้งาน Context ได้ต้องสร้าง Context ขึ้นมาก่อนดังนี้

$ docker context create remote --docker "host=ssh://user@remotemachine"
remote
Successfully created context "remote"

$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST... unix:///var/run/docker.sock swarm
remote ssh://user@remotemachine

ในส่วนของ Host ที่เชื่อมต่อเป็น SSH นั้นอย่าลืมว่าต้องเป็นการเชื่อต่อแบบ Key-based authentication ถึงจะ Remote host ได้

เราสามารถ List containers บน Remote host ด้วยการใช้ --context ออกมาได้ดังนี้

$ docker --context remote ps
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES

เราสามารถกำหนด "remote" context เป็น Default context ที่อนุญาตให้ใช้คำสั่ง Docker commands โดยตรงโดยที่ไม่จำเป็นต้องเพิ่ม --context ดังนี้

$ docker context use remote
remote
Current context is now "remote"

$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default Current DOCKER_HOST ... unix:///var/run/docker.sock swarm
remote * ssh://user@remotemachine

ใช้ docker compose จัดการ context

$ docker --context remote compose up -d
/tmp/_MEI4HXgSK/paramiko/client.py:837: UserWarning: Unknown ssh-ed25519 host key for 10.0.0.52: 'b047f5071513cab8c00d7944ef9d5d1fd'
Creating network "hello-docker_default" with the default driver
Creating hello-docker_backend_1 ... done
Creating hello-docker_frontend_1 ... done

$ docker --context remote ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ddbb380635aa hello-docker_frontend "nginx -g 'daemon of..." 24 seconds ago Up 23 seconds 0.0.0.0:8080->80/tcp hello-docker_web_1
872c6a55316f hello-docker_backend "/usr/local/bin/back..." 25 seconds ago Up 24 seconds hello-docker_backend_1

Compose deployments จัดการหลาย multiple targets

บางทีเราอาจจะต้องจัดการหลาย ๆ Environments เช่น development/test และจำเป็นต้องมีการสลับไปมา

สำหรับตัวอย่างนี้จะเป็น Contexts ทั้ง 3 แบบที่มีการจัดการแตกต่างกันดังนี้:

  • localhost สำหรับการรัน Docker engine ภายใน
  • remote host สำหรับการเชื่อมต่อและจัดการผ่าน SSH
  • docker-in-docker จะคล้าย ๆ แบบ remote host แต่จะเป็นการเชื่อมต่อกับ Docker daemon ผ่าน TCP
EnvironmentContext nameAPI endpoint
localhostdefaultunix:///var/run/docker.sock
remote hostremotessh://user@remotemachine
docker-in-dockerdindtcp://127.0.0.1:2375

จำลอง Docker อีกตัวใน Local ด้วยการรัน Docker-in-Docker container และ Mapped port 2375 กับ Localhost

$ docker run --rm -d -p "2375:2375" --privileged -e "DOCKER_TLS_CERTDIR=" --name dind docker:19.03.3-dind
ed92bc991bade2d41cab08b8c070c70b788d8ecf9dffc89e8c6379187aed9cdc
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed92bc991bad docker:19.03.3-dind "dockerd-entrypoint...." 17 seconds ago Up 15 seconds 0.0.0.0:2375->2375/tcp, 2376/tcp dind

สร้าง Context dind:

$ docker context create dind --docker "host=tcp://127.0.0.1:2375" --default-stack-orchestrator swarm
dind
Successfully created context "dind"

$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST ... unix:///var/run/docker.sock swarm
remote ssh://user@devmachine swarm
dind tcp://127.0.0.1:2375 swarm

ใช้ Context dind และสั่ง deploy

สร้าง Context และสั่ง deploy
$ docker context use dind
dind
Current context is now "dind"

$ docker compose up -d
Creating network "hello-docker_default" with the default driver
Creating hello-docker_backend_1 ... done
Creating hello-docker_frontend_1 ... done

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
951784341a0d hello-docker_frontend "nginx -g 'daemon of..." 34 seconds ago Up 33 seconds 0.0.0.0:8080->80/tcp hello-docker_frontend_1
872c6a55316f hello-docker_backend "/usr/local/bin/back..." 35 seconds ago Up 33 seconds hello-docker_backend_1
เรียกดู Containers ที่ Default context
$ docker --context default ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed92bc991bad docker:19.03.3-dind "dockerd-entrypoint...." 28 minutes ago Up 28 minutes 0.0.0.0:2375->2375/tcp, 2376/tcp dind
สั่ง Deploy บน Remote context
$ docker --context remote compose up -d
/tmp/_MEIb4sAgX/paramiko/client.py:837: UserWarning: Unknown ssh-ed25519 host key for 10.0.0.52: 'b047f5071513cab8c00d7944ef9d5d1fd'
Creating network "hello-docker_default" with the default driver
Creating hello-docker_backend_1 ... done
Creating hello-docker_frontend_1 ... done
เรียกใช้ Default context และสั่ง deploy
$ docker context use default
default
Current context is now "default"

$ docker compose up -d
Creating network "hello-docker_default" with the default driver
Creating hello-docker_backend_1 ... done
Creating hello-docker_frontend_1 ... done

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
077b5e5b72e8 hello-docker_frontend "nginx -g 'daemon of..." About a minute ago Up about a minute 0.0.0.0:8080->80/tcp hello-docker_frontend_1
fc01878ad14e hello-docker_backend "/usr/local/bin/back..." About a minute ago Up about a minute hello-docker_backend_1
ed92bc991bad docker:19.03.3-dind "dockerd-entrypoint...." 34 minutes ago Up 34 minutes 0.0.0.0:2375->2375/tcp, 2376/tcp dind

Application ตัวอย่างตอนนี้ได้รันแล้วบนทั้ง 3 Hosts ลองทดสอบเรียก Frontend service ของแต่ละ Host จากคำสั่งด้านล่าง

#default
$ curl localhost:8080

#dind
$ docker exec -it dind sh -c "wget -O – localhost:8080"

#remote
$ curl 10.0.0.52:8080

Output:

$ curl localhost:8080

## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/


Hello from Docker!

สรุป

การ Deploy แบบ Remote hosts ด้วย Docker Contexts กับ docker compose นั้นไม่ใช่เรื่องใหม่อะไรเพราะคุณสมบัตินี้สามารถทำได้นานแล้วบน Docker รุ่นใหม่

Docker Contexts นั้นมีประโยชน์ในการสั่ง Deploy ในแต่ละ Hosts ด้วยการสลับไปมาระหว่าง env ซึ่งทำให้ง่ายต่อการทดลองหรือการทำ PoC โดยที่ไม่ต้อง Copy files ที่มีการเปลี่ยนแปลงทุก ๆ ครั้งที่ต้องการ Deploy

References

Loading...