ansistrano.deploy

Ansistrano

Estado de Construcción Despliegues Totales Despliegues del Año Despliegues del Mes Despliegues de Hoy

ansistrano.deploy y ansistrano.rollback son roles de Ansible para gestionar fácilmente el proceso de despliegue de aplicaciones en lenguajes como PHP, Python y Ruby. Es una adaptación de Ansible para Capistrano.

Historia

Capistrano es una herramienta de automatización para servidores remotos y actualmente está en la versión 3. La versión 2.0 se pensó originalmente para desplegar aplicaciones RoR. Con plugins adicionales, pudiste desplegar aplicaciones que no son de Rails, como PHP y Python, con diferentes estrategias de despliegue, etapas y mucho más. Me encantaba Capistrano v2. Lo usé mucho. Desarrollé un plugin para él.

Capistrano 2 era una gran herramienta y todavía funciona muy bien. Sin embargo, ya no se mantiene, ya que el equipo original está trabajando en la versión 3. Esta nueva versión no tiene el mismo conjunto de funciones, por lo que es menos potente y flexible. Además, otras nuevas herramientas se están volviendo más fáciles de usar para desplegar aplicaciones, como Ansible.

Así que decidí dejar de usar Capistrano porque v2 ya no se mantiene, v3 no tiene suficientes características y puedo hacer todo lo que Capistrano hacía con Ansible. Si estás buscando alternativas, revisa Fabric o Chef Solo.

Nombre del Proyecto

Ansistrano proviene de Ansible + Capistrano, fácil, ¿verdad?

Estadísticas anónimas de uso de Ansistrano

Hay un paso opcional en Ansistrano que envía una solicitud HTTP a nuestros servidores. Desafortunadamente, las métricas que podemos obtener de Ansible Galaxy son limitadas, así que esta es una de las pocas formas que tenemos para medir cuántos usuarios activos tenemos realmente.

Solo usamos estos datos para estadísticas de uso, pero si no te sientes cómodo con esto, puedes desactivar este paso extra configurando ansistrano_allow_anonymous_stats en false en tus playbooks.

¿Quién está usando Ansistrano?

¿Está Ansistrano listo para usar? Aquí hay algunas empresas que actualmente lo utilizan:

Si también lo estás usando, háznoslo saber a través de un PR a este documento.

Requisitos

Para desplegar tus aplicaciones con Ansistrano, necesitarás:

  • Ansible en tu máquina de despliegue
  • rsync en la máquina objetivo si estás usando la estrategia de despliegue rsync, rsync_direct, o git o si estás utilizando ansistrano_current_via = rsync

Instalación

Ansistrano es un rol de Ansible distribuido globalmente usando Ansible Galaxy. Para instalar el rol de Ansistrano puedes usar el siguiente comando.

$ ansible-galaxy install ansistrano.deploy ansistrano.rollback

Actualización

Si deseas actualizar el rol, debes pasar el parámetro --force al instalar. Por favor, revisa el siguiente comando:

$ ansible-galaxy install --force ansistrano.deploy ansistrano.rollback

Características

  • Rollback en segundos (con el rol ansistrano.rollback)
  • Personaliza tu despliegue con hooks antes y después de pasos críticos
  • Ahorra espacio en disco manteniendo un número fijo de versiones en tus hosts
  • Elige entre estrategias de despliegue SCP, RSYNC, GIT, SVN, HG, HTTP Download o S3 GET (se incluye un paso de descompresión opcional)

Flujo de trabajo principal

Ansistrano despliega aplicaciones siguiendo el flujo de Capistrano.

  • Fase de configuración: Crea la estructura de carpetas para contener tus versiones
  • Fase de actualización de código: Coloca la nueva versión en tus hosts
  • Fase de Symlink: Después de desplegar la nueva versión en tus hosts, este paso cambia el softlink de current a la nueva versión
  • Fase de limpieza: Elimina cualquier versión antigua basada en el parámetro ansistrano_keep_releases (ver "Variables del Rol")

Flujo de Ansistrano

Variables del Rol

vars:
  ansistrano_deploy_from: "{{ playbook_dir }}/" # Donde está mi proyecto local (ruta relativa o absoluta)
  ansistrano_deploy_to: "/var/www/my-app" # Ruta base para desplegar.
  ansistrano_version_dir: "releases" # Nombre de la carpeta de versiones
  ansistrano_shared_dir: "shared" # Nombre de la carpeta compartida
  ansistrano_current_dir: "current" # Nombre del softlink. Rara vez deberías cambiarlo.
  ansistrano_current_via: "symlink" # Estrategia de despliegue que se usará para el código que se deployará en la ruta actual. Las opciones son symlink o rsync
  ansistrano_keep_releases: 0 # Versiones a mantener después de un nuevo despliegue. Ver "Poda de versiones antiguas".

  # Arrays de directorios y archivos a ser compartidos.
  # A continuación, se listan los arrays de directorios y archivos que serán symlinked al directorio de la versión actual después del paso 'update-code' y sus callbacks
  # Notas:
  # * Las rutas son relativas a la carpeta compartida (sin / al inicio)
  # * Si tus elementos están en un subdirectorio, escribe la ruta completa a cada directorio compartido
  #
  # Ejemplo:
  # ansistrano_shared_paths:
  #   - path/to/first-dir
  #   - path/next-dir
  # ansistrano_shared_files:
  #   - my-file.txt
  #   - path/to/file.txt
  ansistrano_shared_paths: []
  ansistrano_shared_files: []

  # Creación de rutas compartidas y base para archivos compartidos.
  # Por defecto, las rutas y directorios compartidos se crean automáticamente si no existen. Pero en algunos escenarios, esas rutas podrían ser symlinks a otros directorios en el sistema de archivos, y el proceso de despliegue podría fallar. Con estas variables puedes deshabilitar las tareas involucradas. Si tienes dos o tres rutas compartidas y no necesitas creación solo para algunas de ellas, siempre podrías desactivar la creación automática y añadir una tarea personalizada en un hook.
  ansistrano_ensure_shared_paths_exist: yes
  ansistrano_ensure_basedirs_shared_files_exist: yes

  # Estrategia de despliegue - método utilizado para entregar código. Las opciones son copy, download, git, rsync, rsync_direct, svn, o s3. 
  ansistrano_deploy_via: rsync 
  # Copy, download y s3 tienen un paso opcional para descomprimir el archivo descargado, que puede ser usado añadiendo _unarchive. 
  # La estrategia rsync_direct omite la copia de archivos en el objetivo, ofreciendo un ligero aumento de velocidad si estás desplegando en hosts compartidos, experimentando un mal rendimiento de archivos, o sirviendo recursos estáticos desde el mismo host al que despliegas tu app y rsync muchos archivos.
  # Puedes revisar todas las opciones dentro del folder tasks/update-code!
  
  ansistrano_allow_anonymous_stats: yes

  # Variables utilizadas en la estrategia de despliegue rsync/rsync_direct
  ansistrano_rsync_extra_params: "" # Parámetros extra para usar al desplegar con rsync en una sola cadena. Aunque Ansible permite un array, esto puede causar problemas si intentamos añadir múltiples argumentos --include como se reportó en https://github.com/ansistrano/deploy/commit/e98942dc969d4e620313f00f003a7ea2eab67e86
  ansistrano_rsync_set_remote_user: yes # Ver [módulo de sincronización de ansible](http://docs.ansible.com/ansible/synchronize_module.html). Las opciones son yes, no.
  ansistrano_rsync_path: "" # Ver [módulo de sincronización de ansible](http://docs.ansible.com/ansible/synchronize_module.html). Por defecto es "sudo rsync", se puede sobrescribir (ejemplo): "sudo -u usuario rsync".
  ansistrano_rsync_use_ssh_args: no # Ver [módulo de sincronización de ansible](http://docs.ansible.com/ansible/synchronize_module.html). Si se establece en yes, utiliza los ssh_args especificados en ansible.cfg.

  # Variables utilizadas en la estrategia de despliegue de Git
  ansistrano_git_repo: [email protected]:USERNAME/REPO.git # Ubicación del repositorio git
  ansistrano_git_branch: master # Qué versión del repositorio desplegar. Puede ser el hash SHA-1 completo de 40 caracteres, la cadena literal HEAD, un nombre de rama, o un nombre de etiqueta
  ansistrano_git_repo_tree: "" # Si se especifica, el subárbol del repositorio que se desplegará
  ansistrano_git_identity_key_path: "" # Si se especifica, este archivo se copia y se usa como la clave de identidad para los comandos git, la ruta es relativa al playbook en el que se utiliza
  ansistrano_git_identity_key_remote_path: "" # Si se especifica, este archivo en el servidor remoto se usa como clave de identidad para los comandos git, la ruta remota es absoluta
  ansistrano_git_identity_key_shred: true # Destruir clave de identidad por defecto pero puede ser sobrescrito a false si encuentras el siguiente problema (https://github.com/ansistrano/deploy/issues/357)
  # Variables opcionales, omitidas por defecto
  ansistrano_git_refspec: ADDITIONAL_GIT_REFSPEC # RefSpec adicional que será utilizado por el módulo 'git'. Usa la misma sintaxis que el comando 'git fetch'.
  ansistrano_git_ssh_opts: "-o StrictHostKeyChecking=no" # Opciones ssh adicionales a utilizar en Git
  ansistrano_git_depth: 1 # Historial adicional truncado al número especificado de revisiones
  ansistrano_git_executable: /opt/local/bin/git # Ruta al ejecutable de git a usar. Si no se proporciona, se utilizará el mecanismo normal para resolver rutas de binarios.

  # Variables utilizadas en la estrategia de despliegue SVN
  # Tenga en cuenta que había un error en el módulo subversion en la serie Ansible 1.8.x (https://github.com/ansible/ansible-modules-core/issues/370) así que solo está soportado desde Ansible 1.9
  ansistrano_svn_repo: https://svn.company.com/project # Ubicación del repositorio svn
  ansistrano_svn_branch: trunk # Qué rama del repositorio desplegar.
  ansistrano_svn_revision: HEAD # Qué revisión del repositorio desplegar.
  ansistrano_svn_username: usuario # Nombre de usuario de autenticación SVN
  ansistrano_svn_password: Pa$$word # Contraseña de autenticación SVN
  ansistrano_svn_environment: {} # Diccionario con variables de entorno para tareas svn (https://docs.ansible.com/ansible/playbooks_environment.html)

  # Variables utilizadas en la estrategia de despliegue HG
  ansistrano_hg_repo: https://[email protected]/USERNAME/REPO # Ubicación del repositorio hg
  ansistrano_hg_branch: default # Cualquier identificador de rama que funcione con hg -r, como rama denominada, bookmark, hash de commit...

  # Variables utilizadas en la estrategia de despliegue de descarga
  ansistrano_get_url: https://github.com/someproject/somearchive.tar.gz
  ansistrano_download_force_basic_auth: false # no hay valor por defecto ya que esto solo es soportado desde Ansible 2.0
  ansistrano_download_headers: "" # no hay valor por defecto ya que esto solo es soportado desde Ansible 2.0

  # Variables utilizadas en la estrategia de despliegue S3
  ansistrano_s3_bucket: s3bucket
  ansistrano_s3_object: s3object.tgz # Añadir el sufijo _unarchive a el ansistrano_deploy_via si tu objeto es un paquete (es decir: s3_unarchive)
  ansistrano_s3_region: eu-west-1
  ansistrano_s3_rgw: false # debe ser Ansible >= 2.2. use Ceph RGW para proveedores de nube compatibles con S3
  ansistrano_s3_url: http://rgw.example.com # cuando use Ceph RGW, establecer url
  # Variables opcionales, omitidas por defecto
  ansistrano_s3_aws_access_key: YOUR_AWS_ACCESS_KEY
  ansistrano_s3_aws_secret_key: YOUR_AWS_SECRET_KEY
  ansistrano_s3_ignore_nonexistent_bucket: false
  
  # Variables utilizadas en la estrategia de despliegue GCS
  ansistrano_gcs_bucket: gcsbucket
  ansistrano_gcs_object: gcsobject.tgz # Añadir el sufijo _unarchive a el ansistrano_deploy_via si tu objeto es un paquete (es decir: s3_unarchive)
  ansistrano_gcs_region: eu-west-1 # https://cloud.google.com/storage/docs/bucket-locations
  # Variables opcionales, omitidas por defecto
  ansistrano_gcs_access_key: YOUR_GCS_ACCESS_KEY # navegar a Cloud console > Storage > Settings > Interoperability
  ansistrano_gcs_secret_key: YOUR_GCS_SECRET_KEY

  # Hooks: tareas personalizadas si las necesitas
  ansistrano_before_setup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-before-setup-tasks.yml"
  ansistrano_after_setup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-after-setup-tasks.yml"
  ansistrano_before_update_code_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-before-update-code-tasks.yml"
  ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-after-update-code-tasks.yml"
  ansistrano_before_symlink_shared_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-before-symlink-shared-tasks.yml"
  ansistrano_after_symlink_shared_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-after-symlink-shared-tasks.yml"
  ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-before-symlink-tasks.yml"
  ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-after-symlink-tasks.yml"
  ansistrano_before_cleanup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-before-cleanup-tasks.yml"
  ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-after-cleanup-tasks.yml"

{{ playbook_dir }} es una variable de Ansible que contiene la ruta al playbook actual.

Despliegue

Para desplegar con Ansistrano, necesitas realizar algunos pasos:

  • Crea un nuevo archivo hosts. Consulta la documentación de inventario de ansible si necesitas ayuda. Este archivo identificará todos los hosts a los que se desplegará. Para entornos multietapa, consulta Entornos multietapa.
  • Crea un nuevo playbook para desplegar tu aplicación, por ejemplo, deploy.yml
  • Configura las variables del rol (ver Variables del Rol)
  • Incluye el rol ansistrano.deploy como parte de un play
  • Ejecuta el playbook de despliegue

ansible-playbook -i hosts deploy.yml

Si todo está configurado correctamente, este comando creará la siguiente estructura aproximada de directorios en tu servidor. Revisa cómo se vería la estructura de carpetas después de uno, dos y tres despliegues.

-- /var/www/my-app.com
|-- current -> /var/www/my-app.com/releases/20100509145325
|-- releases
|   |-- 20100509145325
|-- shared
-- /var/www/my-app.com
|-- current -> /var/www/my-app.com/releases/20100509150741
|-- releases
|   |-- 20100509150741
|   |-- 20100509145325
|-- shared
-- /var/www/my-app.com
|-- current -> /var/www/my-app.com/releases/20100512131539
|-- releases
|   |-- 20100512131539
|   |-- 20100509150741
|   |-- 20100509145325
|-- shared

Despliegues en serie

Para evitar diferentes marcas de tiempo al desplegar en varios servidores usando la opción serial, debes establecer la variable ansistrano_release_version.

ansible-playbook -i hosts -e "ansistrano_release_version=`date -u +%Y%m%d%H%M%SZ`" deploy.yml

Rollback

Para realizar un rollback con Ansistrano, necesitas configurar el despliegue y ejecutar el playbook de rollback.

ansible-playbook -i hosts rollback.yml

Si intentas hacer rollback con cero o una versión desplegada, se lanzará un error y no se realizará ninguna acción.

Las variables que puedes ajustar en el rol de rollback son menos que en el de despliegue:

vars:
  ansistrano_deploy_to: "/var/www/my-app" # Ruta base para desplegar.
  ansistrano_version_dir: "releases" # Nombre de la carpeta de versiones
  ansistrano_current_dir: "current" # Nombre del softlink. Rara vez deberías cambiarlo.
  ansistrano_rollback_to_release: "" # Si se especifica, la aplicación volverá a esta versión; de lo contrario, la versión anterior.
  ansistrano_remove_rolled_back: yes # Puedes cambiar esta configuración para mantener la versión revertida en el servidor para una inspección posterior
  ansistrano_allow_anonymous_stats: yes

  # Hooks: tareas personalizadas si las necesitas
  ansistrano_rollback_before_setup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-rollback-before-setup-tasks.yml"
  ansistrano_rollback_after_setup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-rollback-after-setup-tasks.yml"
  ansistrano_rollback_before_symlink_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-rollback-before-symlink-tasks.yml"
  ansistrano_rollback_after_symlink_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-rollback-after-symlink-tasks.yml"
  ansistrano_rollback_before_cleanup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-rollback-before-cleanup-tasks.yml"
  ansistrano_rollback_after_cleanup_tasks_file: "{{ playbook_dir }}/<tu-config-de-despliegue>/my-rollback-after-cleanup-tasks.yml"

Entorno multietapa (desarrollo, preproducción, producción, etc.)

Si deseas desplegar en diferentes entornos como desarrollo, preproducción y producción, se recomienda crear diferentes archivos de hosts. Una vez hecho esto, puedes especificar un archivo de hosts diferente al ejecutar el playbook de despliegue utilizando el parámetro -i. En cada archivo de hosts, puedes especificar diferentes usuarios, contraseñas, parámetros de conexión, etc.

ansible-playbook -i hosts_devel deploy.yml

ansible-playbook -i hosts_preprod deploy.yml

ansible-playbook -i hosts_prod deploy.yml

Hooks: Tareas personalizadas

Normalmente, necesitarás reiniciar tu servidor web después del paso de Symlink, o descargar tus dependencias antes de Code update o incluso hacerlo en producción antes de Symlink. Por lo tanto, para realizar tus tareas personalizadas, tienes algunos hooks que Ansistrano ejecutará antes y después de cada uno de los 3 pasos principales. Este es el principal beneficio en comparación con otros roles de despliegue similares.

-- /mi-maquina-local/my-app.com
|-- hosts
|-- deploy.yml
|-- mis-tareas-personalizadas
|   |-- before-code-update.yml
|   |-- after-code-update.yml
|   |-- before-symlink.yml
|   |-- after-symlink.yml
|   |-- before-cleanup.yml
|   |-- after-cleanup.yml

Por ejemplo, para reiniciar apache después del paso de Symlink, añadiremos en after-symlink.yml

- name: Reiniciar Apache
  service: name=httpd state=reloaded
  • P: ¿Dónde agregarías el envío de notificación por correo electrónico después de un despliegue?
  • P: (para desarrolladores PHP y Symfony) ¿Dónde limpiarías la caché?

Puedes especificar un archivo de tareas personalizadas para antes y después de cada paso usando las variables de rol ansistrano_before_*_tasks_file y ansistrano_after_*_tasks_file. Consulta "Variables del Rol" para más información.

Variables en tareas personalizadas

Al escribir tus archivos de tareas personalizadas, puedes necesitar algunas variables que Ansistrano te hace disponibles:

  • {{ ansistrano_release_path.stdout }}: Ruta a la versión actual del despliegue (probablemente la que usarás más)
  • {{ ansistrano_releases_path }}: Ruta a la carpeta de versiones
  • {{ ansistrano_shared_path }}: Ruta a la carpeta compartida (donde se pueden almacenar los activos comunes de las versiones)
  • {{ ansistrano_release_version }}: Nombre del directorio relativo para la versión (por defecto igual al timestamp actual en UTC)

Poda de versiones antiguas

En entornos de entrega continua, posiblemente tendrás un gran número de versiones en producción. Tal vez tengas mucho espacio y no te importe, pero es una práctica común mantener solo un número personalizado de versiones.

Después del despliegue, si quieres eliminar versiones antiguas, solo debes configurar la variable ansistrano_keep_releases al número total de versiones que deseas mantener.

Veamos tres despliegues con una configuración de ansistrano_keep_releases: 2:

-- /var/www/my-app.com
|-- current -> /var/www/my-app.com/releases/20100509145325
|-- releases
|   |-- 20100509145325
|-- shared
-- /var/www/my-app.com
|-- current -> /var/www/my-app.com/releases/20100509150741
|-- releases
|   |-- 20100509150741
|   |-- 20100509145325
|-- shared
-- /var/www/my-app.com
|-- current -> /var/www/my-app.com/releases/20100512131539
|-- releases
|   |-- 20100512131539
|   |-- 20100509150741
|   |-- 20100509145325
|-- shared

Observa cómo la versión 20100509145325 ha sido eliminada.

Ejemplo de Playbook

En la carpeta example, puedes ver un proyecto de ejemplo que muestra cómo desplegar una pequeña aplicación con Ansistrano.

Para ejecutarlo, necesitarás tener Vagrant y los roles de ansistrano instalados. Revisa https://www.vagrantup.com para más información sobre Vagrant y nuestra sección de Instalación.

$ cd example/my-playbook
$ vagrant up
$ ansible-playbook -i hosts deploy.yml

Y después de ejecutar estos comandos, el index.html ubicado en la carpeta my-app se desplegará en ambas cajas de vagrant.

Para probar el playbook de rollback, necesitarás ejecutar deploy.yml al menos dos veces (para que haya algo a lo que volver). Y una vez hecho esto, solo necesitas ejecutar

$ ansible-playbook -i hosts rollback.yml

Puedes revisar ejemplos más avanzados dentro de la carpeta test que se ejecutan contra Travis-CI.

Proyectos de ejemplo

Hemos añadido soporte de Ansistrano para otros proyectos en los que estamos trabajando.

Como ejemplo, mira el registro de ejecución del despliegue de LastWishes:

PLAY [Desplegar la aplicación de últimos deseos en mi servidor] ************************************

GATHERING FACTS ***************************************************************
ok: [quepimquepam.com]

TASK: [ansistrano.deploy | Asegurar existencia de la ruta base de despliegue] ***
ok: [quepimquepam.com]

TASK: [ansistrano.deploy | Asegurar que exista la carpeta de versiones] ***
ok: [quepimquepam.com]

TASK: [ansistrano.deploy | Asegurar que exista la carpeta de elementos compartidos] ***
ok: [quepimquepam.com]

TASK: [ansistrano.deploy | Obtener timestamp de la versión] ***********
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Obtener ruta de la versión] ****************
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Obtener ruta de versiones] ***************
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Obtener ruta compartida (en el caso de rsync)] ***
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Rsync archivo de aplicación a la copia compartida remota (en el caso de rsync)] ***
changed: [quepimquepam.com -> 127.0.0.1]

TASK: [ansistrano.deploy | Desplegar código existente en servidores] ***
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Desplegar código existente en servidores remotos] ***
skipping: [quepimquepam.com]

TASK: [ansistrano.deploy | Actualizar repositorio remoto] ********
skipping: [quepimquepam.com]

TASK: [ansistrano.deploy | Exportar una copia del repositorio] *******
skipping: [quepimquepam.com]

TASK: [ansistrano.deploy | Desplegar código a los servidores] *****
skipping: [quepimquepam.com]

TASK: [ansistrano.deploy | Copiar versión de despliegue en archivo REVISION] ***
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Mejorar el código de la versión] *****
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Cambiar softlink a nueva versión] ***
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Recargar Apache] *******************
changed: [quepimquepam.com]

TASK: [ansistrano.deploy | Limpiar versiones] ***************
skipping: [quepimquepam.com]

PLAY RECAP ********************************************************************
quepimquepam.com           : ok=14   changed=10   unreachable=0    failed=0

Se están hablando de nosotros

Licencia

MIT

Otros recursos

Acerca del proyecto

Ansible role to deploy scripting applications like PHP, Python, Ruby, etc. in a Capistrano style

Instalar
ansible-galaxy install ansistrano.deploy
Licencia
mit
Descargas
2.5M
Propietario
Deploying applications with Ansible in Capistrano style