Cómo usar Docker para desarrollar en WordPress

Publicada en WordPress.

¡Feliz año nuevo, querido lector! Espero que hayas pasado unas fantásticas navidades y que la resaca de hoy no sea demasiado dura. Yo, por mi parte, he decidido empezar el año con fuerza y traerte lo que hace unas semanas os prometí por Twitter: un tutorial para desarrollar en WordPress con Docker.

Y es que después de haber usado el típico LAMP (Linux + Apache + MySQL + PHP) y estar harto de pelearme con configuraciones y tener todas las instalaciones conviviendo y compartiendo recursos; después de haber probado Vagrant y haber sufrido su lentitud (supongo que ejecutarlo sobre Virtual Box en un ordenador que pronto cumplirá los 6 añitos no es la mejor idea); después de ver cómo Antonio disfruta desarrollando con Local by Flywheel mientras a mí me tocaba esperar a que se dignaran a sacar una versión para Linux… Después de todas estas penas, decidí que ya había llegado el momento de aprender cómo funciona Docker y cómo montar entornos de desarrollo rápidos, cómodos y multiplataforma con él.

Si quieres aprender un poquito sobre Docker y quieres poder usarlo en tu día a día, sigue leyendo. Te recomiendo que leas toda la entrada, porque no es hasta el final de la misma donde te explicaré cómo uso yo Docker exactamente. ¡Vamos allá!

Docker

Docker es un programa que permite empaquetar software en «contenedores» y ejecutarlos encima de máquinas virtuales. Hasta aquí, no difiere mucho de otras soluciones como Vagrant. La ventaja de esta forma de proceder (la cual podríamos comparar con, por ejemplo, el clásico LAMP o MAMP o WAMP) es que los contenedores se ejecutan en entornos aislados, de tal forma que:

  • un contenedor no puede, en principio, acceder a otro, lo cual aumenta la seguridad del sistema,
  • todas las herramientas, librerías, ficheros de configuración, etc. que necesita el contenedor para funcionar están empaquetados en él mismo, con lo que no «ensucian» nuestro ordenador con software innecesario ni aparecen incompatibilidades entre ellos.

En mi opinión, la ventaja de Docker sobre otros softwares para ejecutar contenedores es que por defecto usa runC como entorno de virtualización, con lo que todo el software que se ejecuta en el contenedor es gestionado por el sistema operativo del anfitrión. En otras palabras, se comparten un montón de recursos entre el anfitrión (nuestro ordenador) y el huésped (el contenedor que ejecutamos) y, por lo tanto, todo es mucho más rápido y eficiente. Ideal para un ordenador de 2013 como el mío 😉

Instalación de Docker

Para instalar Docker en Linux (Debian/Ubuntu), lo único que tenemos que hacer es ejecutar el siguiente comando en una consola:

y esperar a que acabe el proceso.

Un pequeño inconveniente de Docker es que, por defecto, para poder usarlo deberás ejecutarlo con permisos de administrador usando sudo. Pero, tal y como nos cuentan en la propia documentación del proyecto, basta con que añadamos nuestro usuario en el grupo docker para no tener que usar sudo cada vez:

cambiando your-user por el nombre de tu usuario (usuario del sistema operativo, ¿eh?). Por cierto, es posible que tengas que cerrar tu sesión en el sistema operativo y volver a iniciarla para que te aparezca el nuevo grupo y puedas usar docker sin sudo.

Cómo instalarlo en Mac o en Windows

Si estás usando alguno de estos otros dos sistemas operativos, aquí te dejo un par de enlaces sobre cómo instalarlo en ellos:

Cómo usar Docker

Vale, ahora que ya tenemos Docker en nuestro ordenador instalado y listo para ser usado es el momento de probarlo. Como te decía, Docker es una herramienta que nos permite lanzar aplicaciones empaquetadas en contenedores. Para ello, deberemos ejecutar el siguiente comando desde un terminal:

Por ejemplo, el primer comando que podemos ejecutar es:

para ver la aplicación clásica de «Hola Mundo» ejecutándose dentro de un contenedor Docker. Si echamos un vistazo a la salida que vemos por terminal, lo primero que veremos es esto:

Lo que nos está diciendo esto es que no teníamos ningún contenedor llamado hello-world en local y, por lo tanto, Docker ha dado por sentado que tenía que ir a buscarlo al repositorio de contenedores que tiene, decargarlo y montarlo en local para ejecutarlo. Y con esto descubrimos uno de los primeros aspectos interesantes de Docker: existe un repositorio de contenedores que podemos usar, parecido a los directorios de plugins o temas que tenemos en WordPress.

Finalmente, vemos que el resto de la salida que tenemos en nuestro terminal es:

el cual se corresponde a la ejecución en sí del contenedor «Hola Mundo». Y si ahora volviéramos a ejecutar el comando anterior, veríamos únicamente el resultado del contenedor (nos ahorraríamos la parte de descargarlo, vaya).

Y esto es así porque, aunque no lo veamos, el contenedor está instalado y configurado en Docker. Si ejecutamos:

veremos cómo aparece creado el contenedor que acabamos de descargarnos y ejecutar (y que, por cierto, ya no está en ejecución):

Para borrarlo, basta con ejecutar:

y listo. Por cierto, fíjate que el numerito que he puesto se corresponde con el CONTAINER ID del contenedor.

Entonces, ahora que ya entiendes un poco cómo funciona Docker, vamos a ver qué es Docker Compose y por qué vamos a usarlo con WordPress.

Docker Compose

Como puedes imaginar, en el repositorio de contenedores de Docker existe un contenedor (varios, de hecho) de WordPress. Dicho contenedor tiene varios inconvenientes:

  • Por un lado, con él sólo no podremos ejecutar WordPress, ya que no incluye la base de datos. Así que si queremos usar WordPress debemos tener corriendo una base de datos en nuestro propio ordenador, en otra máquina o, ¿cómo no?, en otro contenedor.
  • Por otro lado, el contenedor de WordPress dispone de varios parámetros de configuración que debemos especificar para que funcione (por ejemplo, dónde está la base de datos y qué credenciales usar para conectarse a ella).

Gestionar docker a pelo es un poco tedioso: tienes que levantar los contenedores en orden, meter los parámetros de configuración por línea de comandos, etc. Por suerte, existe una herramienta llamada docker-compose que nos permite crear un fichero de configuración con todos los contenedores que queremos levantar, establecer las dependencias entre ellos y configurarlos individualmente. Veamos un ejemplo sencillo.

Empecemos por crear un directorio vacío donde meter el fichero de configuración. Por ejemplo, crea el directorio ~/docker/test y añade un fichero de texto plano llamado docker-compose.yml con el siguiente contenido:

No te asustes, que se trata de un fichero muy sencillo. Fíjate bien. En él definimos dos servicios: mysql y wordpress. En el primer servicio (mysql) indicamos la imagen que queremos descargarnos (un MySQL versión 5.7) y algunos parámetros de configuración. Los más relevantes son la redirección de puertos (del 8081 del anfitrión al 3306 del huésped) y parámetros de la base de datos en sí (usuario, contraseña, etc).

El segundo servicio es WordPress (wordpress) y sigue un patrón parecido. Aquí, por ejemplo, indicamos que queremos usar el puerto 8080 para acceder a nuestro WordPress. También especificamos que mysql es una dependencia de WordPress (este contenedor no puede funcionar sin el otro). Y también indicamos parámetros adicionales como, por ejemplo, dónde está la base de datos y cómo acceder a ella.

Una vez tenemos el fichero listo, ejecutamos:

y Docker se descargará las imágenes de WordPress y MySQL (si no las tenemos ya) y pondrá en marcha nuestro WordPress.

Cuando esté listo (dale un momento, porque a veces tarda un poco en tenerlo todo en marcha), podremos ir a nuestro navegador web y en la dirección http://localhost:8080 (fíjate que es el mismo puerto que hemos puesto en el fichero de configuración) encontraremos nuestro WordPress esperándonos para completar su instalación:

Instalar WordPress en un contenedor Docker
Instalar WordPress en un contenedor Docker.

A partir de este punto, si quieres nuevos WordPress en paralelo, sencillamente tendrías que repetir el proceso. Es decir, creas un nuevo directorio en ~/docker/, añades el fichero docker-compose.yml, cambias los puertos para acceder a él (por ejemplo, 8082 para WordPress y 8083 para MySQL) para que no colisione con el contenedor que ya tenemos en marcha y lo pones a correr también.

Cuando acabes de trabajar, puedes ejecutar:

para detener la instancia. ¡Cuidado! Si en lugar de stop usaras down:

también detendrías la instancia pero, además, se eliminaría, con lo que la próxima vez que hicieras el docker-compose up empezaría todo el proceso de cero (base de datos vacía, WordPress sin instalar, etc).

Docker y el desarrollo en WordPress

Llegados a este punto, ya casi tienes todo lo necesario para desarrollar un plugin o un tema WordPress con Docker. Espero que las explicaciones que te he dado hasta aquí te hayan ayudado a entender un poco mejor cómo funciona Docker.

Ahora, pues, es hora de que te presente exactamente la configuración que uso yo para desarrollar en WordPress con Docker. En concreto, se trata de una configuración que soluciona los dos únicos puntos que quedan por tratar:

  • ¿Cómo puedo hacer que el proyecto en el que estoy trabajando esté dentro del contenedor Docker?
  • ¿Puedo usar nombres de dominio del tipo http://content.local en lugar de http://localhost:puerto con Docker?

Pues vamos a responder estas dos cuestiones y, ahora sí, ya tendrás todos los ingredientes para crear entornos de desarrollo WordPress en Docker como un campeón.

Cómo meter mi proyecto dentro de Docker

En mi caso, cuando monto un entorno de desarrollo en WordPress es para desarrollar un nuevo plugin. En este caso, lo que a mí me gustaría es que mi plugin estuviera dentro de la carpeta wp-content/plugins/ del entorno de desarrollo. Por desgracia, aún no hemos visto cómo hacer que el directorio (que, recordemos, está dentro del contenedor huésped) esté visible desde el sistema de archivos de mi ordenador anfitrión.

Aunque hay varias opciones sobre qué contenidos del contenedor están mapeados a una carpeta de mi ordenador (que los tengo disponibles, vaya), la que más me gusta a mí es la que mapea la carpeta de mi plugin a una carpeta del plugin dentro del contenedor. Veamos cómo lograrlo.

Supongamos que estoy desarrollando mi plugin en ~/dev/nelio-content/. Como quiero poder usar mi plugin Nelio Content en un entorno de desarrollo Docker, lo primero que haré será añadir el ya famoso fichero docker-compose.yml con todo lo que hemos visto hasta ahora. Pero ojo, esta vez tendrá algún parámetro de configuración extra:

¿Ves cuál es la diferencia? Sencillamente he añadido una nueva opción en el servicio WordPress llamada volumes en la que le digo el directorio actual (identificado por el punto .) lo tienes que mapear (indicado por los dos puntos :) al directorio /var/www/html/wp-content/plugins/nelio-content (que es donde está instalado WordPress, seguido de wp-content/plugins/ y seguido del nombre de mi propio plugin). Fíjate que la sintaxis es la misma que cuando mapeábamos los puertos.

Y ya está, haciendo esto conseguimos que nuestro plugin aparezca dentro del WordPress que acabamos de «crear». Yendo al Escritorio » Plugins podemos activarlo y ver qué tal funciona en un entorno seguro y aislado.

Cómo usar nombres de dominio

Entrar en las instalaciones de desarollo usando localhost y números de puerto es bastante pesado; es muchísimo más cómo usar un nombre de dominio del tipo http://content.local. Para ello necesitaremos realizar algunos pasos extra, pero una vez lo tengas montado ya todo será muy sencillo.

Lo primero que necesitamos es decirle al ordenador que un cierto nombre de dominio (por ejemplo, content.local) está en el propio ordenador. Para ello, editamos el fichero /etc/hosts (como sudo) y añadimos la línea que indica esto:

A continuación necesitamos un proxy que sepa que cuando nos llega una petición a http://content.local, queremos que sea atendida por http://localhost:8080. Esto es muy sencillo de montar (en serio). Sencillamente crea una nueva carpeta ~/docker/proxy con el siguiente fichero de configuración docker-compose.yml:

A continuación, ejecuta docker network create proxy y pon en marcha el nuevo contenedor con docker-compose up -d. Esto creará una nueva red que todos nuestros proyectos usarán (en un par de párrafos te explico qué es esto) y pondrá en marcha el contenedor que traduce nombres bonitos a localhost con puerto.

Finalmente, lo único que tenemos que hacer es modificar la configuración de nuestro proyecto (el que habíamos creado en el apartado anterior) para que, cuando se ponga en marcha, avise a nuestro proxy de que está disponible para atender peticiones y bajo qué dominio lo va a hacer:

Soy consciente de que parece un poco magia negra, pero no lo es. Si te fijas, lo único que hemos añadido en nuestro Docker son las siguientes cosas:

  • En la configuración de WordPress, en el apartado environment, hemos añadido dos nuevos parámetros: VIRTUAL_HOST y VIRTUAL_PORT. Estos dos atributos son los que se le pasarán al contenedor del proxy para que se pueda auto-configurar y sepa que las peticiones a http://content.local tienen que ir a http://localhost:8080.
  • Como queremos que WordPress pueda comunicarse con el proxy, tenemos que meter a los dos en la misma red. Esto lo hacemos metiendo la opción networks en el servicio WordPress e indicando que WordPress está en la red llamada frontend (podría haberle puesto el nombre que me diera la gana). Por otro lado, al final del documento docker-compose.yml especificamos que dicha red es externa y su nombre (proxy), lo cual se corresponde con la red que habíamos metido en el contenedor que hemos creado en ~/docker/proxy y que hemos llamado proxy.
  • Finalmente, metemos a la base de datos y al propio WordPress en otra red privada para ellos dos, que hemos llamado backend. Esto permite que WordPress se comunique con MySQL pero que nadie se meta en medio.

¡Y eso es todo! Espero que te haya gustado la entrada de hoy y, si tienes cualquier duda o te atascas en cualquier punto, aproveches la sección de comentarios para preguntar.

Imagen destacada de Abigail Lynn.

FlojaNo está malBienMuy bien¡Impecable! (6 votos, promedio: 5,00 de 5)
Cargando…

12 comentarios en «Cómo usar Docker para desarrollar en WordPress»

  1. Buenas David, te comento mi problema, segui paso a paso la guia para poder levantar un sitio WordPress que ya tenia en funcionamiento por lo que agregue el sitio entero en el volumen del container `wordpress`:

    `- .:/var/www/html/`

    Pero al momento de probar tengo el siguiente problema, tenes idea que me esta faltando?

    Warning: require_once(/var/www/html/wp-config.php): failed to open stream: Permission denied in /var/www/html/wp-load.php on line 37

    Fatal error: require_once(): Failed opening required ‘/var/www/html/wp-config.php’ (include_path=’.:/usr/local/lib/php’) in /var/www/html/wp-load.php on line 37

    OS: Mac OS
    Docker version 18.09.0, build 4d60db4
    docker-compose version 1.23.2, build 1110ad01

    Gracias!

    1. Hola Nicolas,

      Si te he entendido bien, estás metiendo el docker-compose.yml directamente en la raíz de un proyecto WordPress que ya existía. En general, esto no lo recomendaría, porque ahora estás cargando la configuración que ya tenías del WordPress anterior a tu Docker… y además hay problemas de permisos: estás importando en Docker un sistema de ficheros que tiene un propietario (tú) con unos ciertos permisos, pero dentro de Docker ese usuario no existe y se usa uno diferente para el servidor. En fin, que te recomiendo que sigas la guía tal cual o que montes los plugins/temas que quieras uno a uno (añadiendo más líneas en la sección de volumes).

      Un saludo,
      David

  2. Hola David, muy sencillo y practico tu post. Pero tengo dudas al respecto de como funciona el contenedor wordpress porque pensé que al usar un dns no habría necesidad de usar puertos en la dirección final de dasarrollo, pero la url queda de la siguiente forma http://content.local:8080 en vez de http://content.local

    1. Gracias, Darwel; me alegra saber que mi entrada te pudo ayudar.

      En cuanto al tema de los puertos, como te comento necesitas instalar y arrancar una segunda imagen de Docker (la que comento justo al final de mi entrada jwilder/nginx-proxy), que es la que se encarga de hacer la traducción de un nombre de dominio (ejemplo, content.local) a otra máquina y puerto (localhost:8080). Si no hace el paso de arrancar esa imagen y enlazar tu imagen de WordPress con ella, lo único que estarás haciendo es crear un alias de localhost. Presta especial atención a la última sección de la entrada y asegúrate de tener todo el entorno bien montado.

      Ya me dirás qué tal.

      Un saludo,
      David

  3. Enhorabuena por el post, sencillo y práctico. Siempre me había parecido un poco engorroso docker pero con este tuto (el cual me guardo en favoritos) parece hasta fácil.

    Para sacar nota, entiendo que al igual que haces con el plugin, podría hacerse con themes, pero, aquí parece que lo haces copiando de local al entorno generando en docker. ¿Podría hacerse haciendo checkout de un repo en git?

    Saludos

    1. Gracias, Antonio. Me alegra saber que te gusta el tutorial 😉 Y sí, lo que explico es totalmente aplicable a temas.

      En cuanto a lo que preguntas sobre Git… no sé si te entiendo mucho. El proyecto con el que trabajas (ya sea un tema o un plugin) es lo que, obviamente, tienes controlado en el Git. Hasta ahí todo es como siempre: haces pull, aplicas cambios, commit y push. La única diferencia es que tu proyecto incluye un fichero docker-compose.yml que te permite arrancar un contenedor docker con una instancia de WordPress que incluirá tu proyecto. ¿Se entiende?

      Un saludo,
      David

  4. Hola, buenas, muchas gracias por este tutorial, pero hay alguna forma de acceder a los archivos creados con el wordpress de manera local.

    También me ha surgido un problema al seguir este tutorial, cuando he intentado apagar los contenedores, no me deja hacerlo y intentando realizar ideas mías, he acabo desinstalando docker y aun así, cuando me conecto al puerto 8080 de localhost, aparece aun el wordpress, cuando no tengo ni siquiera docker instalado. Todo esto lo estoy haciendo sobre una maquina virtual de ubuntu, no me genera muchos problemas ya que he creado otra maquina, pero en la maquina que empecé inicialmente, el puerto 8080 siempre esta conectado al wordpress y no soy capaz de quitarlo.

    Un saludo

    1. Hola Sergio,

      hay alguna forma de acceder a los archivos creados con el wordpress de manera local

      Sí, claro. Lo más sencillo es poniendo la directiva volumes y mapeando los directorios que te interesen de una máquina a otra. En el tutorial yo explico cómo mapear un plugin en concreto, pero en principio puedes mapear cualquier cosa, incluso la raíz del propio WordPress. Ahora bien, si optas por ello, puedes tener problemas con los permisos de usuario, porque el propietario del directorio de WordPress en Docker puede tener un UID y GID diferentes al de tu usuario en la máquina host.

      cuando he intentado apagar los contenedores, no me deja hacerlo

      Si lo has creado con docker-compose, basta con ejecutar un docker-compose stop desde el directorio donde has iniciado el contenedor. Otra opción es hacer un docker ps para ver todos los contenedores que hay en marcha y pararlos uno a uno con un docker stop {ID_CONTENEDOR}.

  5. Excelente post,

    tengo un problema al momento de levantar el docker del proxy:

    MacBook-Pro-de-Elias:proxy eliasbg$ docker-compose up -d
    Pulling nginx-proxy (jwilder/nginx-proxy:)…
    latest: Pulling from jwilder/nginx-proxy
    a5a6f2f73cd8: Pull complete
    2343eb083a4e: Pull complete
    251439d5b33c: Pull complete
    0150289a3195: Pull complete
    196dcbce1a9b: Pull complete
    8826dc3389ea: Pull complete
    c7a4bc596c6f: Pull complete
    8000a1ad9fc5: Pull complete
    3bea426c29a7: Pull complete
    8378fe8463bb: Pull complete
    ERROR: error pulling image configuration: unexpected EOF

    ¿A qué crees que se deba?

    Gracias por la ayuda de antemano.

    1. Pues vaya, no sé qué puede pasar. Quizás sea un problema con la versión de docker que tienes en el mac… Prueba con un docker pull jwilder/nginx-proxy y a ver si hay más suerte.

  6. Un lifehack para que no tengas que cerrar y abrir la sesión cuando agregas a un grupo un usuario. Ejecuta «exec bash» si usas bash o «exec zsh» si usas zsh como yo. Básicamente «exec <nombre_de_la_shell_que_uses".

    Saludos

    1. Muchas gracias por el tip! Aunque esto sólo consigue que el nuevo grupo esté activo en dicha shell, ¿no? 🤔 Sea como fuere, siempre viene bien tener más trucos en el bolsillo ☺️

Deja un comentario

No publicaremos tu correo electrónico. Los campos obligatorios están marcados con: •

He leído y acepto la Política de privacidad de Nelio Software

Al marcar la casilla de aceptación estás dando tu legítimo consentimiento para que tu información personal se almacene en SiteGround y sea usada por Nelio Software con el propósito único de publicar aquí este comentario. Contáctanos para corregir, limitar, eliminar o acceder a tu información.