0

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

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

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 [ "sh", "-c", "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:

The script src/init.php simply connects to the DB, and generates the tables that the application needs, if not already present.

My problem now is that the Docker container's execution always terminates with the successful execution of /src/init.php (php-apache exited with code 0). I know this is normal with Docker, as the container only persists as long as the CMD is running, according to the docs. But how can I make sure that the container keeps running; and that the init.php script is simply launched to assured that the application has everything it needs, upon startup of the container?

UPDATE

I've tried to use to set this up via init.sh with the following contents:

#!/bin/sh
php src/init.php
apache2 -D FOREGROUND

Then I replaced:

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

with

CMD ["sh", "-c", "/var/www/html/start.sh"]

This successfully executes the PHP script, but fails to execute the apache command, saying:

[core:warn] [pid 10] AH00111: Config variable ${APACHE_RUN_DIR} is not defined

So it seems that apache's variables are not accessible by the shell execution??

5
  • Start a different command that keeps running. For example Apache. Nov 17, 2022 at 9:11
  • Well how? I tried ENTRYPOINT [ "sh", "-c", "php src/init.php" ], then after that CMD ["sh", "-c", "apache2 -D FOREGROUND"] in my Dockerfile, and I still get the apache exit.
    – DevelJoe
    Nov 17, 2022 at 9:16
  • Docker doesn't work like that. You can only run a single command in a container. Write a script that executes your PHP script and than runs Apache. Or an infinite loop, or whatever. Nov 17, 2022 at 9:18
  • ok will try. This post here telling to use both ENTRYPOINT and CMD gave me the idea. If you know the contents of such a shell script, feel free to let me know; I've little experience in these things.
    – DevelJoe
    Nov 17, 2022 at 9:19
  • 1
    You misunderstood that post. Nov 17, 2022 at 9:22

1 Answer 1

0

Ok got a working solution now. First of all, the reason for:

[core:warn] [pid 10] AH00111: Config variable ${APACHE_RUN_DIR} is not defined

was that I used the wrong command tool to run apache in the foreground. using apache2ctl instead of apache2 did the job (in apache2, apache's environment variables seemed to be unavailable).

Next, the final contents of my start.sh are:

#!/bin/sh
wait-for-it.sh db:3306 --strict --timeout=30 -- php init.php && apache2ctl -D FOREGROUND

Now there's an additional thing I've added, as you see. docker-compose starts containers simultaneously (see this, section "No connections until MariaDB init completes"). When you thus start the container of the present application on a machine which did not have the mariadb image pulled yet, it is very likely that you will try to execute init.php, by which you attempt to connect to your db, without your db container actually being fully initialized. This again results in the beloved SQLSTATE[HY000] [2002] Connection refused error. Docker itself mentions the wait-for-it solution as a workaround, which is why I've used it. All it does in the code shown above is:

  • check during max 30 seconds if the address db:3306 is available.
  • if it becomes available within that time, execute everything after --.
  • if it does not become available within that time, interrupt the execution (which is when your container initialization would fail).

For a stricter approach to this, you could also code within your init.php to re-attempt to connect to your DB for max x times after waiting for an interval of 5s if the connection fails; and that would do the job too.

Everything's fully working now!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.