martes, 18 de noviembre de 2014

Swap en servidores de integración

En los servidores de integración tengo el mínimo de RAM necesaria para que funcionen ya que no es importante el tiempo de respuesta ni que estén continuamente levantados.

Ahora mismo es bastante normal que me falle al desplegar una nueva versión si tiene que compilar alguna libreria, y tengo que parar todos los servicios para que tenga memoria suficiente.

Tambien me llegan de vez en cuando mails de Logcheck avisando de que se ha quedado la máquina sin memoria:

System Events
=-=-=-=-=-=-=
Nov  8 08:00:16 staging kernel: [338269.889408] ruby invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
Nov  8 08:00:16 staging kernel: [338269.889414] ruby cpuset=/ mems_allowed=0$ sudo apt-get install ntp

Hasta ahora he aceptado estos problemas sin darles mayor importancia ya que solo ocurren en integración. Esta mañana hablando con mi amigo David me ha comentado que estaba instalando un servidor y que le había añadido swap siguiendo las indicaciones de un artículo de la magnífica documentación de Digital Ocean.

Es una solución evidente que no se me había ocurrido hasta ahora. En producción nunca configuro swap por temas de rendimiento, pero en integración no hay ningún problema en añadirla y me soluciona los problemas comentados anteriormente.

Así que he creado una receta de ansible para añadir swap a un servidor basada en ese artículo.

# Based on https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04
- name: Create swap file
  command: fallocate -l 1G /swapfile creates=/swapfile

- name: Set swap file permissions
  file: dest=/swapfile owner=root group=root mode=600

- name: Check if swap already exists
  shell: "swapon -s | grep '/swapfile'"
  register: swapfile
  ignore_errors: True

- name: Set up the swap space
  command: mkswap /swapfile
  when: swapfile|failed

- name: Enable swap
  command: swapon /swapfile
  when: swapfile|failed

- name: Make the swap file permanent
  mount: name=none src=/swapfile fstype=swap opts=sw passno=0 dump=0 state=present

- name: Set swappiness
  sysctl: name=vm.swappiness value=10 state=present

- name: Set vfs_cache_pressure
  sysctl: name=vm.vfs_cache_pressure value=50 state=present

El playbook se podría hacer un poco más genérico con el tamaño y el nombre de la swap en ficheros de configuración, pero para mi caso no es necesario.

Por último, como en los comandos mkswap y swapon no hay ninguna opción para que no execute si ya existe, he añadido una condición chequeando previamente si ya está creada la swap en la máquina.



viernes, 19 de septiembre de 2014

Request Queuing en New Relic

Una de las herramientas que usamos en teowaki es New Relic para monitorizar el rendimiento de la API. Nos permite saber que páginas son las más lentas para optimizarlas y que areas de la aplicación necesitan mejoras.

Ayer por la noche nos empezaron a llegar alertas por mail de problemas con el Apdex, que mide el rendimiento de la aplicación




No había ningún motivo aparente, ya que no habíamos hecho ningún cambio significativo en los servidores ni había picos significativo de tráfico. Tampoco había carga en las máquinas y todos los servicios estaban levantados correctamente (redis, memcache, unicorn, nginx).

El problema era con el Request Queuing, que había subido desde prácticamente cero hasta unos 100ms, donde se había quedado estable. Este parámetro mide el tiempo desde que llega la petición al servidor web (en nuestro caso nginx) hasta que es servida por el servidor de aplicaciones (en nuestro caso unicorn).

Si tiene un valor alto quiere decir que el servidor de aplicaciones no es capaz de servir todas las peticiones que le llegan y se quedan encoladas en el servidor web y la solución habitual es optimizar la aplicación para que sirva las peticiones más rápido o añadir nuevos servidores de aplicaciones para distribuir la carga.

Para que aparezca esta métrica en las gráficas de New Relic, se debe añadir la cabecera X-Request-Start tal y como explican en la documentación.

Como la primera opción de reiniciar nginx y unicorn por si tenían algún problema no funcionó, el siguiente paso fue leer con detalle la documentación. Si el servidor web y el servidor de aplicaciones están en diferentes servidores físicos, hay que tener cuidado con el retraso entre relojes de los servidores, lo que llaman Clock Skew, que se soluciona instalando NTP.

NTP es un protocolo que permite sincronizar el reloj de nuestros servidores con bastante precisión a partir de una red de servidores distribuida y la instalación es tan simple como añadir un paquete
$ sudo apt-get install ntp
En este tutorial de Digital Ocean hay más detalles de configuración, pero en mi caso con la configuración por defecto ha sido suficiente. Y unos minutos despues de instalarlo los tiempos de respuesta han vuelto a su valor habitual


No se el motivo por el que se ha desincronizado de los servidores, pero bueno, al menos ya está solucionado :)



















viernes, 5 de septiembre de 2014

Eutanasia. El derecho a decidir

Normalmente en este blog siempre escribo sobre temas técnicos, pero a veces, pasan cosas en la vida que te hacen reflexionar, como hace unos años cuando escribí Sobre la (in)competencia médica.

El viernes pasado por la tarde, despues de un par de días con dolor de estómago y algo de fiebre, me subió la fiebre a casi 39, así que nos fuimos al centro médico, que nos redirigió a urgencias. Despues de unas horas y algunas pruebas, me diagnosticaron una apendicitis aguda y a la mañana siguiente me operaron de urgencias. No fue la mejor forma de celebrar que ese mismo día nos habían aprobado la beta de datawaki en heroku y que empezaba las vacaciones, pero las cosas vienen como vienen.

La primera reflexión es la suerte que tengo de vivir en un pais desarrollado, si esto mismo me hubiera pasado en un país subdesarrollado (o hace 100 años), la evolución más probable había sido que se me habría roto el apéndice, y ahora mismo ya estaría muerto o retorciéndome de dolor con una peritonitis sin posible cura.

En el post que he comentado antes, hablaba sobre lo despistada que sigue estando la medicina tradicional en temas musculares y óseos. En cambio en este caso no tengo ninguna queja, me parece que el trato recibido y la profesionalidad de todos los implicados en el hospital Ramón y Cajal de Madrid ha sido excelente.

Una vez operado, la planta de cirugía estaba completa, así que los días que he estado en el hospital los he pasado en otra planta, donde la mayoría de pacientes eran mayores y dependientes.

Mi compañero de habitación era un señor de 79 años con leucemia, ingresado por problemas respiratorios. Nos dijo que no podían hacerle un transplante de médula debido a su edad, así que tendrá que convivir con la enfermedad lo que le queda de vida. El pobre hombre lo estaba pasando bastante mal, con malestar general y estaba muy delgado y cansado.

Uno de los efectos de la leucemia, es la sudoración nocturna, lo que no le permitía dormir y en cuanto se dormía, se le empapaba la cama y se despertaba con frío. Además estaba con fiebre, lo que le incrementaba aún mas el malestar.

Dentro de unos días probablemente se encontrará algo mejor, le darán el alta y volverá a estar unas semanas en casa, hasta que de nuevo le ataque otra enfermedad, ya que por las defensas bajas de la leucemia está enfermo bastante a menudo, y probablemente tendrán que volver a ingresarlo.

Hablando con él nos estuvo contando un poco su vida. Por lo que nos contó ha tenido una vida muy feliz, con un trabajo de responsabilidad del que estaba bastante orgulloso, felizmente casado, y con cuatro hijos ya mayores todos con la vida resuelta.

Al pobre hombre se le veía agotado, sin ganas de vivir, además, nos contó que su mujer estaba con Alzheimer bastante avanzado y hablaba con muchísima pena de ella. Decía que la echaba mucho en falta.

Y de aquí el título del post. Me parece muy triste que una persona con una enfermedad sin cura, a la que solo le esperan algunas temporadas de encontrarse más o menos bien, junto con otras en las que realmente lo pasa mal y sin ilusion por vivir no tenga el derecho de morir dignamente.

Está claro que muchísima gente prefiere seguir viviendo hasta la muerte natural y que la mayoría no optaría por esa opción, pero espero que algún día tengamos la opción de poder decidir y no estar sufriendo cuando ya crees que has hecho todo lo que tenías que hacer en este mundo.

Si algún día estoy en ese estado, espero que al menos me den la opción, y si aquí no puedo y es lo que quiero, que me trasladen a donde si se pueda


Por último, si teneis dolor agudo estomacal con fiebre alta, rápido a urgencias, que es una combinación que puede ser muy grave....
















miércoles, 20 de agosto de 2014

Capistrano y Monit

Últimamente me ha pasado un par de veces que al hacer una subida a producción, Capistrano y Monit no se llevan muy bien.

El problema es que en el deploy se reinician algunos servicios, como Sidekiq, y si coincide con el chequeo de Monit, se lanza otro reinicio, con el resultado de tener 2 Sidekiq arrancados en producción.

Tambien puede pasar que quieras parar algún servicio manualmente, por ejemplo Unicorn desde una tarea de Capistrano y Monit lo levante automáticamente.

Afortunadamente, la solución es simple, consiste en añadir unas tareas en Capistrano y dar permisos al usuario de la aplicación para ejecutar Monit.

Para ejecutar los comandos de Monit, se necesitan permisos de sudo, pero por seguridad, no queremos dar acceso sudo sin más. Tampoco interesa dar acceso a toda la funcionalidad de Monit, ya que se podría parar cualquier servicio monitorizado con Monit externo a la aplicación.

Así que solo daremos permiso para ver el estado de la monitorización (monit summary) y parar o iniciar la monitorización (monit monitor y monit unmonitor)

La forma de conseguirlo es tan simple como añadir un nuevo fichero /etc/sudoers.d/monit con esos permisos
my_user ALL = NOPASSWD: /usr/bin/monit summary, /usr/bin/monit unmonitor *, /usr/bin/monit monitor *
No le pedimos la contraseña al usuario para ejecutar estos comandos, ya que el usuario no tiene contraseña asignada (solo le permitimos acceder por medio de clave pública).

Y ese mismo fichero creado desde ansible

- name: Allow execute monit commands to users
  action: 'lineinfile dest=/etc/sudoers.d/monit state=present create=yes regexp="{{item}}" line="{{item}} ALL = NOPASSWD: /usr/bin/monit summary, /usr/bin/monit unmonitor *, /usr/bin/monit monitor *" validate="visudo -cf %s"'
  with_items:
    - my_user
Una vez que tenemos permisos, creamos un módulo de capistrano con las diferentes tareas a ejecutar en lib/capistrano/tasks


namespace :monit do
  desc 'Monit summary'
  task :summary do
    on roles :app do
      puts capture :sudo, :monit, :summary
    end
  end

  desc 'Monit unmonitor'
  task :unmonitor do
    on roles :app do
      puts capture :sudo, :monit, :unmonitor, :sidekiq
    end
  end

  desc 'Monit monitor'
  task :monitor do
    on roles :app do
      puts capture :sudo, :monit, :monitor, :sidekiq
    end
  end
end

Y invocamos esas tareas al empezar y al acabar el deploy

after 'deploy:starting',  'monit:unmonitor'
after 'deploy:finished',  'monit:monitor'
Además, la tarea summary permite al usuario monitorizar el estado del servidor desde línea de comandos
$ cap production monit summary
...
System 'teowaki.com'                Running
Process 'sidekiq'                   Running
Process 'sshd'                      Running
Filesystem 'rootfs'                 Accessible
...
Por último, comentar que la gema capistrano-sidekiq incluye algunas tareas similares, pero no me cuadraba su uso en mi caso, ya que me obliga a cambiar el nombre del servicio monitorizado.

lunes, 30 de junio de 2014

Ansible y Digital Ocean

Justo hoy tenía prevista hacer una subida a producción del API de Teowaki con bastantes cambios de configuración que llevo probando un tiempo en local.

La subida incluye la actualización a Rails 4.1.2, y bastantes gemas, entre las que está Capistrano 3, que ha supuesto reescribir los ficheros de deploy. Para acabar de completarlo, tambien quiero actualizar a Ruby 2.1.2.

Está claro que esta subida tiene todos los números para que algo falle y dejar caído producción un rato...

Así que he optado por la opción de crear un nuevo servidor donde probar todos esos cambios y aprovecho para ir actualizando a Ubuntu Trusty. Una vez creado el servidor y comprobado que todo está bien, simplemente cambiaré el balanceador para que apunte al nuevo servidor y apagaré el servidor antiguo.

Aprovechando estos cambios le he dado un nuevo empujón a la configuración de ansible, creando los servidores desde los playbook en lugar de crearlos desde la consola de Digital Ocean, como hacía hasta ahora.

Continuando la entrada del mes pasado, donde conté como hacer Bootstrap con Ansible, aquí cuento como crear los servidores.

Según la documentación, el primer paso es instalar las dependencias, en este caso sólo dopy, un wrapper de la API de Digital Ocean en Python.
$ sudo pip install dopy
Para facilitar la configuración, nos bajamos los ficheros de inventorio
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/plugins/inventory/digital_ocean.py
$ chmod +x digital_ocean.py
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/plugins/inventory/digital_ocean.ini
Creamos un par de variables de entorno con la API_KEY y el CLIENT_ID, que los consultamos en la configuración de Digital Ocean
export DO_API_KEY=MI_API_KEY
export DO_CLIENT_ID=MI_CLIENT_ID
Y chequeamos si la configuración es correcta listando los droplets que tenemos creados (con -h o --help puedes ver las opciones disponibles)
$./digital_ocean.py --droplets --pretty
Para crear un droplet, necesitamos 4 datos principales, la imagen que usaremos para crearlo, el tamaño, la region donde lo creamos y la clave SSH que usaremos para acceder. Todos esos datos tienen un ID único en Digital Ocean, que podemos consultar con el script anterior (con los parámetros images, sizes, regions y ssh-keys)
$ ./digital_ocean.py --sizes --pretty
{
  "sizes": [
    {
      "cost_per_hour": "0.00744", 
      "cost_per_month": "5.0", 
      "cpu": "1", 
      "disk": "20", 
      "id": "66", 
      "memory": "512", 
      "name": "512MB", 
      "slug": "512mb"
    }, 
...
Para simplificar la creación de nuevos droplets, he añadido en el fichero hosts una sección local (el droplet se crea desde localhost, no desde un servidor remoto)
[local]
localhost
Y un fichero de variables de grupo  
$ more group_vars/local.yml 
---
# SIZES:
#   "slug": "512mb", "name": "512MB", "id": "66", "disk": "20", "cpu": "1", "cost_per_month": "5.0"
#   "slug": "1gb", "name": "1GB", "id": "63", "disk": "30", "cpu": "1", "cost_per_month": "10.0"
#   "slug": "2gb", "name": "2GB", "id": "62", "disk": "40", "cpu": "2", "cost_per_month": "20.0",
do_size_512mb: 66
do_size_1gb: 63
do_size_2gb: 62
# IMAGES:
#   "slug": "ubuntu-14-04-x64", "distribution": "Ubuntu", "id": "3240036", "name": "Ubuntu_14.04_x64",
do_image_ubuntu_14_04_x64: 3240036
# REGIONS;
#   "slug": "nyc2", "id": "4", "name": "New_York_2",
do_region_nyc2: 4
# KEYS:
#   "id": "xxxx", "name": "diego"
do_diego_key:  xxxx

Y ya solo nos queda decidir la configuración y el nombre del servidor (SERVER_NAME en el playbook)
---
- hosts: local
  connection: local
  tasks:
  - name: Create new Digital Ocean Droplet
    digital_ocean: >
      state=present
      command=droplet
      name=SERVER_NAME
      size_id={{ do_size_1gb }}
      region_id={{ do_region_nyc2 }}
      image_id={{ do_image_ubuntu_14_04_x64 }}
      private_networking=yes
      ssh_key_ids={{ do_diego_key }}

Una vez creado, ejecutamos el bootstrap de mi post anterior y los roles del servidor.

Existen soluciones mucho más completas para provisionar los servidores como ansible-provisioner, pero en mi caso con hacer la creación manualmente modificando este playbook es más que suficiente.





miércoles, 28 de mayo de 2014

Bootstrap con Ansible

La semana pasada en el grupo DevOps de Madrid, hubo una magnífica charla introductoria de Maykel Moya sobre Ansible. Parte de lo que explico en este post lo contó con una demo en directo.

Estuve hablando con Javier Vidal sobre el tema y al día siguiente me comento en twitter



Y en lugar de compartirselo sólo a Javi, he creado esta entrada en el blog :)

Para ello, he separado el role de bootstraping de nuestra instalación de Teowaki en un proyecto independiente y lo he configurado con Vagrant, para poder probarlo en local sin problemas.

El proyecto está disponible en github.

Los pasos para probarlo son:

Instalar Vagrant en local (previamente debe estar instalado VirtualBox). He optado por instalarlo a partir del .deb en lugar de a partir de apt porque la opción apt me instalaba los paquetes de ruby 1.9, y ruby ya lo tengo instalado en local con RVM.

$ wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.6.2_x86_64.deb
$ sudo dpkg -i vagrant_1.6.2_x86_64.deb
$ vagrant -v
Vagrant 1.6.2
$ vagrant init ubuntu/trusty64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Una vez instalado, simplemente con vagrant up levantamos el servidor, que escucha por defecto por ssh en el puerto 2222 en localhost

Más información sobre la instalación en las páginas de Vagrant y Ansible.

El siguiente paso es configurar el servidor en Ansible, que es tan simple como definir en un fichero hosts los datos de conexión al servidor. En mi caso he definido un grupo de servidores 'development' que incluye al servidor dev

[development]
dev ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222

Por otra parte configuro la conexión a vagrant en ansible.cfg, de forma que no hay que escribirla en cada conexión desde ansible

[defaults]
hostfile = hosts
private_key_file=~/.vagrant.d/insecure_private_key
remote_user = vagrant

Y probamos que la conexión al servidor desde ansible funciona

$ ansible dev -m ping
dev | success >> {
    "changed": false, 
    "ping": "pong"
}

Sin el fichero de configuración la instrucción habría sido:

$ ansible dev -i hosts --private-key=~/.vagrant.d/insecure_private_key -u vagrant -m ping

Una vez comprobado que conectamos, cambiamos el fichero development.yml con la configuración local y ya podemos instalar el servidor


$ ansible-playbook bootstrap.yml

Por último, las tareas incluídas en el role son:
  • Instalación y actualización de paquetes de ubuntu
  • Copia de ficheros de configuración, en este caso solo /etc/environment
  • Configuración del locale
  • Creación de usuarios, asignando grupos y clave pública
  • Configuración de seguridad, instalando Fail2ban y cambiando ssh para que no permita ni acceso SSH de root ni acceso con contraseña (sólo por clave pública)
  • Instalación y configuración de Nullmailer y Mandrill. El motivo de uso lo explique en mi anterior post
  • Instalación de la monitorización de servidores de NewRelic


  • Para que funcione correctamente la instalación se debe actualizar el fichero development.yml con la configuración local (usuarios y sus claves públicas, emails, dominio, claves de acceso a Mandrill y NewRelic, ...) tal y como cuento en el README del proyecto.


    jueves, 24 de abril de 2014

    Nullmailer y Mandrill como alternativa a un servidor de mail local

    Nunca me ha gustado la idea de tener un servidor de mail local instalado en producción, por dos motivos principales:

    El primero es que según el uso (o mal uso) que le des al mail desde tu aplicación, puede que los usuarios te marquen como spam y que la IP de tu servidor se añada a una lista negra. En ese caso, tus mails salientes quedarán bloqueados y no se enviarán, y conseguir que te borren de una lista negra no es tarea fácil.

    El segundo es que hay que configurarlo, he escuchado muchas historias de miedo de lo doloroso que es administrar un servidor de correo y prefiero no pasar por eso.

    Hace un tiempo, la opción habitual era utilizar un servicio externo para las newsletters, como MailChimp y el resto de mails enviarlos desde la aplicación.

    El siguiente paso natural fue enviar tambien el mail transaccional de la aplicación (bienvenida, olvido de password, ...) desde una plataforma externa, usando su SMTP o su API. Un ejemplo sería Mandrill y las diferentes integraciones que tiene.

    Pero en nuestro caso queríamos ir un paso más y directamente no tener un servidor local para envío de los mails de sistema (p.e. los cron). Y aquí es donde aparece Nullmailer.

    Nullmailer es un MTA que usa el mismo formato que sendmail y permite redirigir el mail saliente a un servidor externo, de forma que desde tu servidor nunca se enviará el mail.

    En Ubuntu Trusty está disponible como paquete. La versión en Ubuntu Precise no soporta SSL, pero hay un backport que si lo soporta.

    La configuración es bastante simple, solo hay que cambiar 4 ficheros en el directorio /etc/nullmailer:

    • adminaddr con el email del administrador
    • me con el nombre del servidor
    • defaultdomain con el dominio de tu servidor
    • remotes con la configuración del servidor de correo

    La configuración completa la podeis encontrar perfectamente explicada en este post de OpenSourceHacker , escrito por el mismo que hizo el backport de Nullmailer a Precise


    Por último, destacar que Nullmailer no escucha en el puerto 25 como cualquier SMTP, así que si algún servicio depende de SMTP, se debe configurar por separado.

    Un ejemplo de este último caso es monit, donde se debe configurar un servidor para enviar las alertas. Así que lo que haremos, será que apunte directamente al SMTP de mandrill

      server {
      set mailserver smtp.mandrillapp.com port 587
        username "youremail@domain.com" password "your_password"
        using tlsv1
      }
    


    lunes, 24 de marzo de 2014

    Configurando SSL en nginx

    Informe SSL de teowaki.com por sslabs.com


    La configuración básica de SSL en nginx es muy sencilla. Lo primero es tener una versión de nginx compilada con el módulo ngx_http_ssl_module. Si usas la versión estandar de tu distribución linux lo más seguro es que ya tenga soporte. Puedes comprobarlo con

    $ nginx -V
    ...
    configure arguments: --prefix=/usr/share/nginx ...--with-http_ssl_module ...
    

    En el virtual host indicaremos que vamos a usar SSL y el path de los ficheros de certificados. El proveedor donde contrates el certificado te dirá los pasos para generar estos ficheros. Como ejemplo las instrucciones de Comodo y Linode

    server {
      listen        443 ssl;
    
      ssl_certificate      /path_to/cert.crt;
      ssl_certificate_key  /path_to/cert.key;
    
    }
    

    Simplemente con estas tres líneas ya tenemos nuestro servidor con soporte SSL funcionando. Pero para que tu configuración sea óptima, hay cuantos parámetros más a tener en cuenta.


    SPDY

    SPDY es un protocolo creado por google para reducir el tiempo de carga de las páginas web. Está soportado en Firefox y Chrome desde hace tiempo y en IE desde la version 11.

    Al igual que para tener soporte de SSL, se debe compilar nginx con el módulo correspondiente, ngx_http_spdy_module.

    Como me da mucha pereza compilar nginx cada vez que lanzan una nueva versión, tenemos dos alternativas, usar binarios precompilados por Passenger o usar un ppa que incluya ese módulo

    He optado por la segunda opción, usando los paquetes de Chris Lea. Están compilados usando la versión de desarrollo de nginx. Aunque es la versión de desarrollo es una versión estable que tambien usan en la web de nginx tal como explican en las FAQ.

    La configuración de nuevo es tan simple como añadir spdy en la directiva listen

      listen        443 ssl spdy;
    


    TLS

    La configuración por defecto de nginx soporta SSL v3 y TLS versiones 1.0, 1.1 y 1.2.

    Todos los navegadores modernos soportan TLS, y SSL v3 es un protocolo antiguo y tiene algunos problemas de seguridad, así que he decidido deshabilitarlo

      # enables TLS protocols, but not SSL which is weak and should no longer be used.
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;


    Strict Transport Security (HSTS)

    Si vamos a usar siempre conexión segura con nuestro servidor, añadiendo la cabecera Strict Transport Security, indicamos al navegador que aunque el usuario escriba la URL con http, siempre debe conectarse de forma segura usando https.

    Es una buena práctica de seguridad y muchos servicios como Twitter o Paypal añaden esta cabecera en sus respuestas.

      # Remember this setting for 365 days
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    

    Ciphers

    Hay mucha literatura sobre los algoritmos de cifrado que se deberían permitir, en nuestro caso he optado por seguir las recomendaciones de Comodo.

    Actualización 23-04-2014: Basado en este post sobre SSL Ciphers, he actualizado la configuración, desactivando RC4.

    Tambien he añadido otra directiva para que sea el servidor el que decida que protocolo usar entre los disponibles en lugar de dejar la elección al cliente.

      # Disables all weak ciphers
      ssl_prefer_server_ciphers on;
      ssl_ciphers ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM;
      ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
    




    OCSP Stapling

    Al conectarse el navegador a una web segura, hace una petición adicional a la autoridad certificadora para asegurarse de que el certificado es válido usando OCSP (Online Certificate Status Protocol).

    Por medio de OCSP Stapling, se evita esa petición adicional incluyendo la respuesta OCSP en la misma conexión SSL, con lo que reducimos el tiempo de conexión.

    Puedes leer más en Cloudfare, en la Wikipedia, y comprobar si tu servidor lo soporta siguiendo las instrucciones de Unmitigated Risk.

      # enable ocsp stapling
      ssl_stapling on;
    


    SSL Session

    Por último, siguiendo las recomendaciones de nginx para reducir la carga del procesador habilitamos la caché de sesiones SSL y aumentamos el timeout

      ssl_session_cache    shared:SSL:10m;
      ssl_session_timeout  10m;
    



    Con esta configuración conseguimos una implementación SSL robusta y eficiente. Si conoces algún truco o mejora, por favor, dejalo en los comentarios, gracias!


    miércoles, 19 de febrero de 2014

    Agrupando el log de rails con rsyslog

    En teowaki el servicio de API lo tenemos balanceado entre dos servidores. Los objetivos principales de balancearlo, más que la carga, han sido la protección ante fallos y la posibilidad de hacer pruebas con diferentes configuraciones en los servidores.

    Por ejemplo, inicialmente teníamos ruby 2.0 y unicorn en ambos servidores. En Navidad se publicó la versión 2.1, la instalamos en uno de los servidores y durante unas semanas tuvimos las dos versiones conviviendo en produccion. Las conclusiones fueron que más o menos el tiempo de respuesta bajaba un  20% y el consumo de memoría subía tambien sobre un 20%, así que decidimos instalarlo en las dos máquinas.

    De la misma forma queremos hacer la prueba más adelante de usar diferentes servidores de aplicaciones simultáneamante (passenger / unicorn / puma) para ver cual funciona mejor. Tambien tenemos la intención de probar JRuby 9K en cuanto esté listo para producción.

    El problema del balanceo es que el log queda distribuido y es imposible seguir una petición. Afortunadamente hay una solución muy simple usando rsyslog.

    El primer paso es cambiar la configuración de rails para que use syslog, que viene por defecto en ruby, por lo que no es necesario instalar ninguna gema. Tan simple como añadir una línea en config/production.rb, donde 'teowaki-api' en nuestro caso indica el nombre que usaremos para la configuración de rsyslog.

    $ more config/production.rb 
    ...
    config.logger = Syslog::Logger.new 'teowaki-api'
    

    A partir de ahora, el log en modo production lo procesará rsyslog.

    Para configurar rsyslog, primero de todo editamos /etc/rsyslog.conf en el servidor que recibirá los logs (misc en nuestro caso), permitiendo acceso por TCP en el puerto 10514 y dando permiso de escritura solo a los servidores de API.

    En la configuración por defecto de rsyslog el puerto de TCP es 514, pero en ubuntu hay un problema de privilegios al usar puertos por debajo del 1024, así que he usado el 10514

    $ more /etc/rsyslog.conf
    ....
    # provides TCP syslog reception
    $ModLoad imtcp
    $InputTCPServerRun 10514
    ...
    $AllowedSender TCP, api01, api02
    

    Ahora creamos el fichero de configuración cada servidor de API, donde le decimos que guarde el log en /var/log en local y que tambien lo mande al servidor misc al puerto 10514

    
    $ more /etc/rsyslog.d/40-teowaki-api.conf 
    if $programname == 'teowaki-api' then /var/log/rails/teowaki-api.log 
    if $programname == 'teowaki-api' then @@misc:10514 
    & ~  
    

    Y finalmente, en el servidor misc le decimos que guarde el log, que le llegará desde los servidores de API, en /var/log

    $ more /etc/rsyslog.d/40-teowaki-api.conf 
    if $programname == 'teowaki-api' then /var/log/rails/teowaki-api.log 
    & ~  
    

    El número 40 indica la prioridad del fichero, para que se ejecute antes que el fichero 50-default.conf, tal y como explican en este blog, que fue el que me dio la idea para esta configuración

    Y esto es todo, así de simple y ya tenemos la configuración completa.

    La instalación en producción la he hecho con un par de roles de ansible, uno para los servidores de API y otro para el servidor que recibe los logs y además le he añadido logrotate, pero bueno, de ansible ya hablaremos en otro post...

    Más adelante quiero probar otras soluciones más potentes como Kibana y logstash, pero por ahora ya tenemos el log centralizado, que era el objetivo inicial.



    jueves, 6 de febrero de 2014

    Recuperando el blog

    Hace ya casi 6 años escribí la primera entrada en este blog. Mi idea inicial era escribir una entrada cada mes, mantuve bien el ritmo en el 2008 y el 2009, el 2010 empecé a flaquear y en el 2013 no he escrito ni una triste entrada :(

    El 2013 ha sido un año de muchos cambios, dejé la empresa donde había estado trabajando desde el 2007 para intentar junto a Javi la loca aventura de montar un producto SaaS que haga más felices a los desarrolladores. Desde diciembre tenemos teowaki en producción, y estamos bastante orgullosos del resultado.

    Uno de las consecuencias ha sido que mi stack tecnológico ha cambiado mucho. Estos años la mayoría de mis proyectos estaban basados en ruby on rails en el servidor, MySQL como almacen de datos y una parte cliente que cada vez iba siendo más compleja, pero casi siempre basada en las vistas de rails y jQuery.

    Aprovechando que teniamos 'poder absoluto' para decidir la tecnología a usar, optamos por una arquitectura que por ahora está dando muy buen resultado.

    Para empezar, queríamos que la API fuera un ciudadano de primera clase del proyecto, así que hemos desarrollado por un backend en modo API y el front es simplemente un cliente más de esa API.

    Tambien hemos empezado a usar redis para mucho más que para la gestión de colas y nuestra base de datos es PostgreSQL.

    Aunque ya hacía algo de sistemas anteriormente, tambien me he introducido un poco más en el mundo devops, instalando los servidores con Ansible. Otro cambio ha sido que en lugar de usar apache / passenger como servidor web y de aplicaciones, estamos probando la combinación nginx / unicorn.

    Del mismo modo, he tenido que resetear lo que sabía de JavaScript, aprendiendo a usar AngularJS en el cliente de la API.

    Así que creo que tengo bastantes cosas que contar sobre mi trabajo y voy a intentar recuperar el ritmo de una entrada por mes, a ver si lo consigo al menos durante otros dos años como cuando empecé este blog.