Перевірка 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.