Create a Docker instance : Symfony / MariaDB / Nginx

Pierre Belin
Pierre Belin
Create a Docker instance : Symfony / MariaDB / Nginx
Table of Contents
Table of Contents

If you are here, you want to improve your skill in Docker.

I imagine you are actually developing with a third-party application as WAMP/MAMP.

It was a good setup, 5 years ago...

Now, this is a typical bad practice!

Why?

Using WAMP/MAMP

The list is far from short :

  • You depend on WAMP installation...
  • The SSL support sucks...
  • The port handling isn't easy to manage
  • You're 99% sure you are not iso with the production
  • ...

Directly on Linux

The only thing I can disagree with is that your computer doesn't containerize your project.

After several coding years, you have installed tons of extensions/packages. It's hard to determine which extensions your project needs.

I know and I agree, you have to know what each extension does. But are you 100% sure you didn't miss anything?

In my opinion, I prefer not to leave anything to chance.

The better you containerize each project, the easier you create your production environment. It's important to have an iso environment between development and production.

Don't worry, it's not that difficult to create your own environment with Docker.

For me, Docker is a must-have for every developer.

Here, I'll show you how to create a Symfony/MariaDB/Nginx environment with Docker!

Let's start a new project called my myapp!


Initiate Docker with Docker Compose

The first thing to do is to install Docker. The installation contains Docker and Compose, the 2 tools that we'll use day!

When the installation is over, we start with the first step create the file docker-compose.yml

It will contain all the services of our stack!

version: '3.7'

services:
  ...

networks:
  myapp:
docker-compose.yml

Check out your Docker version (docker -v) to validate if your Compose version is correct: https://docs.docker.com/compose/compose-file/

This is how Compose works.

Compose uses the docker-compose.yml to instantiate containers. As the opposite of Docker (without Compose), you can instantiate linked containers at the same time.

It allows you to create a poll of containers for a unique function, and to delete them at the same time when you'll not need them anymore. We call them services.

Each service is instantiated from an image from Docker Hub (it centralizes all public images).

Moreover, each service has its own parameters (image, environment, ports, network...) which allow to configure them.

Database: MariaDB

As a database, we'll use the latest images from MariaDB.

services:
  db:
    image: mariadb:latest # The image from Docker Hub
    container_name: myapp_db 
    environment: # This is my settings, change them as you prefer
      MYSQL_ROOT_PASSWORD: PASSWORD_ROOT
      MYSQL_DATABASE: DATABASE_NAME
      MYSQL_USER: USER
      MYSQL_PASSWORD: PASSWORD
    networks:  # Allows to communicate with other services with the same network
      - myapp
docker-compose.yml

Don't forget to change all variables with your own.

And you can also add the service phpMyAdmin if you want an interface to access your database from your browser:

...
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    container_name: myapp_phpmyadmin
    ports:
      - 8080:80 
    environment:
      PMA_HOST: db # Same name that the database service, here db
      MYSQL_ROOT_PASSWORD: PASSWORD_ROOT # Same password as MYSQL_ROOT_PASSWORD
    networks:
      - myapp # You see here the same network as the service db
docker-compose.yml

It's all we need to have a database and a front interface!

PHP

The PHP image is a bit more complex than those seen previously.

There is no official image that contains all Symfony packages.

So we have to create our own Docker image with all needed packages (composer included) in the file Dockerfile-php. It's based on the image php:7.4-fpm, with additional installations.

FROM php:7.4-fpm

RUN apt-get update && apt-get install -y
# You can check all possible installation here : https://gist.github.com/chronon/95911d21928cff786e306c23e7d1d3f3
RUN apt-get install -y --no-install-recommends \
        git \
        zlib1g-dev \
        libxml2-dev \
        libzip-dev \
        libpq-dev \
        nano \
    && docker-php-ext-install \
        zip \
        intl \
        pdo \
        mysqli \
        pdo_mysql \
        opcache
# Install Composer !
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN curl -sS https://get.symfony.com/cli/installer | bash
RUN mv /root/.symfony/bin/symfony /usr/local/bin/symfony

# Set the default directory inside the container
WORKDIR /var/www/symfony
Dockerfile-php
...
  php:
    container_name: myapp_php
    depends_on:
      - db
    build:
      context: .
      dockerfile: Dockerfile-php
    environment: # We set some environments variables to facilitate debug
      APP_ENV: dev
      APP_DEBUG: 1
    volumes:
      - files:/var/www/symfony # It has to match with the WORKDIR inside the docker file
    networks:
      - myapp # Still same network
docker-compose.yml

2 lines are here :

  • build: docker-compose will build our custom image in this container
  • volumes: link a volume name to a path inside the image.

The important thing here is that you want to manipulate the exact same files. So you have to create a link between your local application and the Docker PHP.

This link is volumes. Add the following code at the end of the file.

...
volumes:
  files: # Same name as inside the container php
    driver: local
    driver_opts:
      type: 'none'
      o: 'bind'
      device: LOCAL/PATH/TO/MYAPP # Change with the project path on your computer
docker-compose.yml

PHP and Symfony are all set!

Nginx

Finally, we end by setting Nginx to create access to our Symfony project.

....
  nginx:
    container_name: myapp_nginx
    depends_on:
      - php # We need to load PHP for the Nginx configuration file
    build:
      context: .
      dockerfile: Dockerfile-nginx
    ports:
      - 8000:80 # Redirect Docker port 80 to localhost port 8000. So you'll access to Nginx with localhost:8000
      - 8443:443 # Same for HTTPS
    networks:
      - myapp
docker-compose.yml

Exactly the same way as the PHP image, we'll create our own Nginx image to include Let's Encrypt support if you need to develop with HTTPS protocol.

FROM nginx:latest

COPY ./build/nginx/default.conf /etc/nginx/conf.d/

RUN apt update
RUN apt install -y --no-install-recommends \
        python3-acme \
        python3-certbot \
        python3-mock  \
        python3-openssl \
        python3-pkg-resources \
        python3-pyparsing \
        python3-zope.interface

RUN apt install -y --no-install-recommends python3-certbot-nginx
Dockerfile-nginx

And we create a custom Nginx configuration file as the default configuration.

server {
    listen 80;

    #listen 443 ssl;
    #listen [::]:443 ssl;
    #ssl_certificate /etc/ssl/certs/localhost.crt;
    #ssl_certificate_key /etc/ssl/private/localhost.key;

    root /var/www/symfony/public;
    server_name localhost;

    index index.php index.html;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000; # Same name as the PHP service (php)
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    error_log /var/log/nginx/myapp.error.log;
    access_log /var/log/nginx/myapp.access.log;
}
build/nginx/default.conf

The final docker-compose.yml

Wait, I added some parameters to make it fully customizable. To make easier the database dump, we also create a volume for SQL files.

So copy this one to get the final file.

version: '3.7'

services:
  db:
    image: mariadb:latest # The image from Docker Hub
    container_name: myapp_db 
    environment: # This is my settings, change them as you prefer
      MYSQL_ROOT_PASSWORD: PASSWORD_ROOT
      MYSQL_DATABASE: DATABASE_NAME
      MYSQL_USER: USER
      MYSQL_PASSWORD: PASSWORD
    volumes:
      - sql:/var/lib/mysql/
    networks:  # Allows to communicate with other services with the same network
      - myapp
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    container_name: myapp_phpmyadmin
    ports:
      - 8080:80
    environment:
      PMA_HOST: db # Same name that the database service, here db
      MYSQL_ROOT_PASSWORD: PASSWORD_ROOT # Same password as MYSQL_ROOT_PASSWORD
    networks:
      - myapp # You see here the same network as the service db
  php:
    container_name: myapp_php
    depends_on:
      - db
    build:
      context: .
      dockerfile: Dockerfile-php
    environment:
      APP_ENV: dev
      APP_DEBUG: 1
    volumes:
      - files:/var/www/symfony
    networks:
      - myapp
  nginx:
    container_name: myapp_nginx
    depends_on:
      - php # We need to load PHP for the Nginx configuration file
    build:
      context: .
      dockerfile: Dockerfile-nginx
    ports:
      - 8000:80 # Redirect Docker port 80 to localhost port 8000. So you'll access to Nginx with localhost:8000
      - 8443:443 # Same for HTTPS
    networks:
      - myapp

networks:
  myapp:

volumes:
  files: # Same name as inside the container php
    driver: local
    driver_opts:
      type: 'none'
      o: 'bind'
      device: LOCAL/PATH/TO/MYAPP # Change with the project path on your computer
  sql:
    driver: local
    driver_opts:
      type: 'none'
      o: 'bind'
      device: LOCAL/PATH/TO/MYSQL # Change with the project path on your computer
docker-compose.yml (final)

Start the Docker instance

The next step is to build our containers from the docker-compose.yml

Warning: you need to be on the same path as the file to launch the command!

docker-compose build

Also, pay attention to the indentation if you don't want to waste hours for silly mistakes.

I recommend you add the parameter --no-cache at the end of the command line.

When you build, docker-compose doesn't rebuild modification outside the docker-compose.yml file. If you want, for example, to change the file Dockerfile-php or default.conf, it won't rebuild.

Now, you can launch: docker-compose up -d

Here we are, we have access to our instance NGINX: http://localhost:8000 and even to our PHPMyAdmin: http://localhost:8080

You can start and stop your containers directly with the Docker UI installed, or you can use those commands docker-compose start docker-compose stop.

To delete your containers: docker-compose down. It won't delete your project files linked to Docker since you created a volume.


Launch command in Docker instance

Last and not the less, the command to access our PHP container bash :

docker exec -it myapp_php bash

It will connect you to the container bash. Now you can launch your Symfony command directly in your Docker.

symfony new myapp

Last thing: copy the entire content  myapp in the parent folder symfony (to match the Nginx configuration file).

Now you have your environment Docker for Symfony! Let's code :)

Isn't it beautiful?



Join the conversation.

Great! Check your inbox and click the link
Great! Next, complete checkout for full access to Pierre Belin
Welcome back! You've successfully signed in
You've successfully subscribed to Pierre Belin
Success! Your account is fully activated, you now have access to all content
Success! Your billing info has been updated
Your billing was not updated