使用 docker 搭建 hyperf 项目框架遇到 MySQL 连接失败的问题
问题描述:在使用 dockercompose 构建项目时候启动 hyperf 容器无法链接 MySQL
这是我的 docker-compose.yml 配置:
services:
hyperf:
build:
context: .
# image: hyperf/hyperf:7.4-alpine-v3.11-swoole
#容器的重启策略
#no: 默认策略,不自动重启容器。
#on-failure: 只在容器异常退出(退出状态码非0)时重启容器。
#unless-stopped: 在容器退出时总是重启容器,但如果容器被手动停止(如使用 docker stop 命令),则不会在 Docker 守护进程重启时自动启动容器。
#always: 在容器退出时总是重启容器,并且在 Docker 守护进程重启时也会自动启动容器。
restart: always
environment:
- TIMEZONE='${TIMEZONE}'
ports:
- 9501:9501
- 9502:9502
- 9503:9503
volumes:
- ./:/opt/www
depends_on:
- mysql
entrypoint: ["php", "/opt/www/bin/hyperf.php", "start"]
mysql:
environment:
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
#允许 root 用户从任意主机连接到 MySQL 服务器。这里的 % 是一个通配符,表示任意主机。
#'root'@'localhost' 表示 root 用户只能从 localhost 连接
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
image: mysql:8.0
ports:
- '${DB_PORT:-3306}:3306'
volumes:
#数据库持久化
- mysql_data:/var/lib/mysql
#初始化数据库
- './docker/mysql/db_dump.sql:/docker-entrypoint-initdb.d/db_dump.sql'
# 挂载数据库日志目录
- ./docker/mysql/log:/var/log
# - ./docker/mysql/my.cnf:/etc/my.cnf
# 挂载自定义配置文件目录
- ./docker/mysql/slow-query-log.cnf:/etc/mysql/conf.d/slow-query-log.cnf
# command: "--default-authentication-plugin=mysql_native_password"
redis:
image: redis
restart: no
# rabbitmq:
# image: rabbitmq:management
# restart: always
# ports:
# - "4369:4369"
# - "5672:5672"
# - "15672:15672"
# - "25672:25672"
# environment:
# - RABBITMQ_DEFAULT_USER=mineadmin
# - RABBITMQ_DEFAULT_PASS=123456
volumes:
mysql_data:
dockerfile:
# Default Dockerfile
#
# @link https://www.hyperf.io
# @document https://hyperf.wiki
# @contact group@hyperf.io
# @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
FROM hyperf/hyperf:7.4-alpine-v3.11-swoole
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
APP_ENV=prod \
SCAN_CACHEABLE=(true)
# update
RUN set -ex \
# show php version and extensions
&& php -v \
&& php -m \
&& php --ri swoole \
# ---------- some config ----------
&& cd /etc/php7 \
# - config PHP
&& { \
echo "upload_max_filesize=128M"; \
echo "post_max_size=128M"; \
echo "memory_limit=1G"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99_overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- clear works ----------
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
WORKDIR /opt/www
# Composer Cache
# COPY ./composer.* /opt/www/
# RUN composer install --no-dev --no-scripts
COPY . /opt/www
RUN composer install --no-dev -o && php bin/hyperf.php
EXPOSE 9501
#CMD ["tail", "-f", "/dev/null"]
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
hyperf 框架中的 .env 文件:
APP_NAME=skeleton
APP_ENV=dev
#守护进程
DAEMONIZE=1
DB_DRIVER=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=u7cloud
DB_USERNAME=u7cloud
DB_PASSWORD=u7cloud@MySQL!
DB_CHARSET=utf8mb4
#DB_COLLATION=utf8mb4_unicode_ci
DB_COLLATION=utf8mb4_general_ci
DB_PREFIX=
REDIS_HOST=redis
REDIS_AUTH=
REDIS_PORT=6379
REDIS_DB=0
启动:
docker-compose up --build
[+] Building 16.4s (10/10) FINISHED docker:default
=> [hyperf internal] load build definition from Dockerfile
=> => transferring dockerfile: 1.53kB
=> [hyperf internal] load metadata for docker.io/hyperf/hyperf:7.4-alpine-v3.11-swoole
=> [hyperf internal] load .dockerignore
=> => transferring context: 2B
=> [hyperf 1/5] FROM docker.io/hyperf/hyperf:7.4-alpine-v3.11-swoole
=> [hyperf internal] load build context
=> => transferring context: 1.00MB
=> CACHED [hyperf 2/5] RUN set -ex && php -v && php -m && php --ri swoole && cd /etc/php7 && { echo "upload_max_filesize=128M"; echo "post_max_size=128M"; echo "memory_limit=1G"; echo "date.timezone=Asia/Sha 0.0s
=> CACHED [hyperf 3/5] WORKDIR /opt/www
=> [hyperf 4/5] COPY . /opt/www
=> [hyperf 5/5] RUN composer install --no-dev -o && php bin/hyperf.php
=> [hyperf] exporting to image
=> => exporting layers
=> => writing image sha256:217be6c8e5e126a55ff2111af14061846460ef29247832dce98ca9ecb5e95104
=> => naming to docker.io/library/yunque_admin-hyperf
[+] Running 3/3
✔ Container yunque_admin-redis-1 Created
✔ Container yunque_admin-mysql-1 Created
✔ Container yunque_admin-hyperf-1 Recreated
Attaching to hyperf-1, mysql-1, redis-1
redis-1 | 1:C 31 May 2024 02:25:29.503 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-1 | 1:C 31 May 2024 02:25:29.503 * Redis version=7.2.5, bits=64, commit=00000000, modified=0, pid=1, just started
redis-1 | 1:C 31 May 2024 02:25:29.503 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis-1 | 1:M 31 May 2024 02:25:29.503 * monotonic clock: POSIX clock_gettime
redis-1 | 1:M 31 May 2024 02:25:29.503 * Running mode=standalone, port=6379.
redis-1 | 1:M 31 May 2024 02:25:29.504 * Server initialized
redis-1 | 1:M 31 May 2024 02:25:29.504 * Loading RDB produced by version 7.2.5
redis-1 | 1:M 31 May 2024 02:25:29.504 * RDB age 23 seconds
redis-1 | 1:M 31 May 2024 02:25:29.504 * RDB memory usage when created 0.83 Mb
redis-1 | 1:M 31 May 2024 02:25:29.504 * Done loading RDB, keys loaded: 0, keys expired: 0.
redis-1 | 1:M 31 May 2024 02:25:29.504 * DB loaded from disk: 0.001 seconds
redis-1 | 1:M 31 May 2024 02:25:29.504 * Ready to accept connections tcp
mysql-1 | 2024-05-31 02:25:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.37-1.el9 started.
mysql-1 | 2024-05-31 02:25:29+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
mysql-1 | 2024-05-31 02:25:29+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.37-1.el9 started.
mysql-1 | '/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
mysql-1 | 2024-05-31T02:25:30.112759Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
mysql-1 | 2024-05-31T02:25:30.113403Z 0 [Warning] [MY-011068] [Server] The syntax 'log_slow_slave_statements' is deprecated and will be removed in a future release. Please use log_slow_replica_statements instead.
mysql-1 | 2024-05-31T02:25:30.113423Z 0 [Warning] [MY-011068] [Server] The syntax 'expire-logs-days' is deprecated and will be removed in a future release. Please use binlog_expire_logs_seconds instead.
mysql-1 | 2024-05-31T02:25:30.115781Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.37) starting as process 1
mysql-1 | 2024-05-31T02:25:30.126707Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
hyperf-1 |
hyperf-1 | In Connection.php line 1082:
hyperf-1 |
hyperf-1 | SQLSTATE[HY000] [2002] Connection refused (SQL: select `id`, `from_id`, `fr
hyperf-1 | om_type`, `crontab_time` from `crontab_task` where `from_type` = system)
hyperf-1 |
hyperf-1 |
hyperf-1 | In Connector.php line 106:
hyperf-1 |
hyperf-1 | SQLSTATE[HY000] [2002] Connection refused
hyperf-1 |
hyperf-1 |
hyperf-1 | start
hyperf-1 |
hyperf-1 | Connection failed: SQLSTATE[HY000] [2002] Connection refused
排查问题
从输出来看 hyperf 容器好像无法连接 mysql,为了防止 docker-compose up 启动 hyperf 容器的时候 MySQL 容器还没有启动导致
SQLSTATE[HY000] [2002] Connection refused
所以我在 docker-compose.yml 文件中配置了重启策略:restart: always
然后在 hyperf 容器尝试手动连接到 mysql,于是在 bin/hyperf.php 启动文件尝试连接数据库:
#!/usr/bin/env php
<?php
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');
error_reporting(E_ALL);
date_default_timezone_set('Asia/Shanghai');
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
require BASE_PATH . '/vendor/autoload.php';
$host = 'mysql';
$dbname = 'u7cloud';
$user = 'u7cloud';
$pass = 'u7cloud@MySQL!';
try {
$conn = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
echo "Connected successfully";
} catch (PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
// Self-called anonymous function that creates its own scope and keep the global namespace clean.
try {
(function () {
Hyperf\Di\ClassLoader::init();
/** @var \Psr\Container\ContainerInterface $container */
$container = require BASE_PATH . '/config/container.php';
$application = $container->get(\Hyperf\Contract\ApplicationInterface::class);
$application->run();
})();
} catch (Exception|Throwable $e) {
var_dump($e);
}
这里的数据库配置和 config/autoload/database.php 的配置是一致的
- 重启项目:
从输出来看第一次启动 hyperf 报错:SQLSTATE[HY000] [2002] Connection refuseddocker-compose up --build [+] Building 16.2s (10/10) FINISHED docker:default => [hyperf internal] load build definition from Dockerfile => => transferring dockerfile: 1.53kB => [hyperf internal] load metadata for docker.io/hyperf/hyperf:7.4-alpine-v3.11-swoole => [hyperf internal] load .dockerignore => => transferring context: 2B => [hyperf 1/5] FROM docker.io/hyperf/hyperf:7.4-alpine-v3.11-swoole => [hyperf internal] load build context => => transferring context: 1.01MB => CACHED [hyperf 2/5] RUN set -ex && php -v && php -m && php --ri swoole && cd /etc/php7 && { echo "upload_max_filesize=128M"; echo "post_max_size=128M"; echo "memory_limit=1G"; echo "date.timezone=Asia/Sha 0.0s => CACHED [hyperf 3/5] WORKDIR /opt/www => [hyperf 4/5] COPY . /opt/www => [hyperf 5/5] RUN composer install --no-dev -o && php bin/hyperf.php => [hyperf] exporting to image => => exporting layers => => writing image sha256:b66f8604b419674225cdcfe0a7f40cb35e02304133724236b7cd8f276322477f => => naming to docker.io/library/yunque_admin-hyperf [+] Running 3/3 ✔ Container yunque_admin-redis-1 Created ✔ Container yunque_admin-mysql-1 Created ✔ Container yunque_admin-hyperf-1 Recreated Attaching to hyperf-1, mysql-1, redis-1 redis-1 | 1:C 31 May 2024 02:53:07.958 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo redis-1 | 1:C 31 May 2024 02:53:07.958 * Redis version=7.2.5, bits=64, commit=00000000, modified=0, pid=1, just started redis-1 | 1:C 31 May 2024 02:53:07.958 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf redis-1 | 1:M 31 May 2024 02:53:07.958 * monotonic clock: POSIX clock_gettime redis-1 | 1:M 31 May 2024 02:53:07.958 * Running mode=standalone, port=6379. redis-1 | 1:M 31 May 2024 02:53:07.959 * Server initialized redis-1 | 1:M 31 May 2024 02:53:07.959 * Loading RDB produced by version 7.2.5 redis-1 | 1:M 31 May 2024 02:53:07.959 * RDB age 24 seconds redis-1 | 1:M 31 May 2024 02:53:07.959 * RDB memory usage when created 0.83 Mb redis-1 | 1:M 31 May 2024 02:53:07.959 * Done loading RDB, keys loaded: 0, keys expired: 0. redis-1 | 1:M 31 May 2024 02:53:07.959 * DB loaded from disk: 0.000 seconds redis-1 | 1:M 31 May 2024 02:53:07.959 * Ready to accept connections tcp mysql-1 | 2024-05-31 02:53:08+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.37-1.el9 started. mysql-1 | 2024-05-31 02:53:08+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' mysql-1 | 2024-05-31 02:53:08+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.37-1.el9 started. mysql-1 | '/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock' mysql-1 | 2024-05-31T02:53:08.621464Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead. mysql-1 | 2024-05-31T02:53:08.621498Z 0 [Warning] [MY-011068] [Server] The syntax 'log_slow_slave_statements' is deprecated and will be removed in a future release. Please use log_slow_replica_statements instead. mysql-1 | 2024-05-31T02:53:08.621505Z 0 [Warning] [MY-011068] [Server] The syntax 'expire-logs-days' is deprecated and will be removed in a future release. Please use binlog_expire_logs_seconds instead. mysql-1 | 2024-05-31T02:53:08.622443Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.37) starting as process 1 mysql-1 | 2024-05-31T02:53:08.628274Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. hyperf-1 | hyperf-1 | In Connection.php line 1082: hyperf-1 | hyperf-1 | SQLSTATE[HY000] [2002] Connection refused (SQL: select `id`, `from_id`, `fr hyperf-1 | om_type`, `crontab_time` from `crontab_task` where `from_type` = system) hyperf-1 | hyperf-1 | hyperf-1 | In Connector.php line 106: hyperf-1 | hyperf-1 | SQLSTATE[HY000] [2002] Connection refused hyperf-1 | hyperf-1 | hyperf-1 | start hyperf-1 | hyperf-1 | Connection failed: SQLSTATE[HY000] [2002] Connection refused mysql-1 | 2024-05-31T02:53:08.853962Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. mysql-1 | 2024-05-31T02:53:09.040485Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. mysql-1 | 2024-05-31T02:53:09.040528Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. mysql-1 | 2024-05-31T02:53:09.045163Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. mysql-1 | 2024-05-31T02:53:09.066378Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock mysql-1 | 2024-05-31T02:53:09.066467Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.37' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL. hyperf-1 | Connected successfully hyperf-1 exited with code 0 hyperf-1 | Connected successfully hyperf-1 exited with code 0 hyperf-1 | Connected successfully hyperf-1 exited with code 0 ^Bhyperf-1 | Connected successfully
因为配置了重启策略,然后 hyperf 容器重启输出:Connected successfully,说明 MySQL 是连接成功了,但是后面却异常退出了
解决方法
暂时还没有找到什么原因
本作品采用《CC 协议》,转载必须注明作者和本文链接
感谢李铭昕大佬的指点
因为我在 .env 配置了 #守护进程 DAEMONIZE=1,配置了守护进程,hyperf 启动进程就自己退出了,docker 看到你进程退出了,就认为你没有启动成功,所以 docker一直重启
.env 文件中设置 DAEMONIZE=1 会导致 Hyperf 在后台运行,这意味着 Hyperf 的主进程会在启动后立即退出,而不是保持前台运行。 当 Docker 运行一个容器时,它会监视容器的主进程(PID 1)。如果这个进程退出,Docker 会认为容器已经完成了它的工作并退出。如果配置了 restart: always,Docker 会不断地重启容器,因为它认为容器没有成功运行。 这是使用 Docker 运行后台进程时的一个常见问题。Docker 的设计理念是一个容器运行一个前台进程。
最简单的解决方案可能就是设置 DAEMONIZE=0,让 Hyperf 在前台运行