Descubre los contenidos más relevantes sobre tecnología

Despliegue con Deployer (II)

Escrito por Conasa | 20-mar-2017 10:12:48

En el anterior post vimos cómo realizar la configuración previa de SSH, necesaria para realizar un despliegue con Deployer. En este veremos cómo realizar un despliegue sencillo y alguna de las opciones básicas de configuración y uso que ofrece Deployer.

Configuración del despliegue con Deployer

Para ver el uso básico de un despliegue con Deployer podemos empezar usando un proyecto muy simple, que conste de un único fichero index.php. Por ejemplo, el clásico Hello World!:

<?php echo "Hello World!"; 

Este proyecto deberá estar versionado en un repositorio git (accesible desde la máquina destino), y el fichero index.php guardado en la rama master del mismo. Una vez creado el proyecto y el repositorio, podemos empezar a definir el script de despliegue con Deployer. Para ello, nos posicionamos con la terminal en cualquier directorio (puede ser el mismo del proyecto) y ejecutamos:

$ dep init 

Deployer nos preguntará de qué tipo es nuestro proyecto (por ejemplo Symfony, Laravel, etc). En nuestro caso elegimos Common:

Please select your project type (defaults to common): [0] Common [1] Laravel [2] Symfony [3] Yii [4] Zend Framework [5] CakePHP [6] CodeIgniter [7] Drupal > 

Deployer creará en ese directorio un script llamado deploy.php con el siguiente contenido por defecto:

<?php namespace Deployer; require 'recipe/common.php'; // Configuration set('ssh_type', 'native'); set('ssh_multiplexing', true); set('repository', 'git@domain.com:username/repository.git'); set('shared_files', []); set('shared_dirs', []); set('writable_dirs', []); // Servers server('production', 'domain.com') ->user('username') ->identityFile() ->set('deploy_path', '/var/www/domain.com'); // Tasks desc('Restart PHP-FPM service'); task('php-fpm:restart', function () { // The user must have rights for restart service // /etc/sudoers: username ALL=NOPASSWD:/bin/systemctl restart php-fpm.service run('sudo systemctl restart php-fpm.service'); }); after('deploy:symlink', 'php-fpm:restart'); desc('Deploy your project'); task('deploy', [ 'deploy:prepare', 'deploy:lock', 'deploy:release', 'deploy:update_code', 'deploy:shared', 'deploy:writable', 'deploy:vendors', 'deploy:clear_paths', 'deploy:symlink', 'deploy:unlock', 'cleanup', 'success' ]); // [Optional] if deploy fails automatically unlock. after('deploy:failed', 'deploy:unlock'); 

Como se puede ver en la segunda línea, Deployer ha incluido el fichero recipe/common.php ya que hemos elegido el tipo Common al realizar el dep init. Esto es lo que Deployer llama una receta (recipe), es decir, una serie de tareas y configuración por defecto adaptada a diferentes tipos de proyectos y frameworks PHP.

A continuación viene la sección de configuración general (// Configuration), en la que indicamos que utilizaremos SSH nativo como medio de conexión y la URL al repositorio de código (set(‘repository’, …)). Por defecto, se obtiene el código de la rama master del repositorio, pero se puede modificar añadiendo la instrucción set(‘branch’, ‘nombre-de-la-rama’);.

El despliegue con Deployer nos permite además definir ficheros y directorios compartidos (set(‘shared_files’, []);, etc.), es decir, que no cambian entre despliegues. Esto es especialmente útil para directorios de logs, sesiones o ficheros de configuración que no deberían sobreescribirse con cada despliegue. En este caso no vamos a definir ninguno.

En la siguiente sección (// Servers) definiremos el servidor en el que vamos a realizar el despliegue (podemos añadir más si es necesario):

  • server(‘production’, ‘domain.com’): Establecemos la URL a nuestro servidor (2º parámetro) identificado como production (podemos ponerle el nombre que queramos).
  • ->user(‘username’): Usuario con el que nos conectamos al servidor.
  • ->identityFile(): Utilizar los ficheros de claves pública/privada (generados en la sección anterior).
  • ->set(‘deploy_path’, ‘/var/www/domain.com’);: El directorio en la máquina destino en el que se desplegará el proyecto. En nuestro caso podría ser var/www/hello-world por ejemplo.

Por último viene la sección de tareas (// Tasks) en la que se definen los pasos necesarios para realizar el despliegue. Por ejemplo, se está definiendo una tarea task(‘php-fpm:restart’, … para resetear PHP-FPM, que se ejecutará después de la tarea deploy:symlink (crear el enlace simbólico a la release actual). Si no utilizamos PHP-FPM, es necesario eliminar esta tarea.

A continuación se define la tarea principal de despliegue deploy especificando una serie de sub-tareas a realizar. Aconsejo consultar la documentación y el código de Deployer para saber más acerca de la función de cada una de ellas.

En nuestro proyecto Hello World, además es necesario eliminar la línea ‘deploy:vendors’,, correspondiente a la tarea de instalación de Composer, ya que no estamos utilizando librerías de terceros. Así pues, nuestro script deploy.php quedaría parecido a:

<?php namespace Deployer; require 'recipe/common.php'; // Configuration set('ssh_type', 'native'); set('ssh_multiplexing', true); set('repository', 'git@domain.com:username/repository.git'); //Modificar // Servers server('production', 'domain.com') //Modificar ->user('username') //Modificar ->identityFile() ->set('deploy_path', '/var/www/hello-world'); // Tasks desc('Deploy your project'); task('deploy', [ 'deploy:prepare', 'deploy:lock', 'deploy:release', 'deploy:update_code', 'deploy:shared', 'deploy:writable', 'deploy:clear_paths', 'deploy:symlink', 'deploy:unlock', 'cleanup', 'success' ]); // [Optional] if deploy fails automatically unlock. after('deploy:failed', 'deploy:unlock'); 

…donde será necesario modificar los valores de las líneas marcadas con //Modificar para cada caso concreto.

Realizando el despliegue con Deployer

Una vez configurado nuestro script deploy.php, podemos realizar el despliegue con Deployer a través del comando:

$ dep deploy 

o en el caso de haber definido varios servidores:

$ dep deploy [nombre-servidor] 

por ejemplo para el servidor production (server(‘production’, …):

$ dep deploy production 

Si todo va bien deberíamos ver el siguiente resultado:

<img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:prepare <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:lock <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:release <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:update_code <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:shared <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:writable <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:clear_paths <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:symlink <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task deploy:unlock <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task cleanup <img draggable="false" class="emoji" alt="✔" src="https://s.w.org/images/core/emoji/12.0.0-1/svg/2714.svg"> Executing task success Successfully deployed! 

Si revisamos el directorio configurado en la variable deploy_path en la máquina destino (en este ejemplo /var/www/hello-world) veremos que Deployer ha creado las siguientes carpetas:

$ ls -al total 20 drwxr-xr-x 5 root root 4096 Feb 19 22:49 . drwxrwxr-x 5 root root 4096 Feb 19 22:45 .. lrwxrwxrwx 1 root root 10 Feb 19 22:49 current -&gt; releases/1 drwxr-xr-x 2 root root 4096 Feb 19 22:53 .dep drwxr-xr-x 3 root root 4096 Feb 19 22:49 releases drwxr-xr-x 2 root root 4096 Feb 19 22:45 shared 
  • releases: Carpeta donde se guarda el código desplegado. Por defecto Deployer guarda los últimos 5 despliegues realizados en carpetas numeradas (1, 2, 3, etc.) de manera que el código correspondiente al primer despliegue se encontrará en la carpeta /releases/1, el del siguiente en /releases/2 y así sucesivamente.
  • current: Enlace simbólico a la carpeta que contiene el último despliegue y por tanto la versión más actualizada de nuestra aplicación. Esta carpeta será la que deberemos configurar como root en el servidor web.
  • shared: Ficheros y directorios compartidos. Cada una de las carpetas en releases contendrá enlaces simbólicos a los directorios y ficheros compartidos en shared.

Si realizamos otro despliegue con Deployer veremos que se coloca en la carpeta /releases/2 y actualiza el enlace simbólico current para que apunte a esta carpeta. Esto nos permite realizar despliegues sin necesidad de parar la aplicación y mantener las releases anteriores en caso de que algo vaya mal y queramos hacer un rollback. Si queremos volver a la versión anterior podemos ejecutar el comando:

$ dep rollback 

…que actualizará el enlace simbólico current para que apunte a la release anterior.

Conclusiones del despliegue con Deployer

En este post hemos visto lo básico para instalar, configurar y utilizar Deployer para desplegar una aplicación PHP muy simple. Frente a otras alternativas, un despliegue con Deployer tiene la ventaja de estar escrito en un lenguaje familiar para los desarrolladores de aplicaciones PHP, de manera que es más sencillo configurarlo y extenderlo para adecuarlo a cada necesidad. Además la configuración del fichero deploy.php es sencilla y el resultado acaba siendo bastante legible y fácil de entender.

Si os habéis quedado con ganas de más os recomiendo echarle un vistazo a la documentación de la herramienta, ya que permite casos de uso más avanzados como:

  • Definición de tareas personalizadas.
  • Definición de múltiples servidores (incluso usando ficheros YAML) agrupados opcionalmente en varias fases.
  • Ejecución de tareas locales (en vez de remotas), que permite preparar el despliegue en la máquina local y después copiar los ficheros a la máquina destino.