Search code examples
phpdockersymfonydocker-composecomposer-php

Symfony install not working in docker container


I ran into some issue with symfony install within docker. I would like to create a docker image, that automaticly install symfony and runs it. I'm using docker-compose. Here is my docker-compose.yml file:

version: "3"

networks:
   nginx-php81-mysql8-node:

services:
   # nginx
   nginx-service:
      image: nginx:stable-alpine
      container_name: nginx-container
      ports:
         - "8080:80"
      volumes:
         - ./app:/var/www/html
         - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
      depends_on:
         - php81-service
         - mysql8-service

   # php
   php81-service:
      build:
         context: .
         dockerfile: docker/php/Dockerfile
      container_name: php81-container
      ports:
         - "8000:8000"
      volumes:
         - ./app:/var/www/html
         - ./docker/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
         - ./docker/php/conf.d/error_reporting.ini:/usr/local/etc/php/conf.d/error_reporting.ini

Here is my Dockerfile:

FROM php:8.1-fpm

WORKDIR /var/www/html

RUN apt-get update && apt-get install -y zlib1g-dev g++ git libicu-dev zip libzip-dev zip \
    && docker-php-ext-install intl opcache pdo pdo_mysql \
    && pecl install apcu \
    && docker-php-ext-enable apcu \
    && docker-php-ext-configure zip \
    && docker-php-ext-install zip

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

RUN curl -sS https://get.symfony.com/cli/installer | bash
RUN mv /root/.symfony5/bin/symfony /usr/local/bin/symfony

RUN composer create-project symfony/skeleton:^6.3 .

RUN pecl install xdebug \
    && docker-php-ext-enable xdebug

RUN touch /var/log/xdebug.log && \
    chmod 777 /var/log/xdebug.log

The problem is, when my image is building, I can see that is creating the symfony project, I can't see any error in the process. But, when I enter the container, my /var/www/html folder is empty, there should be the files of the symfony project.

EDIT

Okey now I know what is the problem. Because I mount my local app folder to the container it is empty. When I run the container, my empty folder has benne mounted.

So, is there a way to install symfony inside the container and the same time map the folder to my local folder?


Solution

  • This is an issue with understanding the difference between Build Time and Run Time.

    Your Dockerfile is a set of instructions that dictates your Image build. Volumes get mounted when your Container runs.

    You're installing your files into your Image's working directory, and then when you run your image, you're mounting an empty folder into that same directory, leading to.... an empty project.

    A good example on how to get around this is to look at how some popular applications set up the installation process for their images at run time. They typically do this by creating an Entrypoint Script (which runs when the containers start), checking for the existence of certain key files, and then copying over the sources from a different directory into the working directory.

    Here's an example of how Wordpress does it: https://github.com/docker-library/wordpress/tree/master/latest/php8.2/fpm-alpine

    At build time, they install the code into /usr/src/wordpress from their own repository: https://github.com/docker-library/wordpress/blob/master/latest/php8.2/fpm-alpine/Dockerfile#L100

    RUN set -eux; \
        version='6.4.1'; \
        sha1='35e62d935d6a93097366476e389752dd55d8a077'; \
        \
        curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \
        echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \
        \
    # upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress
        tar -xzf wordpress.tar.gz -C /usr/src/; \
        rm wordpress.tar.gz; \
        \
    # https://wordpress.org/support/article/htaccess/
        [ ! -e /usr/src/wordpress/.htaccess ]; \
        { \
            echo '# BEGIN WordPress'; \
            echo ''; \
            echo 'RewriteEngine On'; \
            echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \
            echo 'RewriteBase /'; \
            echo 'RewriteRule ^index\.php$ - [L]'; \
            echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \
            echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \
            echo 'RewriteRule . /index.php [L]'; \
            echo ''; \
            echo '# END WordPress'; \
        } > /usr/src/wordpress/.htaccess; \
        \
        chown -R www-data:www-data /usr/src/wordpress; \
    # pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root
    # wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507
        mkdir wp-content; \
        for dir in /usr/src/wordpress/wp-content/*/ cache; do \
            dir="$(basename "${dir%/}")"; \
            mkdir "wp-content/$dir"; \
        done; \
        chown -R www-data:www-data wp-content; \
        chmod -R 1777 wp-content
    

    Then in their Entrypoint, they check for certain files and folders. If they don't exist, they copy them into /var/www/html: https://github.com/docker-library/wordpress/blob/master/latest/php8.2/fpm-alpine/docker-entrypoint.sh#L34

    echo >&2 "WordPress not found in $PWD - copying now..."
            if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then
                echo >&2 "WARNING: $PWD is not empty! (copying anyhow)"
            fi
            sourceTarArgs=(
                --create
                --file -
                --directory /usr/src/wordpress
                --owner "$user" --group "$group"
            )
            targetTarArgs=(
                --extract
                --file -
            )
            if [ "$uid" != '0' ]; then
                # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted"
                targetTarArgs+=( --no-overwrite-dir )
            fi
            # loop over "pluggable" content in the source, and if it already exists in the destination, skip it
            # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded)
            for contentPath in \
                /usr/src/wordpress/.htaccess \
                /usr/src/wordpress/wp-content/*/*/ \
            ; do
                contentPath="${contentPath%/}"
                [ -e "$contentPath" ] || continue
                contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc.
                if [ -e "$PWD/$contentPath" ]; then
                    echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)"
                    sourceTarArgs+=( --exclude "./$contentPath" )
                fi
            done
            tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}"
            echo >&2 "Complete! WordPress has been successfully copied to $PWD"
    

    tl;dr; Use ENTRYPOINTs to assist in handling your runtime needs inside your build.