我正在尝试确保我的应用容器在db容器启动和READY TO接受连接之前不会运行迁移/启动。
所以我决定使用healthcheck并依赖于docker compose file v2中的选项。
在应用程序中,我有以下
app:
...
depends_on:
db:
condition: service_healthy
另一方面,db具有以下健康检查
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
我尝试了几种方法,如:
test: ["CMD", "test -f var/lib/mysql/db"]
test: ["CMD", "echo 'SELECT version();'| mysql"]
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
有没有人有解决方案?
答案 0 :(得分:52)
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
在db容器运行正常之前,api容器才会启动(基本上直到mysqladmin启动并接受连接。)
答案 1 :(得分:7)
如果您可以更改容器以等待mysql准备就绪。
如果您没有要将数据库连接到的容器的控件,则可以尝试等待特定端口。
为此,我使用一个小脚本来等待另一个容器暴露的特定端口。
在此示例中, myserver 将等待 mydb 容器的端口 3306 可以访问。
name phone email
0 1 1 hi@hi.com
您可以找到脚本wait-for-it文档here
答案 2 :(得分:4)
如果您使用的是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
,因为它可以在Alpine容器中运行(兼容sh
,不依赖于bash
)。缺点是它取决于netcat
,因此,如果您决定使用它,请确保已在容器中安装了netcat
,或将其安装在Dockerfile中,例如:
RUN apt-get -q update && apt-get -qy install netcat
我也forked the wait-for
项目,以便它可以检查健康的HTTP状态(它使用wget
)。然后您可以执行以下操作:
command: sh -c 'bin/wait-for http://api/ping -- jest test'
答案 3 :(得分: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 secrets而不是env变量(但这也可以通过常规的env var来实现)。 $$
用于文字$
,当传递到容器时会被剥离。
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 :(得分:4)
我遇到了同样的问题,为此我创建了一个外部bash脚本(受Maxim的启发)。将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.
我已经删除了健康检查,因为这种方法现在已经无效了。
答案 5 :(得分:3)
我根据以下示例修改了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
类似。
依赖于Web的容器代码
depends_on:
mysql:
condition: service_healthy
答案 6 :(得分:3)
您好,我使用 docker-compose v2.1 进行了简单的健康检查,我使用了:
/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\"
基本上,它以作为示例用户mysql
在数据库中使用密码SHOW DATABASES;
来运行简单的root
命令rootpasswd
。< / p>
如果命令成功执行,则数据库已启动并准备就绪,因此运行状况检查路径。您可以使用interval
,以便每隔一段时间进行测试。
删除其他字段以提高可见性,这就是您的docker-compose.yaml
中的样子。
version: '2.1'
services:
db:
...
healthcheck:
test: "/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\""
interval: 2s
timeout: 20s
retries: 10
app:
...
depends_on:
db:
condition: service_healthy
答案 7 :(得分:3)
由于v3 condition: service_healthy
不再可用。
这个想法是,开发人员应该在应用程序内部实现崩溃恢复机制。
但是,对于简单的用例而言,解决此问题的一种简单方法是使用restart
选项。
如果mysql服务状态导致您的应用程序运行exited with code 1
,则可以使用可用的restart
策略选项之一。例如on-failure
version: "3"
services:
app:
...
depends_on:
- db:
restart: on-failure
答案 8 :(得分:2)
这应该足够了
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
答案 9 :(得分:1)
我想为此提供另一种解决方案,在其中一条评论中提到但并未真正解释:
有一个工具叫wait-for-it
,在
上提到
https://docs.docker.com/compose/startup-order/
这个怎么运作?您只需指定脚本在准备好时需要定期检查的主机和端口。如果是,它将执行您提供给它的程序。您还可以指定检查 host:port
是否准备就绪的时间。对我来说,这是最有效的最干净的解决方案。
这是我的 docker-compose.yml
文件中的片段。
version : '3'
services:
database:
build: DatabaseScripts
ports:
- "3306:3306"
container_name: "database-container"
restart: always
backend:
build: backend
ports:
- "3000:3000"
container_name: back-container
restart: always
links:
- database
command : ["./wait-for-it.sh", "-t", "40", "database:3306", "--", "node", "app.js"]
# above line does the following:
# check periodically for 40 seconds if (host:port) = database:3306 is ready
# if it is, run 'node app.js'
# app.js is the file that is connecting with the db
frontend:
build: quiz-app
ports:
- "4200:4200"
container_name: front-container
restart: always
默认等待时间为 20 秒。关于它的更多细节可以在
上找到
https://github.com/vishnubob/wait-for-it。
我在 2.X 和 3.X 版本上尝试过 - 它在任何地方都能正常工作。
当然,您需要向容器提供 wait-for-it.sh
- 否则它将无法工作。
为此,请使用以下代码:
COPY wait-for-it.sh <DESTINATION PATH HERE>
我在 /backend/Dockerfile
中添加了它,所以它看起来像这样:
FROM node
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app
COPY wait-for-it.sh /usr/src/app
RUN npm install
COPY . /usr/src/app
EXPOSE 3000
CMD ["npm", "start"]
要检查一切是否正常,请运行 docker-compose logs
。在日志中的某个地方一段时间后,您应该会看到类似的输出:
<container_name> | wait-for-it.sh: waiting 40 seconds for database:3306
<container_name> | wait-for-it.sh: database:3306 is available after 12 seconds
注意:此解决方案由 BartoszK 在之前的评论中提供。