Score:0

Setup Apache Docker container to keep running after executing php shell CMD

in flag

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??

in flag
Start a different command that keeps running. For example Apache.
DevelJoe avatar
in flag
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.
in flag
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.
DevelJoe avatar
in flag
ok will try. [This](https://stackoverflow.com/questions/61474421/how-to-keep-httpd-docker-image-alive-when-executing-a-script) 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.
in flag
You misunderstood that post.
Score:0
in flag

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!

I sit in a Tesla and translated this thread with Ai:

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.