ruby-on-rails - Rails 容器无法使用 gitlab ci 连接到 mysql 容器
问题描述
我正在为具有构建、测试和发布阶段的 Rails 应用程序设置一个简单的 gitlab ci:
build:
stage: build
script:
- docker build --pull -t $TEST_IMAGE .
- docker push $TEST_IMAGE
test:
stage: test
services:
- docker:dind
script:
- docker pull $TEST_IMAGE
- docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=mysql_strong_password mysql:5.7
- docker run -e RAILS_ENV=test --link mysql:db $TEST_IMAGE bundle exec rake db:setup
build
成功构建 docker 镜像并推送到注册表
test
启动另一个我用作主机数据库的 mysql 容器,但在建立与 mysql 的连接时失败。
Couldn't create database for {"host"=>"db", "adapter"=>"mysql2", "pool"=>5, "username"=>"root", "encoding"=>"utf8", "timeout"=>5000, "password"=>"mysql_strong_password", "database"=>"my_tests"}, {:charset=>"utf8"}
(If you set the charset manually, make sure you have a matching collation)
rails aborted!
Mysql2::Error: Can't connect to MySQL server on 'db' (111 "Connection refused")
--network
我还尝试使用而不是方法创建单独的 docker 网络link
,但没有帮助。
这只发生在 Gitlab runner 实例上。当我在本地机器上执行这些步骤时,它工作正常。
经过大量阅读,我认为这是 docker executor 的错误。我错过了什么吗?
解决方案
拒绝连接表示容器知道如何相互连接,但目标容器在所选端口上没有任何接受连接。这很可能意味着您在数据库完成初始化之前启动应用程序。我的建议是更新/创建您的应用程序或在您的应用程序容器中创建一个入口点,轮询数据库以使其启动并运行,如果它没有启动几分钟后失败。我还建议使用网络而不是链接,因为不推荐使用链接并且不会优雅地处理重新创建的容器。
您看到的行为记录在 mysql 映像中:
在 MySQL 初始化完成之前没有连接
如果容器启动时没有初始化数据库,则会创建一个默认数据库。虽然这是预期的行为,但这意味着在初始化完成之前它不会接受传入的连接。在使用同时启动多个容器的自动化工具(例如 docker-compose)时,这可能会导致问题。
如果您尝试连接到 MySQL 的应用程序没有处理 MySQL 停机时间或等待 MySQL 正常启动,那么在服务启动之前放置一个连接重试循环可能是必要的。有关官方图像中此类实现的示例,请参阅 WordPress 或 Bonita。
从链接的 wordpress 示例中,您可以看到他们的重试代码:
$maxTries = 10;
do {
$mysql = new mysqli($host, $user, $pass, '', $port, $socket);
if ($mysql->connect_error) {
fwrite($stderr, "\n" . 'MySQL Connection Error: (' . $mysql->connect_errno . ') ' . $mysql->connect_error . "\n");
--$maxTries;
if ($maxTries <= 0) {
exit(1);
}
sleep(3);
}
} while ($mysql->connect_error);
在不更改应用程序本身的情况下等待 mysql 的示例入口点脚本可能如下所示:
#!/bin/sh
wait-for-it.sh mysql:3306 -t 300
exec "$@"
来自wait-for-it.sh
vishnubob /wait-for-it,exec "$@"
最后将 pid 1 替换为您传递的命令(例如bundle exec rake db:setup
)。这种方法的缺点是数据库可能会在它真正准备好接受连接之前监听端口,所以我仍然建议在重试循环中使用您的应用程序进行完全登录。
推荐阅读
- javascript - NodeJs AWS-SDK s3.upload 上传功能未按预期抛出错误
- c++ - 编译器在这里执行哪个循环优化?
- git - Windows 10 上 bitbucket 服务器的多个 ssh 密钥
- swift - 可重复使用的视图控制器,在情节提要中具有视图(一个场景中的多个)
- powershell - VSCode 终端中的“<”运算符是否有解决方法?
- php - npm run dev 不会结束
- linux - 一个 bash 脚本,用于解压 linux 子目录中的所有文件
- flutter - Flutter:我是否必须为我的应用程序的每个部分创建一个块?它的最佳做法是什么?
- kubernetes - 如何自动删除失败的 Kubernetes Ceph 节点?
- python-3.x - Python:xarray 和 h5py 不兼容