Estou tentando ter certeza de que meu contêiner de aplicativo não execute migrações / inicie até que o contêiner db seja iniciado e PRONTO PARA aceitar conexões.
Portanto, decidi usar a verificação de integridade e depende da opção no docker compose file v2.
No aplicativo, tenho o seguinte
app:
...
depends_on:
db:
condition: service_healthy
O db, por outro lado, tem a seguinte verificação de integridade
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
Eu tentei algumas abordagens como:
- certificando-se de que o db DIR foi criado
test: ["CMD", "test -f var/lib/mysql/db"]
- Obtendo a versão do mysql:
test: ["CMD", "echo 'SELECT version();'| mysql"]
- Execute ping no administrador (marca o contêiner db como íntegro, mas não parece ser um teste válido)
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
Alguém tem uma solução para isso?
mysql
docker
docker-compose
dockerfile
John Kariuki
fonte
fonte
mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Respostas:
O contêiner api não será iniciado até que o contêiner db esteja saudável (basicamente até que o mysqladmin esteja ativo e aceitando conexões).
fonte
mysqladmin ping
retornará um falso positivo se o servidor estiver em execução, mas ainda não aceitar conexões.condition
underdepends_on
não é compatível com a versão 3+test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
- se você definiuMYSQL_ROOT_PASSWORD
naenvironments
seção.Se você estiver usando docker-compose v3 + ,
condition
uma opção dedepends_on
foi removida .O caminho recomendado é usar
wait-for-it
, em vez dissodockerize
, ouwait-for
. Em seudocker-compose.yml
arquivo, altere o comando para ser:command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'
Eu pessoalmente prefiro,
wait-for
pois pode ser executado em um contêiner Alpine (sh
compatível, sem dependênciabash
). A desvantagem é que isso dependenetcat
, então, se você decidir usá-lo, certifique-se de ternetcat
instalado no contêiner ou instale-o em seu Dockerfile, por exemplo com:RUN apt-get -q update && apt-get -qy install netcat
Eu também fiz um fork do
wait-for
projeto para que ele possa verificar o status de HTTP saudável (ele usawget
). Então você pode fazer algo assim:command: sh -c 'bin/wait-for http://api/ping -- jest test'
PS: Um PR também está pronto para ser mesclado para adicionar essa capacidade de
wait-for
projeto.fonte
Isso deve ser o suficiente
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
fonte
$
?$
Im acessando a variável env do host, então eu suponho? isso é bom obrigado!Se você pode alterar o container para esperar que o mysql esteja pronto, faça isso.
Se você não tem o controle do contêiner ao qual deseja conectar o banco de dados, pode tentar aguardar a porta específica.
Para isso, estou usando um pequeno script para esperar por uma porta específica exposta por outro container.
Neste exemplo, myserver aguardará a porta 3306 do container mydb ser alcançável.
# 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
Você pode encontrar a documentação do script wait-for-it aqui
fonte
wait-for-it.sh
anteriormente, mas substitui o Dockerfile padrão, certo? Como é o entrypoint.sh?Olá, por uma verificação de integridade simples usando docker-compose v2.1 , usei:
Basicamente ele executa um
mysql
comando simplesSHOW DATABASES;
usando como exemplo o usuárioroot
com a senharootpasswd
no banco de dados.Se o comando for bem-sucedido, o banco de dados está ativo e pronto para verificar o caminho. Você pode usar
interval
o teste no intervalo.Removendo o outro campo para visibilidade, aqui está como ficaria no seu
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
fonte
command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
--execute \"SHOW DATABASES;\"
foi o que o fez esperar por mim até que o banco de dados estivesse disponível para o aplicativo acessarModifiquei o
docker-compose.yml
conforme o exemplo a seguir e funcionou.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
No meu caso,
../schemaAndSeedData
contém vários arquivos sql schema e data seed.Design your own check script
pode ser semelhante a seguirselect * from LastSchema.LastDBInsert
.Enquanto o código do contêiner dependente da web era
depends_on: mysql: condition: service_healthy
fonte
LastSchema.LastDBInsert
um padrão do MySQL ou um mecanismo de banco de dados específico?Adicionando uma solução atualizada para a abordagem de verificação de integridade. Snippet simples:
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; }
Explicação : Como
mysqladmin ping
retorna falsos positivos (especialmente para senha incorreta), estou salvando a saída em uma variável temporária e usandogrep
para encontrar a saída esperada (mysqld is alive
). Se encontrado, ele retornará o código de erro 0. Caso não seja encontrado, estou imprimindo a mensagem inteira e retornando o 1 código de erro.Snippet estendido:
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
Explicação : Estou usando os segredos do docker em vez de variáveis env (mas isso também pode ser obtido com vars env regulares). O uso de
$$
é para literal$
sinal que é retirado quando passado para o contêiner.Resultado
docker inspect --format "{{json .State.Health }}" db | jq
em várias ocasiões:Tudo certo:
{ "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 não está ativo (ainda):
{ "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" } ] }
Senha incorreta:
{ "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" } ] }
fonte
Eu tive o mesmo problema, criei um script bash externo para esse fim (é inspirado na resposta da Maxim). Substitua
mysql-container-name
pelo nome do seu contêiner MySQL e também é necessária a senha / usuário: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
Em meu MakeFile, eu chamo este script logo após minha
docker-compose
chamada:wait-for-mysql: ## Wait for MySQL to be ready bin/wait-for-mysql.sh run: up wait-for-mysql reload serve ## Start everything...
Então posso chamar outros comandos sem ter o erro:
Exemplo de saída:
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.
Excluí a verificação de integridade, pois agora ela é inútil com essa abordagem.
fonte
REINICIAR COM FALHA
Uma vez que a v3
condition: service_healthy
não está mais disponível. A ideia é que o desenvolvedor implemente um mecanismo de recuperação de falhas dentro do próprio aplicativo. No entanto, para casos de uso simples, uma maneira simples de resolver esse problema é usar arestart
opção.Se o status do serviço mysql causar seu aplicativo,
exited with code 1
você pode usar uma dasrestart
opções de política disponíveis. por exemplo,on-failure
fonte