En la parte I de *Docker: crear entorno de desarrollo web local *te mostré de forma clara y sencilla lo que es Docker y cómo funciona. También te facilité algunos comandos útiles que una vez que domines con más fluidez el uso de contenedores, serán tu mejor aliado.
Ahora bien, manos a la obra y comencemos a configurar nuestro entorno de desarrollo web local usando Docker. Aprovecho para recordarte que configuraremos nuestros servicios y contenedores para usar Nginx + PHP 7.2 + MySQL 5.7 y probaremos que todo funciona con una instalación de .
La estructura de directorios que usaremos sera esta:
/var/www/html/docker-devenv-sf4/
|-- docker
| |-- app
| | |-- Dockerfile
| | |-- php.ini
| | |-- xdebug.ini
| |-- mysql
| | |-- Dockerfile
| | |-- mysql.cnf
| |-- nginx
| | |-- Dockerfile
| | |-- default.template
| | |-- nginx.conf
| | |-- openssl.cnf
|-- symfony42
|-- docker-compose.yml
|-- .env
Los pasos a seguir para la creación de nuestro entorno de desarrollo web local con Docker son:
1. Crear nuestros servicios y contenedores.
Docker, entre uno de sus tantas formas para generar y ejecutar contenedores de servicios, utiliza una herramienta llamada docker-compose la cual requiere de un fichero “ docker-compose.yml “ en donde definiremos todos aquellos servicios que deseamos para nuestro entorno de desarrollo.
Visto de forma sencilla, este fichero es como el “ composer.json” de “ Composer”, o el “ package.json” de npm
Antes de comenzar, debemos situarnos en el directorio donde queremos tener nuestro proyecto, en mi caso suelo utilizar el directorio base “/var/www/html “.
Ejecutamos:
cd /var/www/html
Seguidamente, crearemos un directorio para nuestro proyecto ejemplo de Symfony 4.
mkdir docker-devenv-sf4
cd docker-devenv-sf4
Ahora vamos a crear el resto de directorios que vamos a necesitar más adelante:
mkdir docker
mkdir -p docker/app
mkdir -p docker/mysql
mkdir -p docker/nginx
Procedemos a crear los ficheros necesarios, que en conjunto con el “ docker-compose.yml “ armarán nuestro entorno de desarrollo.
/var/www/html/docker-devenv-sf4/docker/app/Dockerfile
FROM php:7.2-fpm
ARG timezone
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
git \
unzip \
libicu-dev \
zlib1g-dev \
libssl-dev \
pkg-config \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
RUN set -xe \
&& docker-php-ext-configure \
intl \
&& docker-php-ext-install \
intl \
opcache \
zip \
pdo \
pdo_mysql \
pdo_pgsql \
&& pecl install \
apcu \
xdebug \
mongodb \
redis \
&& docker-php-ext-enable \
apcu \
xdebug \
mongodb \
redis
COPY php.ini /usr/local/etc/php/php.ini
RUN sed -i -e "s#TIMEZONE#$timezone#g" /usr/local/etc/php/php.ini
COPY xdebug.ini /tmp/xdebug.ini
RUN cat /tmp/xdebug.ini >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
RUN rm /tmp/xdebug.ini
COPY --from=composer:1.6 /usr/bin/composer /usr/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN mkdir -p /var/www/.composer && chown -R www-data /var/www/.composer
RUN usermod -u 1000 www-data
WORKDIR /var/www/html
/var/www/html/docker-devenv-sf4/docker/app/php.ini
apc.enable_cli = 1
date.timezone = TIMEZONE
session.auto_start = Off
short_open_tag = Off
opcache.max_accelerated_files = 20000
realpath_cache_size = 4096K
realpath_cache_ttl = 600
/var/www/html/docker-devenv-sf4/docker/app/xdebug.ini
xdebug.remote_enable=1
xdebug.remote_connect_back=1
xdebug.idekey="PHPSTORM"
xdebug.remote_port=9001
/var/www/html/docker-devenv-sf4/docker/mysql/Dockerfile
FROM mysql:5.7
COPY mysql.cnf /etc/mysql/conf.d/custom.cnf
/var/www/html/docker-devenv-sf4/docker/mysql/mysql.cnf
# /var/www/html/docker-devenv-sf4/docker/mysql/mysql.cnf
[mysqld]
# Version 5.5.3 introduced "utf8mb4", which is recommended
collation-server = utf8mb4_unicode_ci
character-set-server = utf8mb4
/var/www/html/docker-devenv-sf4/docker/nginx/Dockerfile
FROM nginx:1.13
ARG nginx_host
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
openssl \
&& rm -rf /var/lib/apt/lists/*
COPY openssl.cnf /etc/ssl/openssl.cnf
RUN sed -i -e "s/NGINX_HOST/$nginx_host/g" /etc/ssl/openssl.cnf
RUN openssl genrsa -des3 -passout pass:NotSecure -out /etc/ssl/server.pass.key 2048
RUN openssl rsa -passin pass:NotSecure -in /etc/ssl/server.pass.key -out /etc/ssl/server.key
RUN rm /etc/ssl/server.pass.key
RUN openssl req -new -passout pass:NotSecure -key /etc/ssl/server.key -out /etc/ssl/server.csr \
-subj "/C=MX/ST=Mexico/L=Mexico/O=Symfony/CN=$nginx_host" -config /etc/ssl/openssl.cnf
RUN openssl x509 -req -sha256 -days 365 -in /etc/ssl/server.csr -signkey /etc/ssl/server.key \
-out /etc/ssl/server.crt -extensions v3_req -extfile /etc/ssl/openssl.cnf
ADD nginx.conf /etc/nginx/
RUN rm /var/log/nginx/access.log /var/log/nginx/error.log
/var/www/html/docker-devenv-sf4/docker/nginx/default.template
server {
listen 80;
listen 443 ssl;
server_name ${NGINX_HOST};
ssl_certificate /etc/ssl/server.crt;
ssl_certificate_key /etc/ssl/server.key;
root /var/www/html/public;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass app:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/app_error.log;
access_log /var/log/nginx/app_access.log;
}
/var/www/html/docker-devenv-sf4/docker/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
/var/www/html/docker-devenv-sf4/docker/nginx/openssl.cnf
HOME = .
RANDFILE = $ENV::HOME/.rnd
oid_section = new_oids
[ new_oids ]
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ./demoCA
certs = $dir/certs
crl_dir = $dir/crl
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem
serial = $dir/serial
crlnumber = $dir/crlnumber
crl = $dir/crl.pem
private_key = $dir/private/cakey.pem
RANDFILE = $dir/private/.rand
x509_extensions = usr_cert
name_opt = ca_default
cert_opt = ca_default
default_days = 365
default_crl_days= 30
default_md = default
preserve = no
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca
string_mask = utf8only
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = AU
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Internet Widgits Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
basicConstraints=CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ v3_req ]
basicConstraints = CA:TRUE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = NGINX_HOST
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ proxy_cert_ext ]
basicConstraints=CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
[ tsa ]
default_tsa = tsa_config1
[ tsa_config1 ]
dir = ./demoCA
serial = $dir/tsaserial
crypto_device = builtin
signer_cert = $dir/tsacert.pem
certs = $dir/cacert.pem
signer_key = $dir/private/tsakey.pem
signer_digest = sha256
default_policy = tsa_policy1
other_policies = tsa_policy2, tsa_policy3
digests = sha1, sha256, sha384, sha512
accuracy = secs:1, millisecs:500, microsecs:100
clock_precision_digits = 0
ordering = yes
tsa_name = yes
ess_cert_id_chain = no
/var/www/html/docker-devenv-sf4/.env
NGINX_HOST=docker.sf4.local
TZ=Europe/Madrid
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=sf4_db
MYSQL_USER=dockersf
MYSQL_PASSWORD=dockersf
XDEBUG_REMOTE_HOST=172.17.0.1
Lo siguiente que haremos para poder ilustrarte y explicarte a nivel general los aspectos más importante para la definición de nuestro entorno, es crear dicho fichero “ docker-compose.yml “, para esto ejecutamos:
# Creamos el fichero docker-compose.yml
touch docker-compose.yml
# Editamos el fichero
vim docker-compose.yml
Nuestro fichero final debe lucir de la siguiente forma:
version: '3'
services:
app:
container_name: sf4_app
build:
context: ./docker/app
args:
- timezone=${TZ}
depends_on:
- mysql
volumes:
- ./symfony42:/var/www/html:rw,cached
env_file:
- .env
environment:
XDEBUG_CONFIG: remote_host=${XDEBUG_REMOTE_HOST}
PHP_IDE_CONFIG: serverName=${NGINX_HOST}
nginx:
container_name: sf4_nginx
build:
context: ./docker/nginx
args:
- nginx_host=${NGINX_HOST}
ports:
- "8008:80"
- "443:443"
depends_on:
- app
volumes:
- ./symfony42/public:/var/www/html/public:ro
- ./docker/nginx/default.template:/etc/nginx/conf.d/default.template
- nginx_log:/var/log/nginx
env_file:
- .env
command: /bin/bash -c "envsubst '$$NGINX_HOST'
< /etc/nginx/conf.d/default.template
> /etc/nginx/conf.d/default.conf
&& exec nginx -g 'daemon off;'"
mysql:
container_name: sf4_mysql
build:
context: ./docker/mysql
volumes:
- ./.data/mysql:/var/lib/mysql:rw
env_file:
- .env
ports:
- "3306:3306"
volumes:
nginx_log:
Como puedes notar, es un simple fichero con una serie de parámetros que sirven para indicarle a docker que debe hacer, ejecutar y configurar para desplegar cada servicio en un contenedor.
Algunos parámetros hablan por sí solos, pero trataré de detallarte los más importantes.
build: Le indica a docker que la base del contenedor debe ser generada partiendo de un Dockerfile o una imagen de repositorio.
context: Le indica a al build que debe tirar de un Dockerfile para la instalación del sistema operativo y cualquier otra instrucción que se haya definido en él.
args: Son argumentos que se pueden pasar al Dockerfile como parte de la configuración necesaria del contenedor.
ports: Corresponde al mapping de puertos desde el cliente hacia el host, en nuestro caso por ejemplo para nginx, accedemos al servicio por el puerto 8008 hacia un servicio de nginx que corre en el puerto 80 dentro del contenedor.
depends_on: Crea dependencia entre contenedores, de tal forma que hasta que el contenedor indicado en este parámetro no este inicializado, el servicio que se esta definiendo con este parámetro, no se iniciará.
env_file: Su nombre lo indica, permite indicar que fichero de variables de entorno se va a utilizar para la configuración de cada contenedor de servicios.
volumes: Permite mapear mediante enlaces simbólicos los directorios que deseemos. En nuestro caso por ejemplo para nginx, realizamos un enlace simbólico de nuestro directorio symfony42 para que los ficheros puedan ser accedidos por el contenedor como si en realidad estuviesen dentro de el. Aquí es donde esta la magia en la cual podemos programar desde fuera del contenedor y ejecutar desde el navegador nuestra aplicación siendo el contenedor quien posee el servidor web, mysql, entre otros.
2. Generar el Build.
Para generar el build solo basta con ejecutar el siguiente comando:
docker-compose build
Con esto, Docker, leerá el fichero que acabamos de crear y comenzará a descargar las imágenes necesarias, a crear los contenedores e instalar todo lo necesario en base a lo que hayamos predefinido anteriormente.
Este proceso puede tomar ciertos minutos ya que irá creando según los contenedores y servicios definidos en el fichero “ docker-composer.yml “ y ejecutando las instrucciones definidas en cada Dockerfile para la instalación de sistema operativo, servicios, configuraciones entre otros.
Si todo marcha bien, podremos pasar al paso número 3.
3. Añadir dominio al fichero “hosts”
Ya con todo preparado para el despliegue de nuestros contenedores, debemos añadir a nuestro fichero hosts un “mapping” de nuestro dominio a utilizar para el proyecto para que apunte a localhost.
Para esto debemos editar el fichero “/etc/hosts”:
vim /etc/hosts
y añadir lo siguiente:
127.0.0.1 docker.sf4.local
4. Inicializar nuestros contenedores.
Lo siguiente es inicializar nuestros contenedores, y por ende, nuestros servicios. Para ello debemos ejecutar lo siguiente:
docker-compose up -d
El parámetro -d es para ejecutar los contenedores de forma desacoplada, es decir, que una vez que se ejecuten, libere el proceso en la terminal y queden funcionando en segundo plano.
Este comando siempre va a verificar que si hubo algún cambio en nuestro fichero “ docker-compose.yml “, realice el build antes de inicializar los respectivos contenedores.
También, es posible forzar hacer el build mediante el parámetro “ -build “, por ejemplo:
docker-compose up -d
Si todo marcha bien veremos lo siguiente:
Creating network "docker-devenv-sf4_default" with the default driver Creating volume "docker-devenv-sf4_nginx_log" with default driver Creating sf4_mysql ... done
Creating sf4_app ... done
Creating sf4_nginx ... done
5. Probar nuestro entorno de desarrollo.
Para probar nuestro entorno, vamos a realizar una simple instalación de Symfony 4 y probar que todo vaya bien. Los pasos a seguir son:
1.- Lo primero que haremos es, situarnos dentro del directorio del proyecto (/var/www/html/docker-devenv-sf4)
2.- Instalaremos el “ Instalador de Symfony “ ejecutando lo siguiente:
3.- Instalamos la versión web de Symfony 4.2:
# Instalamos la versión full symfony new
4.- El paso anterior nos creará un nuevo directorio llamado “ symfomy42” con toda la instalación de Symfony. Lo siguiente que haremos será instalar los paquetes que vienen definidos en el “ composer.json “ para esto:
composer update
5.- Finalmente podemos ir a nuestro navegador para verificar que todo este en orden, para ello ingresamos en la barra de de direcciones:
[http://docker.sf4.local:8008](http://docker.sf4.local:8008)
Nos debe aparecer esto:

Y de esta forma ya tenemos listo nuestro entorno de desarrollo web local con Docker. Puedes descargar todo el proyecto de ejemplo desde **mi repositorio.**
Recuerda que si tienes alguna sugerencia o pregunta, no dudes en dejar tus comentarios al final del post.
Si te gustó este post, ayúdame a que pueda servirle a muchas más personas, compartiendo mis contenidos en tus redes sociales.
Espero que este post haya sido de gran ayuda para ti, y como siempre, cualquier inquietud o duda que tengas, puedes contactarme por cualquiera de las vías disponibles, o dejando tus comentarios al final de este post. También puedes sugerir que temas o post te gustaría leer a futuro.