Contenerización de una aplicación PHP
Escriba un Dockerfile de PHP listo para producción
Contenerización de una aplicación PHP es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
Por qué contenerizar PHP
Distribuir PHP de forma reproducible implica fijar la versión exacta del intérprete, las extensiones y las bibliotecas del sistema operativo junto con el código. Una imagen de Docker proporciona el mismo php -v y el mismo conjunto de ext-* en todos los entornos: portátil, CI y producción.
En esta lección crearemos una imagen lista para producción: PHP-FPM, solo las extensiones necesarias, configuraciones ajustadas, un usuario sin privilegios de root y una comprobación de estado.
FPM frente a base de Apache
Las imágenes oficiales de PHP vienen en distintas variantes. Para una aplicación web de producción detrás de nginx/traefik, prefiera php:8.3-fpm-alpine (pequeña) o php:8.3-fpm (Debian, glibc; presenta menos sorpresas con las bibliotecas nativas).
cli: workers, colas y cronfpm: gestor de procesos FastCGI, para combinar con nginxapache: Apache incluido, práctico pero más pesado
Fije la versión menor. No use nunca :latest en producción.
# Base image choice in your Dockerfile
FROM php:8.3-fpm-alpine AS base
# Why alpine? ~30MB base vs ~140MB Debian.
# Tradeoff: musl libc, occasional native-extension friction.Instalación de extensiones
No ejecute nunca apt install php-xxx dentro de estas imágenes; utilice los asistentes incluidos docker-php-ext-install, docker-php-ext-configure y pecl. El script install-php-extensions (mlocati) es el atajo de facto que obtiene por usted los encabezados de desarrollo adecuados.
FROM php:8.3-fpm-alpine
# Grab the helper that resolves build deps automatically
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions \
pdo_mysql \
opcache \
intl \
zip \
redis \
bcmathComposer en la imagen
Copie el binario de Composer desde su imagen oficial en lugar de descargar un instalador con curl. Ejecute composer install con --no-dev y --optimize-autoloader para producción, y copie solo composer.json/composer.lock primero, para que la capa de dependencias se almacene en caché independientemente de los cambios en el código fuente.
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
# Cache-friendly: deps layer invalidates only when lock changes
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-scripts \
--no-autoloader \
--prefer-dist
COPY . .
RUN composer dump-autoload --optimize --classmap-authoritativeAjuste de php.ini
La imagen base incluye las plantillas php.ini-production y php.ini-development. Active la de producción y después coloque sus propias sobreescrituras en conf.d: ese directorio se combina al final, por lo que sus valores prevalecen.
Valores clave para producción: opcache.enable=1, opcache.validate_timestamps=0 (código inmutable en la imagen) y un memory_limit razonable.
# Activate production ini
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Custom overrides win because conf.d loads last
COPY docker/php/zz-app.ini $PHP_INI_DIR/conf.d/zz-app.iniEl archivo de sobreescritura de OPcache
Esta es la mejora individual más importante para producción. Con validate_timestamps=0, PHP nunca comprueba los archivos en cada solicitud, pero eso significa que DEBE volver a construir la imagen para desplegar cambios (exactamente lo que queremos en contenedores inmutables).
; docker/php/zz-app.ini
memory_limit = 256M
expose_php = Off
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.preload = /app/preload.php
opcache.preload_user = www-dataEjecutar como usuario sin privilegios de root
La imagen ya define www-data. Ejecutar FPM como root aumenta innecesariamente la superficie de ataque. Establezca la propiedad de las rutas que se pueden escribir (caché y registros) y cambie de usuario con USER antes de CMD.
¿El proceso maestro de FPM sigue enlazándose a puertos privilegiados? No: FPM escucha en el puerto 9000 (sin privilegios), por lo que utilizar un usuario sin privilegios es sencillo.
# Make runtime-writable dirs owned by the runtime user
RUN chown -R www-data:www-data /app/var /app/storage 2>/dev/null || true
USER www-data
EXPOSE 9000
CMD ["php-fpm"]Comprobaciones de estado
Los orquestadores necesitan una señal que indique que FPM está realmente activo, no solo que el proceso existe. cgi-fcgi puede hacer ping al endpoint /status o /ping de FPM. Habilite primero pm.status_path y ping.path en el grupo de procesos de FPM.
# In www.conf pool config:
; ping.path = /ping
; ping.response = pong
# Dockerfile HEALTHCHECK using cgi-fcgi
RUN install-php-extensions @composer >/dev/null 2>&1 || true
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET \
cgi-fcgi -bind -connect 127.0.0.1:9000 || exit 1Ensamblaje del Dockerfile
Este es un Dockerfile de producción coherente y de una sola etapa. En la próxima lección lo dividiremos en varias etapas para eliminar las herramientas de compilación. Observe el orden: dependencias → configuración → código fuente → autoload → cambio de usuario.
FROM php:8.3-fpm-alpine
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions pdo_mysql opcache intl zip redis
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY docker/php/zz-app.ini $PHP_INI_DIR/conf.d/
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
COPY . .
RUN composer dump-autoload --optimize --classmap-authoritative && \
chown -R www-data:www-data /app/var
USER www-data
EXPOSE 9000
CMD ["php-fpm"]Verificación de la compilación
Después de compilar, haga una comprobación rápida de lo que realmente se incorporó a la imagen: la versión de PHP, las extensiones cargadas y que la validación de OPcache esté desactivada. Un sencillo script de CLI confirma el contrato de ejecución del que depende su aplicación.
<?php
// Run inside the container: php verify.php
echo 'PHP ' . PHP_VERSION . PHP_EOL;
$required = ['pdo_mysql', 'opcache', 'intl', 'zip'];
foreach ($required as $ext) {
printf("%-12s %s\n", $ext, extension_loaded($ext) ? 'OK' : 'MISSING');
}
var_dump((bool) ini_get('opcache.enable'));
?>Limpieza del contexto de compilación
Un .dockerignore mantiene pequeño el contexto de compilación y evita que los secretos y un exceso de archivos de vendor se filtren en la imagen y rompan la caché. Excluya vendor, el sistema de control de versiones, los archivos de entorno y las herramientas locales.
# .dockerignore
.git
.gitignore
vendor/
node_modules/
.env
.env.*
tests/
*.md
docker-compose*.yml
storage/logs/*
var/cache/*Comprobación rápida
¿Por qué se establece opcache.validate_timestamps=0 en una imagen de producción?
Resumen
Creó una imagen de PHP-FPM para producción: una imagen base 8.3-fpm-alpine fijada por versión, instaló solo las extensiones necesarias mediante el script instalador, copió Composer y las dependencias almacenadas en caché en su propia capa, activó el php.ini de producción con una sobreescritura de OPcache, cambió al usuario www-data y añadió una comprobación de estado de FPM.
Hábitos clave: fije las versiones, almacene en caché la capa de dependencias, ejecute como usuario no root, desactive la validación de marcas de tiempo y mantenga reducido el contexto de compilación con .dockerignore.
Preguntas frecuentes
¿La lección «Contenerización de una aplicación PHP» es gratis?
Sí — el texto completo de «Contenerización de una aplicación PHP» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Contenerización de una aplicación PHP»?
Escriba un Dockerfile de PHP listo para producción Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Contenerización de una aplicación PHP»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Contenerización de una aplicación PHP
- Compilaciones multietapa y optimización
- Docker Compose para entornos locales
- CI/CD con GitHub Actions