- Published on
จัดลำดับการ start services ใน docker-compose
- Authors
- Name
- Somprasong Damyos
- @somprasongd
จัดลำดับการ start services ใน docker-compose
ถ้าเราโปรเจคของเรามีการใช้งาน docker container หลายๆ ตัว วิธีการที่จัดการ container ทั้งหมด เราจะใช้ docker-compose เป็นตัวจัดการ เมื่อเราสั่งรัน ตัว docker-compose จะทำการรันทุกๆ services (containers) ขึ้นมาพร้อมๆ กัน แบบสุ่มลำดับการรันไม่แน่นอนในแต่ละครั้งที่
ก็ดูเหมือนจะไม่มีปัญหาอะไร แต่ถ้าเรามี service api ที่จะทำการเชื่อมต่อฐานข้อมูลตอนเริ่มต้นการทำงาน แต่ service ยังไม่เริ่มรันเลย ก็จะทำให้ service api ของเรานั้นรันไม่สำเร็จไปด้วย
ในบทความนี้จะใช้ตัวอย่างของการรัน Kong API Gateway ซึ่งจะติดตั้งตามลำดับ ดังนี้
- ระบบฐานข้อมูล postgres
- รัน migration เพื่อเตรียมฐานข้อมูลสำหรับ kong
- kong api gateway
สร้าง docker-compose.yml
เริ่มจากสร้าง docker-compose.yml โดยอ้างอิงวิธีการติดตั้งตามจาก docs นี้
version: '3.8'
services:
kong-database:
image: postgres:9.6-alpine
restart: always
ports:
- '5432:5432'
environment:
- TZ=Asia/Bangkok
- PGTZ=Asia/Bangkok
- POSTGRES_USER=kong
- POSTGRES_PASSWORD=kongpass
- POSTGRES_DB=kong
volumes:
- pg-data:/var/lib/postgresql/data
kong-migrate:
image: kong/kong-gateway:2.8.1.4-alpine
environment:
- KONG_DATABASE=postgres
- KONG_PG_HOST=kong-database
- KONG_PG_USER=kong
- KONG_PG_PASSWORD=kongpass
command: 'kong migrations bootstrap'
kong-gateway:
image: kong/kong-gateway:2.8.1.4-alpine
environment:
- KONG_DATABASE=postgres
- KONG_PG_HOST=kong-database
- KONG_PG_USER=kong
- KONG_PG_PASSWORD=kongpass
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
- KONG_PROXY_ERROR_LOG=/dev/stderr
- KONG_ADMIN_ERROR_LOG=/dev/stderr
- KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl
- KONG_ADMIN_GUI_URL=http://localhost:8002
ports:
- 8000:8000
- 8443:8443
- 8001:8001
- 8444:8444
- 8002:8002
- 8445:8445
- 8003:8003
- 8004:8004
konga:
image: pantsel/konga
environment:
- NODE_ENV=development
- TOKEN_SECRET=konga_token_secret
ports:
- 1337:1337
volumes:
pg-data:
ทดลองรัน docker-compose up
docker-compose up
Creating network "kong_default" with the default driver
Creating volume "kong_pg-data" with default driver
Starting kong_konga_1 ... done
Starting kong_kong-gateway_1 ... done
Starting kong_kong-migrate_1 ... done
Starting kong_kong-database_1 ... done
จะเห็นว่า services ทั้งหมดจะ start ขึ้นมาพร้อมๆ กัน แต่ไม่ได้เรียงลำดับตามที่เราต้องการ
จัดลำดับโดยใช้ depends_on
เราจะใช้ depends_on
เพิ่มเข้าไปใน service เพื่อบอกให้ service ตัวนั้นหยุดรอ service อื่นๆ ที่เป็น dependencies ทั้งหมดเริ่ม start ให้หมดก่อน แล้วจึงค่อย start ตัวมันเองขึ้นมา
โดยสิ่งที่ต้องการ คือ ต้องให้ kong-database เริ่ม start ก่อน kong-migrate และ kong-gateway
version: '3.8'
services:
kong-database:
image: postgres:9.6-alpine
# ...
kong-migrate:
image: kong/kong-gateway:2.8.1.4-alpine
# ...
+ depends_on:
+ - kong-database
kong-gateway:
image: kong/kong-gateway:2.8.1.4-alpine
environment:
# ...
ports:
# ...
+ depends_on:
+ - kong-database
konga:
image: pantsel/konga
# ...
volumes:
pg-data:
เมื่อสั่งรัน docker-compose up
จะเห็นว่า services ของ kong นั้นได้เริ่ม start หลังจาก postgres แล้ว
docker-compose up
Creating network "kong_default" with the default driver
Creating volume "kong_pg-data" with default driver
Creating kong_konga_1 ... done
Creating kong_kong-database_1 ... done
Creating kong_kong-gateway_1 ... done
Creating kong_kong-migrate_1 ... done
แต่จะพบว่าทั้ง kong-migrate และ kong-gateway จะมี error ไม่สามารถทำงานได้เนื่องจาก service kong-database นั้นยังไม่พร้อมใช้งานนั้นเอง
kong-migrate_1 | Error: [PostgreSQL error] failed to retrieve PostgreSQL server_version_num: connection refused
kong-gateway_1 | 2022/08/26 13:31:39 [error] 1#0: init_by_lua error: /usr/local/share/lua/5.1/kong/cmd/utils/migrations.lua:33: Database needs bootstrapping or is older than Kong 1.0
เพิ่มเงื่อนไขให้กับ depends_on
การใช้งานเราสามารถกำหนดเงื่อนเพิ่มเข้าไปได้ว่าจะให้เริ่ม start เมื่อไหร่ โดยมีเงื่อนอยู่ 3 แบบ คือ
service_started
: จะเหมือนกับแบบไม่ใส่เงื่อนไข คือ เริ่ม start ทันที หลังจาก service ที่ระบุ start แล้วservice_healthy
: คือ รอจนกว่า services ที่ระบุ พร้อมทำงานแล้ว โดยดูจากสถานะเป็น “healthy” โดยจะต้องใช้ healthcheck เป็นตัวทดสอบservice_completed_successfully
: คือ รอจนกว่า services ที่ระบุนั้นจะรันสำเร็จแล้ว
การใช้งาน service_healthy
จากปัญหาก่อนหนัานี้ เราจะใช้ service_healthy
เป็นตัวกำหนดเงื่อนให้รอให้ kong-database พร้อมใช้งานก่อน ซึ่งสามารถกำหนดได้ ดังนี้
- เพิ่ม
healthcheck
ให้กับ postgres โดยจะใช้test
เป็นตัวใช้คำสั่งสำหรับการทดสอบ
kong-database:
image: postgres:9.6-alpine
restart: always
ports:
- '5432:5432'
environment:
- TZ=Asia/Bangkok
- PGTZ=Asia/Bangkok
- POSTGRES_USER=kong
- POSTGRES_PASSWORD=kongpass
- POSTGRES_DB=kong
volumes:
- pg-data:/var/lib/postgresql/data
+ healthcheck:
+ test: pg_isready -U kong -h 127.0.0.1
+ interval: 5s
+ timeout: 5s
+ retries: 5
- กำหนดเงื่อนไขให้กับ
depends_on
โดยใช้condition
kong-migrate:
image: kong/kong-gateway:2.8.1.4-alpine
environment:
- KONG_DATABASE=postgres
- KONG_PG_HOST=kong-database
- KONG_PG_USER=kong
- KONG_PG_PASSWORD=kongpass
command: 'kong migrations bootstrap'
+ depends_on:
+ kong-database:
+ condition: service_healthy
kong-gateway:
image: kong/kong-gateway:2.8.1.4-alpine
environment:
- KONG_DATABASE=postgres
- KONG_PG_HOST=kong-database
- KONG_PG_USER=kong
- KONG_PG_PASSWORD=kongpass
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
- KONG_PROXY_ERROR_LOG=/dev/stderr
- KONG_ADMIN_ERROR_LOG=/dev/stderr
- KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl
- KONG_ADMIN_GUI_URL=http://localhost:8002
ports:
- 8000:8000
- 8443:8443
- 8001:8001
- 8444:8444
- 8002:8002
- 8445:8445
- 8003:8003
- 8004:8004
+ depends_on:
+ kong-database:
+ condition: service_healthy
เมื่อลองรัน docker-compose up ดู รอบนี้จะพบว่า kong-migrate นั้นสามารถรันได้เลย แต่ kong-gateway ยังไม่สามารถรันได้ เนื่องจากยัง migrate ฐานข้อมูลยังไม่เสร็จนั้นเอง
kong-gateway_1 | 2022/08/26 13:51:06 [error] 1#0: init_by_lua error: /usr/local/share/lua/5.1/kong/cmd/utils/migrations.lua:37: New migrations available; run 'kong migrations up' to proceed
kong-gateway_1 | stack traceback:
kong-gateway_1 | [C]: in function 'error'
kong-gateway_1 | /usr/local/share/lua/5.1/kong/cmd/utils/migrations.lua:37: in function 'check_state'
kong-gateway_1 | /usr/local/share/lua/5.1/kong/init.lua:539: in function 'init'
kong-gateway_1 | init_by_lua:3: in main chunk
kong-gateway_1 | nginx: [error] init_by_lua error: /usr/local/share/lua/5.1/kong/cmd/utils/migrations.lua:37: New migrations available; run 'kong migrations up' to proceed
kong-gateway_1 | stack traceback:
kong-gateway_1 | [C]: in function 'error'
kong-gateway_1 | /usr/local/share/lua/5.1/kong/cmd/utils/migrations.lua:37: in function 'check_state'
kong-gateway_1 | /usr/local/share/lua/5.1/kong/init.lua:539: in function 'init'
kong-gateway_1 | init_by_lua:3: in main chunk
kong_kong-gateway_1 exited with code 1
ซึ่งเราจะต้องเพิ่มเงื่อนให้ kong-gateway รอให้ kong-migrate ทำงานให้เสร็จก่อนนั้นเอง
การใช้งาน service_completed_successfully
การที่จะกำหนดให้ kong-gateway รอให้ kong-migrate ทำงานให้เสร็จก่อนนั้น เราจะใช้เงื่อนแบบ service_completed_successfully
มาช่วยในการตรวจสอบ โดยมีวิธีการกำหนด แบบนี้
kong-gateway:
image: kong/kong-gateway:2.8.1.4-alpine
environment:
- KONG_DATABASE=postgres
- KONG_PG_HOST=kong-database
- KONG_PG_USER=kong
- KONG_PG_PASSWORD=kongpass
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
- KONG_PROXY_ERROR_LOG=/dev/stderr
- KONG_ADMIN_ERROR_LOG=/dev/stderr
- KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl
- KONG_ADMIN_GUI_URL=http://localhost:8002
ports:
- 8000:8000
- 8443:8443
- 8001:8001
- 8444:8444
- 8002:8002
- 8445:8445
- 8003:8003
- 8004:8004
depends_on:
kong-database:
condition: service_healthy
+ kong-migrate:
+ condition: service_completed_successfully
ทดสอบโดยให้ลบ volumn ของ postgres ออกไปก่อน แล้วลองรันใหม่อีกครั้ง จะเห็นว่า services มีลำดับการ start ตามที่เรากำหนดไว้
docker-compose down -v
docker-compose up
Creating network "kong_default" with the default driver
Creating volume "kong_pg-data" with default driver
Creating kong_kong-database_1 ... done
Creating kong_konga_1 ... done
Creating kong_kong-migrate_1 ... done
Creating kong_kong-gateway_1 ... done
Attaching to kong_konga_1, kong_kong-database_1, kong_kong-migrate_1, kong_kong-gateway_1
และทุก services สามารถทำงานได้แล้ว เมื่อดูจาก log จะเห็น kong-gateway นั้นจะเริ่ม start เมื่อ kong-migrate จบการทำงานเรียบร้อยแล้วตามที่กำหนดไว้
kong_kong-migrate_1 exited with code 0
kong-gateway_1 | 2022/08/26 13:59:42 [warn] 1#0: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /usr/local/kong/nginx.conf:6
kong-gateway_1 | nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /usr/local/kong/nginx.conf:6
kong-gateway_1 | 2022/08/26 13:59:42 [notice] 1#0: [lua] license_helpers.lua:123: read_license_info(): [license-helpers] could not decode license JSON: No license found
สรุป
เมื่อเราต้องการกำหนดลำดับการ start services ใน docker-compose สามารถทำได้โดยการใช้ depends_on มากำหนด service dependencies เพื่อสั่งให้ทุก dependencies start ขึ้นมาก่อน หรือจะใช้เงื่อนไขแบบอื่นก็ได้ เช่น service_healthy
คือ รอจนกว่า service dependencies นั้นๆ จะมีสถานะเป็น “healthy” หรือ service_completed_successfully
คือ รอจนกว่า service dependencies นั้นๆ จะทำงานเสร็จแล้ว
ไฟล์ docker-compose.yml แบบเต็มๆ ดูได้ที่นี่