TRABAJO FIN DE GRADO Título Mejores practicas de despliegue continuo para aplicaciones Symfony2 Autor/es Fernando Arconada Orostegui Director/es Eloy Javier Mata Sotés Facultad Facultad de Ciencias, Estudios Agroalimentarios e Informática Titulación Grado en Ingeniería Informática Departamento Curso Académico 2012-2013 Mejores practicas de despliegue continuo para aplicaciones Symfony2, trabajo fin de grado de Fernando Arconada Orostegui, dirigido por Eloy Javier Mata Sotés (publicado por la Universidad de La Rioja), se difunde bajo una Licencia Creative Commons Reconocimiento-NoComercial-SinObraDerivada 3.0 Unported. Permisos que vayan más allá de lo cubierto por esta licencia pueden solicitarse a los titulares del copyright. © © El autor Universidad de La Rioja, Servicio de Publicaciones, 2013 publicaciones.unirioja.es E-mail: publicaciones@unirioja.es Mejores prácticas de despliegue continuo para aplicaciones Symfony2 Fernando Arconada Orostegui 5 de junio de 2013 Índice general Índice general i Índice de guras iii 1 Resumen 1 2 Abstract 1 3 Introducción 4 DevOps 3.1. 3.2. 3.3. 3.4. 4.1. 4.2. 4.3. Antecedentes . . . . . . . . . . . Problema que se quiere solucionar Elementos a conseguir . . . . . . Flujos de trabajo habituales . . . . . . . . . . . . . . . . . . . . . . . Inculcar una cultura de DevOps . . . . . . Puntos a tener en cuenta para implantar DevOps . . . . . . . . . . . . . . . . . . . Cómo crear una cultura de DevOps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Entornos de producción, pruebas y desarrollo comparables 6.1. 6.2. 6.3. 6.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Diferentes entornos en Sistemas . . . . . . . Entorno de producción . . . . . . . . . . . . Gestión de máquinas virtuales con Vagrant . Entorno de pruebas . . . . . . . . . . . . . . i . . . . . . . . . . . . 6 . . . . . . . . . . de . . . . Infraestructura como código Ventajas . . . . . . . . . . Herramientas . . . . . . . Conguración con Puppet Plataforma de producción . . . . . . . . . . . . . . . . . con éxito una cultura . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5.1. 5.2. 5.3. 5.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 2 3 3 6 6 8 9 12 12 12 13 14 18 18 18 22 23 7 Sistema de control de versiones 24 8 Despliegue como proceso automático 28 9 Mantener la calidad del software 31 10 Monitorización 35 11 Plan B 36 12 Conclusiones 39 7.1. 7.2. 7.3. 8.1. 8.2. 8.3. 9.1. 9.2. 9.3. Modelo de Ramas para Git . . . . . . . . . . . . . . . . . . . . . . . Release Candidates . . . . . . . . . . . . . . . . . . . . . . . . . . . . Estado de commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Herramientas de despliegue . . . . . . . . . . . . . . . . . . . . . . . Alternativas a un script . . . . . . . . . . . . . . . . . . . . . . . . . Capifony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Qué es la integración continua . . . . . . . . . . . . . . . . . . . . . . Métricas y reglas de integración contínua . . . . . . . . . . . . . . . . Phing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1. Los datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2. Rollback con capifony . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3. Página de mantenimiento con capifony . . . . . . . . . . . . . . . . . Bibliografía 24 26 27 28 28 29 31 31 34 36 37 37 40 ii Índice de guras 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. Flujo de trabajo 1.0 (empresa ágil) . . . . . . . . . . . . . . . . . . . . Flujo de trabajo 2.0 (empresa burocrática) . . . . . . . . . . . . . . . . . Actitud normal ante los problemas . . . . . . . . . . . . . . . . . . . . . Actitud buena ante los problemas . . . . . . . . . . . . . . . . . . . . . . Áreas de Desarrollo y Sistemas . . . . . . . . . . . . . . . . . . . . . . . Áreas de aplicación de DevOps . . . . . . . . . . . . . . . . . . . . . . . AMIs de Ubuntu Cloud en Amazon AWS . . . . . . . . . . . . . . . . . Pasos para la creación del entorno de producción . . . . . . . . . . . . . Pasos para la creación del entorno de pruebas y desarrollo . . . . . . . . Modelo de ramas para Git propuesto por NVIE . . . . . . . . . . . . . . Detalle de ramas de develop y master . . . . . . . . . . . . . . . . . . . Ejemplo de gráco generado con PHP Depend . . . . . . . . . . . . . . Ejemplo de gráco de métricas en pirámide generado con PHP Depend . iii . . . . . . . . . . . . . . . . . . . . . . . . . . 4 6 7 7 9 10 15 21 24 25 26 33 33 1 Resumen Poner un proyecto de software en marcha comprende más cosas que desarrollar código. El software es algo vivo, normalmente una vez que se pone online o se entrega al cliente se sigue desarrollando y generando nuevas versiones. En la mayor parte de las empresas, la puesta en producción de una nueva versión es un momento crítico por las incertidumbres que genera. Dentro de las empresas de IT hay dos grupos marcados que creen tener objetivos distintos, desarrollo (dev) y sistemas (ops). Cambiar la mentalidad de estos grupos, automatizar todas las tareas posibles y establecer políticas de calidad y control claras son fundamentales para que el proceso de trasladar valor a los clientes (implementar cambios) sea un proceso frecuente, sencillo y seguro. 2 Abstract Start a software project is more than develop code. Software is alive. You will deploy new releases after run it online for rst time or deliver it to the customer. Most times every software deployment it's a critical moment cause of uncertainty generated. IT rms use to have two kind of teams, developers (devs) and operators (ops). Both think that they goals are dierent but this is not true. Change their minds, automate every task and establish a clear quality policy with checkpoints is something vital to increase the value perceived by our customers (in the end, implement change). So this will become a frequent task, easy and reliable. 1 3 Introducción 3.1. Antecedentes Hasta la actualidad, son muchas las empresas que se han dedicado a crear software sin una reexión seria sobre cómo poner y mantener ese software en producción. El software está vivo y en constante evolución, no sólamente porque esté extendido el modelo de difundir y parchear, sino porque el mercado exige evolucionar y aplicar cambios con celeridad. En los modelos de desarrollo de software se ha ido pasando de metodologías en las que se abordaba la totalidad de las funcionalidades del software en cascada a metodologías ágiles. El desarrollo ágil de software son métodos de ingeniería del software basados en el desarrollo iterativo e incremental, donde los requerimientos y soluciones evolucionan mediante la colaboración de grupos auto organizados y multidisciplinarios. Existen muchos métodos de desarrollo ágil; la mayoría minimiza riesgos desarrollando software en lapsos cortos. El software desarrollado en una unidad de tiempo es llamado una iteración, la cual suele durar de 1 a 4 semanas. Ha habido mucho debate respecto a cómo desarrollar software, pero sólo recientemente se está planteando cómo debemos poner el software en manos de los usuarios, tanto en la primera puesta en producción como en cada una de las iteraciones o parches ofrecidos. Además, los usuarios son cada vez más exigentes y más globales, y por lo tanto toleran peor los problemas de disponibilidad. El objetivo de este documento es ofrecer un ejemplo de modelo de despliegue de aplicaciones en el que se reexione sobre las que pueden ser las mejores prácticas para poner en producción de forma ágil las nuevas versiones de las aplicaciones. Como el modelo de despliegue está muy ligado a la tecnología que utiliza para desarrollar, en el presente trabajo n de grado nos vamos a centrar en aplicaciones desarrolladas con el framework de desarrollo PHP Symfony2. 3.2. Problema que se quiere solucionar El objetivo es encontrar una forma de trabajo en el desarrollo integral de software que no sea el caos típico de la empresa ágil ni tan lento como la empresa burocrática. Hay que encontrar un ujo integral que no implique cruzar los dedos cada vez que se hace un despliegue ni que tenga que pasar una semana para que los usuarios puedan usar las nuevas funcionalidades. Subir cambios a producción debe implicar una garantía de éxito. La infraestructura debe estar al servicio del software, debe ser fácilmente reproducible y gestionable. En el ciclo de vida del software hay 3 etapas claras: desarrollo, pruebas y producción. Desarrollo y producción son inevitables. El entono de pruebas es altamente recomendable. El objetivo de estos entornos es: Desarrollo: entorno donde el equipo de desarrollo crea el software, normalmente es su propio PC, que ellos suelen denominar local. Se suelen realizar muchos cambios al día, del orden de decenas o incluso cientos. También es común ver entornos de desarrollo compartidos entre miembros del equipo de desarrollo. Producción: es el entorno donde se ejecutan las aplicaciones y pueden usarlas los clientes nales. Normalmente este entorno suele ser, al menos, un servidor dedicado, si no un cluster, y lo normal es que tenga un sistema operativo diferente al entorno de desarrollo e incluso una arquitectura diferente. Debe permanecer estable. Suele estar gestionado por el equipo de Sistemas. 2 Pruebas: Se congura prácticamente igual al de producción y sirve para validar cambios e intentar reproducir los errores de producción. 3.3. Elementos a conseguir Para lograr el objetivo de establecer un ujo de despliegue continuo que mantenga la estabilidad del sistema nos proponemos implementar los siguientes elementos: Inculcar una cultura de DevOps, porque esto no se puede lograr sin la implicación de todas las partes, esto es, el departamento de desarrollo y el departamento de sistemas. Desplegar una infraestructura gestionable como código Tener entornos de producción, pruebas y desarrollo comparables para evitar la frase funciona en mi local Utilizar un sistema de control de versiones que sirva para gestionar las diferentes versiones del software tanto desde el punto de vista de desarrollo como de la infraestrutura Hacer que el despliegue de cada cambio sea un proceso automático Vericar que en el código se mantenga una calidad mínima a lo largo del tiempo Establecer un plan B de marcha atrás que se pueda ejecutar de manera automatizada o al menos claramente procedimental. 3.4. Flujos de trabajo habituales Podemos agrupar a las empresas de desarrollo de software en dos grupos según su forma de trabajo, que llamaremos Flujo de trabajo 1.0 y Flujo de trabajo 2.0 El Flujo 1.0 es el propio de empresas que acaban de empezar o de tamaño reducido, normalmente sin diferenciación de funciones. Este ujo se caracteriza por la agilidad y la desorganización formal. El Flujo 2.0 es la forma que han optado numerosas empresas según han ido creciendo para ser capaces de contener el caos. Se caracteriza por la separación de funciones y la burocratización de los procesos. En el presente trabajo intentaremos mostrar una nueva forma de trabajo basada en la entrega continua pero que no por ello cae en la desorganización ni la inestabilidad. Flujo de trabajo 1.0 (empresa ágil) Es el ujo de trabajo más ágil, y también el menos pensado. Se tiene una idea, se desarrolla y se despliega en producción. Suele ser lo habitual en empresas pequeñas y nuevas. El mayor problema es que según avanza el tiempo el caos se apodera de la empresa, aparecen conictos entre los compañeros de diferentes departamentos y empiezan a aparecer, cada vez con más frecuencia errores. Cuando se arregla algo, de repente comienza a fallar una funcionalidad que nadie había tocado hace meses. La infraestructura de servidores acaba tan personalizada que son verdaderas piezas únicas de artesanía informática. El código está escrito de tal manera y tan poco testeado que es difícil que alguien que no sea el autor pueda mantenerlo, suponiendo que no haya pasado mucho tiempo desde que lo escribió. Caracteísticas típicas de este ujo de trabajo son: No hay nada por escrito 3 controlar la complejidad, pero a cambio se ha sacricado la agilidad para hacer llegar los cambios a los usuarios. Las modicaciones sencillas pueden tardar semanas en aplicarse e implican tiempo de muchas personas. El Departamento de Infraestructuras o Sistemas es el responsable de mantener los ratios de disponibilidad de los servicios y por lo tanto no es proclive a realizar cambios. El Departamento de Desarrollo es el responsable de crear las nuevas funcionalidades demandadas por los usuarios lo antes posible y por lo tanto su trabajo es modicar los programas. La burocracia es el pegamento que hace que ambos departamentos se pongan de acuerdo. Estas empresas suelen tener, incluso por escrito, los formularios que debe cumplimentar una aplicación para que sea puesta en producción. Un ejemplo de este ciclo puede ser: En el entorno de desarrollo se despliega cada hora y se utiliza para que los cambios se aprueben internamente. En el entorno de pruebas se despliega una vez al día y se emplea como sistema de validación por parte del usuario nal, en el que verica la funcionalidad, usabilidad etc. En el entono de producción se despliega una vez a la semana, siempre el mismo día y a la misma hora. Siempre se hace el despliegue por una persona autorizada y se exige que el software haya pasado por los entornos de desarrollo y pruebas. Por lo tanto, los cambios tardan en llegar al usuario nal cerca de una semana. Si comparamos la simplicidad de la Figura 1 del Flujo 1.0 con la correspondiente al Flujo 2.0 vemos que ahora hay multitud de estaciones por las que ir pasando desde que tenemos una gran idea hasta que el desarrollo de esa idea llega al usuario nal. 5 4.2. Puntos a tener en cuenta para implantar con éxito una cultura de DevOps Difuminar la distinción entre programadores y administradores de sistemas. Si el programador no tiene experiencia en sistemas se produce el típico en mi local funciona, por ejemplo derivado de que en el servidor la fecha está en formato MM/DD/YYYY porque está congurado en inglés, que es diferente de su PC Windows 7 en español DD/MM/YYYY El procedimiento de paso a producción debe ser plenamente seguro. Hacer los despliegues en producción debe estar plenamente automatizado. Hacer despliegues a mano, aunque estén todos los pasos por escrito en un manual de procedimiento es la forma más fácil de cometer algún error. El código no debe tener bugs. Es prácticamente imposible que el software no tenga fallos, pero hay que incentivar que los programadores adopten técnicas de programación defensiva y apliquen continuamente test unitarios y funcionales. De esta forma, no sólo se evitan errores, sino que mejora la implicación entre los equipos de desarrollo y sistemas, ya que si estos últimos encuentran que cada vez el software es más conable tienen menos excusas para oponerse al cambio. Hay que empezar por el producto mínimo viable. Citando al padre de C++ un sistema grande y complejo que no ha evolucionado a partir de otro más pequeño no funciona y, además, es imposible arreglarlo para que funcione. Por lo tanto el software debemos concebirlo como pequeñas iteraciones frecuentes y trataremos de huir de las grandes versiones con muchos cambios. El diseño debe estar planicado para poder crecer en pequeños trozos. Si la programación se debe hacer en pequeños trozos el diseño debe ser pensado de forma ambiciosa. Un elemento fundamental para lograr este objetivo es reducir el acoplamiento para que determinadas piezas puedan ser fácilmente substituibles sin afectar a otras partes del programa. El sistema debe ser muy fácilmente auditable y depurable. Dado que se van a producir cambios continuamente, hay que poder trazar lo que está sucediendo en cada instante. En este punto el software libre juega un papel muy importante. El sistema debe tener resiliencia. El sistema debe poder funcionar aunque se degraden las condiciones del entorno. Debe existir una política de contención de daños. Antes de pasar nada a producción hay que intentar determinar qué es lo peor que podría pasar e intentar tener un plan B por si se produce el desastre. La organización debe aceptar que el software es algo vivo. Como tal, crece y evoluciona. Cada día aparecen nuevas funcionalidades, y, por contra, alguna que otra desaparece o deja de funcionar temporalmente. De igual forma los plazos se vuelven algo deslizante, el software no se desarrolla, evoluciona y, como todas las evoluciones, su velocidad de cambio y la dirección que tomen pueden ser muy variables en función del entorno. Debe existir un sistema de documentación continua. Si al software se le añaden cosas todos los días, llega un momento en el que evoluciona hasta un punto en el cual 8 Sistemas tiene información sobre el entorno de ejecución. Actualmente Sistemas esta inundado de información y agrega esta información como parte de su propio sistema de monitorización. Sin embargo si esta información no uye hacia Desarrollo se pierde una oportunidad de que éstos aprendan y mejoren. Área 3 Desarrollo solo aporta nuevas funcionalidades sin tener en cuenta otros objetivos no funcionales La idea de este área es integrar al equipo de Desarrollo dentro del equipo de Sistemas. No se trata sólo de juntar los equipos sino también los objetivos y las actividades de desarrollo dentro del área de sistemas. Desarrollo debe tener en cuenta no solamente objetivos funcionales sino que tienen que considerar otros objetivos como la estabilidad y la capacidad como parte del propio desarrollo. Práctica: Poner la estabilidad y la capacidad como objetivos de Desarrollo. Objetivos: Alinear objetivos Compartir incentivos Área 4 Mientras se desplegaba el software en producción por primera vez se descubre que van a ser necesarias nuevas máquinas para poder ejecutar el software. Hay que tratar de integrar al equipo de Sistemas dentro del departamento de Desarrollo. Ambos equipos deben trabajar juntos para conseguir la mejor solución posible. El equipo de Sistemas está sumamente implicado en el desarrollo consultando y aportando ideas a la solución a desarrollar, no apareciendo solamente cuando la funcionalidad ya está programada. Práctica: Sistemas aporta feedback sobre el diseño de la aplicación en desarrollo de manera temprana y frecuente. Objetivos: Desarrollo gana feedback sobre si la funcionalidad a desarrollar es factible. Compartir conocimiento. 11 5 Infraestructura como código Cometer un error es humano, propagar el error de forma automática a todos los servidores es #devops - DevOps Borat Muchas de las corrientes actuales tratan de automatizar todas las partes posibles del ciclo de desarrollo del software. En este sentido están entre otros los test unitarios o los sistemas de integración continua, sin embargo la parte de sistemas y servidores sigue siendo un trabajo artesanal. La infraestructura incluye todas las partes de la solución que no son desarrolladas como el software de la aplicación en sí, es decir, servidores web, sistemas operativos, tareas programadas, etc. El concepto de infraestructura como código se reere a intentar gestionar la infraestructura como si de un desarrollo software se tratase. Esto se hace posible de manera sencilla gracias a la virtualización y herramientas como Puppet, Chef o Cfengine. 5.1. Ventajas Las principales ventajas son: Se tiene un control de versiones de las conguraciones de forma que se pueda supervisar quien hace los cambios, qué cambios se han hecho y tener la posibilidad de volver atrás en un momento dado. Los entornos dejan de estar instalados manualmente para hacerlo mediante un proceso automático, de forma que se evitan errores y los sistemas son fácilmente reproducibles de manera able. Se mejora considerablemente la documentación y la auditoría, puesto que en el software de gestión de conguraciones y el control de versiones queda reejado el proceso de puesta en marcha y ejecución de los servidores. Se reduce los tiempos dedicados a la administración de sistemas, puesto que las labores de mantenimiento se hacen de forma automatizada y centralizada. 5.2. Herramientas Gestionar una infraestructura como código se puede hacer con diversas herramientas. Un sistema de control de versiones es indispensable, pero de eso no hablaremos ahora. Aunque esta gestión de sistemas se puede hacer con scripts es recomendable emplear utilidades como Puppet, Chef, Cfengine y otros. Puppet Puppet gestiona sistemas Linux/Unix y MS Windows de forma declarativa. El usuario describe los recursos del sistema y sus estados utilizando un DSL (Domain Specic Language) de Ruby en cheros de texto llamados manifests. Puppet descubre la información del sistema con una utilidad llamada Facter y junto con los manifests crea un catálogo que aplica a los sistemas de destino. El resultado de aplicar esta catálogo es reportado a un nodo central. Chef Es una herramienta de gestión de conguraciones escrita en Ruby y Erlang. Chef describe los recursos y estados de los sistemas en cheros llamados recetas. Los atributos de los 12 sistemas gestionados bajo Chef se reportan al servidor central que los indexa mediante Solr y junto las recetas genera la conguración a aplicar. Cfengine Cfengine es una herramienta muy madura de gestión de conguraciones, se basa en la Teoría Promesas de Mark Burgess. Esta herramienta facilita la automatización de sistemas y el mantenimiento de sistemas de computadores de gran escala, incluyendo la gestión de servidores, estaciones de trabajo, dispositivos de red y dispositivos móviles. Elección de herramienta Vamos a elegit Puppet por las siguientes razones. Descartamos Cfengine porque posteriormente vamos a usar Vagrant para el entorno de desarrollo y este sólo soporta Chef o Puppet, además el lenguaje de Cfengine es complicado y cuestan bastante depurar las promesas (lenguaje de Cfengine). El lenguaje DSL de Puppet resulta cómodo para los administradores de sistemas y el de Chef para los programadores de Ruby. Ambos tienen una buena documentación, aunque Puppet tiene más libros escritos. La comunidad alrededor de Puppet y Chef es grande y crece cada día si bien es cierto que al ser Puppet un producto más maduro tiene más comunidad y ésta es muy activa. Otro elemento clave a la hora de elegir Puppet ha sido la inversion de 30 millones de dolares en la herramienta por parte de VMware que es el lider en soluciones de virtualización y pensamos que esto puede marcar una diferencia para hacer que Puppet llegue al corazón de las empresas. Por otro lado, el entorno servidor de Chef es algo más complicado de desplegar que el entorno de Puppet. De todas formas estamos totalmente seguros de que es fácil que cada uno encuentre sus propias razones para hacer que se decante hacia una u otra herramienta sin que ello signique una limitación de sus posibilidades a la hora de desplegar una infraestructura como código. 5.3. Conguración con Puppet Como hemos comentado anteriormente, la conguración de una infraestructura con Puppet se realiza mediante un maniesto que describe los recursos del sistema y sus estados. A continuación podemos observar un ejemplo de un maniesto de Puppet para la conguración de un servidor LAMP (Linux Apache MySQL PHP) stage{bootstrap:} Stage[bootstrap]->Stage[main] node default{ class {'bootstrap': stage=> bootstrap} class {'apache': } class {'mysql': } class { 'mysql::server': config_hash => { 'root_password' => 'toor' } } mysql::db { `myapp': user => `dbuser', password => 'i6vbunhkj', 13 host grant => 'localhost', => ['all'], } class { 'mysql::backup': backupuser => 'backupuser', backuppassword => 'binyuvgmngv', backupdir => '/tmp/backups', } php::module { [ 'curl', 'gd', 'mcrypt', 'mysql' ]: notify => Class['php::fpm::service'], } # Add a pool definition php::fpm::pool { 'www':} } Este maniesto es muy sencillo y congurar un servidor de producción requeriría una cuantas líneas más. Nuestro ejemplo lo que hace es instalar Apache, instalar MySQL y crear una BD así como congurar una tarea de backup para la todas las BD, instalar PHP y los módulos requeridos. 5.4. Plataforma de producción Un aspecto importante a la hora de montar nuestra infraestructura como código es decidir cuál va a ser el entorno de producción donde se van a ejecutar las aplicaciones. Existen diferentes opciones, desde emplear servidores físicos hasta irnos a la nube con algún proveedor de IaaS (Infraestructura como Servicio), pasando por montar nuestra nube privada. La elección en este caso va a ser emplear la nube de Amazon AWS porque es un proveedor de IaaS consolidado que pueden emplear desde pequeñas startups a grandes empresas con unos costes contenidos y mucha exibilidad para la infraestructura. Otro punto en favor de Amazon AWS es que dispone de un API muy potente para gestionar todos aspectos de la infraestructura y esto está totalmente alineado con el concepto de infraestructura como código de forma que podemos manejarla desde scripts. Además Amazon AWS tiene una comunidad grande y hay una base de conocimiento considerable relacionada con sus servicios y diferentes arquitecturas que se pueden montar para cada situación. El presente trabajo está orientado a aplicaciones Symfony2 por lo tanto es evidente que el mejor entorno de ejecución para producción es un sistema LAMP. De los sistemas operativos Linux disponibles podemos optar por dos grandes familias Ubuntu y RedHat/CentOS. Para ambas familias existe soporte comercial y se ejecutan perfectamente en el IaaS elegido que es Amazon AWS. En cuanto al sistema operativo, RedHat lo descartamos porque no queremos pagar la licencia. Entre Ubuntu y CentOS nos decantamos por Ubuntu porque dispone de gran cantidad de software con versiones actualizadas, existe una versión de S.O. con soporte extendido y sobre todo, porque a diferencia de CentOS, provee de AMIs (las AMIs son las instancias de servidor de Amazon AWS) ociales con el S.O. instalado. Estas AMIs de Ubuntu vienen con Cloud-Init incorporado y Cloud-Init está soportado por Amazon AWS. Cloud-Init sirve para personalizar el servidor en el momento del arranque de forma que no sea necesario tener una copia propia personalizada o tener que hacer una personalización manual tras el arranque. 14 La versión concreta del S.O. elegido es Ubuntu 12.04 por ser la versión de soporte extendido (LTS) y elegimos una arquitectura de 64bit para ejecutar la instancia en el servidor más pequeño de Amazon y luego podemos escalar fácilmente a servidores más grandes. La instancia de servidor más pequeña a considerar es la M1.Small puesto que las microinstancias están más orientadas a desarrollo o ser ejecutadas como apoyo en ráfagas puntuales de carga. Instancia pequeña M1 Memoria de 1,7 GiB 1 unidad informática EC2 (1 núcleo virtual con 1 unidad informática EC2) 160 GB de almacenamiento de instancia Plataforma de 32 o de 64 bit Rendimiento de E/S: moderado Instancia optimizada para EBS disponible: No Nombre de API: m1.small Cómo inicializar el servidor de producción El servidor de producción se basa en una imagen Ubuntu 12.04 64 bit con almacenamiento instance.store. Elegimos este tipo de almacenamiento porque es más barato y sencillo de gestionar. Figura 7: AMIs de Ubuntu Cloud en Amazon AWS Las imagenes de Ubuntu Cloud para Amazon AWS se pueden encontrar en http://cloudimages.ubuntu.com/locator/ec2/. Estas imágenes de Ubuntu tienen una instalación mínima de software e incluyen cloud-init. Amazon AWS, desde su consola, permite especicar conguraciones de cloud-init para inicializar el servidor. Con cloud-init personalizamos la instancia del servidor en el momento del arranque sin tener que modicar el AMI en sí empleamos la siguiente conguracion: #cloud-config package_update: true package_upgrade: true package_reboot_if_required: true apt_preserve_sources_list: true packages: - git - puppet 15 ssh_pwauth: False ssh_authorized_keys: - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjMjVJph1xs 06c2MOPYQufgdfdaS6rsJLQ71DPnkxZVGMaH UOVSxbQ/7UBmMzASo61H7r1vJgr8rgdbtyebpc+04ig+G5QgHg8BlUl BzKs4b+xaeka17z/807l4CP4fYV5W4TyxN b7H/mI6zZQ/mNli75bAkvYSXWN2+7AZ8IGM+GTPo4Q WCJKT9i//uxNcgCu++e7haGMjyXKEM+6hQLnNC8F5Z5 BInlhY8zDfxzHVghvzIwNX3IvgvHoqIvCYJmMDgfpdR2F91g09 HtQ/Pr4m5CVD0q9CO0eaOr24Z6e5p9q8tBenOcX3d/0hy+d6TNq5QoZEk/f mis-keys disable_ec2_metadata: false disable_root: false locale: es_ES.UTF-8 timezone: Europe/Madrid resize_rootfs: True hostname: `server-01.myfirm.com' output: {all: '| tee -a /var/log/cloud-init-output.log'} runcmd: - [ sh, -c ,"cat >/root/.ssh/id_rsa <<EOF \n -----BEGIN RSA PRIVATE KEY-----\n MIIEpAIBAAKCrf5trddsdvdfIy8djSEGKDFf5SxPFGcykXXTuMSPCrfZdTtCN9vF\n a2Igke43GJ5hvIIFGWuuQn1ncfxha8e+j9h+lD3yZAdgEI4AzI8Yceqb/ItcJBnR\n Iilz2UeEDOaIbtTMXPVc5yeMVLYUFLzCHINPo8SKt2MRpr5AeIjjbAsu+0Bm0/sD\n u1Uvu4SgaxLZE/JqmwNAhnS2D88TOYh5N0dwL1yRbNeN0CoRiYCBjBr1q4oCLzVI\n +2cijaPM/mVTTfDM5QoBB+lBD+FZUl/lZdjU2u9FzUpVvSwffjBNg5cW12//4LkH\n X08D3cm8T2FACXHyillC44pNALwSHEWqzaaYqQIDAQABAoIBAGU0CCGEINutQw3e\n LDXfeTSFve+N9p08GF1AKnZvmCGTkA2zoobjEhaq+locrBPMktw3rkjcsQA37qFo\n OZ9QukrxnEWQrahYrVp7IsZVizdn9kopmY6NuXJhkEkQlqHi1Q1agqYqLYQCnslO\n lmlr3XbtFrC+fRfusSG8eUEuSp/PPr8aDsRVOnzzAp8woj/sXxVuIVcgoJVtyfwF\n 4MG8F6zyT54tSHx0E81XzduMgCqjxrsLNJnrtDQUq8yvHWYwYoEa4Utc8sN57Uo8\n oIPwx439w/qMo0D6Zqf/xQ6EzuLWpsZz0PAp/+ZEsYPZMFNCs+71jBgGMFe4vJFI\n NoBYu+gzoy0v6iM3Xi6S6NmnjcnHoJjCMD3+Ugai/2B7PbqUeZ45rHhCwyOnsA4R\n 6PtLn78yUQaNUJjqS80Bxa0CgYEAny+iqfyBlAmudrv4DLRWCloKwQiRufKLsFe6\n 3SxLGkrWFCqpasWq0UV/imnGZRm8hV6npJ1YomFReVM4/200oyIU4th6jaasFpl7\n MWikiibnGjvgtsjqzBPyJUp6/9d1Ts90kGlt4sBzgO+aUgX60KHjKYYyXsTKmzeb\n eehyC5ECgYAglD+xo6vU3SflOcM5CuCM7jfNumPGEj4wwb/U1Dq1k3akW7RfsWyw\n GL+oCCaXgym9QqP5d3zpfiZ4wxHB90nRq3txhOU4tZwjho/A/kG5IMULVInAvKd0\n 9XcRdzHP3d2ChO1yJztFgH8//vJ5OZP+lkLcNkdd3sbG0IvL4qTwzg==\n -----END RSA PRIVATE KEY-----\n EOF"] - [ sh, -c ,"echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABA AABAQDQx/TTz0gjLx2 NIQYoMV/lLE8UZzKRddO4xI8Kt9l1O0I328VrYiCR7jcYnmG8 ggUZa65CfWdx/GFrx76P2H6UPfJkB2AQjgDMjxhx6pv8i1wkGd EiKXPZR4QM5ohu1Mxc9VznJ4xUt hQUvMIcg0+jxIq3YxGmvkB4iONsCy77QGbT+wO7VS+7hKBrEtk 16 T8mqbA0CGdLYPzxM5iHk3R3AvXJFs143QKh GJgIGMGvWrigIvNUj7Z yKNo8z+ZVNN8MzlCgEH6UEP4VlSX+Vl2NTa70XNSlW9LB9+ME2Dlx bXb//guQdfTwPdybxPYUAJcfKKWULjik0AvBIcRar Nppip root@kyoki-01 > /root/.ssh/id_rsa.pub"] - chmod 600 /root/.ssh/id_rsa - sed 's/^ //' -i /root/.ssh/id_rsa - rm -Rf /etc/puppet.orig - mv /etc/puppet /etc/puppet.orig - ssh-keyscan -H bitbucket.org > /root/.ssh/known_hosts - git clone git@bitbucket.org:myuser/puppet-lamp.git /etc/puppet - puppet apply -v /etc/puppet/site.pp apt_sources: - source: deb http://apt.puppetlabs.com precise devel keyid: 4BD6EC30 El script hace lo siguiente: Actualiza los paquetes del sistema operativo Instala las fuentes de paquetes de puppetlabs Instala los paquetes de git y puppet Deshabilita la entrada SSH usando password Instala una clave pública autorizada para poder entrar como root vía SSH Establece la clave pública/privada del servidor a una concreta y conocida que está autorizada para acceder a un repositorio de git alojado en bitbucket.com Se conecta al servidor de bitbucket y se descarga un repositorio git que contiene maniestos de puppet Ejecuta los maniestos de puppet que se encargan de terminar de congurar el servidor 17 6 Entornos de producción, pruebas y desarrollo comparables Como ya hemos comentado antes, en ciclo de vida del software hay 3 etapas claras: desarrollo, pruebas y producción. Desarrollo y producción son inevitables. El entono de pruebas es altamente recomendable. 6.1. Diferentes entornos en Sistemas Dentro de lo que es la losofía de DevOps hay que acercar las prácticas de Desarrollo a los entornos de Sistemas y por lo tanto deberían haber entornos diferenciados de producción, pruebas y desarrollo que permitan probar las conguraciones de los servidores antes de aplicarlos en producción. 6.2. Entorno de producción Para el entorno de producción proponemos Amazon AWS. Podemos usar la consola web de Amazon o si queremos algo más automatizado podemos crear un script usando, por ejemplo, la librería boto de python. Este script podría encargarse de lanzar instancias de los servidores con nuestra conguración, parar instancias en ejecución y listar los servidores en ejecución #!/usr/bin/python __author__ = 'fernando' from string import Template import boto.ec2 import urllib import yaml import time import argparse import sys class InstanceMgr: def __init__(self): yamlData = yaml.load(file('./settings.yaml')) self.region = yamlData['aws']['region'] self.instance_type = yamlData['aws']['instance_type'] self.ami_id = yamlData['aws']['ami_id'] self.key_name = yamlData['aws']['key_name'] self.instance_tags = yamlData['aws']['instance_tags'] self.security_groups = yamlData['aws']['security_groups'] self.cloud_init_template_vars = yamlData['cloud-init']['template_vars'] self.cloud_init_template_url = yamlData['cloud-init']['template_url'] self.connection = boto.ec2.connect_to_region(self.region) def get_script(self, filename = None): if filename == None: 18 filename = self.cloud_init_template_url template = urllib.urlopen(filename).read() return Template(template).substitute( self.cloud_init_template_vars ) def launch(self): reservation = self.connection.run_instances( image_id = self.ami_id, instance_type = self.instance_type, key_name = self.key_name, security_groups = self.security_groups, user_data = self.get_script() ) instance = reservation.instances[0] self.connection.create_tags([instance.id], self.instance_tags) self.instance = instance return instance def find_instances_by_tag(self, tag, value): foundInstances = [] reservations = self.connection.get_all_instances(filters={'instance-state-name': 'running'}) for reservation in reservations: for instance in reservation.instances: if tag in instance.tags and instance.tags[tag] == value: foundInstances.append(instance) return foundInstances def find_all_running(self): foundInstances = [] reservations = self.connection.get_all_instances(filters={'instance-state-name': 'running'}) for reservation in reservations: for instance in reservation.instances: foundInstances.append(instance) return foundInstances def terminate(self, instances): for instance in instances: self.connection.terminate_instances(instance.id) if __name__ == '__main__': parser = argparse.ArgumentParser(description= 19 'Lanza una instancia de un proyecto en AWS EC2') group = parser.add_mutually_exclusive_group() group.add_argument( '--launch', help= 'lanza una instancia AWS EC2', action='store_true', default=False) group.add_argument( '--list-instances', help='listado de instancias activas', action='store_true', default=False) group.add_argument( '--delete-project', help='termina las instancias etiquetadas con un Project=projectid') args = parser.parse_args() if not args.launch and not args.delete_project and not args.list_instances: parser.print_help() instanceMgr = InstanceMgr() if args.launch: print instanceMgr.launch() while instanceMgr.instance.state == 'pending': instanceMgr.instance.update() print 'waiting for 30s until instance in ready state' time.sleep(30) print instanceMgr.instance.public_dns_name #print instanceMgr.get_script() sys.exit(0) if args.delete_project: print 'Borrando imagenes del projecto=' + args.delete_project instances = instanceMgr.find_instances_by_tag('Project', args.delete_project) instanceMgr.terminate(instances) for instance in instances: print 'Terminada -> ' + instance.id + ' ' + instance.public_dns_name sys.exit(0) if args.list_instances: instances = instanceMgr.find_all_running() for instance in instances: print instance.id + ' ' + instance.public_dns_name for tag in instance.tags: print "\t" + tag + '=' + instance.tags[tag] sys.exit(0) El script es muy sencillo pero nos puede simplicar la gestión de nuestros servidores en Amazon AWS. Como los datos de producción son sensibles, este script crea el script de cloud-init basándose en una plantilla y rellenándola con los datos de un chero settings.yaml en el que se pueden poner las passwords, claves ssh, etc 20 La instancia de EC2 lee la conguración de cloud-init que es muy sencilla y que básicamente le indica al servidor cómo recuperar los maniestos de Puppet de Bitbucket para proceder a autocongurarse. Una vez que se aplican los maniestos de Puppet (puede tardar de 5 a 10 minutos) el servidor de producción ya está listo para recibir la aplicación 6.3. Gestión de máquinas virtuales con Vagrant Vagrant es una herramienta que gestiona máquinas virtuales (VM) y aplica maniestos u otros cheros de conguración de sistemas en una computadora local. Con un simple chero de conguración (Vagrantle), Vagrant levanta y provisiona una máquina virtual nueva en minutos a partir de una plantilla. Las plantillas de máquinas virtuales (Boxes) se conguran mediante herramientas como Chef o Puppet. Además provee de otras utilidades como montar carpetas de la máquina virtual a directorios locales y mapeo de puertos entre la máquina virtual y el antrión. Las máquinas virtuales que gestiona Vagrant pueden estar en nuestro PC de escritorio con VirtualBox o VMware o en la nube de Amazon AWS. ¾Cómo ayuda Vagrant a trabajar en Sistemas? Concretamente en el apartado de infraestructura como código, permite desarrollar un ujo de trabajo donde probar las conguraciones antes de desplegarlas en producción. Se crean los entornos de desarrollo y pruebas para ser usados por los equipos de Sistemas. ¾Cómo ayuda Vagrant al DevOps? Vagrant permite que los desarrolladores se involucren más en Sistemas de forma que tengan un conocimiento de cómo se conguran los servidores. Además se elimina la frase de Funciona en mi local puesto que las máquinas virtuales que levanta Vagrant son iguales (o al menos están conguradas igual) que los servidores de producción. Y en cualquier momento se pueden destruir las máquinas virtuales y recrearlas desde cero limpias. Las máquinas virtuales de los diferentes entornos están conguradas igual porque se comparten los mismos maniestos de Puppet (en nuestro caso) y la misma plantilla base de máquina virtual para las Boxes de Vagrant que para los servidores de producción (en nuestro caso Ubuntu Cloud 12.04) Cómo crear la box de Vagrant Para crear la box de Vagrant (la máquina virtual) vamos a basarnos en la misma imagen de Ubuntu que empleamos para producción en Amazon AWS. En la página http://cloudimages.ubuntu.com/vagrant/precise/podemos encontrar dos cheros que contienen imágenes de boxes de Vagrant para 32 y 64 bits. Estas boxes tienen instalado cloud-init y Puppet. Para que el diagrama de arranque de la máquina virtual sea lo mas parecido posible al entorno de producción, vamos a seguir empleando cloud-init. Por ello necesitaremos que la box de Vagrant lea los cheros de conguración de cloud-init de nuestro PC, así que necesitaremos tener instalado un servidor web en nuestro ordenador que permita leer los cheros user-data y meta-data de cloud-init. Pasos a realizar: Creamos una VM usando el disco vmdk de la box de Vagrant a usar Arrancamos la VM, hacemos login como vagrant/vagrant y modicamos /etc/defaults/ grub para que añada en la línea de arranque ds=nocloud-net\;s=http://10.0.2.2/ 10.0.2.2 es la IP correspondiente a nuestro PC en el entorno de VirtualBox. Aplicamos los cambios de GRUB mediante el comando update-grub 22 Actualizamos las VirtualBox Guest Additions Eliminamos la entrada correspondiente a eth0 en el chero /etc/udev/rules.d/70persistent-net.rules Se apaga la VM y se clona el disco de la VM que acabamos de arrancar para reemplazar el disco VMDK de la box vboxmanage clonehd UUID-del-disco-de-la-vmdd9e05c92789 ./box.vmdk format VMDK Con esto obtenemos una box de Vagrant congurada para ser empleada en los equipos de desarrollo. Cómo usar la box de Vagrant Una vez que tenemos la box lista procederemos de la siguiente forma: Inicializamos un proyecto de Vagrant (vagrant init) Conguramos el Vagrantle a nuestro gusto Arrancamos la VM por primera vez (vagrant up). A este paso le cuesta un poco y al terminar tendremos una VM en nuestro PC congurada igual que el servidor de producción Probamos los cambios a la aplicación que estamos desarrollando dentro de la VM. Como el primer arranque de la VM tarda bastante (10 minutos) no haremos vagrant destroy todos los días si no queremos perder ese tiempo, y nos limitaremos a apagarla encenderla. Eso sí, al menos se debería destruir y recrear la VM una vez a la semana. Como Vagrant permite ejecutar maniestos de Puppet desde una carpeta del proyecto Vagrant, emplearemos esta posibilidad para hacer cambios propios de nuestro entorno y tenerlos separados de forma que podamos ofrecérselos a la gente de Sistemas para aplicarlos en producción. 6.4. Entorno de pruebas Como se ha comentado anteriormente, el entorno de pruebas es deseable que sea lo más parecido posible al entorno de producción y por ello emplearemos la misma imagen de Ubuntu Cloud pero empaquetada como una box de Vagrant en vez de como AMI de Amazon EC2. A esta imagen le aplicaremos los mismos maniestos de Puppet que se aplican en producción de forma que se conguren exactamente igual. Pasos para la creación del entorno El entorno de pruebas se lanza con Vagrant, pero Vagrant no tiene una forma de pasar metadatos a las instancias de la misma forma que tiene Amazon AWS EC2 para poder así darle a la máquina virtual una conguración de cloud-init. Para suplir esto se congura la box de Vagrant para que recoja la conguración de cloud-init ya generada de un servidor web (que puede estar en nuestro propio ordenador). Una vez que la máquina virtual tiene la conguración de cloud-init procede como en el entorno de producción, es decir, recoge de BitBucket los maniestos de Puppet necesarios y se autocongura según le indican éstos. 23 Figura 10: Modelo de ramas para Git propuesto por NVIE 25 Ramas principales Las ramas principales son master y develop. La rama master en el repositorio principal origin siempre contiene código que está listo para ponerse en producción. La rama develop reeja el estado actual de desarrollo entregado. Es decir con funcionalidades terminadas aunque todavía no aprobado para pasar a producción. El código de esta rama es el que normalmente se ejecuta en los servidores de pruebas. Cuando el código de develop esta listo para ser integrado en producción se hace un merge de develop a master. Figura 11: Detalle de ramas de develop y master Otras ramas axiliares Las ramas de feature representan una funcionalidad que está siendo desarrollada. Estas ramas se crean a partir del código de develop. El código de estas ramas se ejecuta normalmente en los entornos de desarrollo. Cuando se termina la funcionalidad, se hace un merge de esta rama a develop y se elimina la rama de feature. Las ramas de hotx se crean cuando se detecta un bug en producción y por lo tanto el origen de esta rama está en master. Una vez que se corrige el bug se hace un merge a producción para corregir el error y también se hace un merge hacia develop para que el bug no se arrastre. Una vez hechos los merges se borra la rama. La rama de release tiene el origen en develop y hace merge en master y develop. Su nalidad es corregir pequeños bugs y preparar el código para producción, por ejemplo cambiando los números de versión o escribiendo las RELEASES NOTES. 7.2. Release Candidates ¾Que es una Release Candidate? Un cambio en el código puede que sea o no una Release en si. Si miras el cambio y te preguntas ¾debería poner en en producción este cambio? La respuesta a esta pregunta no da sólo una pista. Es el proceso de build, despliegue y test que aplicamos a ese cambio el que valida si el cambio debe conformar una release o no. ** 26 Cada commit es una release potencial **, cada commit que un desarrolador hace sobre la base de código se entiende que añade valor al negocio de alguna forma y mejora el sistema en el que estamos trabajando. El trabajo de la integración continua es vericar cada release candidate para ver si puede encajar o no producción. No hay que olvidar que una mejora (feature) sólo está terminada cuando entrega valor al usuario, y esto sólo sucede cuando está en producción. 7.3. Estado de commit El estado de commit empieza con un cambio en el estado del proyecto, es decir, un commit en el sistema de control de versiones. Termina con un informe de fallo, o si tiene éxito, con una colección de artefactos y elementos desplegables para ser usados en los siguientes pasos del proceso de despliegue. Idealmente, el estado de estado de commit dura menos de cinco minutos y ciertamente menos de diez. El estado de commit representa, de más de una forma, la entrada en el ujo de despliegue. Funciona de la siguiente manera: alguien hace un cambio en el sistema de control de versiones, el servidor de integración continua detecta el cambio, se descarga el código fuente y ejecuta una serie de tareas que incluyen: Compilar, si es necesario, y empezar a ejecutar sobre el código fuente integrado. Crear binarios (o tar.gz, rpm, deb. . . ) que puedan ser desplegables Ejecutar algún tipo de análisis necesario para vericar la calidad y salud del código Crear otros artefactos necesarios como migraciones de base de datos o generar datos de test, etc Para los desarrolladores, el estado de commit es el ciclo de feedback más importante en el proceso de desarrollo. Los fallos en los tests que se ejecutan en este punto normalmente se atribuyen a 3 causas: errores de sintaxis, errores semánticos y errores con la conguración 27 8 Despliegue como proceso automático Desplegar el código en producción o pruebas, y quizás también en desarrollo, es algo que debe hacerse pulsando un botón (ejecutando un script). Hay que evitar los procesos manuales, estos conllevan tiempo y son propensos a errores por mucho que esos procesos se hayan repetido. 8.1. Herramientas de despliegue Existen diferentes herramientas que pueden ayudarnos a hacer el despliegue de manera automática: Ant: es una herramienta de construcción para aplicaciones Java con diferentes tipos de tareas que pueden incluir en el script, como por ejemplo hacer FTP o crear un zip. Phing: es el equivalente a Ant pero orientado a PHP. Es una herramienta muy completa ideal para ejecutar validación de métricas, tests unitarios y depliegues de aplicaciones PHP. Capifony: es una herramienta especíca de despliegue de aplicaciones Symfony. Esta herramienta tiene diversas utilidades orientadas al despliegue y es fácilmente extendible. Fable: La libería de python Fabric es posiblemente una de las mejores soluciones para la creación de scripts de despliegue con carácter general. Provee un conjunto de utilidades relacionadas con el proceso de despliegue de aplicaciones por SSH y que son la base para la creación de nuestros scripts. Nuestra elección es emplear Capifony porque es una utilidad especíca para el entorno en que nos centramos qué es Symfony2. Con Capifony podemos ejecutar comandos de Symfony2, hacer rollbacks sencillos de las versiones y crear un único script de despliegue que se adapte a los diferentes servidores de despliegue. Si decidiésemos invertir recursos para tener algo más personalizado o consideramos desplegar además otro tipo de software, Python Fabric sería la solución elegida. 8.2. Alternativas a un script Una alternativa a hacer el despliegue mediante una herramienta especíca, o un script, es sincronizar diferentes ramas del sistema de control de versiones en los servidores. Por ejemplo, podemos sincronizar la rama master con el directorio del servidor web correspondiente. También podemos hacer esto con la rama develop para el servidor de pruebas. Con las aplicaciones PHP es posible porque no es necesario compilar los fuentes ni crear un paquete como en otros lenguajes. Una ventaja de este sistema es que podemos poner una tarea programada que sincronice los fuentes en determinado horario y así establecer un calendario de despliegue separado para producción y desarrollo, y si hay commits nuevos en las ramas de develop o master se aplicarán en los servidores. El inconveniente de este sistema es que buscamos una exibilidad mas allá de aplicar nuestros cambios en un momento determinado, además muchas veces es necesario ejecutar tareas adicionales a copiar los fuentes, como por ejemplo hacer modicaciones en la base de datos. 28 8.3. Capifony Capifony es una extensión de Capistrano (herramienta de deploy de Ruby) que incorpora recetas especícas para Symfony. Hay otra extensión de Capistrano que vamos a incorporar y es Capistrano-ext concretamente para poder hacer un script multistage, es decir, que el mismo script nos sirva para producción (en Amazon EC2) y en pruebas (en una box de Vagrant) Script de deploy de capifony deploy.rb set :stages, %w(production staging) set :default_stage, "staging" set :stage_dir, "app/config" require 'capistrano/ext/multistage' set :branch, "develop" set :application, "TuitLawyer_application" set :deploy_to, "/var/www/tuitlawyer" set :app_path, "app" set :user, "root" set :scm, :git default_run_options[:shell]= '/bin/bash' set :deploy_via, :copy set :copy_strategy, :export set :model_manager, "doctrine" set :keep_releases, 5 set :use_sudo, false # Be more verbose by uncommenting the following line logger.level = Logger::MAX_LEVEL set set set set set set set :use_composer, true :shared_files, ["app/config/parameters.yml"] :shared_children, [app_path + "/logs", web_path + "/uploads", "vendor"] :writable_dirs, ["app/cache", "app/logs"] :webserver_user, "www-data" :permission_method, :acl :use_set_permissions, true before "symfony:cache:warmup", "symfony:doctrine:schema:update" after "deploy:setup", "setup:parameters_init" namespace :setup do desc "Copy default parameters file to remote server" task :parameters_init, :roles => :app do capifony_pretty_print "--> Copying parameters.yml.dist" run "if [[ ! -d #{deploy_to}/#{shared_dir}/app/config/ ]]; then mkdir -p #{deploy_to}/#{shared_dir}/app/config/; fi" 29 end top.upload "#{Dir.pwd}/app/config/parameters.yml.dist.#{stage}", "#{deploy_to}/#{shared_dir}/app/config/parameters.yml" end stagging.rb ssh_options[:port] = 2222 set :branch, "develop" set :repository, "/Users/fernando/tuitlawyer" set :update_vendors, false set :clear_controllers, false server 'sf.local', :app, :web, :primary => true production.rb ssh_options[:port] = 22 set :branch, "develop" set :repository, "https://farconada@bitbucket.org/farconada/tuitlawyer.git" set :update_vendors, false set :clear_controllers, true server 'ec2-107-20-125-86.compute-1.amazonaws.com', :app, :web, :primary => true ssh_options[:keys] = "/home/fernando/Dropbox/ec2-keys/id-mis-keys" 30 9 Mantener la calidad del software De nada sirve tener un sistema que permita actualizar rápidamente las versiones del software si, a medida que crece, vamos perdiendo conanza en que haga lo que debe hacer y sospechamos que está lleno de más errores. Al mismo tiempo que fomentamos la agilidad para poner en producción los cambios, hay que aumentar los procesos que revisan que nuestro software sigue siendo conable. Estos procesos se deben ejecutar frecuentemente (por ejemplo tras cada commit en el sistema de control de versiones) y, a ser posible, de manera automatizada. Vamos a controlar dos aspectos fundamentales: Las métricas y reglas de integración continua Los tests de software automáticos 9.1. Qué es la integración continua Una característica extraña pero común en muchos proyectos de software es que durante largos periodos de tiempo en el proceso de desarrollo la aplicación no se encuentra en un estado funcional. Normalmente durante el desarrollo la aplicación está rota. Esto es especialmente cierto en proyectos que usan ramas que duran mucho tiempo o que dejan los tests de aceptación para el nal. En cambio, hay otros proyectos que sólo durante unos minutos la aplicación permanece rota con los últimos cambios. La diferencia entre un tipo de proyecto y el otro es la integración continua. La integración continua requiere que cada vez que alguien haga un commit se construya la aplicación entera y se que se ejecuten una serie de tests automatizados. Es un cambio de paradigma. Sin integración continua el software está roto hasta que alguien pruebe que funciona. Con la integración continua el funcionamiento del software está comprobado en cada nuevo cambio. Para implementar la integración continua hacen falta tres cosas: Un sistema de control de versiones en el que se deposite todo lo relativo al proyecto. Un sistema automatizado de build para la aplicación y ejecutar los tests. La aceptación de los miembros del equipo, puesto que la integración continua es una práctica y no una herramienta. 9.2. Métricas y reglas de integración contínua En este aspecto vamos a emplear herramientas como phploc que permite obtener datos de métricas de software como éstos: phploc 1.7.4 by Sebastian Bergmann. Directories: Files: Lines of Code (LOC): Cyclomatic Complexity / Lines of Code: Comment Lines of Code (CLOC): Non-Comment Lines of Code (NCLOC): 31 14 54 5149 0.02 1464 3685 Namespaces: Interfaces: Traits: Classes: Abstract: Concrete: Average Class Length (NCLOC): Methods: Scope: Non-Static: Static: Visibility: Public: Non-Public: Average Method Length (NCLOC): Cyclomatic Complexity / Number of Methods: Anonymous Functions: Functions: Constants: Global constants: Class constants: 15 0 0 54 3 (5.56%) 51 (94.44%) 63 252 252 (100.00%) 0 (0.00%) 220 (87.30%) 32 (12.70%) 13 1.37 0 0 12 0 12 En importante jarnos en la complejidad ciclomática porque nos da una idea de lo difícil que puede ser crear pruebas automáticas de software. La complejidad ciclomática indica el número de caminos independientes dentro de un fragmento de código. La información relativa a CLOC (Comment Lines of Code) y NCLOC (NonComment Lines of Code) nos da una idea de lo documentado que está el código así como el tamaño neto de las clases y métodos que, en números reducidos, implica simplicidad para leer y entender cada método. Otras herramientas como PHP Depend también nos calculan métricas e incluso generan grácos como éstos Otra herramienta recomendable para integrar es PHPMD (PHP Mess Detector) . Esta herramienta trata de analizar el código y encontrar: Posibles bugs Código subóptimo (problemas de diseño, nomenclatura. . . ) Expresiones muy complicadas Parámetros, métodos y propiedades sin usar Además permite extender la herramienta con reglas propias. PHP CodeSnier una herramienta que debemos integrar para mantener las calidad del software, pero esta utilidad no genera métricas de software ni detecta posibles errores. Esta herramienta se encarga de vericar que se cumplen los estándares de codicación de código fuente de nuestra organización. Esto es fundamental porque el código fuente pasa por diversas manos con el tiempo, y manteniendo un estándar hace más fácil que pase de una manos a otra. Simplemente hay que pensar en algo tan sencillo como la nomenclatura de las variables, si se usa nomenclatura CamelCase o no, o la disposición de las llaves {} en las instrucciones de condición y bucle, etc. 32 9.3. Phing Phing es una herramienta de construcción de software equivalente a la utilidad ant de Java. Esta herramienta es muy útil para poder ejecutar todas las pruebas que verican las métricas y tests de forma automática en un solo comando. Junto con la ejecución de los tests, también se puede emplear phing para que ejecute otra serie de tareas que sería recomendable efectuar en cada commit (justo antes de cada commit), como por ejemplo: Eliminación de cheros temporales Minicación de cheros de CSS y JS Generar documentación de phpdoc Ejemplo de script build.xml de Phing para ejecutar test de PhpUnit <?xml version="1.0"?> <project name="BankAccount" basedir="." default="test"> <target name="test"> <phpunit haltonfailure="true" printsummary="true"> <batchtest> <fileset dir="."> <include name="*Test.php"/> </fileset> </batchtest> </phpunit> </target> </Project> 34 10 Monitorización Hay libros enteros dedicados a la monitorización y aquí no nos podemos extender en este tema. Simplemente comentar que, con independencia de la monitorización propia de los equipos de Sistemas, hay que establecer otros sistemas más orientados al negocio y la funcionalidad del software en sí para comprobar que todos los cambios que vamos haciendo tienen un impacto positivo en los usuarios y que no se nos ha escapado nada a los tests automatizados de software. Por esto es conveniente tener alertas de Google Analytics, vigilar que no se estén produciendo errores HTTP 500, comprobar los tiempos de respuesta, etc. 35 11 Plan B Todo despliegue debe tener un plan B, algo que hacer en caso de que algo salga mal. Aun con un proceso automatizado de despliegues es posible que algo falle, por ejemplo porque no hemos considerado los datos de producción y los datos que disponemos para probar no son equiparables. La primera idea que viene a la cabeza como plan B es volver a la versión anterior, esto es sencillo cuando lo único que se ha cambiado es el código y todo lo tenemos en el sistema de control de versiones. Pero aun con esta idea de que volver atrás un cambio que sólo afecte al código puede ser fácil, hay que tener alguna otra alternativa como por ejemplo tener preparada una página que avise de que la web se encuentra en mantenimiento y delimitar la ventana de trabajo que una vez superada debe disparar el proceso de restauración de backup. Siempre hay que poder hacer rollback de los cambios desplegados. Hacer debug de los problemas en un entorno de producción casi seguro terminará en largas noches, errores, desafortunadas consecuencias y usuarios enfadados. En los planes de rollback hay dos importantes obstáculos: Si la release desplegada cambia los datos puede ser difícil volver atrás. Si la release cambia más de un sistema entonces el proceso de rollback se complica. Hay dos principios generales que hay que seguir cuando creamos un plan para hacer rollback de una release desplegada: Asegurarnos de que el entorno de producción, incluyendo las bases de datos y los sistemas de cheros, tienen un backup antes de hacer el despliegue. Practicar el plan de rollback, incluyendo la restauración del backup y la migración de la base de datos. Bajo determinadas circunstancias, y teniendo en cuenta que tenemos todo automatizado, es posible que sea más sencillo volver a provisionar un entorno de producción nuevo. Hay un par de buenas razones para hacer esto: Si no tienes un proceso automatizado de rollback pero tienes un proceso automatizado de despliegue y provisión de entornos, entonces es más sencillo puesto que es una operación que tiene un tiempo jo y menos riesgos al ser automatizado. Es el mismo proceso que has probado cientos de veces antes y hacer rollback es bastante más infrecuente. 11.1. Los datos Una de las cosas más difíciles a la hora de hacer rollback es la parte relativa a los datos de la aplicación: las bases de datos. En las bases de datos no existe un sistema de control de versiones como en el código. Como mucho podemos mantener el esquema de los datos pero al irse insertando tuplas continuamente es muy difícil pasar de una versión a otra del esquema de base da datos. Un backup frecuente es importante pero corremos el riesgo de perder los datos generados entre los espacios de los ciclos de backup. Existen algunas soluciones que tratan de paliar los problemas de versionar las bases de datos: Dbdeploy 36 Liquidbase Doctrine migrations El funcionamiento de las tres herramientas es muy similar, las versiones de la base de datos se numeran y cada versión tiene dos scripts asociados (que creamos nosotros), uno para avanzar de versión y otro para retroceder. De esta forma podemos indicarle al programa a qué versión queremos ir. Si nuestra aplicación Symfony2 emplea el ORM Doctrine2 usaremos Doctrine migrations porque es simplemente incorporar una librería más de PHP y se integra dentro de los comandos de Symfony2. Si queremos una solución más genérica podemos emplear Liquidbase o dbdeploy. Con capifony podemos hacer un backup de la base de datos y descargárnosla a nuestro ordenador justo antes de actualizar el código , únicamente añadiendo una línea. before 'deploy:update_code', 'deploy:web:disable', 'database:dump:remote' 11.2. Rollback con capifony Hacer rollback (al menos del código) con capifony es un proceso automático. Capifony crea un sistema de enlaces y carpetas que hacen muy sencillo cambiar de una versión a otra, además tiene un comando especíco para hacer rollback, cap deploy:rollback lrwxrwxrwx 1 root root 43 abr 3 11:24 current -> /var/www/app/releases/20130403092221 drwxrwxr-x 4 root root 4096 abr 3 11:23 releases drwxrwxr-x 5 root root 4096 abr 2 22:03 shared -bash-3.2# ls -l releases/ total 8 drwxrwxr-x 6 gitandroid games 4096 abr 2 22:37 20130402203623 drwxrwxr-x 6 gitandroid games 4096 abr 3 11:23 20130403092221 Así, para capifony es tan sencillo hacer rollback como mover los enlaces simbólicos a la carpeta de la versión anterior. Además también tiene comandos que ejecutan acciones de Doctrine migrations que se podría ejecutar junto al rollback 11.3. Página de mantenimiento con capifony Con capifony podemos poner y quitar de una forma automática una página de mantenimiento. Para poner la página sólo tenemos que ejecutar cap deploy:web:disable REASON=hardware upgrade UNTIL="12pm Central Time y capifony creará la página. Con cap deploy:web:enable se elimina la página de mantenimiento. Para que la página de mantenimiento sea efectiva hay que crear unas reglas de rewrite de apache de forma que detecte si existe la página de mantenimiento y en caso de existir muestre ese contenido y un error HTTP 503. <VirtualHost *:80> ... RewriteEngine On 37 # Show maintenance page if it exists ErrorDocument 503 /system/maintenance.html RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$ RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f RewriteCond %{SCRIPT_FILENAME} !maintenance.html RewriteRule ^.*$ - [redirect=503,last] ... </VirtualHost> Si queremos que la página de mantenimiento se ponga automáticamente y se quite en cada deploy se puede hacer de forma automática simplemente incorporando un par de líneas al script de deploy. before 'deploy:update_code', 'deploy:web:disable' after 'deploy:restart', 'deploy:web:enable' 38 12 Conclusiones Para poder hacer despliegues de forma frecuente es necesario que los equipos de Desarrollo y Sistemas comprendan que están trabajando juntos para lograr el mismo objetivo. Alredor de la idea de que los equipos de Sistemas y Desarrollo trabajen de manera conjunta sin enfrentamiento se gesta la losofía de DevOps, que es recomendable implantar en la empresa para poder adaptarse a los cambios que requiere el mercado con agilidad. El código a desplegar debe ser conable y eso se tiene que poder vericar de forma automática. La infraestructura debe ser tan conable como el propio código evitando dudas de sistemas no comparables entre los entornos de desarrollo, pruebas y producción. Hay que automatizar todo lo que sea posible, los tests de código, la vericación de calidad el proceso de despliegue y la propia infraestructura. Hay una serie de herramientas software de reciente aparición que permiten de una forma sencilla automatizar procesos que hace unos años era imposible de automatizar especialmente en el área de sistemas y que permiten aplicar a la infraestructura los mismos modelos de desarrollo que en el software (desarrollo, pruebas, producción, control de versiones etc) Aún con todo, algo puede salir mal y por lo tanto siempre hay que tener un plan de marcha atrás y monitorizar el cambio de manera continuada. 39 Bibliografía [1] Phing. http://www.phing.info/. [2] phploc. https://github.com/sebastianbergmann/phploc. [3] P. Debois, Devops areas - codifying devops practices, May 2012. [4] J. Humble and D. Farley, Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation (Addison-Wesley Signature Series . Addison- Wesley Professional, 1 ed., Aug. 2010. [5] J. G. L. de Araujo, Que demonios es eso de devops (y porquedebería interesarme), Feb. 2011. [6] Scrum, Jan. 2013. Page Version ID: 63232584. [7] A continous delivery git branching model. http://nxvl.blogspot.com.es/2012/07/acontinous-delivery-git-branching-model.html. [8] capistrano. https://github.com/capistrano/capistrano. [9] M. Hüttermann, DevOps for Developers . Apress, 1 ed., Sept. 2012. [10] Vagrant. http://www.vagrantup.com/. [11] Using PHP_Code_Snier techPortal. http://techportal.inviqa.com/2009/10/12/usphp_code_snier/. [12] 2013.01.11 tech talk on git and continuous delivery | big nerd ranch. http://learn.bignerdranch.com/videos/2013-01-11-tech-talk-on-git-and-continuousdelivery. Continuous Integration: Improving Software Quality and Reducing Risk Pulling Strings with Puppet: Conguration Management Made Easy [13] P. M. Duvall, S. Matyas, and A. Glover, . Addison-Wesley Professional, 1 ed., June 2007. [14] J. Turnbull, . Apress, 1 ed., Feb. 2008. [15] capifony symfony and symfony2 deployment. http://capifony.org/. [16] G. Kim, K. Behr, and G. Spaord, and Helping Your Business Win The Phoenix Project: A Novel About IT, DevOps, . IT Revolution Press, Jan. 2013. [17] Chef | opscode. http://www.opscode.com/chef/. [18] VMware invests $30 million in puppet labs | puppet labs. https://puppetlabs.com/blog/vmware-invests-30-million-in-puppet-labs/. [19] P. Sturgeon, Puppet or chef ? | blog. http://philsturgeon.co.uk/blog/2012/10/puppetor-chef. [20] Software release life cycle, Mar. 2013. Page Version ID: 547341157. 40 [21] P. Swartout, Continuous delivery and DevOps: A Quickstart Guide . Packt Publishing, Nov. 2012. [22] Software deployment, Mar. 2013. Page Version ID: 540776027. [23] Conguration management software | open source conguration management - CFEngine - distributed conguration management. http://cfengine.com/. [24] Continuous delivery, Jan. 2013. Page Version ID: 531155254. [25] The buildmeister: ment process Automating with the phing, PHP dbdeploy deploy- and FTP. http://www.buildmeister.com/articles/automating_the_php_deployment_process_with_phing,_dbdeploy_a [26] Vagrant. http://www.vagrantup.com/. [27] Manual :: PHP_CodeSnier. http://pear.php.net/manual/en/package.php.php- codesnier.php. [28] J. Loope, Managing Infrastructure with Puppet . O'Reilly Media, Inc., June 2011. [29] M. Pichler, PHPmd. http://phpmd.org/, Dec. 2012. [30] P. Swartout, Continuous Delivery and DevOps: A Quickstart guide . Packt Publishing, Nov. 2012. [31] Capistrano, Mar. 2013. Page Version ID: 541197302. [32] E. Zygmuntowicz, B. Tate, and C. Begin, Guide Deploying Rails Applications: A Step-By-Step . Pragmatic Bookshelf, 1 ed., May 2008. [33] Fabric fabric 1.6 documentation. http://docs.fable.org/en/1.6/. [34] M. Zandstra, Continuous integration, in PHP Objects, Patterns, and Practice , pp. 427450, Apress, Jan. 2010. [35] Puppet vs. chef - the battle wages on. https://www.scriptrock.com/articles/puppetvs-chef-battle-wages/. [36] Development environment (software development process) - wikipedia, the free encyclopedia. http://en.wikipedia.org/wiki/Development_environment_(software_development_process). Deploying Rails: Automate, Deploy, Scale, Maintain, and Sleep at Night Deploying with JRuby: Deliver Scalable Web Apps using the JVM [37] T. Copeland and A. Burns, . Pragmatic Bookshelf, July 2012. [38] J. Kutner, . Pragmatic Bookshelf, July 2012. [39] M. Hüttermann, Infrastructure as code, in DevOps for Developers , pp. 135156, Apress, Jan. 2012. [40] A successful git branching model nvie.com. http://nvie.com/posts/a-successful-gitbranching-model/. 41 [41] Behat BDD for PHP. http://behat.org/. [42] The PHP quality assurance toolchain. http://phpqatools.org/. [43] enterprise - architecture programmers. - dev vs stage environment vs prod environment http://programmers.stackexchange.com/questions/117945/dev-vs- stage-environment-vs-prod-environment. [44] The problem with separating data from puppet code | puppet labs. https://puppetlabs.com/blog/the-problem-with-separating-data-from-puppet-code/. [45] Metodología de desarrollo de software, Jan. 2013. Page Version ID: 61865375. [46] dvdalvarez, DevOps. integre las operaciones para una entrega continua, Nov. 2012. [47] Apache ant - welcome. http://ant.apache.org/. 42