Score:0

Getting a SQLSTATE[HY000] [2002] Connection refused in PHP Apache Docker container using mariadb, if connected via Dockerfile's CMD (PHP CLI works)

in flag

I'm trying to run a Docker container based on:

  • PHP 8.1
  • Apache 2.4
  • MariaDB (latest official docker image)

It all starts up without any issue; but I'm unable to connect with the Docker Container's DB via PDO.

Dockerfile:

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:

According to the majority of the posts and answers from different forums, I've tried to use both localhost as well as 127.0.0.1 as the value of the environment variable MARIADB_HOST (and besides, I'm inerested in knowing why that should be supposed to work?). Anyway, it does not solve the problem, the following php code (content of 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
    ]
);

Always results in:

SQLSTATE[HY000] [2002] Connection refused.

UPDATE

I came a step further by replacing this line here:

MARIADB_HOST: "127.0.0.1"

with the name of my database service; being:

MARIADB_HOST: db

I found this out because I found out at the same time that I can only login to the adminer dashboard if I use db as the host. Hence this was rather a guess, and I would still love to understand why this works now..?

This however still does not fully work yet. It only works if I omit the CMD line in the dockerfile, build the container, migrate into its terminal via docker exec -t -i php-apache /bin/bash, and run the command php src/init.php from within there. If I try to do so via CMD form the Dockerfile (as detailed above), I get the mentioned error. What am I still missing? I would of course prefer to automate this init call with the running start of the Docker container. Digging further...

Score:0
in flag

Got it for real now. The connection was refused because the environment variables were only used if the script is executed via shell, and via Docker's CMD - as I used it - no shell processing occurred. From the docs, I also found this:

Unlike the shell form, the exec form does not invoke a command shell. This means that normal shell processing does not happen. For example, CMD [ "echo", "$HOME" ] will not do variable substitution on $HOME. If you want shell processing then either use the shell form or execute a shell directly, for example: CMD [ "sh", "-c", "echo $HOME" ]. When using the exec form and executing a shell directly, as in the case for the shell form, it is the shell that is doing the environment variable expansion, not docker.

When used in the shell or exec formats, the CMD instruction sets the command to be executed when running the image.

If you use the shell form of the CMD, then the will execute in /bin/sh -c

Using:

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

instead of:

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

Did the job!

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.