"SQLSTATE[HY000] [2002] Connection refused" у Laravel в GitHub Actions

Перекладено ШІ
Оригінал: Laravel News
Оновлено: 13 лютого, 2025
Чи стикалися ви з помилкою «SQLSTATE[HY000] [2002] Connection refused» під час налаштування GitHub Actions для вашого додатку на Laravel? У нашій статті ми розглянемо три поширені причини цієї помилки та надамо рішення для їх усунення. Читайте далі, щоб дізнатися, як ваш CI/CD потік може працювати бездоганно!

GitHub Actions — це популярний інструмент для налаштування безперервної інтеграції (CI) для ваших репозиторіїв, розміщених на GitHub. За його допомогою можна визначити робочі процеси у файлах YAML, що автоматизують різні завдання, як-от запуск тестів, статичний аналіз та деплойменти.

Поширеною помилкою, з якою ви можете стикнутися, налаштовуючи GitHub Actions CI workflow для Laravel-додатка, що використовує MySQL, є "SQLSTATE[HY000] [2002] Connection refused". Ця помилка виникає, коли ваш додаток не може підключитися до бази даних MySQL, зазвичай під час виконання міграцій на початку тестів. Ви можете побачити подібне повідомлення у виході вашого робочого процесу на GitHub:

SQLSTATE[HY000] [2002] Connection refused (Connection: mysql, SQL: select exists (select 1 from information_schema.tables where table_schema = 'testing' and table_name = 'migrations' and table_type in ('BASE TABLE', 'SYSTEM VERSIONED')) as `exists`)
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:825
  821▕                     $this->getName(), $query, $this->prepareBindings($bindings), $e
  822▕                 );
  823▕             }
  824▕
➜ 825▕             throw new QueryException(
  826▕                 $this->getName(), $query, $this->prepareBindings($bindings), $e
  827▕             );
  828▕         }
  829▕     }

Причина цієї помилки не завжди очевидна — повідомляється лише про відмову у підключенні.

У цій статті ми розглянемо три поширені причини виникнення цієї помилки у вашому GitHub Actions workflow та способи їх виправлення.

# Дійсний GitHub Actions Workflow

Перед тим як розглянути можливі причини помилки "SQLSTATE[HY000] [2002] Connection refused", давайте ознайомимося з базовим дійсним GitHub Actions workflow для Laravel-додатка, що використовує MySQL.

Ми будемо звертатися до цього прикладу протягом усієї статті, щоб продемонструвати проблеми та їх вирішення.

Ось приклад GitHub Actions workflow, що запускає тести для Laravel-додатка з MySQL:

name: Запустити тести

on: [push]

jobs:
  tests:
    name: Запустити тести
    runs-on: ubuntu-latest

    # Налаштування MySQL служби
    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
      # Витягти репозиторій
      - uses: actions/checkout@v4
        with:
          fetch-depth: 1

      # Налаштування PHP 8.3
      - name: Налаштувати PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3

      # Встановлення залежностей Composer
      - name: Запустити composer install
        run: composer install -n --prefer-dist

      # Копіювання .env.ci до .env та генерування ключа застосунку
      - name: Підготувати Laravel-додаток
        run: |
          cp .env.ci .env
          php artisan key:generate

      # Запуск тестів
      - name: Запустити тести
        run: php artisan test

У наведеному вище workflow ми створюємо завдання під назвою tests, яке запускається при кожному пуші до репозиторію. Завдання працює на PHP 8.3, MySQL 8 та останній версії Ubuntu. Перед виконанням тестів ми встановлюємо залежності Composer, копіюємо файл .env.ci до .env, генеруємо ключ застосунку, а потім запускаємо тести.

У цій статті ми припускаємо, що файл .env.ci містить правильні змінні середовища для виконання workflow на GitHub Actions. Основні з них — це дані підключення до бази даних MySQL:

DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=testing
DB_USERNAME=root
DB_PASSWORD=password

Тепер перейдемо до поширених причин помилки "SQLSTATE[HY000] [2002] Connection refused" і як їх виправити.

# Неправильний порт MySQL

Найпоширенішою причиною помилки "SQLSTATE[HY000] [2002] Connection refused" є використання неправильного номера порту.

# Динамічний номер порту

За замовчуванням MySQL працює на порту 3306. Проте, коли ми запускаємо його у контейнері служби в GitHub Actions workflow, йому випадковий номер порту на раннері робочого процесу. Це означає, що ви ніколи не знаєте, який номер порту потрібно використовувати для підключення до MySQL.

Наприклад, у цьому файлі workflow GitHub Actions вказується відкриття порту 3306 на службі MySQL:

name: Запустити тести

on: [push]

jobs:
  tests:
    name: Запустити тести
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: testing
        ports:
          - 3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
      # ...
      - name: Запустити тести
        run: php artisan test

Ми відкриваємо порт 3306 у контейнері служби MySQL і дозволяємо GitHub Actions відобразити його на випадковий порт на раннері. Це означає, що ми не можемо покладатися на номер порту 3306 для підключення до MySQL.

Якщо ми спробуємо запустити наш workflow, ми отримаємо помилку "SQLSTATE[HY000] [2002] Connection refused", оскільки намагаємось підключитися до MySQL, використовуючи порт 3306 у нашому файлі .env.ci, а не номер порту, призначений GitHub Actions.

Однак ми можемо отримати номер порту, який GitHub Actions призначає до порту 3306 контейнера служби, за допомогою: ${{ job.services.mysql.ports['3306'] }}. Використовуючи це, ми можемо оновити змінну середовища DB_PORT в нашому етапі "Запустити тести":

- name: Запустити тести
  run: php artisan test
  env:
    DB_PORT: ${{ job.services.mysql.ports['3306'] }}

Припустимо, що раннеру workflow було призначено порт 1234 для доступу до порту 3306 контейнера MySQL. Це означало б, що змінна середовища DB_PORT буде встановлена на 1234.

# Закодований номер порту

Якщо ви хочете явно вказати номер порту для доступу до служби MySQL, ви можете зробити це у файлі workflow. Це може бути корисно, якщо ви хочете уникнути випадкового призначення номера порту GitHub Actions.

У нашому дійсному прикладі ми відображаємо порт 3306 служби MySQL на порт 3306 на раннері, використовуючи синтаксис: WORKFLOW_RUNNER_PORT:SERVICE_PORT. У нашому випадку це 3306:3306:

services:
  mysql:
    image: mysql:8
    env:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: testing
    ports:
      - 3306:3306
    options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

Таким чином, ми можемо отримати доступ до служби MySQL, використовуючи існуючі значення, зазначені у нашому файлі .env.ci, оскільки вони налаштовані на порт 3306.

Проте іноді вам може знадобитися використовувати інший номер порту, наприклад, якщо у вас конфлікти з іншими службами, що працюють на тому ж порті. У такому випадку можна змінити номер порту для служби MySQL у файлі workflow. Наприклад, змінимо номер порту на 33306:

services:
  mysql:
    image: mysql:8
    env:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: testing
    ports:
      - 33306:3306
    options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

Тепер, щоб отримати доступ до MySQL, ви можете або оновити файл .env.ci, щоб використовувати новий номер порту:

DB_HOST=127.0.0.1
DB_PORT=33306
DB_DATABASE=testing
DB_USERNAME=root
DB_PASSWORD=password

Або ви можете вказати змінну середовища у файлі workflow, як було показано раніше:

- name: Запустити тести
  run: php artisan test
  env:
    DB_PORT: ${{ job.services.mysql.ports['3306'] }}

# У workflow не визначено службу MySQL

Ще одна причина, через яку може виникнути помилка, — відсутність визначення служби MySQL у вашому workflow. Це може бути легкою помилкою, особливо якщо ви новачок у GitHub Actions або скопіювали workflow з іншого джерела, забувши включити службу MySQL.

Давайте подивимося, як виглядатиме наш workflow без служби MySQL:

name: Запустити тести

on: [push]

jobs:
  tests:
    name: Запустити тести
    runs-on: ubuntu-latest

    # ⚠️ Відсутня служба MySQL! ⚠️

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Налаштувати PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3

      - name: Запустити composer install
        run: composer install -n --prefer-dist

      - name: Підготувати Laravel-додаток
        run: |
          cp .env.ci .env
          php artisan key:generate

      - name: Запустити тести
        run: php artisan test

Як видно, без попереджувального повідомлення в наведеному вище прикладі може не бути відразу очевидно, що служба MySQL відсутня. Але при запуску цього workflow ви побачите помилку "SQLSTATE[HY000] [2002] Connection refused".

Щоб виправити цю ситуацію, можна додати службу MySQL, зазначивши її в нашому дійсному прикладі:

name: Запустити тести

on: [push]

jobs:
  tests:
    name: Запустити тести
    runs-on: ubuntu-latest

    # Налаштування MySQL служби
    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
      # ...

# MySQL ще не доступний

Ще однією причиною помилки "SQLSTATE[HY000] [2002] Connection refused" може бути те, що контейнер служби mysql запущений, але MySQL ще не готовий приймати з'єднання та обробляти запити.

У нашому дійсному прикладі workflow ми бачимо команду, зазначену в опціях служби MySQL:

options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

Ця команда перевіряє готовність MySQL до прийняття з'єднань і не дозволяє workflow продовжуватися, поки він не буде готовий. Команда mysqladmin ping виконується кожні 10 секунд з тайм-аутом у 5 секунд і повторюється тричі. Якщо одна з перевірок пройде успішно, MySQL готовий, і решта workflow продовжить виконання. Якщо MySQL не буде готовий після трьох спроб, workflow завершиться з помилкою.

Якщо ми не маємо цієї перевірки, як тільки служба mysql запуститься, workflow продовжить до наступного етапу, незалежно від того, чи готовий MySQL до прийняття з'єднань.

В багатьох випадках MySQL буде готовий до моменту запуску тестів, оскільки перед їх виконанням задіяні інші кроки, такі як налаштування PHP, установка залежностей Composer та підготовка Laravel-додатка. Це вже додає деякий час перед виконанням тестів, і MySQL найчастіше готовий на цей момент. Однак це не завжди гарантовано, особливо якщо у вас є інші етапи, що потребують раннього доступу до MySQL. Тому мати цю перевірку корисно.

Без перевірки ваш файл YAML для workflow виглядатиме так:

name: Запустити тести

on: [push]

jobs:
  tests:
    name: Запустити тести
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306

    steps:
      # ...

Ми можемо додати перевірку в кінець визначення служби MySQL:

mysql:
  image: mysql:8
  env:
    MYSQL_ROOT_PASSWORD: password
    MYSQL_DATABASE: testing
  ports:
    - 3306:3306
  options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

# Висновок

У цій статті ми розглянули три поширені причини, чому ви можете зіштовхнутися з помилкою "SQLSTATE[HY000] [2002] Connection refused" у вашому GitHub Actions workflow для Laravel-додатка, що використовує MySQL.

Як ми бачили, помилка може виникати через використання неправильного номера порту, відсутність визначення служби MySQL у workflow або ж через те, що MySQL не готовий приймати з’єднання.

Сподіваюся, ця стаття дала вам уявлення про те, як виправити цю помилку та забезпечити безперебійну роботу вашого GitHub Actions workflow.

Крім проблем з MySQL у GitHub Actions, ви також можете зіткнутися з неприємними помилками у своїх production-додатках. Тому важливо використовувати хороші інструменти моніторингу помилок, які допоможуть вам швидко виявити та виправити проблеми. Чудовим інструментом для цього є Honeybadger, який може допомогти у відстеженні, пріоритизації та виправленні помилок. Перевага використання Honeybadger в тому, що він безшовно інтегрується з Laravel, тому ви зможете швидко налаштувати його. Рекомендую вам ознайомитися з ним, щоб мати спокій за те, що ваші додатки працюють безперебійно.