Joh*_*uki 60 mysql docker dockerfile docker-compose
我试图确保我的应用程序容器在db容器启动和READY TO接受连接之前不运行迁移/启动.
所以我决定使用healthcheck并依赖于docker compose file v2中的选项.
在应用程序中,我有以下内容
app:
...
depends_on:
db:
condition: service_healthy
Run Code Online (Sandbox Code Playgroud)
另一方面,db具有以下健康检查
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
Run Code Online (Sandbox Code Playgroud)
我尝试了几种方法,如:
test: ["CMD", "test -f var/lib/mysql/db"]
test: ["CMD", "echo 'SELECT version();'| mysql"]
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
有人有解决方案吗?
Joh*_*uki 64
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
Run Code Online (Sandbox Code Playgroud)
在db容器运行正常之前,api容器才会启动(基本上直到mysqladmin启动并接受连接.)
Cap*_*pot 11
如果您使用的是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'
Run Code Online (Sandbox Code Playgroud)
我个人比较喜欢,wait-for因为它可以在Alpine容器中运行(sh兼容,不依赖bash)。缺点是它取决于netcat,因此,如果您决定使用它,请确保已netcat安装在容器中或将其安装在Dockerfile中,例如使用:
RUN apt-get -q update && apt-get -qy install netcat
Run Code Online (Sandbox Code Playgroud)
我还分叉了该wait-for项目,以便它可以检查健康的HTTP状态(使用wget)。然后,您可以执行以下操作:
command: sh -c 'bin/wait-for http://api/ping -- jest test'
Run Code Online (Sandbox Code Playgroud)
为健康检查方法添加更新的解决方案。简单片段:
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; }
Run Code Online (Sandbox Code Playgroud)
说明:由于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
Run Code Online (Sandbox Code Playgroud)
说明:我使用的是docker secrets而不是 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"
}
]
}
Run Code Online (Sandbox Code Playgroud)
DB 尚未启动(尚未启动):
{
"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"
}
]
}
Run Code Online (Sandbox Code Playgroud)
密码错误:
{
"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"
}
]
}
Run Code Online (Sandbox Code Playgroud)
如果您可以更改容器以等待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
Run Code Online (Sandbox Code Playgroud)
您可以在此处找到脚本wait-for-it文档
我遇到了同样的问题,我为此目的创建了一个外部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
Run Code Online (Sandbox Code Playgroud)
在我的 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...
Run Code Online (Sandbox Code Playgroud)
然后我可以调用其他命令而不会出现错误:
驱动程序中发生异常: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...
mysqld is alive
php bin/console doctrine:schema:drop --force
Dropping database schema...
[OK] Database schema dropped successfully!
Run Code Online (Sandbox Code Playgroud)
由于 v3condition: service_healthy不再可用。这个想法是开发人员应该在应用程序本身内实现崩溃恢复机制。但是,对于简单的用例,解决此问题的简单方法是使用restart选项。
如果 mysql 服务状态导致您的应用程序exited with code 1无法使用,您可以使用restart可用的策略选项之一。例如,on-failure
version: "3"
services:
app:
...
depends_on:
- db:
restart: on-failure
Run Code Online (Sandbox Code Playgroud)
condition已添加回来,所以现在您可以再次使用它。不需要wait-for脚本。如果您用于scratch构建映像,则无论如何都无法运行这些脚本。
对于API服务
api:
build:
context: .
dockerfile: Dockerfile
restart: always
depends_on:
content-db:
condition: service_healthy
...
Run Code Online (Sandbox Code Playgroud)
对于数据库块
content-db:
image: mysql:5.6
restart: on-failure
command: --default-authentication-plugin=mysql_native_password
volumes:
- "./internal/db/content/sql:/docker-entrypoint-initdb.d"
environment:
MYSQL_DATABASE: content
MYSQL_TCP_PORT: 5306
MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
healthcheck:
test: "mysql -uroot -p$MYSQL_ROOT_PASSWORD content -e 'select 1'"
interval: 1s
retries: 120
Run Code Online (Sandbox Code Playgroud)
你可以试试这个docker-compose.yml:
version: "3"
services:
mysql:
container_name: mysql
image: mysql:8.0.26
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test_db
MYSQL_USER: test_user
MYSQL_PASSWORD: 1234
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: "mysql $$MYSQL_DATABASE -u$$MYSQL_USER -p$$MYSQL_PASSWORD -e 'SELECT 1;'"
interval: 20s
timeout: 10s
retries: 5
volumes:
mysql-data:
Run Code Online (Sandbox Code Playgroud)
我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
Run Code Online (Sandbox Code Playgroud)
就我而言,../schemaAndSeedData包含多个模式和数据种子sql文件。Design your own check script可以类似于以下内容select * from LastSchema.LastDBInsert。
虽然网络依赖的容器代码是
depends_on:
mysql:
condition: service_healthy
Run Code Online (Sandbox Code Playgroud)
您好,我使用docker-compose v2.1进行了简单的健康检查,我使用了:
/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\"
Run Code Online (Sandbox Code Playgroud)
基本上,它运行的简单mysql命令SHOW DATABASES;使用,例如,在用户root使用口令rootpasswd 在数据库中。
如果命令成功执行,则数据库已启动且已准备就绪,因此运行状况检查路径。您可以使用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
Run Code Online (Sandbox Code Playgroud)
This should be enough
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
Run Code Online (Sandbox Code Playgroud)
healthcheck虽然与 一起使用service_healthy是一个很好的解决方案,但我想要一个不依赖于运行状况检查本身的不同解决方案。
我的解决方案利用atkrad/wait4x图像。Wait4X 允许您等待端口或服务进入请求的状态,并具有可自定义的超时和间隔时间。
services:
app:
build: .
depends_on:
wait-for-db:
condition: service_completed_successfully
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=test
- MYSQL_DATABASE=test
wait-for-db:
image: atkrad/wait4x
depends_on:
- db
command: tcp db:3306 -t 30s -i 250ms
Run Code Online (Sandbox Code Playgroud)
示例 docker-compose 文件包含以下服务:
app- 这是数据库实例准备好后连接到数据库的应用程序
depends_on等待wait-for-db服务成功完成。(使用0退出代码退出)db- 这是MySQL服务wait-for-db- 该服务等待数据库打开其端口
command: tcp db:3306 -t 30s -i 250ms- 等待TCP协议3306端口,超时30秒,每250毫秒检查一次端口经过其他解决方案后,mysqladmin ping对我来说不起作用。这是因为mysqladmin即使 MySQL 服务器已启动但不接受端口上的连接,也会返回成功错误代码(即 0)3306。对于初始启动,MySQL 服务器将在端口上启动服务器0以设置 root 用户和初始数据库。这就是检测出现假阳性的原因。
这是我的健康检查测试:
test: ["CMD-SHELL", "exit | mysql -h localhost -P 3306 -u root -p$$MYSQL_ROOT_PASSWORD" ]
Run Code Online (Sandbox Code Playgroud)
exit |在成功连接期间关闭 MySQL 输入提示。
我的完整 Docker Compose 文件:
test: ["CMD-SHELL", "exit | mysql -h localhost -P 3306 -u root -p$$MYSQL_ROOT_PASSWORD" ]
Run Code Online (Sandbox Code Playgroud)