Próbuję się upewnić, że mój kontener aplikacji nie uruchamia migracji / uruchamiania, dopóki kontener bazy danych nie zostanie uruchomiony i GOTOWY DO akceptowania połączeń.
Postanowiłem więc skorzystać z funkcji Healthcheck i zależy od opcji w pliku redagowania docker v2.
W aplikacji mam następujące elementy
app:
...
depends_on:
db:
condition: service_healthy
Z drugiej strony baza danych ma następującą kontrolę stanu
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
Wypróbowałem kilka podejść, takich jak:
- upewniając się, że db DIR został utworzony
test: ["CMD", "test -f var/lib/mysql/db"]
- Pobieranie wersji mysql:
test: ["CMD", "echo 'SELECT version();'| mysql"]
- Pinguj administratora (oznacza kontener db jako zdrowy, ale nie wygląda na prawidłowy test)
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
Czy ktoś ma na to rozwiązanie?
mysql
docker
docker-compose
dockerfile
John Kariuki
źródło
źródło
mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Odpowiedzi:
Kontener API nie uruchomi się, dopóki kontener db nie będzie zdrowy (w zasadzie dopóki mysqladmin nie uruchomi się i nie zaakceptuje połączeń).
źródło
mysqladmin ping
zwróci fałszywie dodatni wynik, jeśli serwer działa, ale jeszcze nie akceptuje połączeń.condition
underdepends_on
nie jest obsługiwany w wersji 3+test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
- jeśli określonoMYSQL_ROOT_PASSWORD
wenvironments
pkt.Jeśli używasz docker-compose v3 + ,
condition
jako opcjadepends_on
została usunięta .Zalecana ścieżka jest użycie raczej
wait-for-it
,dockerize
albowait-for
. Wdocker-compose.yml
pliku zmień polecenie na:command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'
Osobiście wolę,
wait-for
ponieważ może działać w kontenerze Alpine (sh
kompatybilny, bez zależnościbash
). Wadą jest to, że to zależynetcat
, więc jeśli zdecydujesz się go użyć, upewnij się, że masznetcat
zainstalowany w kontenerze lub zainstaluj go w swoim Dockerfile, na przykład za pomocą:RUN apt-get -q update && apt-get -qy install netcat
Rozdzieliłem
wait-for
również projekt, aby mógł sprawdzić stan HTTP (używawget
). Następnie możesz zrobić coś takiego:command: sh -c 'bin/wait-for http://api/ping -- jest test'
PS: PR jest również gotowy do połączenia, aby dodać tę zdolność do
wait-for
projektu.źródło
To powinno wystarczyć
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
źródło
$
?$
Im uzyskując dostęp do zmiennej env z hosta, a następnie przypuszczam? to miło, dziękuję!Jeśli możesz zmienić kontener, aby czekać, aż mysql będzie gotowy, zrób to.
Jeśli nie masz kontroli nad kontenerem, do którego chcesz podłączyć bazę danych, możesz spróbować zaczekać na określony port.
W tym celu używam małego skryptu, aby czekać na określony port udostępniony przez inny kontener.
W tym przykładzie, mójserwer będzie czekać na porcie 3306 z mydb pojemniku być osiągalny.
# 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
Dokumentację dotyczącą czekania na to skrypt można znaleźć tutaj
źródło
wait-for-it.sh
wcześniej, ale zastępuje domyślny plik Dockerfile, prawda? Jak wygląda entrypoint.sh?Cześć za prostą kontrolę stanu przy użyciu docker-compose v2.1 , użyłem:
Zasadniczo uruchamia proste
mysql
polecenie,SHOW DATABASES;
używając jako przykładu użytkownikaroot
z hasłemrootpasswd
w bazie danych.Jeśli komenda się powiedzie, baza danych jest gotowa do działania, więc ścieżka kontroli kondycji. Możesz użyć,
interval
więc testuje w odstępach czasu.Po usunięciu drugiego pola ze względu na widoczność, oto jak by to wyglądało w twoim
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
źródło
command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
--execute \"SHOW DATABASES;\"
co sprawiło, żeZmodyfikowałem
docker-compose.yml
zgodnie z poniższym przykładem i zadziałało.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
W moim przypadku
../schemaAndSeedData
zawiera wiele schematów i plików sql do inicjowania danych.Design your own check script
może być podobny do następującegoselect * from LastSchema.LastDBInsert
.Podczas gdy zależny od sieci kod kontenera to
depends_on: mysql: condition: service_healthy
źródło
LastSchema.LastDBInsert
domyślny silnik MySQL jest specyficzny?Miałem ten sam problem, stworzyłem w tym celu zewnętrzny skrypt basha (jest on inspirowany odpowiedzią Maxima). Zastąp
mysql-container-name
nazwą kontenera MySQL, a także potrzebne jest hasło / użytkownik: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
W moim MakeFile wywołuję ten skrypt zaraz po
docker-compose
wywołaniu:wait-for-mysql: ## Wait for MySQL to be ready bin/wait-for-mysql.sh run: up wait-for-mysql reload serve ## Start everything...
Następnie mogę wywołać inne polecenia bez błędu:
Przykład danych wyjściowych:
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.
Usunąłem kontrolę stanu, ponieważ jest teraz bezużyteczna w tym podejściu.
źródło
RESTART ON-FAILURE
Ponieważ
condition: service_healthy
wersja 3 nie jest już dostępna. Chodzi o to, że programista powinien zaimplementować mechanizm odzyskiwania po awarii w samej aplikacji. Jednak w przypadku prostych przypadków prostym sposobem rozwiązania tego problemu jest użycierestart
opcji.Jeśli status usługi mysql powoduje, że Twoja aplikacja do
exited with code 1
Ciebie trafia, możesz skorzystać z jednej zrestart
dostępnych opcji polityki. na przykład,on-failure
źródło
Dodanie zaktualizowanego rozwiązania do metody sprawdzania kondycji. Prosty fragment:
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; }
Objaśnienie : Ponieważ
mysqladmin ping
zwraca fałszywe alarmy (szczególnie w przypadku nieprawidłowego hasła), zapisuję dane wyjściowe do zmiennej tymczasowej, a następnie używamgrep
do znalezienia oczekiwanego wyniku (mysqld is alive
). Jeśli zostanie znaleziony, zwróci kod błędu 0. Jeśli nie zostanie znaleziony, drukuję całą wiadomość i zwracam kod błędu 1.Rozszerzony fragment:
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
Wyjaśnienie : używam sekretów Dockera zamiast zmiennych env (ale można to również osiągnąć za pomocą zwykłych zmiennych env). Użycie
$$
jest dla$
znaku dosłownego, który jest usuwany podczas przesyłania do kontenera.Wyjście z
docker inspect --format "{{json .State.Health }}" db | jq
różnych okazji:Wszystko w porządku:
{ "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 nie działa (jeszcze):
{ "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" } ] }
Złe hasło:
{ "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" } ] }
źródło