Docker-compose检查mysql连接是否准备就绪

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)

我尝试了几种方法,如:

  1. 确保创建db DIR test: ["CMD", "test -f var/lib/mysql/db"]
  2. 获取mysql版本: test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. Ping管理员(将数据库容器标记为健康,但似乎不是有效的测试) 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启动并接受连接.)

  • 仅供2017年人参加:版本3+不支持`depends_on`下的`condition` (40认同)
  • 如果服务器正在运行但尚未接受连接,则mysqladmin ping`将返回误报. (11认同)
  • 要使用密码检查:`test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]` -如果你在 `environments` 部分定义了 `MYSQL_ROOT_PASSWORD`。 (8认同)
  • 我使用的是版本 3.9 的 Compose 文件,并且“condition”字段有效。 (3认同)
  • 请注意,使用分离的 Compose Spec“条件”已再次添加到“depends_on”中:https://github.com/compose-spec/compose-spec/blob/a4a7e7c/spec.md#long-syntax-1为此,需要 Compose 1.27.0 或更高版本:https://github.com/docker/compose/releases/tag/1.27.0 (2认同)

Cap*_*pot 11

如果您使用的是docker-compose v3 +,则已删除condition的选项。depends_on

推荐的途径是使用相当wait-for-itdockerize或者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)

  • 我认为这永远不会起作用。或者曾经工作过。“service_completed_successively”表示应用程序将等待,直到指定的服务以 0 代码退出。从文档中:“service_completed_successively:指定在启动依赖服务之前,依赖项预计会成功完成。” (17认同)
  • 我使用的是 3.9 版本,无论文档如何说明,该条件似乎都有效。 (4认同)
  • `条件`被添加回来 (2认同)
  • 对我来说不起作用,service_completed_successively,我的意思是数据库初始化有效,但主应用程序没有启动。有什么建议么? (2认同)

Max*_*ted 9

为健康检查方法添加更新的解决方案。简单片段:

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)


non*_*ono 8

如果您可以更改容器以等待mysql准备就绪.

如果您没有要将数据库连接到的容器的控件,则可以尝试等待特定端口.

为此,我使用一个小脚本来等待另一个容器暴露的特定端口.

在这个例子中,MYSERVER将等待端口3306mydb的容器可到达.

# 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文档

  • 警告:MySQL 5.5(可能还有较新的版本)仍可以在初始化时作出响应。 (5认同)

COi*_*Oil 8

我遇到了同样的问题,我为此目的创建了一个外部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)


Ham*_*ari 7

重启失败

由于 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)


leo*_*ger 7

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)


yue*_*n26 7

你可以试试这个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)


Muk*_*wal 5

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)

  • 警告:在撰写文件的“版本3”中,“条件”支持不再可用。参见https://docs.docker.com/compose/compose-file/#depends_on (3认同)

Syl*_*are 5

您好,我使用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)

  • 警告:对于撰写文件的“版本 3”,“条件”支持不再可用。见 https://docs.docker.com/compose/compose-file/#depends_on (2认同)
  • `--execute \"SHOW DATABASES;\"` 是什么让它等待我直到数据库可供应用程序访问 (2认同)

Mak*_*min 5

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)

  • @InsOp 您必须在运行状况检查测试命令中使用特殊语法来转义以 $ 开头的环境变量,即 $$MYSQL_PASSWORD 将导致 $MYSQL_PASSWORD,在这个具体示例中,它本身将导致 mypassword (7认同)
  • 双“$”有什么用? (3认同)

Dáv*_*abó 5

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毫秒检查一次端口


Ahm*_*man 5

经过其他解决方案后,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)