Obtendo um SQLSTATE[HY000] [2002] Conexão recusada no contêiner PHP Apache Docker usando mariadb, se conectado via CMD do Dockerfile (PHP CLI funciona)

Obtendo um SQLSTATE[HY000] [2002] Conexão recusada no contêiner PHP Apache Docker usando mariadb, se conectado via CMD do Dockerfile (PHP CLI funciona)

Estou tentando executar um contêiner Docker baseado em:

  • PHP 8.1
  • Apache 2.4
  • MariaDB (imagem oficial mais recente do docker)

Tudo começa sem nenhum problema; mas não consigo me conectar ao banco de dados do Docker Container via PDO.

Arquivo Docker:

FROM php:8.1-apache

WORKDIR /var/www/html/

RUN pecl install xdebug \
    && apt update \
    && apt install libzip-dev -y \
    && docker-php-ext-enable xdebug \
    && a2enmod rewrite \
    && docker-php-ext-install zip \
    && rm -rf /var/lib/apt/lists/* \
    && docker-php-ext-install pdo pdo_mysql

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY composer.json .

RUN groupadd -r user && useradd -r -g user user
USER user
RUN composer install --no-dev

COPY . .

EXPOSE 80

CMD ["php","src/init.php"]

docker-compose.yml:

services:

  php:
    build: ./php
    depends_on:
      - db
      - adminer
    container_name: php-apache
    ports:
      - 80:80
    volumes:
      # setup xdebug to be able to use PHP step debugger, if needed
      - ./php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      - ./php/conf.d/error_reporting.ini:/usr/local/etc/php/conf.d/error_reporting.ini
      # apache config (server name)
      - ./apache/apache2.conf:/etc/apache2/apache2.conf
      # apache config (rewrite rule to reroute all requests to unknown resources through to REST controller)
      - ./apache/000-default.conf:/etc/apache2/sites-enabled/000-default.conf
      # Source code
      - ./php/src:/var/www/html/src
      # unbind local composer components
      - /php/vendor
      - /php/composer.lock
      - /php/composer.phar
    environment:
      MARIADB_HOST: "127.0.0.1"
      MARIADB_USER: root
      MARIADB_PASSWORD: top_very_secret
      MARIADB_DB: apidb

  adminer:
    image: adminer
    depends_on:
      - db
    restart: always
    ports:
      - 8080:8080

  db:
    image: mariadb
    container_name: db
    volumes:
      - maria-db-storage:/var/lib/mysql
    environment:
      MARIADB_ROOT_PASSWORD: top_very_secret
      MARIADB_DATABASE: apidb
    ports:
      - 3306:3306

volumes:
  maria-db-storage:

De acordo com a maioria das postagens e respostas de diferentes fóruns, tentei usar tanto localhostquanto 127.0.0.1o valor da variável de ambiente MARIADB_HOST(e além disso, estou interessado em saber por que isso deveria funcionar?). De qualquer forma, não resolve o problema, o seguinte código php (conteúdo de src/init.php):

new PDO(
    "mysql:host={$_ENV['MARIADB_HOST']};dbname={$_ENV['MARIADB_DB']}",
    $_ENV['MARIADB_USER'],
    $_ENV['MARIADB_PASSWORD'],
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
);

Sempre resulta em:

SQLSTATE[HY000] [2002] Connection refused.

ATUALIZAR

Dei um passo adiante ao substituir esta linha aqui:

MARIADB_HOST: "127.0.0.1"

com o nome do meu serviço de banco de dados; ser:

MARIADB_HOST: db

Descobri isso porque descobri ao mesmo tempo que só posso fazer login no painel do administrador se usar dbcomo host. Portanto, isso foi um palpite, e eu ainda adoraria entender por que isso funciona agora..?

No entanto, isso ainda não funciona totalmente. Só funciona se eu omitir a CMDlinha no dockerfile, construir o contêiner, migrar para seu terminal via docker exec -t -i php-apache /bin/bashe executar o comando php src/init.phpde lá. Se eu tentar fazer isso através CMDdo formulário Dockerfile(conforme detalhado acima), recebo o erro mencionado. O que ainda estou faltando? É claro que eu preferiria automatizar essa chamada init com o início da execução do contêiner Docker. Indo mais longe...

Responder1

Entendi de verdade agora. A conexão foi recusada porque as variáveis ​​de ambiente só foram utilizadas se o script for executado via shell, e via Docker CMD- como eu usei - não ocorreu nenhum processamento de shell. Nos documentos, também encontreiesse:

Ao contrário do formulário shell, o formulário exec não invoca um shell de comando. Isso significa que o processamento normal do shell não acontece. Por exemplo, CMD [ "echo", "$HOME" ] não fará substituição de variáveis ​​em $HOME. Se você deseja processamento de shell, use o formulário de shell ou execute um shell diretamente, por exemplo: CMD [ "sh", "-c", "echo $HOME" ]. Ao usar o formulário exec e executar um shell diretamente, como no caso do formulário shell, é o shell que está fazendo a expansão da variável de ambiente, não o docker.

Quando usada nos formatos shell ou exec, a instrução CMD define o comando a ser executado ao executar a imagem.

Se você usar o formato shell do CMD, ele será executado em /bin/sh -c

Usando:

CMD [ "sh", "-c", "php src/init.php" ]

em vez de:

CMD [ "php", "src/init.php" ]

Fiz o trabalho!

informação relacionada