Kiểm tra Docker-compile xem kết nối mysql đã sẵn sàng chưa


92

Tôi đang cố gắng đảm bảo rằng vùng chứa ứng dụng của mình không chạy quá trình di chuyển / bắt đầu cho đến khi vùng chứa db được khởi động và SN SÀNG chấp nhận kết nối.

Vì vậy, tôi quyết định sử dụng kiểm tra sức khỏe và phụ thuộc vào tùy chọn trong tệp soạn thảo docker v2.

Trong ứng dụng, tôi có những thứ sau

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

Mặt khác, db có kiểm tra sức khỏe sau

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

Tôi đã thử một số cách tiếp cận như:

  1. đảm bảo rằng db DIR được tạo test: ["CMD", "test -f var/lib/mysql/db"]
  2. Lấy phiên bản mysql: test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. Ping quản trị viên (đánh dấu vùng chứa db là lành mạnh nhưng dường như không phải là thử nghiệm hợp lệ) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

Có ai có giải pháp cho điều này không?


Bạn đã tạo một docker cho DB? Hãy cho tôi biết rằng dữ liệu của bạn là bên ngoài của container này vì lợi ích của sức khỏe ứng dụng của bạn
Jorge Campos

Hoặc ít nhất đây là một công cụ chứa thử nghiệm.
Jorge Campos

Điều này thực sự chỉ dành cho mục đích phát triển / thử nghiệm.
John Kariuki

2
Tôi nghĩ bạn nên sử dụng một lệnh để kết nối và chạy một truy vấn trong mysql, không có mẫu nào bạn cung cấp làm được điều này: đại loại như:mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Jorge Campos

1
@JorgeCampos Được rồi, cảm ơn. Thông thường tôi có một vùng chứa db, nhưng khối lượng ánh xạ cho dir dữ liệu. Vì vậy, nếu vùng chứa gặp sự cố, dữ liệu sẽ vẫn tồn tại trong lần khởi tạo tiếp theo.
S ..

Câu trả lời:


82
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

Vùng chứa api sẽ không bắt đầu cho đến khi vùng chứa db hoạt động tốt (về cơ bản cho đến khi mysqladmin khởi động và chấp nhận kết nối.)


12
mysqladmin pingsẽ trả về giá trị dương giả nếu máy chủ đang chạy nhưng chưa chấp nhận kết nối.
Halfpastfour.am,

53
Chỉ cần FYI để người của năm 2017: conditiondưới depends_onkhông được hỗ trợ trong phiên bản 3+
Mint

@BobKruithof Tôi đang phải đối mặt với cùng một vấn đề ... là có bất kỳ xung quanh công việc, một cái gì đó giống như giấc ngủ hoặc thoát trạng thái cho retry
Mukesh Agarwal

1
@dKen xem câu trả lời của tôi bên dưới stackoverflow.com/a/45058879/279272 , tôi hy vọng nó cũng sẽ hiệu quả với bạn.
Mukesh Agarwal

1
Để kiểm tra điều này bằng cách sử dụng mật khẩu: test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- nếu bạn đã xác định MYSQL_ROOT_PASSWORDtrong environmentsphần.
laimison

22

Nếu bạn đang sử dụng docker-compo v3 + , conditionvì một tùy chọn của depends_onđã bị xóa .

Đường dẫn khuyến cáo là sử dụng chứ không phải wait-for-it, dockerizehoặc wait-for. Trong docker-compose.ymltệp của bạn , hãy thay đổi lệnh của bạn thành:

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

Cá nhân tôi thích wait-forvì nó có thể chạy trong một vùng chứa Alpine ( shtương thích, không phụ thuộc vào bash). Hạn chế là nó phụ thuộc vào netcat, vì vậy nếu bạn quyết định sử dụng nó, hãy đảm bảo rằng bạn đã netcatcài đặt trong vùng chứa hoặc cài đặt nó trong Dockerfile của bạn, chẳng hạn như với:

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

Tôi cũng đã táchwait-for dự án để nó có thể kiểm tra trạng thái HTTP lành mạnh (nó sử dụng wget). Sau đó, bạn có thể làm một cái gì đó như vậy:

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

Tái bút: Một PR cũng đã sẵn sàng được hợp nhất để thêm năng lực đó vào wait-fordự án.


14

Điều này là đủ

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
đôi $để làm gì?
InsOp

5
Cú pháp đặc biệt @InsOp mà bạn phải sử dụng trong lệnh kiểm tra sức khỏe để thoát biến env bắt đầu bằng $, tức là $$ MYSQL_PASSWORD sẽ dẫn đến $ MYSQL_PASSWORD, chính nó sẽ dẫn đến mypassword trong ví dụ cụ thể này
Maksim Kostromin

Vì vậy, với điều này tôi đang truy cập biến env bên trong vùng chứa? với một $Im duy nhất truy cập vào biến env từ máy chủ thì tôi cho là? thật tuyệt, cảm ơn bạn!
InsOp

10

Nếu bạn có thể thay đổi vùng chứa để đợi mysql sẵn sàng làm điều đó.

Nếu bạn không có quyền kiểm soát vùng chứa mà bạn muốn kết nối cơ sở dữ liệu, bạn có thể thử đợi cổng cụ thể.

Vì mục đích đó, tôi đang sử dụng một tập lệnh nhỏ để đợi một cổng cụ thể được tiếp xúc bởi một vùng chứa khác.

Trong ví dụ này, myserver sẽ đợi cổng 3306 của mydb container có thể truy cập được.

# 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

Bạn có thể tìm thấy tài liệu chờ tập lệnh tại đây


Tôi đã thử sử dụng wait-for-it.sh trước đó nhưng nó ghi đè Dockerfile mặc định phải không? Entrypoint.sh trông như thế nào?
John Kariuki

Điểm nhập phụ thuộc vào hình ảnh của bạn. Bạn có thể kiểm tra nó bằng trình kiểm tra docker <image id>. Việc này sẽ đợi dịch vụ khả dụng và gọi cho điểm nhập cảnh của bạn.
nono ngày

Là nó ổn ? Bạn hiểu không?
nono

Có lý. Đúng vậy.
John Kariuki

6
Cảnh báo: MySQL 5.5 (có thể cả phiên bản mới hơn) có thể phản hồi trong khi vẫn khởi tạo.
Blaise

8

Xin chào để kiểm tra sức khỏe đơn giản bằng cách sử dụng docker -omp v2.1 , tôi đã sử dụng:

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

Về cơ bản, nó chạy một mysqllệnh đơn giản SHOW DATABASES;bằng cách sử dụng làm ví dụ người dùng rootcó mật khẩu rootpasswd trong cơ sở dữ liệu.

Nếu lệnh thành công, db sẽ khởi động và sẵn sàng để kiểm tra sức khỏe. Bạn có thể sử dụng intervalđể nó kiểm tra trong khoảng thời gian.

Xóa trường khác để hiển thị, đây là trường sẽ trông như thế nào trong của bạn 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
Cảnh báo: Với "phiên bản 3" của tệp soạn, hỗ trợ "điều kiện" không còn khả dụng. Xem docs.docker.com/compose/compose-file/#depends_on
BartoszK

1
Bạn nên sử dụng tính năng lệnh cùng với tập lệnh wait-for-it.sh . Tôi làm theo cách này:command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
BartoszK,

@BartoszK Tôi không hiểu nó. Bạn có thể vui lòng thêm một câu trả lời đầy đủ với các chi tiết? Tôi đang phải đối mặt với cùng một vấn đề, nhưng tôi không thể làm cho nó hoạt động.
Thadeu Antonio Ferreira Melo

Đảm bảo rằng bạn đang sử dụng v2.1, nếu không, hãy làm theo hướng dẫn mới cho v3.0 trở lên.
Sylhare

1
--execute \"SHOW DATABASES;\"là điều khiến nó chờ đợi tôi cho đến khi cơ sở dữ liệu có sẵn để ứng dụng truy cập
tsuz

6

Tôi đã sửa đổi docker-compose.ymltheo ví dụ sau và nó hoạt động.

  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

Trong trường hợp của tôi ../schemaAndSeedDatacó chứa nhiều tệp sql sơ đồ và dữ liệu gieo hạt. Design your own check scriptcó thể tương tự như sau select * from LastSchema.LastDBInsert.

Trong khi mã vùng chứa phụ thuộc web là

depends_on:
  mysql:
    condition: service_healthy

Điều này có thể hiệu quả với bạn nhưng tôi không chắc liệu điều này có được hỗ trợ trong tất cả các công cụ MySQL hay không.
halfpastfour.am

Tôi đang nói về các công cụ cơ sở dữ liệu như InnoDB, MyISAM, v.v. LastSchema.LastDBInsertMột MySQL mặc định hay công cụ cơ sở dữ liệu cụ thể?
halfpastfour.am

Không, nó cũng không phải là mặc định trong mysql. Nó chỉ là một mẫu. một truy vấn giả.
Mukesh Agarwal,

5
Cảnh báo: Với "phiên bản 3" của tệp soạn, hỗ trợ "điều kiện" không còn khả dụng. Xem docs.docker.com/compose/compose-file/#depends_on
BartoszK

4

Thêm một giải pháp cập nhật cho phương pháp kiểm tra sức khỏe. Đoạn mã đơn giản:

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; }

Giải thích : Vì mysqladmin pingtrả về kết quả dương tính sai (đặc biệt là sai mật khẩu), tôi đang lưu đầu ra vào một biến tạm thời, sau đó sử dụng grepđể tìm kết quả mong đợi ( mysqld is alive). Nếu tìm thấy nó sẽ trả về mã lỗi 0. Trong trường hợp không tìm thấy nó, tôi sẽ in toàn bộ thông báo và trả lại 1 mã lỗi.

Đoạn mã mở rộng:

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

Giải thích : Tôi đang sử dụng các bí mật của docker thay vì các biến env (nhưng điều này cũng có thể đạt được với các env vars thông thường). Việc sử dụng $$là cho $ký hiệu chữ được loại bỏ khi chuyển đến vùng chứa.

Đầu ra từ docker inspect --format "{{json .State.Health }}" db | jqcác trường hợp khác nhau:

Mọi thứ vẫn ổn:

{
  "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 chưa lên (chưa):

{
  "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"
    }
  ]
}

Sai mật khẩu:

{
  "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"
    }
  ]
}

4

Tôi đã gặp vấn đề tương tự, tôi đã tạo một tập lệnh bash bên ngoài cho mục đích này (Nó được lấy cảm hứng từ câu trả lời Maxim). Thay thế mysql-container-namebằng tên của vùng chứa MySQL của bạn và cả mật khẩu / người dùng là cần thiết:

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

Trong MakeFile của tôi, tôi gọi tập lệnh này ngay sau docker-composecuộc gọi của tôi :

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

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

Sau đó, tôi có thể gọi các lệnh khác mà không gặp lỗi:

Đã xảy ra ngoại lệ trong trình điều khiển: SQLSTATE [HY000] [2006] Máy chủ MySQL đã biến mất

Ví dụ đầu ra:

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.

Tôi đã xóa kiểm tra sức khỏe vì bây giờ nó vô dụng với cách tiếp cận này.


3

KHỞI ĐỘNG VỀ SỰ THẤT BẠI

Vì v3 condition: service_healthykhông còn nữa. Ý tưởng là nhà phát triển nên triển khai cơ chế khôi phục sự cố trong chính ứng dụng. Tuy nhiên đối với các trường hợp sử dụng đơn giản, một cách đơn giản để giải quyết vấn đề này là sử dụng restarttùy chọn.

Nếu tình trạng dịch vụ mysql gây ra ứng dụng của exited with code 1bạn, bạn có thể sử dụng một trong các restarttùy chọn chính sách có sẵn. ví dụ,on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.