DEV Community

Adamo Crespi for Serendipity HQ

Posted on • Originally published at io.serendipityhq.com on

Moving Symfony files in the Docker container

Once you have created the first configuration of your basic Docker stack, it is time to move the Symfony’s files into Docker’s web server service.

This will make us able to run the symfony application from inside the web server container, getting the real benefits of using a system as Docker (same environment on any machine, above all).

So, we need a way to move in the container all the files required to make our project run.

How do we do this?

The answer to our question is a really simple word: “Dockerfile” (never heard of it? Ok, it is a not so simple word! But it is simple to use it anyway! 🙂 )

From the Docker documentation about “Dockerfile”:

Docker can build images automatically by reading the instructions from a Dockerfile

A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

Using docker build users can create an automated build that executes several command-line instructions in succession.

So, basically, all what you write in the console to configure your environment has to be put in a Dockerfile to automate the process.

Anytime someone wants to configure the environment to run our app, (s)he will not have to take care of the details as they are all in the Dockerfile: easy and fast!

So, let’s prepare our app to use Dockerfiles.

Create the folder structure and the Dockerfile

First of all we need to create some folders.

So, in the root of your app, create a folder called docker, inside it create another folder called build and inside it another folder called apache, then inside it create a file called Dockerfile.

The final folder structure will be like this:

The next steps are:

  1. Put in the Dockerfile the commands required to build the apache container;
  2. Configure docker-compose.yaml to use this new Dockerfile.

As first thing, instruct the Dockerfile about the Apache image we want to use:

# docker/build/apache/Dockerfile

FROM httpd:2.4

Enter fullscreen mode Exit fullscreen mode

The httpd:2.4 is exactly the same we used in our docker-compose.yaml file.

Now, edit the docker-compose.yaml file so it will use this Dockerfile: we will do this using the build key node:

version: '3.7'
services:
  # "php" was "language" in previous example
  ...

  # Configure the database
  ...

  # Configure Apache
  apache:
    # image: httpd:2.4 <- Remove this node
    build: docker/build/apache
    ports:
      - "8100:80"

Enter fullscreen mode Exit fullscreen mode

Once edited the docker-compose.yaml file, first run docker-compose down to stop all the containers, then run docker-compose build:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose down
Stopping app-aragog-www_mysql_1 ... done
Stopping app-aragog-www_apache_1 ... done
Removing app-aragog-www_php_1 ... done
Removing app-aragog-www_mysql_1 ... done
Removing app-aragog-www_apache_1 ... done
Removing network app-aragog-www_default
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
php uses an image, skipping
mysql uses an image, skipping
Building apache
Step 1/1 : FROM httpd:2.4
 ---> fb2f3851a971

Successfully built fb2f3851a971
Successfully tagged app-aragog-www_apache:latest

Enter fullscreen mode Exit fullscreen mode

As you can see, the php and mysql containers are skipped as they use an image, while the apache container is built as it uses a Dockerfile.

Try to start the containers with docker-compose up -d:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose up -d
Creating network "app-aragog-www_default" with the default driver
Creating app-aragog-www_mysql_1 ... done
Creating app-aragog-www_php_1 ... done
Creating app-aragog-www_apache_1 ... done

Enter fullscreen mode Exit fullscreen mode

And access the URL http://127.0.0.1:8100: you see the message “It works!”.

Ok, the first step is done: the Apache web server is continuing to work also if we are using a different method to configure it.

Now we need to do the second step: add the Symfony files to the document root of Apache.

To do this, we first need to understand what is the Docker Context as it will be extremely useful.

The Docker Context: what is it, why we need to understand it and how to use it

Before diving in the files and their copy in the container, we need to understand what is Docker Context.

From the documentation about build:

The docker build command builds Docker images from a Dockerfile and a “context”.

A build’s context is the set of files located in the specified PATH or URL.

The build process can refer to any of the files in the context.

For example, your build can use a COPY instruction to reference a file in the context.

So, to copy our Symfony files to the container, we need to make them available in the context.

We are using a docker-compose.yaml file, so we need to understand how to specify the context when using it.

The solution? The context key of the build node.

The documentation says:

Either a path to a directory containing a Dockerfile, or a url to a git repository.

When the value supplied is a relative path, it is interpreted as relative to the location of the Compose file.

This directory is also the build context that is sent to the Docker daemon.

But there is more: we can also set a specific Dockerfile to use when setting the context.

This is useful as because

By default the docker build command will look for a Dockerfile at the root of the build context

So, trying to recap what we have read until now in the documentation:

  1. We can copy or add files in the container from our machine only if they are in the “context”;
  2. Using the build key in the docker-compose.yaml file with a path (build: path/to/folder):
    1. Uses the Dockerfile found in this path;
    2. Uses the content of this path as “context” (so we can access only files in it)
  3. In our docker-compose.yaml file, we can:
    1. Specify a specific “context” to use to build the service/container;
    2. Specify a specific Dockerfile to use to build the service/container.

Interesting, isn’t it? Let’s put all those information at work to understand how to copy our Symfony files to the Apache container.

As usual, we will start small.

Trying to copy only a single file test.txt

First, we need a file to copy: create an empty file called test.txt and put it in the root directory of the project: we will try to copy it in the container.

Now, let’s edit our docker-compose.yaml file to specify the desired context and Dockerfile:

# docker-compose.yaml

version: '3.7'
services:
  # "php"
  ...

  # Configure the database
  ...

  # Configure Apache
  apache:
    # build: docker/build/apache <- Remove this node
    # And recreate it as follows
    build:
      context: .
      dockerfile: docker/build/apache/Dockerfile
    ports:
      - "8100:80"

Enter fullscreen mode Exit fullscreen mode

As you can see, we removed the path to the apache folder and, instead, added two more keys:

  • context, that sets the context to the current folder (that is the root folder, as the docker-compose.yaml file is in the root);
  • dockerfile, that specifies which Dockerfile to use to build the container/service that runs Apache

This has two effects:

  • Sets the Docker Context to our root folder as the docker-compose.yaml file is in the root (so we can now use – so, we can copy, too – any file in our root folder);
  • Explicitly specifies the Dockerfile to use, without relying on the auto-discovery capabilities of Docker.

Before this modification, instead, the context was the folder docker/build/apache (and so we were able to copy only files and folders in that folder) and the Dockerfile was found thanks to the auto-discovery capabilities of Docker in finding Dockerfiles in the provided paths.

Now that we have our entire root folder in the Docker Context, edit the Dockerfile that builds Apache to make it able to copy the file test.txt:

# docker/build/apache/Dockerfile

FROM httpd:2.4

# Copy Symfony files: COPY [FROM_MACHINE] [TO_CONTAINER]
COPY test.txt copied-test.txt

Enter fullscreen mode Exit fullscreen mode

We are using the COPY instruction.

As the context is our root folder, we simply use the file name test.txt and copy it to the container as copied-test.txt.

Now run the command docker-compose build:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
php uses an image, skipping
mysql uses an image, skipping
Building apache
Step 1/2 : FROM httpd:2.4
 ---> fb2f3851a971
Step 2/2 : COPY test.txt copied-test.txt
 ---> 287d3e3b6a04

Successfully built 287d3e3b6a04
Successfully tagged app-aragog-www_apache:latest

Enter fullscreen mode Exit fullscreen mode

As usual, php and mysql are skipped, BUT apache has now two steps, and the second one is exactly the COPY instruction.

Let’s see if the file was really copied.

First, recreate the containers running the up command:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose up -d
Recreating app-aragog-www_apache_1 ... done
Starting app-aragog-www_php_1 ... done

Enter fullscreen mode Exit fullscreen mode

Then enter the container (use docker ps to find the ID of the apache container) and list the files in it:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker exec -it e4ed20c531ae bash
root@e4ed20c531ae:/usr/local/apache2# ls
bin build cgi-bin conf copied-test.txt error htdocs icons include logs modules

Enter fullscreen mode Exit fullscreen mode

BINGO! Our test.txt file is there with the name copied-test.txt!

Now that we can use the context to copy files in the container from it, we are ready to further explore the copying of Symfony files to the container.

Moving Symfony’s files into the container

Now that we know how to move files to the container, we are ready to move our entire app to the container.

We can use a simple instruction like this:

COPY . ./htdocs

Enter fullscreen mode Exit fullscreen mode

This will move the entire context (that is our entire root folder) in the creating container.

WARNING: This is not the best way of doing this. For the moment we make simple things, so it is ok to copy the entire root folder.

Later in this series of posts, we will learn how to refine the files copied in the container to speed up and optimize the process.

For the moment, let’s try this quick and dirty solution.

Edit the docker/build/apache/Dockerfile, adding the above instruction:

FROM httpd:2.4

# Copy Symfony files
# COPY test.txt copied-test.txt <- Remove this line (and also the text.txt file!)

COPY . ./htdocs

Enter fullscreen mode Exit fullscreen mode

Rebuild the containers:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
php uses an image, skipping
mysql uses an image, skipping
Building apache
Step 1/2 : FROM httpd:2.4
 ---> fb2f3851a971
Step 2/2 : COPY . ./htdocs
 ---> 3f3528537fe9

Successfully built 3f3528537fe9
Successfully tagged app-aragog-www_apache:latest

Enter fullscreen mode Exit fullscreen mode

And update the running ones:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose up -d
Recreating app-aragog-www_apache_1 ... 
Recreating app-aragog-www_apache_1 ... done
Starting app-aragog-www_php_1 ... done

Enter fullscreen mode Exit fullscreen mode

Now enter the container and check if the files are in it:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b10afca67dd app-aragog-www_apache "httpd-foreground" 8 seconds ago Up 4 seconds 8100->80/tcp app-aragog-www_apache_1
c633398a7d65 mysql:5.7 "docker-entrypoint.s…" 15 hours ago Up 15 hours 3306/tcp app-aragog-www_mysql_1
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker exec -it 8b10afca67dd bash
root@8b10afca67dd:/usr/local/apache2# ls
Docker.md README.md bin build cgi-bin composer.json composer.lock conf config docker docker-compose.yaml docs error htdocs icons include logs modules public src symfony.lock var vendor

Enter fullscreen mode Exit fullscreen mode

Are they there? Very good! We have just moved all the Symfony’s files in the container! 🙂

Let’s try to open our Symfony app: go to http://127.0.0.1:8100

The message “It works!”? What’s happening?

Ok, try this: http://127.0.0.1:8100/public… A list of files? ? And there is our index.php listed ?

Click on it…

What are you seeing? What, the content of the php file, not interpreted but instead served as a simple text file? ?

What is damn happening? ?

Making Apache interpret our PHP files

Technically, we have just created our stack and it works because we are able to make up and running our containers with Apache and MySQL (PHP currently doesn’t work, in fact you have never seen it when running docker ps!).

And it works because we can see the Symfony’s index.php content.

So, technically it is working, practically it isn’t as we are not able to serve the php files as interpreted webpages.

Why does this happen?

Let’s recap our current configuration:

version: '3.7'
services:
  # "php" was "language" in previous example
  php:
    image: php:7.2

  # Configure the database
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}

  # Configure Apache
  apache:
    build:
      context: .
      dockerfile: docker/build/apache/Dockerfile
    ports:
      - "8100:80"

Enter fullscreen mode Exit fullscreen mode

As you can see we are building three containers: one for php, one for mysql and one for apache.

Leaving apart for the moment the container for mysql, let’s examine deeper what we are doing with php and apache containers.

We already know that the php container doesn’t work and never starts nor is built: maybe it is time to understand why!

To build php and apache we are using two images:

  • PHP: php:7.2
  • Apache: httpd:2.4 (this is in the Dockerfile in docker/build/apache)

So, the question at this point is: what happens when we build these images?

The first thing we know is that the php container is skipped. When running docker-compose build, in fact, we can read a clear message:

php uses an image, skipping
Enter fullscreen mode Exit fullscreen mode

The second thing we know is that the php container is never run: using docker ps, in fact, we have never seen it in the list:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d109c4b4c431 app-aragog-www_apache "httpd-foreground" 11 minutes ago Up 11 minutes 8100->80/tcp app-aragog-www_apache_1
844167c3154f mysql:5.7 "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 3306/tcp app-aragog-www_mysql_1

Enter fullscreen mode Exit fullscreen mode

So, in the end, we have this situation:

  1. We have the Apache container running, but without PHP;
  2. We have the PHP container NOT running, and we anyway don’t have PHP (but, as seen in the previous post about how to configure the Apache web server on Docker, if we try to check the version of PHP, we get it, so it seems it is working: very confusing!).

Why?

Let’s see what these images do.

Introducing Docker images (and containers)

A Docker image is nothing more than a Dockerfile with some instructions to build a container.

More precisely

An instance of an image is called a container. You have an image, which is a set of layers as you describe. If you start this image, you have a running container of this image. You can have many running containers of the same image.

You can read more about images and layers here.

The relvant part is this:

A Docker image is built up from a series of layers. Each layer represents an instruction in the image’s Dockerfile. Each layer except the very last one is read-only.

Each layer is only a set of differences from the layer before it. The layers are stacked on top of each other. When you create a new container, you add a new writable layer on top of the underlying layers. This layer is often called the “container layer”. All changes made to the running container, such as writing new files, modifying existing files, and deleting files, are written to this thin writable container layer. The diagram below shows a container based on the Ubuntu 15.04 image.

And this is the representation of what you’ve read until now:

But from where these images come?

From the Docker Hub!

So, to understand why our httpd image doesn’t interpret correctly PHP files and why our php image doesn’t run in a container, we can read the Dockerfiles of them.

And here they are:

Let’s start examining first the httpd page.

If your read it, there is a section called “How to use this image.” that states this:

This image only contains Apache httpd with the defaults from upstream. There is no PHP installed, but it should not be hard to extend. On the other hand, if you just want PHP with Apache httpd see the PHP image and look at the -apache tags.

It is clear, now?

Our service doesn’t interpret PHP files because using the image httpd simply we don’t have it!

So, bascially, we are using the wrong image ?.

Let’s use the right one!

Configuring an Apache web server with PHP (for real!)

So, we need to use the the php image: and this is what we were already using.

But reading the description from the httpd image, it is not sufficient to use the image php:7.2 like we did: we need to use an image tagged with -apache!

So, go to the php image page on Docker Hub.

If you read the page, there is a section called “With Apache” that says:

More commonly, you will probably want to run PHP in conjunction with Apache httpd. Conveniently, there’s a version of the PHP container that’s packaged with the Apache web server.

And the instructions say that we need to use the image php:7.2-apache: very very well ?

So, what happened was this:

  1. We saw the version of PHP because effectively we were building a container with it, so Docker was able to find it and returned its version;
  2. We were not able to serve an interpreted PHP file as we were using the Apache container that is not equipped with PHP.

So, what we need to do now is:

  • Change the image we use for PHP in docker-compose.yaml;
  • Use a Dockerfile to create the php service (to merge the current configuration of the Apache container in the PHP container);
  • Copy all files from our machine to the new PHP image (instead of copying them in the Apache container);
  • Remove the old service with the httpd image (as it will be substituted by the php:7.2-apache image);
  • Remove the folder docker/build/apache as not needed anymore.

This is the resulting docker-compose.yaml file:

version: '3.7'
services:
  # "php" was "language" in previous example
  php:
    build:
      context: .
      dockerfile: docker/build/php/Dockerfile
    ports:
      - "8100:80"

  # Configure the database
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}

  # Configure Apache <- THIS NODE AS TO BE ENTIRELY REMOVED
  # apache:
  # build:
  # context: .
  # dockerfile: docker/build/apache/Dockerfile
  # ports:
  # - "8100:80"

Enter fullscreen mode Exit fullscreen mode

Concretely, we have renamed apache to php and changed the path to the Dockerfile: nothing really complex.

Now the last thing remained is renaming the folder docker/build/apache in docker/build/php.

We also need to change the folder in which we move the files of our app as per documentation:

COPY . /var/www/html/

Where src/ is the directory containing all your PHP code.

So, in the file docker/build/php/Dockerfile, we need to change the FROM instruction to use the image php:7.2-apache:

# FROM httpd:2.4 <- Change this to use the PHP image
FROM php:7.2-apache

# Copy Symfony files
# COPY . ./htdocs # <- Remove this
COPY . /var/www/html/ # <- Keep attention: we have removed the "." (dot) at the beginning of the destination path

Enter fullscreen mode Exit fullscreen mode

Last thing to do is building the image:

MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
Building php
Step 1/2 : FROM php:7.2-apache
7.2-apache: Pulling from library/php
be8881be8156: Already exists
69a25f7e4930: Already exists
65632e89c5f4: Already exists
cd75fa32da8f: Already exists
15bc7736db11: Pull complete
b2c40cef4807: Pull complete
f3507e55e5eb: Pull complete
e6006cdfa16b: Pull complete
a3ed406e3c88: Pull complete
745f1366071d: Pull complete
bdfcada64ad8: Pull complete
86f2b695cc77: Pull complete
5f634a03970a: Pull complete
a329a7ebde19: Pull complete
fb3d2649f534: Pull complete
Digest: sha256:8188b38abe8f3354862845481452cd3b538bc0648e3c5cdef4ef9ee9365fe2d3
Status: Downloaded newer image for php:7.2-apache
 ---> 5e5a59788e34
Step 2/2 : COPY . /var/www/html/
 ---> a0cfd25e4828

Successfully built a0cfd25e4828
Successfully tagged app-aragog-www_php:latest
mysql uses an image, skipping

Enter fullscreen mode Exit fullscreen mode

As we have removed a service, we need to run first docker-compose down --remove-orphans and then docker-compose up -d.

Once containers are created again, we can go to http://127.0.0.1:8100/public and we will see the most beautiful page in the world ?:

We are now ready to start building our Symfony based app… or maybe we aren’t?

Mmm, no, we are not still ready ???

What do we need to do now? ?

Conclusions and next steps

Well, also if we are able to copy the Symfony’s files in the web server container (that now is not httpd anymore, but, instead, php:7.2-apache) and we are able to make the web server serve interpreted PHP files, we still have some other things to do:

  • Understand why we can see Symfony only pointing directly to the public folder (SPOILER: DocumentRoot?);
  • Checking our web server meets all the Symfony’s requirements;
  • Maybe, also refine our copying of files as it is currently very dirty and heavy.

But for the moment we have reached some great goals:

  • We know how to create a real Apache web server on Docker;
  • We know how to deal with images, to understand how to chose and use them (now, for example, you can use MongoDB if you like: go to search for it!);
  • We know how to copy files in the containers and in doing this, we have understood what are Dockerfiles (one of the foundation of Docker!), what is the Docker Context, how it works and how we can manipulate it (almost: there is at least one other thing you need to know to master it… Next post!).

Those are a lot of things and we are closer to our goal of fully using Symfony on Docker!

The next post will teach you how to solve the problems I mentioned.

In the meantime, remember to “Make. Ideas. Happen.”.

I wish you flocking users!

L'articolo Moving Symfony files in the Docker container proviene da ÐΞV Experiences by Serendipity HQ.

Top comments (0)