Crear un contenedor con Docker en Linux
Crear un contenedor con Docker nos recuerda al paradigma de programación POO, donde creamos una clase y luego la instanciamos en un objeto. En Docker es algo parecido, primero creamos una imagen y luego la instanciamos en un contenedor.
Para este laboratorio didáctico, vamos a desplegar con Docker un proyecto llamado ‘proyecto-php‘ desarrollado con RoadRunner PHP, que es un servidor de aplicaciones PHP de alto rendimiento, administrador de procesos y balanceador de carga escrito en Go. Nuestro ‘proyecto-php‘ trabaja junto con una base de datos MySQL.
Damos por sentado que el proyecto con RoadRunner ya funciona perfectamente antes de contenerizarlo, y que ya tenemos a Docker instalado, si no, el siguiente artículo nos muestra cómo hacerlo.
Crear un contenedor con Docker en Linux
El objetivo: El contenedor Docker deberá contener la app en PHP corriendo con RoadRunner, y la base de datos MySQL se mantendrá instalada directamente en el sistema operativo (no vamos a contenerizar a MySQL).
Antes de comenzar a ‘volear comandos’, debemos tener listo nuestro archivo Dockerfile (archivo llamado igual, Dockerfile) dentro del directorio del proyecto:
cd proyecto-php touch Dockerfile
Este es el contenido de nuestro Dockerfile:
# Dockerfile Final y Robusto (Modo Workers/Pipes)
# 1. IMAGEN BASE
FROM php:8.3-cli
# 2. INSTALACIÓN DE DEPENDENCIAS DEL SISTEMA
RUN apt-get update && apt-get install -y \
git \
unzip \
libzip-dev \
libpq-dev \
default-mysql-client \
&& rm -rf /var/lib/apt/lists/*
# 3. INSTALACIÓN DE EXTENSIONES DE PHP
RUN docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_pgsql zip mysqli
# 4. INSTALACIÓN DE COMPOSER
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# 5. COPIA DE CÓDIGO Y BINARIO DE ROADRUNNER
COPY . .
# 6. INSTALACIÓN DE ROADRUNNER
# Copiamos el binario 'rr' desde el directorio del proyecto
# y lo movemos a una ubicación que está en el PATH del contenedor.
COPY rr /usr/local/bin/rr
RUN chmod +x /usr/local/bin/rr
# 7. INSTALACIÓN DE DEPENDENCIAS DE LA APLICACIÓN
# RUN composer clear-cache
# RUN composer install --no-dev --optimize-autoloader
# 8. EXPOSICIÓN Y COMANDO DE INICIO
EXPOSE 8080
# Como 'rr' está en /usr/local/bin, se ejecuta directamente
CMD ["rr", "serve", "-c", ".rr.yaml"]
Pasos para la creación del contenedor
1- Entramos al directorio de nuestro proyecto
2- Construimos la imagen (yo la llame proyecto-rr):
sudo docker build -t proyecto-rr .
3- Creamos el contenedor
sudo docker run --rm -p 9090:8080 --name proyecto-php --add-host host.docker.internal:172.17.0.1 proyecto-rr
(Como el comando docker run es para crear instancias, el volverlo a usar por segunda con el mismo nombre de instancia – proyecto-php – nos dará error, por eso usamos la opción --rm, para que al detenerlo borre esa instancia. Usamos la opción --rm cuando estamos en proceso de desarrollo. La mejor práctica es crear la instancia docker run y para volverla a ejecutar usamos docker start proyecto-php)
Al comando docker run podemos adicionar la opción --restart unless-stopped para que el contenedor se vuelva a ejecutar cuando se reinicie la máquina.
4- Como la app necesita conectarse a la BD directamente, debemos conocer la IP del de nuestro contenedor, esto con el fin de relacionarla con el usuario MySQL. Entonces ejecutamos:
sudo docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' proyecto-php
(me arroja la 172.17.0.2)

Permisos vistos desde Webmin
5- Vamos al archivo de configuración de MySQL Server, y cambiamos el parámetro bind-address para que MySQL acepte las solicitudes desde esa IP:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf bind-address = 172.17.0.2
Por ende, la dirección IP del gateway de Docker es la 172.17.0.1, para confirmarlo ejecutamos:
sudo docker network inspect bridge --format='{{(index .IPAM.Config 0).Gateway}}
6- Instanciar el proyecto en productivo: Con la IP del gateway de Docker instanciamos el contenedor para productivo:
sudo docker run --rm -p 9090:8080 --name proyecto-php --add-host host.docker.internal:172.17.0.1 proyecto-rr
Cuando instanciamos el contenedor para productivo, la estamos ejecutamos sin volumen, para que la imagen queda estática. Esto le da máxima portabilidad al ser complemente independiente, pero los cambios que hagamos en nuestro proyecto de PHP no se verán reflejados en productivo. Para que los cambios se vean reflejados en Productivo, debemos construir de nuevo la imagen y ejecutar el docker estático nuevamente con docker run. En mi caso, voy a crear una nueva imagen del proyecto solo para la instancia Productivo, asi:
sudo docker build -t proyecto-rr:prod .
7- Instanciar el proyecto en modo desarrollo:
sudo docker run --rm -p 9090:8080 --name proyecto-php --add-host host.docker.internal:172.17.0.1 -v /home/usuario/proyecto:/var/www/html proyecto-rr
La opción -v /home/usuario/proyecto enlaza el directorio de nuestro proyecto con el directorio de trabajo del contenedor, es decir, es como un acceso directo, no es que haga una copia. Así, cualquier cambios que realicemos en nuestro proyecto se verá reflejado automáticamente en el contenedor.
La conexión de PHP a la base de datos
La conexión a MySQL de forma procedimental luce generalmente así:
$con = mysqli_connect('localhost', 'produccion', 'contraseña', 'base_de_datos'); if (!$con) { // Es mejor usar $worker->error() para notificar a RoadRunner un error fatal al inicio Worker::create()->error("❌ Error de conexión a MySQL: " . mysqli_connect_error()); exit(1); }
Ahora la misma conexión pero a través de Docker: $con = mysqli_connect('host.docker.internal', 'produccion', 'contraseña', 'base_de_datos'); if (!$con) { // Es mejor usar $worker->error() para notificar a RoadRunner un error fatal al inicio Worker::create()->error("❌ Error de conexión a MySQL: " . mysqli_connect_error()); exit(1); }
En Resumen
Creamos un contenedor instanciando la imagen
sudo docker run
En adelante seguimos ejecutando el contenedor con:
sudo docker start
Listamos los contenedores creados:
sudo docker ps -a
Listamos la imágenes:
sudo docker image ls
Borramos un contenedor:
sudo docker rm id_del_contenedor
Borramos una imagen:
sudo docker image rm
NO se puede borrar una imagen sin antes borrar los contenedores que se instanciaron a partir de ella.
Y eso es todo.
Comparte esto en
