Symfony + Docker + traefik

Esta vez ha tocado el turno de dockerizar Symfony, normalmente en mi entorno de trabajo tengo todo dockerizado para poder tener las mismas características que los servidores donde subimos el código o tal vez probar una versión nueva.

Recuerda que este tutorial va de la mano de Traefik que lo tenemos como proxy, si quieres saber como instalarlo y configurarlo puedes seguir este tutorial que realice en su momento Docker + Traefik + Soketi

En Laravel lo tengo así, pero no había probado con Symfony, así que os dejo aquí por si a alguno le sirve, la forma como lo he hecho.

Primeramente vamos a instalar Symfony usando composer, yo he usado una imagen de composer, viendo la documentación en Symfony la versión LTS es la 6.4.X este mismo proceso se puede aplicar para la última versión que actualmente a la fecha de esta publicación es la 7.X

docker run --rm -u $(id -u):$(id -g) -v $(pwd):/app composer create-project symfony/skeleton symfony "^6.4"

Dentro de este directorio creamos una carpeta llamada docker y creamos un archivo .env, en el agregaremos una serie de variables de entorno para poder levantar nuestro Symfony con Docker.
En mi caso como quiero usarlo con Mysql, he reutilizado las variables de entorno que uso en Laravel, dependerá de tu configuración para adaptarlo a tus necesidades.

Agregamos estos datos al archivo .env

DB_HOST=symfony6_db
DB_PORT=3306
DB_DATABASE=symfony6
DB_USERNAME='tu-usuario'
DB_PASSWORD='tu-clave'

IMAGE_PHP="ssheduard/php:8.3-fpm-alpine"
NETWORK="traefik_net"

El siguiente paso va ser crear nuestro archivo docker-compose.yml donde vamos a tener Nginx/Mysql/Php

version: "3.7"

services:
    symfony6_nginx:
        image: nginx:1.23.0-alpine
        container_name: symfony6_nginx
        restart: unless-stopped
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.symfony6_nginx.rule=Host(`symfony6.local`)"
            - "traefik.http.routers.symfony6_nginx.entrypoints=web"
        volumes:
            - ./nginx/nginx.conf:/etc/nginx/nginx.conf
            - ./nginx/conf.d/:/etc/nginx/conf.d/
            - ../:/var/www
        depends_on:
            - symfony6_php
        links:
            - symfony6_php

    symfony6_php:
        image: ${IMAGE_PHP}
        container_name: symfony6_php
        restart: unless-stopped
        volumes:
            - ../:/var/www
            - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
        environment:
            PHP_OPCACHE_VALIDATE_TIMESTAMPS: "1"
        working_dir: /var/www

    symfony6_db:
        image: mysql:8.0
        restart: unless-stopped
        container_name: symfony6_db
        tty: true
        ports:
            - "33021:3306"
        environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            TZ: Europe/Madrid
        volumes:
            - dbdata:/var/lib/mysql/
            - ./mysql/my.cnf:/etc/mysql/my.cnf

#Docker Networks
networks:
    default:
        external: true
        name: ${NETWORK}

#Volumes
volumes:
    dbdata:
        driver: local

Ahora como último paso vamos a crear una serie de carpetas donde vamos a dejar nuestras configuraciones, tanto para php, mysql y nginx.
Debemos crear esta estructura de carpetas e iremos agregando una serie de archivos en cada una de ellas.

Vamos a abrir la carpeta nginx/config.d y crearemos el archivo default.conf y dentro agregamos esta configuración

server {
    listen [::]:80 default_server;
    listen 80 default_server;
    server_name _;

    sendfile off;

    root /var/www/public;
    index index.php index.html;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to index.php
        try_files $uri $uri/ /index.php?$query_string;
    }


    # Pass the PHP scripts to PHP-FPM
    # Change php-fpm endpoint. E.g: php-fpm:9090 (service_name:port)
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass symfony6_php:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_index index.php;
        include fastcgi_params;
        # Set the "fastcgi_read_timeout" parameter
        fastcgi_read_timeout 600;
    }

    # Deny access to dot files by default
    location ~ /\. {
        log_not_found off;
        deny all;
    }

}

Debemos tener en cuando que debemos agregar en fastcgi_pass el nombre que asignamos a php (symfony6)

fastcgi_pass symfony6_php:9000;

Ahora en la carpeta mysql creamos el archivo my.cnf, dentro debemos agregar estos datos.

[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log

Vamos con la carpeta php y dentro crearemos el archivo local.ini y agregamos el siguiente contenido.

[PHP]
memory_limit = 256
upload_max_filesize = 10M
post_max_size = 10M
max_execution_time=300

[opcache]
opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 1
opcache.revalidate_freq = 0
opcache.preload_user = www-data

; Configuración recomendada para desarrollo
opcache.consistency_checks = 1
opcache.validate_permission = 1
opcache.file_cache = /tmp

Recuerda adaptarlo a tus necesidades el archivo local.ini

Y por último nos queda crear este archivo nginx/nginx.conf, el cual vamos a agregar estos datos.

# Sets the worker threads to the number of CPU cores available in the system for best performance.
# Should be > the number of CPU cores.
# Maximum number of connections = worker_processes * worker_connections
worker_processes auto;

# Log errors and warnings to this file
# This is only used when you don't override it on a server{} level
error_log /dev/stderr warn;

user nginx nginx;

# Maximum number of open files per worker process.
# Should be > worker_connections.
worker_rlimit_nofile 8192;

events {
  # If you need more connections than this, you start optimizing your OS.
  # That's probably the point at which you hire people who are smarter than you as this is *a lot* of requests.
  # Should be < worker_rlimit_nofile.
  worker_connections 8000;
}

# The file storing the process ID of the main process
pid /run/nginx.pid;

# Free some CPU cycles
timer_resolution 500ms;

http {
    include       mime.types;
    default_type  application/octet-stream;

    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    # Define custom log format to include reponse times
    log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          '$request_time $upstream_response_time $pipe $upstream_cache_status';

    # to boost I/O on HDD we can disable access logs
    access_log off;
    error_log /dev/stderr;

     # Hide used software
    server_tokens off;

    # copies data between one FD and other from within the kernel
    # faster than read() + write()
    sendfile on;

    # the sendfile_max_chunk directive to limit the amount of data transferred in a single sendfile() call
    sendfile_max_chunk 512k;

    # send headers in one piece, it is better than sending them one by one
    tcp_nopush on;

    # don't buffer data sent, good for small data bursts in real time
    tcp_nodelay on;

    gzip on;
    # gzip_static on;
    gzip_min_length 10240;
    gzip_comp_level 1;
    gzip_vary on;
    gzip_disable msie6;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types
        # text/html is always compressed by HttpGzipModule
        text/css
        text/javascript
        text/xml
        text/plain
        text/x-component
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/rss+xml
        application/atom+xml
        font/truetype
        font/opentype
        application/vnd.ms-fontobject
        image/svg+xml;

    # allow the server to close connection on non responding client, this will free up memory
    reset_timedout_connection on;

    # request timed out -- default 60
    client_body_timeout 30;

    # if client stop responding, free up memory -- default 60
    send_timeout 10;

    # server will close connection after this time -- default 75
    keepalive_timeout 75;

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    # Write temporary files to /tmp so they can be created as a non-privileged user
    client_body_temp_path /tmp/client_temp;
    proxy_temp_path /tmp/proxy_temp_path;
    fastcgi_temp_path /tmp/fastcgi_temp;
    uwsgi_temp_path /tmp/uwsgi_temp;
    scgi_temp_path /tmp/scgi_temp;

    # Include custom configurations
    include conf.d/*.conf;
}

Esta es una configuración por defecto, adaptarlo a tus necesidades.

Realizado todas estas configuraciones ya podemos levantar el servicio

Ejecutamos en la terminal

docker-compose up -d

Nos debe mostrar algo como esto

Podemos entrar al contenedor y verificar que esta corriendo sin problemas

Genial ahora como último paso recuerdas que al inicio te comente que estábamos usando Traefik, solo nos quedaría crear una entrada en nuestro archivo hosts (etc/hosts) y agregamos la url que definimos en nuestro docker-compose.

127.0.0.1	symfony6.local www.symfony6.local

Si entramos a nuestro navegador escribiendo la ruta: http://symfony6.local nos debería mostrar la siguiente página

Como puedes ver está es una guía super sencilla de como poder instalar Symfony con Docker.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *