Docker Compose healthcheck เขียนอย่างไรให้พร้อมใช้งาน
เข้าใจการประเมิน healthcheck ของ Docker Compose เหตุผลที่ depends_on ไม่ได้รอความพร้อม และตัวอย่าง readiness check สำหรับ Postgres กับแอป พร้อมข้อผิดพลาดที่พบบ่อย
Docker Compose healthcheck ทำงานอย่างไรจริง
Docker Compose healthcheck คือคำสั่งหนึ่งรายการที่ Docker เรียกใช้ภายในคอนเทนเนอร์ตามช่วงเวลาที่กำหนด Docker จะไม่อ่าน log ไม่ตรวจสอบ port และไม่ตรวจสอบรายการ process แต่จะเรียกใช้คำสั่ง อ่าน exit code และจัดเก็บสถานะเดียวไว้บนคอนเทนเนอร์ ได้แก่ starting, healthy หรือ unhealthy ค่า exit code 0 หมายถึงสถานะ healthy ค่า exit code อื่นหมายถึงสถานะ unhealthy และ Docker สงวน exit code 2 ไว้ ดังนั้นอย่าส่งค่าดังกล่าวโดยตั้งใจ
นี่คือกลไกทั้งหมด ปัญหา healthcheck เกือบทั้งหมดมีสาเหตุเดียวกัน คือคำสั่งที่เขียนขึ้นตอบคำถามคนละข้อกับคำถามที่ต้องการตรวจสอบ คู่มือนี้ถือว่าคุณทราบวิธี เขียน compose file บน VPS แล้ว และเริ่มอธิบายต่อจากจุดที่ stack เริ่มทำงานผิดลำดับ
services:
api:
image: ghcr.io/example/api:1.4.0
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/healthz"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30sค่า test มีรูปแบบที่ใช้งานได้ 2 แบบ รายการที่เริ่มด้วย CMD จะเรียกใช้คำสั่งโดยตรงโดยไม่ผ่าน shell ดังนั้น pipe, && และการขยายค่าตัวแปรจะไม่ทำงาน รายการที่เริ่มด้วย CMD-SHELL จะส่งส่วนที่เหลือเป็นสตริงเดียวให้กับ /bin/sh -c ภายในคอนเทนเนอร์ ซึ่งเป็นรูปแบบที่ต้องใช้เมื่อการตรวจสอบต้องอาศัยไวยากรณ์ของ shell สตริงทั่วไปจะถูกตีความเป็น CMD-SHELL รายการที่มี ["NONE"] เพียงรายการเดียวจะลบ healthcheck ที่ image กำหนดไว้ใน Dockerfile
การตรวจสอบจะทำงานภายในคอนเทนเนอร์ ดังนั้น binary ทุกตัวที่ระบุในคำสั่งต้องมีอยู่ใน image นั้น ตรวจสอบเรื่องนี้ก่อน เพราะ image ขนาดเล็กที่ไม่มี curl จะทำให้คอนเทนเนอร์มีสถานะ unhealthy อย่างถาวร โดยสาเหตุจะไม่ปรากฏใน application log ทดสอบด้วยตนเองดังนี้:
docker compose exec api curl --versionหากไม่มี binary ระบบจะแสดง OCI runtime exec failed: exec: "curl": executable file not found in $PATH: unknown โดยทั่วไป image ที่ใช้ Alpine จะมี BusyBox wget มาให้แทน ดังนั้นคำสั่งตรวจสอบจะเป็น ["CMD", "wget", "-q", "-O", "-", "http://localhost:8080/healthz"]
, และ ทำงานร่วมกันอย่างไร
การตั้งค่า 5 รายการจะควบคุมเวลา ค่าเริ่มต้นมาจาก Docker Engine ไม่ใช่ Compose
interval: ระยะเวลาระหว่างการตรวจสอบ 2 ครั้ง หลังจากคอนเทนเนอร์พ้นช่วงเริ่มต้นแล้ว ค่าเริ่มต้นคือ 30stimeout: ระยะเวลาสูงสุดที่การตรวจสอบ 1 ครั้งใช้ได้ ก่อนที่ Docker จะยุติการตรวจสอบและนับครั้งนั้นเป็นความล้มเหลว ค่าเริ่มต้นคือ 30sretries: จำนวนครั้งที่ล้มเหลวติดต่อกันก่อนที่สถานะจะเปลี่ยนเป็นunhealthyค่าเริ่มต้นคือ 3start_period: ช่วงเวลาเผื่อหลังจากคอนเทนเนอร์เริ่มทำงาน ค่าเริ่มต้นคือ 0sstart_interval: ความถี่ที่การตรวจสอบทำงานระหว่างช่วงเริ่มต้น ค่าเริ่มต้นคือ 5s และต้องใช้ Docker Engine 25.0 หรือใหม่กว่า
กฎสำคัญคือ ระหว่างช่วงเริ่มต้น การตรวจสอบที่ล้มเหลวจะ ไม่ นับรวมใน retries และคอนเทนเนอร์จะยังคงอยู่ในสถานะ starting เมื่อการตรวจสอบสำเร็จเป็นครั้งแรก คอนเทนเนอร์จะเปลี่ยนเป็น healthy และช่วงเริ่มต้นจะสิ้นสุดทันที แม้เวลาส่วนใหญ่ของช่วงดังกล่าวจะยังไม่ได้ใช้ หากช่วงเริ่มต้นสิ้นสุดลงขณะที่การตรวจสอบยังล้มเหลว การนับตามปกติจะเริ่มขึ้น และคอนเทนเนอร์ต้องล้มเหลวติดต่อกัน retries ครั้งก่อนที่จะถูกกำหนดเป็น unhealthy
ดังนั้น เวลาสูงสุดนับจากคอนเทนเนอร์เริ่มทำงานจนถึง unhealthy คือ start_period บวกกับ retries คูณด้วย interval แล้วบวก timeout เมื่อใช้ค่าจากไฟล์ข้างต้น จะเท่ากับ 30 บวก 5 คูณ 13 ซึ่งเท่ากับ 95 วินาที จดตัวเลขนี้ไว้ก่อนกำหนด timeout สำหรับ deploy เพราะ rollout ที่ยุติหลังจาก 60 วินาทีจะไม่มีทางเห็นคอนเทนเนอร์นี้เข้าสู่สถานะสุดท้ายได้
ข้อผิดพลาดที่พบบ่อยคือเพิ่มค่า retries เพื่อรองรับการเริ่มทำงานที่ช้า วิธีนี้ใช้ได้ในครั้งแรก แต่จะส่งผลเสียตลอดไป: service ที่ต้องใช้ retries 8 ครั้งจึงเริ่มทำงานได้ จะยอมให้เกิดความล้มเหลวติดต่อกัน 8 ครั้งใน production ก่อนที่ระบบจะตรวจพบ ใช้ start_period แทน เพราะค่านี้มีผลเฉพาะก่อนการตรวจสอบจะสำเร็จเป็นครั้งแรก
เหตุใด depends_on เพียงอย่างเดียวจึงไม่รับประกันอะไร
รูปแบบย่อของ depends_on เป็นสาเหตุหลักของความสับสนส่วนใหญ่
api:
depends_on:
- dbความหมายมีเพียงอย่างเดียว: ให้เริ่มคอนเทนเนอร์ db ก่อนคอนเทนเนอร์ api Compose จะรอให้สร้างและเริ่มคอนเทนเนอร์แล้ว แต่จะไม่รอให้ PostgreSQL เริ่มต้นระบบครั้งแรกเสร็จ และจะไม่รอให้พอร์ต 5432 ยอมรับการเชื่อมต่อ แอปพลิเคชันของคุณเริ่มทำงานหลังจากนั้นประมาณ 1 วินาที เชื่อมต่อไปยังพอร์ตที่ยังไม่มีโปรแกรมใดกำลังรอรับการเชื่อมต่อ แล้วจบการทำงาน ใน log คุณจะเห็น Connection refused หรือ FATAL: the database system is starting up เมื่อเซิร์ฟเวอร์ทำงานแล้วแต่ยังอยู่ระหว่างการกู้คืน
รูปแบบเต็มคือสิ่งที่ผู้ใช้ต้องการจริง:
api:
depends_on:
db:
condition: service_healthy
restart: true
migrate:
condition: service_completed_successfullycondition มี 3 ค่า service_started มีความหมายเดียวกับรูปแบบย่อ service_healthy จะหน่วงการเริ่มบริการที่ขึ้นต่อกันไว้จนกว่าบริการที่เป็น dependency จะรายงานสถานะ healthy ซึ่งจะมีความหมายก็ต่อเมื่อ dependency นั้นกำหนด healthcheck ไว้ในไฟล์ compose หรือใน image ของบริการนั้น service_completed_successfully จะรอคอนเทนเนอร์แบบทำงานครั้งเดียว เช่น การย้ายฐานข้อมูล จนกว่าจะจบการทำงานด้วยสถานะ 0
มีฟิลด์เพิ่มเติมอีก 2 รายการอยู่ถัดจาก condition restart: true จะสั่งให้ Compose เริ่มบริการนี้ใหม่หลังจากอัปเดตบริการที่เป็น dependency แล้ว required: false จะเปลี่ยน dependency ที่หายไปจากข้อผิดพลาดให้เป็นคำเตือน
ขณะนี้มาถึงข้อจำกัดที่ทำให้หลายคนเข้าใจผิด เงื่อนไขเหล่านี้จะถูกประเมินเมื่อเริ่ม stack เงื่อนไขเหล่านี้กำหนดลำดับการเริ่มต้น ไม่ใช่กฎสำหรับควบคุมดูแล หากฐานข้อมูลเริ่มต้นใหม่ตอน 3 ในตอนเช้า จะไม่มีการประเมิน service_healthy ใหม่ และจะไม่มีการเริ่มแอปพลิเคชันของคุณใหม่เพื่อให้เป็นไปตามเงื่อนไขนั้นอีกครั้ง โค้ดแอปพลิเคชันยังคงต้องเชื่อมต่อใหม่ด้วยตนเอง docker compose up --no-deps api จะข้ามกลไกทั้งหมดนี้โดยออกแบบไว้แล้ว และการเริ่มคอนเทนเนอร์โดยตรงด้วย docker start ก็เช่นกัน
เขียนการตรวจสอบที่ทดสอบความพร้อม ไม่ใช่แค่การมีอยู่ของโพรเซส
การตรวจสอบเช่น pgrep nginx เป็นเพียงการพิสูจน์ว่ามีรายการโพรเซสอยู่ในตารางโพรเซสเท่านั้น แต่มันไม่ได้พิสูจน์ว่าบริการนั้นสามารถตอบสนองต่อคำขอได้จริง แอปพลิเคชันเว็บสามารถคงสถานะการเปิด socket ไว้ได้นานหลังจากที่ database pool ล่มไปแล้ว และการตรวจสอบโพรเซสก็จะยังคงแสดงสถานะปกติ (green) ตลอดช่วงเวลาที่ระบบขัดข้อง
ให้สั่งให้คอนเทนเนอร์ทำงานตามหน้าที่ที่มันถูกสร้างมา:
- สำหรับบริการ HTTP ให้ร้องขอไปยัง endpoint จริง เนื่องจาก
-fทำให้curl -fsSออกจากโปรแกรมด้วยรหัส 22 เมื่อเซิร์ฟเวอร์ตอบกลับด้วยสถานะ 400 ขึ้นไป ดังนั้นสถานะ 500 จากแอปที่พังจะถือเป็นการตรวจสอบที่ล้มเหลว เอกสารของ curl เองระบุว่า-fไม่ถือว่าปลอดภัยต่อความผิดพลาด (fail-safe) โดยเฉพาะกับสถานะการยืนยันตัวตน 401 และ 407 ดังนั้นให้ชี้การตรวจสอบไปยัง endpoint ที่ไม่จำเป็นต้องเข้าสู่ระบบ - สำหรับ PostgreSQL ให้ใช้
pg_isreadyซึ่งจะออกรหัส 0 เมื่อเซิร์ฟเวอร์ยอมรับการเชื่อมต่อ, 1 เมื่อปฏิเสธการเชื่อมต่อ, 2 เมื่อไม่มีการตอบสนองเลย และ 3 เมื่อพารามิเตอร์ที่คุณระบุไม่ถูกต้อง - สำหรับ Redis ให้ใช้
redis-cli pingซึ่งจะพิมพ์PONGและออกรหัส 0 - สำหรับ MariaDB อิมเมจอย่างเป็นทางการจะมีสคริปต์
healthcheck.shมาให้ และhealthcheck.sh --connect --innodb_initializedคือรูปแบบที่ผู้ดูแลอิมเมจแนะนำไว้ในเอกสาร
pg_isready มีกับดักหนึ่งที่ควรทราบ ในการเริ่มทำงานครั้งแรกด้วยไดเรกทอรีข้อมูลที่ว่างเปล่า อิมเมจ postgres อย่างเป็นทางการจะรันการตั้งค่าเริ่มต้นกับเซิร์ฟเวอร์ชั่วคราวที่ฟังเฉพาะบน Unix socket เท่านั้น pg_isready ที่ไม่มีการระบุ host จะใช้ socket นั้น ดังนั้นมันจึงสามารถตอบว่า "ยอมรับการเชื่อมต่อ" ได้ในขณะที่พอร์ต TCP 5432 ยังคงปิดอยู่สำหรับแอปพลิเคชันของคุณ ให้ระบุการตรวจสอบไปที่ TCP โดยตรงแล้วปัญหานี้จะหมดไป เพราะเซิร์ฟเวอร์ชั่วคราวจะไม่ตอบสนองผ่านช่องทางนั้น
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
start_period: 30sเครื่องหมายดอลลาร์ซ้อนกันไม่ใช่การพิมพ์ผิด Compose จะขยายค่า $VAR ด้วยตัวเองในขณะที่อ่านไฟล์ ซึ่งจะทำให้ค่าจากสภาพแวดล้อมของโฮสต์ของคุณถูกฝังลงไปในการตรวจสอบ $$ จะทำการ escape ให้เหลือเพียง $ ตัวเดียว เพื่อให้เชลล์ภายในคอนเทนเนอร์ขยายค่าโดยอ้างอิงจากสภาพแวดล้อมของคอนเทนเนอร์เอง
สแตก Postgres และแอปที่เริ่มทำงานตามลำดับที่ถูกต้อง
services:
db:
image: postgres:17.5
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env}
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
start_period: 30s
restart: unless-stopped
api:
image: ghcr.io/example/api:1.4.0
environment:
DATABASE_URL: postgres://appuser:${DB_PASSWORD}@db:5432/appdb
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/healthz"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30s
ports:
- "127.0.0.1:8080:8080"
restart: unless-stopped
volumes:
pgdata:เริ่มสแตกและดูการเปลี่ยนแปลงของสถานะ:
docker compose up -d
docker compose psคอลัมน์ STATUS แสดงสถานะ health ไว้ในวงเล็บ คู่ที่พร้อมใช้งานจะแสดง Up 41 seconds (healthy) ในทั้งสองแถว ระหว่างที่ฐานข้อมูลยังเริ่มต้นระบบไม่เสร็จ db จะแสดง Up 4 seconds (health: starting) และ api จะไม่อยู่ในรายการ Compose สร้างคอนเทนเนอร์ api แล้ว แต่ชะลอการเริ่มต้นไว้จนกว่า db จะรายงานสถานะ healthy คอนเทนเนอร์นี้ไม่ปรากฏเพราะ docker compose ps แสดงเฉพาะคอนเทนเนอร์ที่กำลังทำงานตามค่าเริ่มต้น ให้เรียกใช้ docker compose ps -a ในช่วงนั้น แล้ว api จะปรากฏในสถานะ created
หากต้องการดูว่า health check ผ่านหรือล้มเหลวเพราะเหตุใด ให้ดู health log:
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q db)"Docker เก็บผลลัพธ์ล่าสุดไว้จำนวนหนึ่ง โดยแต่ละรายการมีเวลาเริ่มต้น เวลาสิ้นสุด ExitCode และ Output ของคำสั่ง ระบบจะตัดทอนเอาต์พุตที่จัดเก็บไว้ ดังนั้น health check ที่พิมพ์เนื้อหาหน้ายาวจะทำให้รายการ log ใช้งานไม่ได้ ควรให้ health check แสดงผลน้อยที่สุด
สิ่งที่ Docker ทำเมื่อคอนเทนเนอร์มีสถานะไม่พร้อมใช้งาน
ไม่มีอะไรเกิดขึ้น นี่คือคำตอบที่ทำให้คนส่วนใหญ่ประหลาดใจมากที่สุด
Docker Engine บนโฮสต์เดียวจะไม่เริ่มคอนเทนเนอร์ที่มีสถานะไม่พร้อมใช้งานใหม่ นโยบาย restart: unless-stopped จะตอบสนองเมื่อโพรเซสหลักสิ้นสุดลง แต่คอนเทนเนอร์ที่มีสถานะไม่พร้อมใช้งานยังไม่ได้สิ้นสุดการทำงาน คอนเทนเนอร์อาจค้างอยู่ที่ unhealthy เป็นเวลาหนึ่งสัปดาห์ โดยที่ Compose ไม่ดำเนินการใดกับคอนเทนเนอร์นั้น โหมด Swarm จะแทนที่ task ที่มีสถานะไม่พร้อมใช้งาน แต่สแต็ก Compose แบบปกติบนเซิร์ฟเวอร์เครื่องเดียวจะไม่ทำเช่นนั้น
จึงมีทางเลือกที่เหมาะสมอยู่ 2 ทาง ให้โพรเซสสิ้นสุดการทำงานเมื่อโพรเซสรู้ว่าตนเองทำงานผิดพลาด เพื่อให้นโยบายการเริ่มใหม่มีเหตุให้ทำงาน หรือเฝ้าดูสถานะจากภายนอกและแจ้งเตือนเมื่อพบสถานะดังกล่าว การกำหนด มอนิเตอร์ Uptime Kuma ให้ตรวจสอบ endpoint เดียวกับที่ healthcheck เรียกใช้ จะทำให้ dependency ที่ขัดข้องแสดงขึ้นในทั้งสองส่วน และคุณจะได้รับแจ้งจากมอนิเตอร์แทนที่จะได้รับแจ้งจากผู้ใช้ หากทราฟฟิกไปยังแอปผ่าน reverse proxy Traefik โปรดจำไว้ว่ามุมมองของ proxy ที่มีต่อ backend แยกจากสถานะ health ของ Docker ดังนั้นการตรวจสอบอย่างหนึ่งจึงไม่ครอบคลุมอีกอย่างหนึ่ง
การแก้ไขการตรวจสอบที่ไม่เคยมีสถานะ healthy
เรียกใช้คำสั่งที่เหมือนกันทุกประการด้วยตนเองใน container เดียวกัน แล้วตรวจสอบ exit code:
docker compose exec api curl -fsS http://localhost:8080/healthz; echo "exit=$?"exit=0 ขณะที่ container ยังคงรายงานสถานะ unhealthy แสดงว่า compose test ของคุณแตกต่างจากที่เพิ่งพิมพ์ โดยทั่วไปเกิดจากใช้ CMD ในจุดที่ต้องใช้ไวยากรณ์ของ shell
ข้อผิดพลาดที่เหลือส่วนใหญ่เกิดจาก 2 กรณี กรณีแรกคือใช้ port ไม่ถูกต้อง healthcheck ทำงานภายใน container จึงต้องใช้ port ของ container และห้ามใช้ port ของ host ที่เผยแพร่ไว้ เมื่อใช้ ports: - "8080:3000" แอปพลิเคชันจะรับการเชื่อมต่อที่ port 3000 และการตรวจสอบที่ http://localhost:8080 จะล้มเหลวตลอด แม้เว็บไซต์จะทำงานได้ตามปกติในเบราว์เซอร์ กรณีที่สองคือใช้ host ไม่ถูกต้อง ภายในการตรวจสอบ localhost หมายถึง container เดียวกัน ซึ่งเหมาะสำหรับตรวจสอบ container นั้นเอง แต่ไม่เหมาะสำหรับตรวจสอบ container อื่น หากต้องตรวจสอบ container อื่น ให้ใช้ชื่อ service เช่น db
ยังมีอีกกรณีหนึ่งที่ควรระบุแยกต่างหาก: healthcheck ผ่าน แต่ผู้ใช้ยังพบข้อผิดพลาด กรณีนี้เกิดขึ้นเมื่อ endpoint ส่งค่า 200 แบบคงที่โดยไม่ได้ตรวจสอบการทำงานจริงใด ๆ readiness endpoint ที่ไม่เคย query ฐานข้อมูลจะไม่สามารถบอกได้ว่าฐานข้อมูลหยุดทำงานแล้ว ให้ endpoint เรียกใช้ query จริงที่มีค่าใช้จ่ายต่ำ 1 รายการ
FAQ
เหตุใดแอปของฉันจึงยังเชื่อมต่อไม่สำเร็จ ทั้งที่ depends_on ระบุว่าฐานข้อมูลพร้อมใช้งานแล้ว
เนื่องจาก condition: service_healthy จะถูกประเมินเพียงครั้งเดียวเมื่อเริ่มต้น stack หลังจากนั้นจะไม่ตรวจสอบต่อ หาก container ของฐานข้อมูลเริ่มต้นใหม่ในภายหลัง Compose จะไม่เริ่มแอปพลิเคชันของคุณใหม่เพื่อทำตามเงื่อนไขอีกครั้ง ดังนั้นโค้ดของแอปพลิเคชันต้องมีตรรกะสำหรับการเชื่อมต่อใหม่และการลองซ้ำของตนเอง เงื่อนไขนี้ไม่มีผลเมื่อคุณเริ่มต้น container เดียวด้วย docker start หรือ docker compose up --no-deps
จำเป็นต้องมี healthcheck หรือไม่ หาก image กำหนด healthcheck ไว้อยู่แล้ว
โดยทั่วไปไม่จำเป็น และการเขียนทับมักทำให้การทำงานแย่ลง เนื่องจากผู้ดูแล image ทราบว่า readiness ของซอฟต์แวร์นั้นหมายถึงอะไร ให้เพิ่ม healthcheck ของคุณเองเฉพาะเมื่อ healthcheck ของ image ไม่เหมาะกับการตั้งค่าของคุณ เช่น ตรวจสอบ port ที่คุณเปลี่ยนไปแล้ว หากต้องการปิด healthcheck ของ image ให้กำหนด test: ["NONE"] หรือ disable: true ใน service
healthcheck ควรใช้ curl หรือ wget
ให้ใช้ตัวที่มีอยู่แล้วใน image และตรวจสอบด้วย docker compose exec <service> curl --version ก่อนนำไปใช้งาน หลาย image ที่ใช้ Debian ไม่มีทั้งสองตัว ส่วน image ที่ใช้ Alpine มี BusyBox wget อย่าเพิ่ม package ลงใน image เพียงเพื่อเรียกใช้ healthcheck หากซอฟต์แวร์มี client ของตนเองอยู่แล้ว เช่น pg_isready หรือ redis-cli
container ที่มีสถานะ unhealthy จะถูกเริ่มต้นใหม่โดยอัตโนมัติหรือไม่
Docker Engine จะไม่ทำเช่นนั้นบน host เดียว นโยบายการเริ่มต้นใหม่จะตอบสนองเมื่อ process สิ้นสุดการทำงาน ไม่ใช่เมื่อสถานะ health เปลี่ยนเป็น unhealthy ดังนั้น container ที่มีสถานะ unhealthy จะยังทำงานอยู่และยังขัดข้อง จนกว่าจะมีสิ่งอื่นดำเนินการ ให้ process สิ้นสุดการทำงานเมื่อพบข้อผิดพลาด หรือใช้ monitor ภายนอกเพื่อแจ้งเตือนเมื่อสถานะเปลี่ยนแปลง
start_period ควรมีระยะเวลาเท่าใด
ควรนานพอสำหรับการเริ่มต้นครั้งแรกที่ถูกต้องและใช้เวลานานที่สุดตามที่คุณวัดได้ แล้วเผื่อเวลาเพิ่มเติมอีกเล็กน้อย ให้จับเวลาด้วย docker compose up โดยใช้ volume ว่าง เนื่องจากการเริ่มต้นฐานข้อมูลครั้งแรกช้ากว่าการเริ่มต้นครั้งต่อ ๆ ไปมาก หาก start period นานเกินไป จะเพียงทำให้ผลการตัดสิน unhealthy ครั้งแรกล่าช้าเท่านั้น แต่หากกำหนดจำนวนครั้งที่ลองซ้ำสูงเกินไป จะทำให้การตรวจสอบอ่อนลงตลอดอายุการทำงานของ container ซึ่งเป็นความล้มเหลวที่ร้ายแรงกว่า