Rails контейнер не может подключиться к MySQL контейнеру с помощью gitlab ci - PullRequest
0 голосов
/ 11 ноября 2018

Я настраиваю простой gitlab ci для приложения Rails с этапами сборки, тестирования и выпуска:

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 успешно создает образ докера и отправляет его в реестр

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 вместо link, не помогло.

Это происходит только на экземпляре Gitlab Runner. Когда я выполняю эти шаги на локальной машине, все работает нормально.

После долгих чтений я начинаю думать, что это ошибка в Docker Executor. Я что-то упустил?

1 Ответ

0 голосов
/ 11 ноября 2018

Отказ в соединении означает, что контейнеры знают, как связаться друг с другом, но целевой контейнер не имеет ничего, принимающего соединения на выбранном порту. Скорее всего, это означает, что вы запускаете приложение до завершения инициализации базы данных. Я рекомендую обновить / создать приложение или создать точку входа в контейнере приложения, которая опрашивает базу данных на предмет ее работоспособности и сбоя через несколько минут, если она не запускается. Я бы также рекомендовал использовать сети, а не ссылки, поскольку ссылки устарели и не обрабатывают изящно контейнеры, которые создаются заново.

Поведение, которое вы видите, задокументировано на изображении mysql :

Нет подключений до завершения инициализации MySQL

Если при запуске контейнера база данных не инициализирована, будет создана база данных по умолчанию. Хотя это ожидаемое поведение, это означает, что он не будет принимать входящие соединения, пока такая инициализация не завершится. Это может вызвать проблемы при использовании средств автоматизации, таких как docker-compose, которые запускают несколько контейнеров одновременно.

Если приложение, которое вы пытаетесь подключить к MySQL, не обрабатывает время простоя MySQL или не ожидает, что MySQL будет запущен изящно, тогда может потребоваться установка цикла connect-retry до запуска службы. Для примера такой реализации в официальных изображениях см. 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). Недостатком этого подхода является то, что база данных потенциально может прослушивать порт до того, как она действительно будет готова принимать соединения, поэтому я все же рекомендую выполнить полный вход в систему с вашим приложением в цикле повторов.

...