Proyecto trunco: Devs No Ops

La semana pasada cerré un proyecto que había comenzado a mediados de julio. Quien me contrató fue el gerente del área de desarrollo y la idea era ayudar a mejorar el proceso de delivery implementando prácticas de lo que comúnmente se denomina «DevOps» (automatización de pruebas, builds, deploys, etc, etc).

Propuse hace un primer piloto (formalizado en la contratación de un paquete de horas) trabajando con uno de los equipos de desarrollo para entender la dinámica de software delivery de la organización. El panorama parecía prometedor pues detecté muchas oportunidades de mejora tanto en el área de desarrollo como en operaciones y por ello decidimos hacer un acuerdo de trabajo hasta fin de año.

Ya desde el comienzo el trabajo con el área de operaciones no parecía fácil, bastante resistencia y excusas diversas amparadas en regulaciones del sector. Sin embargo, confiaba en poder «subirlos al barco», pues ya he lidiado con escenarios de este tipo en varias ocasiones.

Lamentablemente, no logramos que operaciones «se suba al barco». Más aún, en un punto es como que decidieron patear en contra, negando permisos, dilatando decisiones e ignorando pedidos. Nunca me había pasado algo así. Con lo cual logramos implementar algunas mejoras con los equipos de desarrollo (versionado, análisis estático de código, automatización del build, etc, etc) pero fue muy poco lo que pudimos avanzar más allá de las prácticas que los developers realizan en sus máquinas de desarrollo. Obviamente que tampoco pudimos avanzar en las cuestiones de colaboración dev<->ops que se plantean dentro del paradigma DevOps. Es por esto que un proyecto que inicialmente estaba pensado para durar hasta diciembre, decidimos cortarlo en octubre, el contexto organizacional no estaba listo aún para encarar una transformación de este tipo.

Como siempre, intento ver el vaso medio lleno y por eso incluso en este caso que bien podría interpretarse como un proyecto fallido creo que hay lecciones aprendidas, las dos más importante que me llevo en este caso son:

  • Reafirmo que si el área de «Ops» no compra, es muy poco probable llegar a buen puerto. A partir de esto, yo debería asegurarme que la gente de Ops esté involucrada desde el momento cero (incluso antes de confirmar la contratación).
  • Si la organización no se percibe como una empresa de tecnología, tal vez sea mejor no meterme, mi forma de trabajo no calza bien en esos casos. Como me dijo una vez mi colega Sergio Villagra, hay proyectos que mejor no hacerlos 🙂

Automatización de pruebas: the wrong way

Si, lo sé, el título es un poco marketinero y bastante incorrecto como suele pasar con afirmaciones tan extremas.

La cuestión es que hoy en día muchas organizaciones/equipos con la intención de abrazar los beneficios de Agile/DevOps descubren que es necesario tener pruebas automatizadas. Hasta ahí vamos bien.

La cuestión se empieza a torcer cuando para automatizar la pruebas ponen gente exclusivamente para hacer eso mientras que los desarrolladores siguen trabajando sin alteración alguna en su dinámica. ¡ooops! No es por ahí.

Es cierto que es necesario tener pruebas automatizadas y que en general sin importar cómo se hagan, es mejor que nada. Pero la teoría y la evidencia indican que en un contexto Agile/DevOps la automatización de pruebas requiere del involucramiento activo de los desarrolladores. Los desarrolladores deben hacer su parte automatizando las pruebas unitarias y luego trabajando de forma cercana con usuarios y testers en las pruebas de aceptación. Podrá después haber algún trabajo adicional de automatización de pruebas para generar un set de regresión, pero si lo desarrolladores hacen lo que acabo de mencionar, entonces este esfuerzo adicional agrega poco valor.

Claro que esto no es trivial, nunca dije que lo fuera. Se requiere de algunas habilidades (duras y blandas) adicionales respecto del enfoque tradicional de desarrollo y testing. Para poder escribir pruebas unitarias necesitamos saber hacerlo pero más allá de eso necesitamos que la arquitectura/código de nuestra aplicación nos lo permitan. También se requieren de algunas habilidades de comunicación/colaboración para que desarrolladores y testers trabajen más cerca y de forma temprana.

Algunas de estas cuestiones las menciono en esta entrevista del año pasado y también la trato con cierta profundidad en mi Taller de Prácticas Técnicas.

Manejo de información sensible con git-secret

Es habitual cuando hacemos aplicaciones tener que lidiar con información sensible como ser contraseñas y tokens de acceso. A esta información sensible se le suele llamar secretos. En la actualidad la práctica habitual es almacenar estos secrets en herramientas creadas específicamente para ello y que suelen llamarse Administradores de Secretos (Secret Managers) o Bóvedas.

Algunas nubes proveen soluciones propietarias para esta cuestión como es el caso de AWS Secret Manager y Azure Key Vault pero también existen soluciones open source como Vault que pueden instalarse on-prem. Usando estas herramientas uno puede guardar en ellas su información sensible (secretos) y luego conectar al administrador de secretos con los ambientes donde quiere usar dicha información.

Pero también existen un conjunto de herramientas que posibilitan guardar secretos en repositorios Git, una práctica no muy recomendada. Estas soluciones son extensiones y/o complementos de Git y lejos están de compararse funcionalmente con los administradores de secretos, pero para proyectos personales, chicos o no críticos, son una opción completamente válida. Básicamente estas soluciones toman los archivos con la información sensible y le aplican algún tipo de encriptación de forma tal que quedan almacenados en Git pero estando encriptados. Soluciones de este tipo son git-crypt y git-secret. Es precisamente esta última herramienta la que vengo usando. A continuación voy a compartir de forma resumida como funciona.

La herramienta funciona como una extensión de Git y en conjunto con GPG, o sea: necesitamos instalar GPG (dependiendo del sistema operativo puede que ya venga instalado) y git-secret (las instrucción de instalación de git-secret son muy simples y están publicadas en su página oficial).

Una vez que tenemos la herramienta instalada podemos ir al repositorio git donde queremos agregar información sensible y lo primero que debemos hacer correr el comando git secret init para indicarle a git-secret que queremos guardar información sensible en el repositorio en cuestión. Este comando va a crear un directorio .gitsecret que será utilizado por git-secret.

A continuación vamos a indicarle a git-secret nuestro mail (asociado a nuestra clave pública gpg) para que podamos tener acceso a la información sensible que guardemos en el repositorio. Esto lo hacemos con el comando git secret tell nicopaez@mimail.com.

A esta altura ya estamos en condiciones de comenzar a agregar información sensible. Supongamos entonces que tenemos un archivo con información sensible llamado secretos.txt y entonces queremos indicarle a git-secret que queremos que maneje este archivo para lo cual ejecutamos git secret add secretos.txt. Un efecto de este comando es que el archivo secretos.txt, que tiene la información sensible sin encriptar ha sido agregado al gitignore porque precisamente no queremos que git lo versione.

El siguiente paso es «ocultar» (encriptar) la información sensible ejecutando git secret hide. Este comando generará un archivo encriptado por cada uno de los archivos que hayamos agregado previamente como secretos. En este caso, se habrá generado un archivo secretos.txt.secret que contendrá la información encriptada del archivo secretos.txt. y que podrá ser agregado sin problemas al repositorio git (es seguro hacerlo porque su contenido está encriptado). El último paso es hacer git add y commit con el archivo secretos.txt.secret.

Resumiendo como queda la historieta:

  • el archivo secretos.txt.secret contiene nuestra información sensible encritada y está versionado en nuestro repositorio git. Toda persona con acceso al repositorio podrá acceder al archivo, pero al estar este encriptado, no podrán ver si contenido.
  • el archivo secretos.txt, no está versionado por git y solo existe localmente en mi máquina para poder ser utilizado por mi aplicación.
  • Si yo me clonara otra vez el repositorio, no tendría el archivo secretos.txt (pues no está versionado en el repositorio) sino que tendría que generarlo a partir de ejecutar git secret reveal. Este comando toma los archivos encriptados (en este caso secretos.txt.secret) y los desencripta. Obviamente para poder hacer esto, el usuario en cuestión tiene que tener permisos para acceder a la información sensible (esto se hace con el comando git secret tell que ya ejecutamos previamente y que a continuación explicaré con mayor detalle.

Hasta aquí tenemos la configuración inicial, con esto de ahora en más cada nuevo archivo con información sensible (o cada modificación) deberemos ejecutar git secret add y git secret hide y a continuación el usual git add y git commit.

Una última cuestión es cómo compartir esta información sensible con otros miembros del equipo. Para esto vamos a necesitar la clave pública gpg de la persona a quien querramos dar acceso (esa persona deberá exportar su clave haciendo gpg –armor –export your.email@address.com > clave_publica_de_un_colega.gpg). Dicha clave será típicamente un archivo que en primer lugar tendremos que importar con gpg haciendo gpg import clave_publica_de_un_colega.gpg. Teniendo la clave importada podremos dar acceso a la persona en cuestión ejecutando git secret tell colega@email.com. Finalmente el último paso, que es fundamental, es reencriptar todos los secrets, para esto primero haremos un git secret reveal y a continuación un git secret hide -d.

Bien, esto es todo. Si deciden utilizar la herramienta para algún proyecto (más allá de probar lo que describí en este artículo) les recomiendo ver la documentación oficial de la herramienta.

Testing sin Testers, una mirada distinta al control de calidad

Tradicionalmente la actividad de control de calidad, usualmente denominada testing, ha sido llevada a cabo por personal con el rol específico de tester. Asimismo es una actividad que en muchos contextos se realiza de forma manual e incluso en algunas organizaciones de una forma no sistematizada y completamente ad-hoc.

Si bien las iniciativas Agile/DevOps han propuesto algunos cambios significativos en la forma de realizar el testing, muchas organizaciones siguen sufriendo estragos con el control de calidad en parte por seguir estrategias contrarias en esencia a lo propuesto por estas iniciativas.

Es por esto que mañana, jueves 16 a las 11:30 hs., estaré disertando sobre este tema en el contexto de las Jornadas de Calidad de Software y Agilidad organizadas por la Universidad Nacional de Misiones y que se realizarán en formato mixto presencial-online. Los interesados pueden registrarse aquí.

En mi disertación repasaré los puntos centrales del movimiento Agile/DevOps en lo referente al testing, compartiré varias experiencias de implementación que me han resultado efectivas y daré algunos consejos para quienes quieran aventurarse al cambio.

Role Smell: Test Automation Engineer

Este es un rol viene ganando cada vez más popularidad desde el auge de DevOps. Ocurre que en muchas organizaciones el testing lo realiza gente que no sabe programar y por ende no puede automatizar los tests que realizan (en realidad es posible utilizar herramientas del tipo record & play, pero en general esa estrategia tiene muchas limitaciones).

En un par ocasiones trabajé con equipos donde había gente en este rol y sinceramente no me pareció una buena idea. En todos los casos la dinámica de trabajo era:

  • en primera instancia un tester (o varios) definía los casos de prueba y los ejecutaba manualmente
  • luego, generalmente en la siguiente iteración, un automatizador (test automation engineer) se encargaba de automatizar algunos de los casos de prueba previamente definidos

De entrada esto me hizo recordar a la época en que una persona hablaba con el usuario (analista), escribía un documento de casos de uso y luego otra persona (desarrollador) leía el documento y programaba la funcionalidad. Si bien aún hoy hay equipos trabajando de esta forma creo que son minoría. Por algo será.

Otra cuestión que me resultaba poderosamente llamativa era la desconexión total entre estos automatizadores y los desarrolladores.

También ocurría que si en una determinada iteración no había trabajo de automatización, esos automatizadores, a pesar de saber programar, no se ponían a programar la aplicación.

Finalmente lo que para mí resultaba más perjudicial: toda la automatización de pruebas quedaba en manos de estos automatizadores, o sea: los desarrolladores no escribían ningún tipo de prueba automatizada, ni siquiera unitarias. Es bien sabido que para tener un buen flujo de entrega continua es necesario tener pruebas automatizadas, pero también es necesario que parte de ese trabajo de automatización lo hagan los propios desarrolladores comenzando por las pruebas unitarias.

En fin, no digo que tener un rol de Test Automation Engineering sea algo malo ni incorrecto. Solo digo que las dinámicas de trabajo que he visto en equipos que tienen personas en dicho rol, no generan un buen flujo de entrega continua. No digo que siempre sea así, pero sí lo fue en los casos que vi de cerca. Dicho esto, mi recomendación es que si tienes en tu equipo una persona en este rol o estás pensando en incorporarla, tal vez deberías detenerte un momento a pensar en la dinámica de trabajo del equipo.

¿Qué es la Ingeniería de Software Continua?

La ingeniería de Software «tradicional» de la que venimos hablando desde los años 60′, que está descripta en los libros clásicos de la disciplina (Presmann, Sommervielle, etc), incluso la descripta en el cuerpo de conocimiento (SWEBoK) no trata las cuestiones relacionadas a la puesta en producción y la operación. Estas cuestiones, que en los últimos 15 años han tomado cada vez más relevancia debido al rol protagónico que ha tomado en software en el negocio, han sido resueltas en los contextos industriales con un conjunto de prácticas que se han englobado bajo lo conocido DevOps y SRE.

La Ingeniería de Software Continua es el nombre «formal» que se le ha dado en la academia a la inclusión de las prácticas DevOps y SRE en la Ingeniería de Software

Dicho de otra forma y de manera simplificada podría decir:

Ingeniería de Software continua = Ingeniería de Software (tradicional) + DevOps/SRE

Preparando Ingeniería de Software Continua @ UBA-Exactas

Hace un tiempo comenté de esta materia que voy a estar dictando en carácter de Profesor Invitado. Pues bien, estoy empezando a ultimar detalles.

En primer lugar la materia la estaré dictando los días miércoles en el horario de 15 a 18. Será en principalmente en modalidad presencial pero con un esquema de aula extendida y algunas clases en modalidad online.En segundo lugar, Francia (perdón, sé que esto me quita seriedad pero no pude contenerme, jajajaja).

El próximo miércoles 19 de Abril a las 16:00 hs. estaré haciendo una charla de presentación de la materia para potenciales estudiantes interesad@s en cursarla. El objetivo de esta charla es básicamente setear expectativas tanto de quienes vayan a cursar la materia como también del equipo docente. O sea, me parece importante que los alumnos sepan antes de anotarse los temas que vamos a estudiar, la propuesta didáctica, mecanismo de evaluación y dedicación requerida. si bien toda esta información podría compartirla en texto, me parece importante poder la dar la chance de hablarlo en vivo y atender inquietudes de los interesados. Al mismo tiempo, como docente, quiero saber la cantidad aproximada de estudiantes que cursarán la materia pues ello podría llevarme a cambiar algunas cuestiones (no es lo mismo un curso para 6 estudiantes que uno para 20). De hecho, si hubiera muy pocos inscriptos (menos de 5) debido al horario de cursada, podría ver de analizar alguna alternativa horaria.

Enseñando prácticas DevOps

La semana pasada presenté en la conferencia ARGENCON 2022, IEEE Biennial Congress of Argentina un artículo formal (experience report) que describe la forma en que abordamos las cuestiones relacionadas a DevOps en el contexto de MeMo2

Este artículo junto con los publicados por Sergio Villagra (Teaching software engineering: an active learning experience) y Carlos Fontela (Learning Communication Skills in Software Development Management Courses: An Active Learning Experience), resume de forma bastante acabada el núcleo de Ingeniería de Software de la carrera de Licenciatura en Análisis de Sistemas de Universidad de Buenos Aires. En un par de semanas el artículo estará disponible en IEEE Explore.

Ingeniería de Software en la Era DevOps

Este el título de la charla/tutorial que dí la semana pasada en el contexto de CIbSE. En Zoom hubo unas 80 personas conectadas pero de las actividades interactivas que propuse, participaron alrededor de 30, un buen número de todas formas.

El punto central de mi de charla fue el hecho de que los escenarios que enfrentamos actualmente en la entrega de software nos llevan a tener que lidiar con ciertas cuestiones que tradicionalmente la ingeniería de software no ha atendido presentes. Al mismo tiempo, dichas cuestiones son centrales dentro del movimiento DevOps. Esto plantea un dilema: ¿es DevOps una disciplina distinta a la Ingeniería de Software? Pues yo creo que no. A mi parecer la Ingeniería de Software debe incluir DevOps. De hecho algunas de prácticas DevOps no son nuevas, sino que han sido parte de la Ingeniería de Software desde hace mucho tiempo. Ejemplo: Integración Continua.

En línea con esta idea, durante mi disertación mencioné varios libros que deberíamos tener presentes a la hora de plantear una Ingeniería de Software que incluya la temática DevOps:

Actualización: ya está disponible el video de la sesión, aquí.

Logs centralizados para aplicaciones Kubernetizadas

Cuando corremos una aplicación en Kubernetes (y en términos más generales en un arquitectura distribuida) donde potencialmente existe más de una instancia de nuestra aplicación resulta conveniente (o incluso imprescindible) poder acceder a los logs de nuestra aplicación en forma centralizada. O sea, en lugar de acceder a cada servidor/nodo donde corre nuestra aplicación, deberíamos poder acceder a una única ubicación y ver ahí mismo los logs de todas las instancias de nuestra aplicación.

Para implementar una solución de este tipo cuando nuestra aplicación corre en Kubernetes existen diferentes estrategias posibles. Voy a referir a continuación 3 estrategias que a mi parecer son bastante habituales, pero antes veamos algunas generalidades.

El tener un log centralizado típicamente implica dos cuestiones: por un lado recolectar los mensajes de log para enviarlos a un lugar central y por otro lado poder acceder a esos mensajes de una forma práctica. Adicionalmente podríamos mencionar el procesamiento de esos mensajes para poder consultarlos de forma más práctica/específica o incluso para accionar ante ciertos mensaje. Esta problemática es tan habitual en la actualidad que existen múltiples productos y servicios para implementarla. Entre las soluciones más populares podemos mencionar New Relic, Datadog, Sumologic y ELK. En general estas soluciones son mucho más amplias que el manejo de logs, son soluciones de monitoreo que incluyen los logs como una funcionalidad particular. Volviendo al foco de este artículo, nos vamos a concentrar puntualmente en el primer paso de este flujo: la recolección de los mensajes de log.

Estrategia Sidecar

Esta estrategia consisten en desplegar dentro cada uno de nuestros pods, a la par del contenedor que corre nuestra aplicación, un contener sidecar que tome los mensajes de log de nuestra aplicación y los envíe a la fuente central. Una form típica de implementar esto es haciendo que nuestra aplicación escriba el log en un archivo de un volumen compartido del cual luego serán leídos por el colector de logs que corre en el contenedor sidecar.

Esta estrategia resulta bastante transparente para la aplicación pero requiere montar el sidecar en cada manifiesto de deployment.

A modo de ejemplo, si utilizamos el servicio de logs centralizados de Sumologic, podemos utilizar esta imagen Docker para montar nuestro contenedor sidecar.

Estrategia Daemonset

En el contexto de kubernetes un daemonset es un pod que corre en cada nodo del cluster de Kubenetes. Entonces podemos desplegar un daemonset que colecte los logs leyendo el stdout de todos contenedores corriendo en el nodo.

Esta estrategia resulta muy económica de implementar, basta hacer que nuestra aplicación escriba los mensajes de log al stdout y desplegar el daemonset. Luego al agregado de nuevas aplicaciones y nodos es completamente transparente.

A modo de ejemplo, si utilizamos el servicio de log de Papertrail, podemos utilizar este daemonset para enviar los logs.

Estrategia Logger con handler/appender remoto

Esta estrategia no tiene nada que ver con Kubernetes y consiste en hacer que sea nuestra aplicación la que directamente envíe los mensajes de log al log central. La forma prolija de implementar esto es usando alguna biblioteca log como Log4J, Log4Net, etc y configurar esta biblioteca con un handler que envíe la información al log central.

Lo interesante de esta solución es que resulta muy simple de implementar. Pero al mismo tiempo tiene un potencial costo de performance que hace que esta estrategia no sea conveniente para algunos escenarios, o sea: si tenemos una aplicación web atendiendo pedidos de nuestros usuarios y en ese contexto el logger van a enviar los mensajes de log via HTTP dentro del mismo proceso podemos tener algún issue de performance. Sin embargo, si nuestra aplicación es un job que corre en background, puede que este costo del log no sea relevante.

A modo de ejemplo, si nuestra aplicación está construída en Ruby podemos utilizar la gema SemanticLogger con un appender http para enviar los Sumologic o Papertrail.