Docker-compose ตรวจสอบว่าการเชื่อมต่อ mysql พร้อมหรือไม่


96

ฉันพยายามตรวจสอบให้แน่ใจว่าที่เก็บแอปของฉันไม่ได้เรียกใช้การโยกย้าย / เริ่มต้นจนกว่าคอนเทนเนอร์ db จะเริ่มทำงานและพร้อมที่จะยอมรับการเชื่อมต่อ

ดังนั้นฉันจึงตัดสินใจใช้ healthcheck และขึ้นอยู่กับ option ใน docker compose file v2.0

ในแอพฉันมีสิ่งต่อไปนี้

app:
    ...
    depends_on:
      db:
      condition: service_healthy

ในทางกลับกันฐานข้อมูลมีการตรวจสุขภาพดังต่อไปนี้

db:
  ...
  healthcheck:
    test: TEST_GOES_HERE
    timeout: 20s
    retries: 10

ฉันได้ลองสองวิธีเช่น:

  1. ตรวจสอบให้แน่ใจว่าได้สร้าง db DIR แล้ว test: ["CMD", "test -f var/lib/mysql/db"]
  2. รับเวอร์ชัน mysql: test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. Ping ผู้ดูแลระบบ (ทำเครื่องหมายที่คอนเทนเนอร์ db ว่ามีประสิทธิภาพดี แต่ดูเหมือนจะไม่ใช่การทดสอบที่ถูกต้อง) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

ใครมีวิธีแก้ปัญหานี้หรือไม่?


คุณสร้างนักเทียบท่าสำหรับฐานข้อมูลหรือไม่? โปรดบอกฉันว่าข้อมูลของคุณอยู่นอกคอนเทนเนอร์นี้เพื่อประโยชน์ในการใช้งานของคุณ
Jorge Campos

หรืออย่างน้อยนี่คือเครื่องทดสอบ
Jorge Campos

นี่เป็นเพียงการพัฒนา / การทดสอบเท่านั้นตามวัตถุประสงค์จริง
John Kariuki

2
ฉันคิดว่าคุณควรใช้คำสั่งเพื่อเชื่อมต่อและเรียกใช้แบบสอบถามใน mysql ไม่มีตัวอย่างใดที่คุณให้ไว้ทำสิ่งนี้:mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Jorge Campos

1
@JorgeCampos โอเคขอบคุณ โดยปกติฉันมีคอนเทนเนอร์ db แต่ไดรฟ์ข้อมูลแผนที่สำหรับ dir ข้อมูล ดังนั้นหากคอนเทนเนอร์ลดลงข้อมูลจะยังคงอยู่ในการสร้างอินสแตนซ์ถัดไป
..

คำตอบ:


86
version: "2.1"
services:
    api:
        build: .
        container_name: api
        ports:
            - "8080:8080"
        depends_on:
            db:
                condition: service_healthy
    db:
        container_name: db
        image: mysql
        ports:
            - "3306"
        environment:
            MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
            MYSQL_USER: "user"
            MYSQL_PASSWORD: "password"
            MYSQL_DATABASE: "database"
        healthcheck:
            test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
            timeout: 20s
            retries: 10

คอนเทนเนอร์ api จะไม่เริ่มทำงานจนกว่าคอนเทนเนอร์ db จะแข็งแรง (โดยทั่วไปจนกว่า mysqladmin จะเปิดขึ้นและยอมรับการเชื่อมต่อ)


12
mysqladmin pingจะส่งคืนค่าบวกเท็จหากเซิร์ฟเวอร์กำลังทำงาน แต่ยังไม่ยอมรับการเชื่อมต่อ
halfpastfour.am

55
เฉพาะ FYI สำหรับคนปี 2017: conditionunder depends_onไม่รองรับในเวอร์ชัน 3+
Mint

@BobKruithof ฉันกำลังเผชิญกับปัญหาเดียวกัน ... มีวิธีแก้ปัญหาบางอย่างเช่นสถานะการนอนหลับหรือการออกเพื่อลองใหม่
Mukesh Agarwal

1
@dKen ดูคำตอบของฉันด้านล่างstackoverflow.com/a/45058879/279272ฉันหวังว่ามันจะได้ผลสำหรับคุณเช่นกัน
Mukesh Agarwal

1
ในการตรวจสอบโดยใช้รหัสผ่าน: test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- หากคุณกำหนดไว้MYSQL_ROOT_PASSWORDในenvironmentsส่วน
laimison

23

หากคุณกำลังใช้นักเทียบท่า-เขียนv3 + , conditionเป็นตัวเลือกของdepends_onได้รับการถอดออก

เส้นทางที่แนะนำคือการใช้ค่อนข้างwait-for-it, หรือdockerize wait-forในdocker-compose.ymlไฟล์ของคุณเปลี่ยนคำสั่งของคุณเป็น:

command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'

ฉันชอบเป็นการส่วนตัวwait-forเนื่องจากสามารถทำงานในคอนเทนเนอร์อัลไพน์ ( shเข้ากันได้ไม่มีการพึ่งพาbash ) ข้อเสียเปรียบขึ้นอยู่กับnetcatดังนั้นหากคุณตัดสินใจที่จะใช้ตรวจสอบให้แน่ใจว่าคุณได้netcatติดตั้งในคอนเทนเนอร์แล้วหรือติดตั้งใน Dockerfile ของคุณเช่น:

RUN apt-get -q update && apt-get -qy install netcat

ฉันยังแยกไฟล์wait-forโครงการเพื่อให้สามารถตรวจสอบสถานะ HTTP ที่ดีได้ (ใช้wget) จากนั้นคุณสามารถทำสิ่งนั้นได้:

command: sh -c 'bin/wait-for http://api/ping -- jest test'

PS: PR พร้อมที่จะรวมเข้าด้วยกันเพื่อเพิ่มขีดความสามารถดังกล่าวให้กับwait-forโครงการ


15

เท่านี้ก็น่าจะเพียงพอแล้ว

version: '2.1'
services:
  mysql:
    image: mysql
    ports: ['3306:3306']
    environment:
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypassword
    healthcheck:
      test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD

2
คู่คืออะไร$?
InsOp

5
ไวยากรณ์พิเศษของ @InsOp ที่คุณต้องใช้ในคำสั่งทดสอบการตรวจสอบความสมบูรณ์สำหรับการหลีกเลี่ยงตัวแปร env เริ่มต้นด้วย $ นั่นคือ $$ MYSQL_PASSWORD จะส่งผลให้เป็น $ MYSQL_PASSWORD ซึ่งจะส่งผลให้เป็น mypassword ในตัวอย่างที่เป็นรูปธรรมนี้
Maksim Kostromin

ด้วยเหตุนี้ฉันจึงเข้าถึงตัวแปร env ภายในคอนเทนเนอร์? ด้วย$Im เพียงครั้งเดียวที่เข้าถึงตัวแปร env จากโฮสต์แล้วฉันคิดว่า? ดีมากขอบคุณ!
InsOp

10

หากคุณสามารถเปลี่ยนคอนเทนเนอร์เพื่อรอให้ mysql พร้อมทำได้

หากคุณไม่มีการควบคุมคอนเทนเนอร์ที่คุณต้องการเชื่อมต่อกับฐานข้อมูลคุณสามารถลองรอพอร์ตเฉพาะ

เพื่อจุดประสงค์นั้นฉันใช้สคริปต์ขนาดเล็กเพื่อรอพอร์ตเฉพาะที่คอนเทนเนอร์อื่นเปิดเผย

ในตัวอย่างนี้myserverจะรอให้พอร์ต3306ของmydb container เข้าถึงได้

# Your database
mydb:
  image: mysql
  ports:
    - "3306:3306"
  volumes:
    - yourDataDir:/var/lib/mysql

# Your server
myserver:
  image: myserver
  ports:
    - "....:...."
  entrypoint: ./wait-for-it.sh mydb:3306 -- ./yourEntryPoint.sh

คุณสามารถดูเอกสารการรอสคริปต์ได้ที่นี่


ฉันพยายามใช้wait-for-it.sh ก่อนหน้านี้ แต่มันแทนที่ Dockerfile เริ่มต้นใช่ไหม entrypoint.sh มีลักษณะอย่างไร?
John Kariuki

จุดเข้าขึ้นอยู่กับภาพของคุณ คุณสามารถตรวจสอบได้ด้วยนักเทียบท่าตรวจ <image id> ควรรอให้บริการพร้อมใช้งานและโทรติดต่อจุดเริ่มต้นของคุณ
nono

ไหวมั้ย? คุณเข้าใจไหม?
nono

เข้าท่า. ใช่.
John Kariuki

6
คำเตือน: MySQL 5.5 (อาจเป็นเวอร์ชันที่ใหม่กว่าเช่นกัน) สามารถตอบสนองได้ในขณะที่ยังเริ่มต้น
Blaise

8

สวัสดีสำหรับการตรวจสุขภาพอย่างง่ายโดยใช้ docker -compose v2.1ฉันใช้:

/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\"

โดยทั่วไปจะรันmysqlคำสั่งง่ายๆSHOW DATABASES;โดยใช้เป็นตัวอย่างผู้ใช้ที่rootมีรหัสผ่านrootpasswd ในฐานข้อมูล

หากคำสั่งสำเร็จ db จะขึ้นและพร้อมดังนั้นเส้นทางตรวจสอบสุขภาพ คุณสามารถใช้intervalเพื่อทดสอบในช่วงเวลา

การลบฟิลด์อื่นเพื่อให้มองเห็นได้นี่คือลักษณะที่ปรากฏในdocker-compose.yamlไฟล์.

version: '2.1'

  services:
    db:
      ... # Other db configuration (image, port, volumes, ...)
      healthcheck:
        test: "/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\""
        interval: 2s
        timeout: 20s
        retries: 10

     app:
       ... # Other app configuration
       depends_on:
         db:
         condition: service_healthy

1
คำเตือน: เมื่อใช้ไฟล์เขียน "เวอร์ชัน 3" การรองรับ "เงื่อนไข" จะไม่สามารถใช้ได้อีกต่อไป ดูdocs.docker.com/compose/compose-file/#depends_on
BartoszK

1
คุณควรใช้คุณสมบัติคำสั่งร่วมกับสคริปต์wait-for-it.sh ฉันทำแบบนี้:command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
BartoszK

@BartoszKI ไม่เข้าใจ คุณช่วยเพิ่มคำตอบพร้อมรายละเอียดได้ไหม ฉันกำลังเผชิญกับปัญหาเดียวกัน แต่ฉันไม่สามารถทำให้มันได้ผล
Thadeu Antonio Ferreira Melo

ตรวจสอบให้แน่ใจว่าคุณกำลังใช้ v2.1 มิฉะนั้นให้ปฏิบัติตามหลักเกณฑ์ใหม่สำหรับ v3.0 ขึ้นไป
Sylhare

1
--execute \"SHOW DATABASES;\"คือสิ่งที่ทำให้ฉันรอจนกว่าฐานข้อมูลจะพร้อมให้แอปพลิเคชันเข้าถึง
tsuz

6

ฉันแก้ไขdocker-compose.ymlตามตัวอย่างต่อไปนี้และใช้งานได้

  mysql:
    image: mysql:5.6
    ports:
      - "3306:3306"
    volumes:       
      # Preload files for data
      - ../schemaAndSeedData:/docker-entrypoint-initdb.d
    environment:
      MYSQL_ROOT_PASSWORD: rootPass
      MYSQL_DATABASE: DefaultDB
      MYSQL_USER: usr
      MYSQL_PASSWORD: usr
    healthcheck:
      test:  mysql --user=root --password=rootPass -e 'Design your own check script ' LastSchema

ในกรณีของฉัน../schemaAndSeedDataมีไฟล์ schema และ data seeding หลายไฟล์ จะคล้ายกับต่อไปนี้Design your own check scriptselect * from LastSchema.LastDBInsert

ในขณะที่โค้ดคอนเทนเนอร์ที่ขึ้นกับเว็บคือ

depends_on:
  mysql:
    condition: service_healthy

สิ่งนี้อาจใช้ได้กับคุณ แต่ฉันไม่แน่ใจว่าสิ่งนี้ได้รับการสนับสนุนในเอ็นจิ้น MySQL ทั้งหมดหรือไม่
halfpastfour น

ฉันกำลังพูดถึงเอ็นจิ้นฐานข้อมูลเช่น InnoDB, MyISAM เป็นต้นLastSchema.LastDBInsertMySQL default หรือโปรแกรมฐานข้อมูลเฉพาะหรือไม่?
halfpastfour น

ไม่มันไม่ใช่ค่าเริ่มต้นใน mysql เช่นกัน มันเป็นเพียงตัวอย่าง แบบสอบถามจำลอง
Mukesh Agarwal

6
คำเตือน: เมื่อใช้ไฟล์เขียน "เวอร์ชัน 3" การรองรับ "เงื่อนไข" จะไม่สามารถใช้ได้อีกต่อไป ดูdocs.docker.com/compose/compose-file/#depends_on
BartoszK

4

การเพิ่มโซลูชันที่อัปเดตสำหรับแนวทางการตรวจสุขภาพ ตัวอย่างง่ายๆ:

healthcheck:
  test: out=$$(mysqladmin ping -h localhost -P 3306 -u foo --password=bar 2>&1); echo $$out | grep 'mysqld is alive' || { echo $$out; exit 1; }

คำอธิบาย : เนื่องจากmysqladmin pingส่งคืนผลบวกเท็จ (โดยเฉพาะสำหรับรหัสผ่านที่ไม่ถูกต้อง) ฉันจึงบันทึกผลลัพธ์เป็นตัวแปรชั่วคราวจากนั้นใช้grepเพื่อค้นหาผลลัพธ์ที่คาดหวัง (mysqld is alive ) หากพบจะส่งคืนรหัสข้อผิดพลาด 0 ในกรณีที่ไม่พบฉันกำลังพิมพ์ข้อความทั้งหมดและส่งคืนรหัสข้อผิดพลาด 1 รายการ

ตัวอย่างข้อมูลเพิ่มเติม:

version: "3.8"
services:
  db:
    image: linuxserver/mariadb
    environment:
      - FILE__MYSQL_ROOT_PASSWORD=/run/secrets/mysql_root_password
      - FILE__MYSQL_PASSWORD=/run/secrets/mysql_password
    secrets:
      - mysql_root_password
      - mysql_password
    healthcheck:
      test: out=$$(mysqladmin ping -h localhost -P 3306 -u root --password=$$(cat $${FILE__MYSQL_ROOT_PASSWORD}) 2>&1); echo $$out | grep 'mysqld is alive' || { echo $$out; exit 1; }

secrets:
  mysql_root_password:
    file: ${SECRETSDIR}/mysql_root_password
  mysql_password:
    file: ${SECRETSDIR}/mysql_password

คำอธิบาย : ฉันใช้ความลับของนักเทียบท่าแทนตัวแปร env (แต่สามารถทำได้กับ env vars ปกติเช่นกัน) การใช้$$สำหรับตัวอักษร$เครื่องหมายซึ่งถูกลอกออกเมื่อส่งผ่านไปยังคอนเทนเนอร์

ผลงานdocker inspect --format "{{json .State.Health }}" db | jqในโอกาสต่างๆ:

ทุกอย่างเรียบร้อย:

{
  "Status": "healthy",
  "FailingStreak": 0,
  "Log": [
    {
    {
      "Start": "2020-07-20T01:03:02.326287492+03:00",
      "End": "2020-07-20T01:03:02.915911035+03:00",
      "ExitCode": 0,
      "Output": "mysqld is alive\n"
    }
  ]
}

DB ไม่ขึ้น (ยัง):

{
  "Status": "starting",
  "FailingStreak": 1,
  "Log": [
    {
      "Start": "2020-07-20T01:02:58.816483336+03:00",
      "End": "2020-07-20T01:02:59.401765146+03:00",
      "ExitCode": 1,
      "Output": "\u0007mysqladmin: connect to server at 'localhost' failed error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 \"No such file or directory\")' Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!\n"
    }
  ]
}

รหัสผ่านผิด:

{
  "Status": "unhealthy",
  "FailingStreak": 13,
  "Log": [
    {
      "Start": "2020-07-20T00:56:34.303714097+03:00",
      "End": "2020-07-20T00:56:34.845972979+03:00",
      "ExitCode": 1,
      "Output": "\u0007mysqladmin: connect to server at 'localhost' failed error: 'Access denied for user 'root'@'localhost' (using password: YES)'\n"
    }
  ]
}

3

ฉันมีปัญหาเดียวกันฉันสร้างสคริปต์ทุบตีภายนอกเพื่อจุดประสงค์นี้ (ได้รับแรงบันดาลใจจากคำตอบ Maxim) แทนที่mysql-container-nameด้วยชื่อคอนเทนเนอร์ MySQL ของคุณและจำเป็นต้องใช้รหัสผ่าน / ผู้ใช้:

bin / wait-for-mysql.sh :

#!/bin/sh
until docker container exec -it mysql-container-name mysqladmin ping -P 3306 -proot | grep "mysqld is alive" ; do
  >&2 echo "MySQL is unavailable - waiting for it... 😴"
  sleep 1
done

ใน MakeFile ของฉันฉันเรียกสคริปต์นี้หลังจากdocker-composeโทรขึ้น:

wait-for-mysql: ## Wait for MySQL to be ready
    bin/wait-for-mysql.sh

run: up wait-for-mysql reload serve ## Start everything...

จากนั้นฉันสามารถเรียกคำสั่งอื่น ๆ ได้โดยไม่ต้องมีข้อผิดพลาด:

เกิดข้อยกเว้นในโปรแกรมควบคุม: SQLSTATE [HY000] [2006] เซิร์ฟเวอร์ MySQL หายไปแล้ว

ตัวอย่างผลลัพธ์:

docker-compose -f docker-compose.yaml up -d
Creating network "strangebuzzcom_default" with the default driver
Creating sb-elasticsearch ... done
Creating sb-redis              ... done
Creating sb-db                 ... done
Creating sb-app                ... done
Creating sb-kibana             ... done
Creating sb-elasticsearch-head ... done
Creating sb-adminer            ... done
bin/wait-for-mysql.sh
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
mysqld is alive
php bin/console doctrine:cache:clear-metadata
// Clearing all Metadata cache entries
[OK] Successfully deleted cache entries.

ฉันได้ลบการตรวจสุขภาพแล้วเนื่องจากวิธีนี้ใช้ไม่ได้ผลแล้ว


2

รีสตาร์ทเมื่อล้มเหลว

เนื่องจาก v3 condition: service_healthyไม่สามารถใช้งานได้อีกต่อไป แนวคิดคือนักพัฒนาควรใช้กลไกสำหรับการกู้คืนข้อขัดข้องภายในแอปเอง อย่างไรก็ตามสำหรับกรณีการใช้งานทั่วไปวิธีง่ายๆในการแก้ไขปัญหานี้คือการใช้restartตัวเลือก

หากสถานะบริการ mysql ทำให้แอปพลิเคชันของคุณเป็นexited with code 1คุณสามารถใช้restartตัวเลือกนโยบายที่มีให้ได้ เช่น,on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.