Stack LAMP con Ansible & Docker
📋 Resumen Ejecutivo
Sección titulada «📋 Resumen Ejecutivo»Este proyecto moderniza el despliegue de aplicaciones PHP (Legacy), evolucionando de una configuración manual a una Infraestructura Inmutable. A diferencia de los despliegues básicos de contenedores, esta solución aborda problemas del mundo real: la necesidad de compilar extensiones de PHP personalizadas (mysqli), la gestión segura de credenciales fuera del código y la estandarización del entorno de desarrollo.
🎯 Objetivos de Ingeniería
Sección titulada «🎯 Objetivos de Ingeniería»- Inmutabilidad: Construcción de imágenes Docker personalizadas (custom builds) en tiempo de despliegue para inyectar dependencias del sistema.
- Seguridad (12-Factor App): Desacoplamiento total de credenciales. El código fuente no contiene secretos; estos se inyectan como variables de entorno.
- Estandarización: Implementación de un
Makefilecomo wrapper para abstraer la complejidad de Python/Ansible y facilitar el onboarding.
🏗️ Arquitectura de la Solución
Sección titulada «🏗️ Arquitectura de la Solución»El diseño implementa un patrón de Build & Deploy Remoto orquestado por Ansible. El nodo de control prepara la configuración y los secretos, mientras que el host remoto construye y ejecuta los contenedores.
graph LR
%% --- Definición de Estilos ---
classDef ansibleNode fill:#fff9c4,stroke:#fbc02d,stroke-width:2px,color:#000000;
classDef dockerNode fill:#e0f7fa,stroke:#0288d1,stroke-width:2px,color:#000000;
classDef secretNode fill:#ffcdd2,stroke:#c62828,stroke-width:2px,color:#000000;
subgraph Control_Node ["Nodo de Control (Dev)"]
style Control_Node fill:#eceff1,stroke:#cfd8dc,color:#000000
Make[("Makefile Wrapper")]:::ansibleNode
Ansible[("Ansible Playbook")]:::ansibleNode
Secrets[("secrets.yml<br/>(GitIgnored)")]:::secretNode
Make --> Ansible
Secrets -.-> Ansible
end
subgraph Remote_Host ["Servidor de Aplicaciones (Ubuntu 24.04)"]
style Remote_Host fill:#0288d1,stroke:#cfd8dc,stroke-width:2px,color:#000000
subgraph Docker_Runtime ["Docker Engine"]
style Docker_Runtime fill:#eceff1,stroke:#455a64,color:#000000
Build[("Docker Build<br/>(PHP + Mysqli)")]:::dockerNode
App[("Container Web<br/>Apache")]:::dockerNode
DB[("Container DB<br/>MariaDB")]:::dockerNode
Ansible -- "1. Upload Dockerfile" --> Build
Build -- "2. Image Created" --> App
Ansible -- "3. Inject ENV Vars" --> App
App -- "Internal DNS" --> DB
end
end
linkStyle default stroke:#333,stroke-width:2px,fill:none;
💻 Implementación Técnica
Sección titulada «💻 Implementación Técnica»La solución utiliza una estructura profesional que separa configuración, secretos y lógica de despliegue.
-
Wrapper de Ejecución (Makefile)
Para evitar problemas de versiones y entornos virtuales, encapsulamos los comandos en un
Makefile. Esto garantiza que cualquier desarrollador use las mismas herramientas.Makefile setup:python3 -m venv .venv.venv/bin/pip install ansible-core docker.venv/bin/ansible-galaxy install -r requirements.ymldeploy:.venv/bin/ansible-playbook playbook/deploy-lamp.yml -
Gestión de Secretos (Patrón de Sobreescritura)
Ansible carga primero las variables públicas (
all.yml) y luego sobreescribe con un archivo de secretos local que está excluido de Git (.gitignore).playbook/deploy-lamp.yml - hosts: app_serversvars_files:- "../group_vars/all.yml" # Estructura pública (Placeholders)- "../group_vars/secrets.yml" # Secretos locales (Real Passwords) -
Lógica del Playbook (Build & Run)
El playbook no solo descarga imágenes, sino que sube un
Dockerfiley compila una imagen personalizada para soportarmysqli.playbook/deploy-lamp.yml tasks:- name: Construir imagen PHP personalizadacommunity.docker.docker_image:name: "{{ custom_image_name }}"tag: latestbuild:path: "{{ remote_project_path }}" # Ruta remota del Dockerfilesource: buildforce_source: true- name: Desplegar Web Containercommunity.docker.docker_container:name: "{{ project_name }}-web"image: "{{ custom_image_name }}:latest" # Usamos la imagen recién creadaenv:# Inyección segura de variables de entornoDB_HOST: "{{ project_name }}-db"DB_USER: "{{ mysql_user }}"DB_PASSWORD: "{{ mysql_pass }}" -
Código Agnóstico (PHP)
El código fuente (
src/index.php) no contiene ninguna credencial. Lee la configuración del entorno, cumpliendo con los principios de The Twelve-Factor App.src/index.php <?php// Lectura segura desde variables de entorno$servername = getenv('DB_HOST');$username = getenv('DB_USER');$password = getenv('DB_PASSWORD');$conn = new mysqli($servername, $username, $password, $dbname);if ($conn->connect_error) {die("❌ Fallo de conexión: " . $conn->connect_error);}echo "✅ Conectado exitosamente a MariaDB.";?> -
Ejecución Estandarizada
Gracias al Makefile, el despliegue se reduce a dos comandos, independientemente de la complejidad subyacente.
Ventana de terminal # Inicialización (Solo la primera vez)make setup# Despliegue idempotentemake deploy
🔍 Análisis de Valor
Sección titulada «🔍 Análisis de Valor»| Característica | Script Bash Tradicional | Ansible + Docker Pro |
|---|---|---|
| Credenciales | Hardcoded en archivos (.php) | Inyectadas en Memoria (ENV) |
| Dependencias | Instalación manual (apt-get) | Docker Build Automático |
| Portabilidad | ”Funciona en mi máquina” | Estandarizado vía Makefile |
Próximos Pasos (Roadmap AZ-104)
Sección titulada «Próximos Pasos (Roadmap AZ-104)»Este proyecto sirve como base para la migración a la nube. Hemos documentado este proceso en la sección de Operaciones Cloud:
-
Migración IaaS: Replicar esta arquitectura utilizando Máquinas Virtuales en Azure.
-
Azure Container Registry (ACR): Mover el proceso de build a la nube.
-
Azure Key Vault: Gestión de secretos centralizada.