sábado, 7 de abril de 2012

Desplegar Symfony2 con Capifony

Seguro que a más de uno con tan solo escuchar la palabra desplegar se le pondrán los pelos de punta. Si hay algo importante en las metodologías ágiles es el famoso DRY (don't repeat yourself), ¿porque romper con ese enfoque para desplegar? no tiene ningún sentido desplegar a la vieja usanza, lidiando con ver que ficheros subir a producción, cuales no y empezar a subir los ficheros mediante FTP o rsync. Para evitarnos esos dolores de cabeza es donde Capifony entra en juego. Capifony nos permitirá desplegar nuestro proyecto con tan solo un comando, tan sencillo como cap deploy. Aunque a ese estado no se llega por arte de magia, primero habrá que realizar ciertas instalaciones y configuraciones, así que vamos a ello.


Salvo que se indique lo contrario las instrucciones deben ser ejecutadas DESDE donde se desplegara la aplicación.

Instalación de Capifony
Para instalar Capifony debemos tener instalado RubyGems y usaremos capistrano_rsync_with_remote_cache para poder desplegar con mayor rapidez, ya que solo se subir los cambios realizados y no todo el respositorio.
 sudo apt-get install rubygems  
 sudo gem capifony  
 sudo gem capistrano_rsync_with_remote_cache  


Configuración del entorno
Lo más cómodo (y seguro) es usar ssh con nuestra clave publica, en lugar de introducir el password cada vez que vayamos a desplegar. En caso de no tener una clave RSA generada, crearla es tan fácil como seguir los pasos del asistente que se nos muestra al ejecutar:
 ssh-keygen  

Copiar la clave publica al SERVIDOR.
Personalmente tuve algunos problemas al conectarme y tratar de copiar mi calve publica desde el terminal, así que tuve que copiarlo mediante cPanel. Hay que copiar el fichero ~/.ssh/id_rsa.pub (nuestra maquina) a ~/.ssh/authorized_keys (servidor)


Para probar si la clave quedo bien instalada, hay que ejecutar:
 ssh usuario@ip -p puerto  


Si en nuestro servidor conviven PHP 5.2 y 5.3, y en el PATH solo se encuentra la versión 5.2, debemos agregar 5.3 al PATH, sino tendremos problemas (ver la ultima sección del post). Para hacerlo tenemos que modificar el fichero (en el servidor) ~/.bashrc y añadir la siguiente linea:
 PATH=/path/to/php5.3:$PATH   

Cerramos la sesión y listo.

Configuración del proyecto
El proyecto se puede desplegar de dos maneras: obteniendo el código desde desarrollo o desde un repositorio. Se detallara como hacerlo desde un repositorio externo, para ver como hacerlo desde desarrollo consultar la documentación de Capifony.

Para crear los ficheros necesarios de capifony basta con ejecutar.
 cd /path/to/project  
 capifony .  

Esto nos creara dos archivos:

  • Capfile: se encuentra en la raiz del proyecto, es el ejecutable que indica que estamos trabajando con Symfony.
  • Deploy.rb: este fichero se encuentra en app/config/ y se usara para configurar Capifony.
Ahora hay que editar Deploy.rb para configurar Capifony, vayamos por partes.

En esta sección se define lo que es la aplicación en si básicamente el nombre y donde se despliega.
 #Application 

 set :application, "acme" 
 set :domain,   "#{application}.com" 
 set :deploy_to,  "/path/to/project" 
 set :app_path,  "app" 
 set :keep_releases, 3 

 role :web,    domain 
 role :app,    domain 
 role :db,     domain, :primary => true 

 set :model_manager, "doctrine"  

La mayoría de los parámetros dejan bien claro que es lo que hacen, aunque algunas aclaraciones no estarían de mas:

keep_releases
es la cantidad de versiones (las mas nuevas) que se dejaran en el servidor al ejecutar cap deploy:cleanup, es decir si desplegamos 10 veces, tendremos 10 versiones en la carpeta releases, al realizar el clean up se eliminar las versiones mas viejas dejando únicamente las mas nuevas, en este caso tres. 

role
si estamos desplegando solo a una maquina, hay que dejarlo tal cual esta, pero si necesitamos desplegar en varias al mismo tiempo, deberá ser algo así:
 role :web, "subdom1.domino.com", "subdom2.dominio.com", "subdom3.dominio.com" 
 role :app, "subdom1.dominio.com", "subdom2.dominio.com", "subdom3.dominio.com" 
 role :db, "db.dominio.com"  

La configuración al repositorio es muy simple,  ssh_options[:forward_agent] = true le indica a capifony que estamos usando ssh para acceder al repositorio, y que la clave se encuentra en nuestra maquina, sino habría que indicar el usuario y el password.
 #Repository  
 set :repository, "url"  
 set :scm,     :git  
 set :deploy_via, :rsync_with_remote_cache  
 set :git_enable_submodules, 1  
 ssh_options[:forward_agent] = true  

Los ficheros compartidos (shared), como lo indica su nombre son compartidos, compartidos entre versiones, por lo que se mantienen de versión a versión. En las ultimas dos opciones se indica que cada vez que se despliegue se actualicen los vendors y se instalen los assets (imágenes, css, etc que se encuentran dentro de un Bundle).
 #Symfony2  
 set :shared_files,   ["app/config/parameters.ini"]  
 set :shared_children,   [app_path + "/logs", web_path + "/uploads", "vendor"]  
 set :update_vendors, true  
 set :dump_assetic_assets, true  

Finalmente se configura el acceso al servidor, si trabajamos en un servidor compartido es IMPORTANTISIMO, indicarle a Capifony que no puede usar sudo, ya que generara problemas de acceso, porque no tendremos privilegios de administrador.
 #Server connection  
 set :user, "user"  
 ssh_options[:port] = "port"  
 set :use_sudo, false  

Despliegue
Finalmente tenemos todo listo para hacer el despliegue.

La primera vez hay que crear la estructura para Capifony. Así que desde la carpeta de nuestro proyecto ejecutamos
 cap deploy:setup  

El cual creara una estructura como la siguiente:
 `-- /path/to/project  
  |-- current   
  |-- releases  
  `-- shared  
   |-- log  
   |-- config  
   `-- web  

Cada vez que despleguemos se creara una carpeta en releases con el timestamp actual, y se enlazara current con la ultima versión.

Un fichero fundamental es app/config/parameters.ini, antes de ejecutar el primer despliegue, tendremos que copiarlo a shared/app/config/parameters.ini


 scp -P puerto app/config/parameters.ini user@domain:/path/to/project/acme/shared/app/config/parameters.ini  

¡Ahora si estamos listos para desplegar!
 cap deploy  

Si, todo fue bien (esperemos que si) nuestra aplicación ya esta desplegada en producción. Y por si fuera poco Capifony nos permite acceder a la consola de Symfony directamente desde nuestra maquina, para ver todos los comando disponibles ejecutar cap -T

Posibles problemas
Sin acceso root
Leé nuevamente la sección Configuración  del proyecto ;)

No such file or directory
Este error me surgio en dos ocasiones, una haciendo referencia a una carpeta de un Bundle y otra a web/images/ y web/js/.

En el primer caso esto pasa cuando en el fichero deps en la URL de algún bundle tenemos  git=git://... en lugar de  git=http://... 

En el otro caso, capifony busca las carpetas web/images/ y web/js/ por lo que al no encontrarlas lanza el error, para solucionarlo hay que crear la carpeta en cuestión en nuestro proyecto con un fichero vacio (para que la detecte git) y agregarla al repositorio.

Parse error: syntax error
En concreto este error me dio bastante problemas, después de muchas horas pude dar con la fuente del problema. El error se genera al indicar manualmente (porque el default es 5.2) el path para el ejecutable de PHP 5.3 para reinstalar los vendors, cuando se terminan de actualizar se actualiza el bootstrap, y en ese momento es cuando se genera el error. Ya que la construcción del bootstrap se hace con el ejecutable de PHP que se encuentra en el path.

Para solucionarlo hay que agregar al PATH la ruta donde se encuentra PHP 5.3. En caso de no poder modificar el path por algún motivo en concreto habría que modificar el shebang de los ficheros que están dando problemas, indicando manualmente la ruta de PHP 5.3, esto ultimo no es muy aconsejable, pero bueno nos puede sacar de un apuro.

2 comentarios:

  1. Muy buena entrada. Muchas gracias por el tutorial. Comentar que en mi caso, el servidor compartido tiene disponibles distintas versiones de php (php , php53 y php54). En mi caso, una solución ha sido añadir en el fichero deploy.rb la entrada:

    set :php_bin, "php53"

    ResponderEliminar
    Respuestas
    1. En teoría si, con eso debería funcionar. En un principio use esa opción pero al momento de desplegar me daba un error (el ultimo error que comento en el post). El caso es el siguiente si tenes un A que llama internamente a un script B, el script B va a tomar el ejecutable de php que este definido en el PATH ignorando el ejecutable que origino la llamada (php53)

      No se si se cumple siempre o no, pero al menos ese fue mi caso.

      Eliminar