Перевірка Docker-Compose, якщо підключення mysql готове


92

Я намагаюся переконатись, що мій контейнер додатків не запускає міграції / старт, поки контейнер db не буде запущений і ГОТОВИЙ прийняти підключення.

Тому я вирішив скористатися перевіркою працездатності та залежить від опції у файлі складання docker v2.

У додатку я маю таке

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

З іншого боку, db має таку перевірку стану здоров'я

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. Пінг адміністратора (позначає контейнер db як здоровий, але, здається, не є дійсним тестом) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

Хтось має рішення цього?


Ви створили докер для БД? Скажіть, будь ласка, що ваші дані знаходяться за межами цього контейнера задля здоров’я вашої програми
Хорхе Кампос, 02

Або, принаймні, це тестовий контейнер.
Хорхе Кампос,

Насправді це лише для розробки / тестування.
Джон Каріукі

2
Думаю, вам слід скористатися командою для підключення та запуску запиту в mysql, жоден із наданих вами зразків не робить цього: щось на зразок:mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Хорхе Кампос

1
@JorgeCampos Добре, дякую. Зазвичай у мене є контейнер db, але обсяги карти для папки даних. Таким чином, якщо контейнер провалиться, дані будуть зберігатися до наступного екземпляру.
С ..

Відповіді:


81
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

53
Просто FYI людям 2017 року: conditionпід depends_onне підтримуються в версії 3+
Mint

@BobKruithof Я стикаюся з тією ж проблемою ... чи є якась робота навколо, щось на зразок сну чи стану виходу для повторної спроби
Мукеш Агарвал

1
@dKen див. мою відповідь нижче stackoverflow.com/a/45058879/279272 , сподіваюся, це буде працювати і для вас.
Мукеш Агарвал

1
Щоб перевірити це за допомогою пароля: test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- якщо ви визначили MYSQL_ROOT_PASSWORDу environmentsрозділі.
laimison

22

Якщо ви використовуєте docker-compose 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проектування.


14

Цього має бути достатньо

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, що саме призведе до мого пароля в цьому конкретному прикладі
Максим Костромін

Отже, з цим доступом до змінної env всередині контейнера? з одним $Im, що отримує доступ до змінної env з хосту, тоді я припускаю? це приємно дякую!
InsOp

10

Якщо ви можете змінити контейнер, дочекавшись готовності mysql, зробіть це.

Якщо у вас немає контролю над контейнером, до якого ви хочете підключити базу даних, спробуйте зачекати на певний порт.

З цією метою я використовую невеликий скрипт, щоб зачекати, поки певний порт буде виставлений іншим контейнером.

У цьому прикладі, MyServer чекатиме порту 3306 від MyDB контейнера досяжним.

# 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?
Джон Каріукі

Точка входу залежить від вашого зображення. Ви можете перевірити це за допомогою docker inspect <image id>. Це повинно зачекати, поки послуга стане доступною, і зателефонувати до точки входу.
ноно

Це нормально? Чи ти розумієш?
nono

Мати сенс. Так.
Джон Каріукі

6
Попередження: MySQL 5.5 (можливо, і новіші версії) може відповісти, поки все ще ініціалізується.
Блез

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 не розумію цього. Не могли б ви додати повну відповідь із деталями? Я стикаюся з точно такою ж проблемою, але я не можу змусити це працювати.
Тадеу Антоніо Феррейра Мело

Переконайтесь, що ви використовуєте v2.1, інакше дотримуйтесь нових вказівок для v3.0 і вище.
Sylhare

1
--execute \"SHOW DATABASES;\"саме це змусило мене чекати, поки база даних не
стане

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містить декілька схем та даних, що засівають файли sql. Design your own check scriptможе бути подібним до наступного select * from LastSchema.LastDBInsert.

Поки код веб-контейнера був залежним

depends_on:
  mysql:
    condition: service_healthy

Це може працювати для вас, але я не впевнений, чи підтримується це у всіх движках MySQL.
halfpastfour.am

Я говорю про механізми баз даних, такі як InnoDB, MyISAM тощо. Чи LastSchema.LastDBInsertє за замовчуванням MySQL чи механізм баз даних?
halfpastfour.am

Ні, це також не за замовчуванням у mysql. Це був лише зразок. фіктивний запит.
Мукеш Агарвал

5
Попередження: У "версії 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

Пояснення : Я використовую секрети docker замість змінних 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"
    }
  ]
}

БД ще не встановлений:

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

4

У мене була та сама проблема, я створив для цього зовнішній скрипт bash (натхненний відповіддю Максима). Замініть 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.

Я видалив перевірку стану здоров’я, оскільки при такому підході це марно.


3

ПЕРЕЗАПУСК ПРИ ПОМИЛКІ

Оскільки v3 condition: service_healthyбільше не доступний. Ідея полягає в тому, що розробник повинен впровадити механізм відновлення збоїв у самому додатку. Однак для простих випадків використання простим способом вирішення цієї проблеми є використання restartопції.

Якщо статус служби mysql викликає вашу програму, exited with code 1ви можете використовувати один із restartдоступних варіантів політики. наприклад,on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.