El health check del ELB de AWS y el contenido duplicado en Google


Como ya había comentado anteriormente, la infraestructura de etece está en la nube de Amazon. Uno de los servicios que tenemos contratados es el de balanceador de carga (ELB) para distribuir el tráfico entre diferentes frontales y así poder autoescalar cuando tenemos picos de tráfico.

Como parte de la configuración del ELB se debe definir el health check. Esto es, una petición que realiza el balanceador a cada una de las máquinas virtuales (EC2) que tiene detrás para determinar si la máquina está funcionando bien o por el contrario tiene algún problema y debe sacarla del balanceo. Este health check se configura mediante:
– protocolo
– puerto
– path de la petición
– timeout de la respuesta
– intervalo entre peticiones
– umbral no sano. Indica el número de veces seguidas en que tolera una respueta incorrecta y a partir de la cuál saca la máquina del balanceador
– umbral sano. Indica el número de veces seguidas en que recibe una respuesta correcta que espera antes de volver a meter una máquina en el balanceador

En el caso de etece tenemos configurado que pida una página cada seis segundos, con un umbral no sano de 2 y un umbral sano de 4. Esto significa que si una máquina no responde adecuadamente durante doce segundos (umbral inferior de 2 con una petición cada 6 segundos), el ELB saca dicha máquina del balanceo. Cuando esa máquina responde correctamente durante 24 segundos (umbral superior de 4 con una petición cada 6 segundos), el ELB la vuelve a incluir en el balanceo.

La petición configurada la hace el ELB a cada una de las EC2 haciendo uso de su DNS público. Esto es un nombre público que tiene cada EC2 durante su tiempo de vida. Si paras la máquina y la vuelves a levantar, cambia su DNS público por lo que no puedes ni debes hacer ninguna configuración mediante ese nombre ya que va a cambiar. Además, si tus frontales autoescalan, todos ellos serán iguales, con la misma configuración pero con diferente DNS público.
Es decir, si yo tengo un EC2 sirviendo peticiones para el dominio midominio.com y ese EC2 tiene un DNS público que es ec2-xx-xx-xx-xx.eu-west-x.compute.amazonaws.com, el ELB va a hacer la petición http configurada en el parámetro path de la petición (por ejemplo /index.html) a este nombre público. Va a pedir http://ec2-xx-xx-xx-xx.eu-west-x.compute.amazonaws.com/index.html. Lo que comprueba el health check es el código de respuesta, dándo únicamente por válida una respuesta 200. Cualquier otra respuesta es considerada respuesta erronea. Por tanto, no puede haber ninguna redirección en esa respuesta.

Hasta aquí no he contado nada nuevo y seguro que muchos ya estáis pensando que soy un poco pesado. Pero cuando empiezas a tirar de la cuerda es cuando ves que hay más tela que cortar y que hay que hilar fino.

Configuración del virtual host de Apache
Si quieres que tu web responda correctamente al health check del ELB evitando las redirecciones, debes tener configurados los virtual host de Apache para que respondan por midominio.com y para el DNS público, que es un valor dinámico. Se podría resolver muy facilmente haciendo que el virtual host de midominio.com sea el virtual host por defecto y todo resuelto. Si pero no. Aquí es donde entra nuestro colega Google.

Si alguna vez, por comprobar el funcionamiento de tu EC2 o porque tienes el EC2 fuera del ELB, haces una petición de tu web por el DNS público, se van a imprimir todos los pixels de analítica, remarketing, conversión etc… que tengas en tu web, pero con un nombre de servidor que no es el de dominio. Google es muy listo y se guarda esa info en algún lugar para luego volver a ella y lo normal es que te indexe varias páginas iguales respondiendo por midominio.com y el DNS público. Total, mismo contenido con dos URLs y eso es penalización segura. Hay que corregirlo en cuanto te das cuenta. En el caso de etece, no somos tan listos como para habernos dado cuenta nosotros solos. Ha sido nuestra amiga Noe la que nos ha avisado del percal. Gracias Noe!!!!

Solución
Después de darle varias vueltas a las diferentes alternativas, hemos decidido que la que más nos gustaba era configurar el virtual host de midominio.com para que redirija todas las peticiones que gestione a midominio.com excepto cuando el user-agent de la petición es el que usa ELB para hacer las peticiones de health check (ELB-HealthChecker/1.0). En ese caso responde por el DNS público y en cualquier otro caso, hace una 301 a midominio.com. De esta manera evitamos que se respondan peticiones por dos URLs distintas y el ELB puede hacer el health check correctamente. Para ello hemos recurrido a la directiva RedirectRule en base a algunas condiciones expecificadas en la directiva RewriteCond, ambas del mod_rewrite de Apache.

Nuestra configuración ha quedado así:

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} !ELB-HealthChecker
RewriteCond %{SERVER_NAME} !midominio.com
RewriteRule /(.*) http://midominio.com/$1 [L,R=301]

 

— ACTUALIZACIÓN 11-07-2013

Si usas CloudFront como CDN usando subdominios de tu dominio, que son servidos por el mismo server, la configuración que había publicado no funciona correctamente ya que, al no ser una petición con el SERVER_NAME midominio.com, al venir desde CloudFront, la regla le hace una 301 a midominio.com y por tanto estaría sirviendo todo el contenido estático nuestro servidor, perdiendo las virtudes del CDN. Por tanto, hay que incluir una nueva condición para evitar la redirección si quien lo pide es CloudFront.

Nuestra nueva configuración ha quedado así:

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} !ELB-HealthChecker
RewriteCond %{HTTP_USER_AGENT} !Amazon\ CloudFront
RewriteCond %{HTTP_HOST} !midominio.com
RewriteRule /(.*) http://midominio.com/$1 [L,R=301]

Para el que no esté muy suelto con las directivas de Apache, esto lo que significa es:

Activamos el motor de reescritura
Si el user-agent de la petición NO es ELB-HealthChecker/1.0 Y….  –> Si es ELB-HealthChecker/1.0 entonces no se hace nada
Si el user-agent de la petición NO es CloudFront Y….  –> Si quien hace la petición es CloudFront entonces no se hace nada
Si el nombre de dominio pedido NO es midominio.com entonces…  –> Si el nombre de dominio ya es midominio.com no se hace nada, de esta manera se evitan los bucles de redirecciones
Se hace una 301 a midominio.com

Espero que este breve artículo le sirva a más de uno para evitar un problema de duplicidad de URLs. Es cierto que lo podía haber explicado de manera más breve pero creo que así se entiende mejor. Como siempre, cualquier comentario o corrección es bienvenida.

9 comentarios en “El health check del ELB de AWS y el contenido duplicado en Google

  1. Una vez tuvimos algo parecido y lo que comentaría como pequeña mejora es que el matching de user agent sea más genérico como !^ELB-HealthChecker (sin la versíon), caso contrario el día que Amazon actualice la versión del user agent la volvés a tener.

    Saludos!

  2. Yo tuve una parecida con el health check interno que hacen los que configuran el balanceador. Y hasta que te encuentras la primera vez que solo vale un 200 y las redirecciones son un «servidor caido»…

    Como comentario, diría que el título del post no acaba de corresponderse con el contenido, y puede que mucha gente no lo lea pensando que ya sabe de que va la cosa. El título habla del punto intermedio que mencionas, pero el post trata de como solucionar la cuestión de que no te penalize Google mientras mantienes una respuesta 200 pura al health check.

    • Tienes razón en que el título no es demasiado indicativo del contenido. Lo puse antes de empezar a escribir y al final el post tiene más que ver sobre cómo evitar problemas de URLs duplicadas con el health check de ELB.
      Un saludo

  3. Hola,

    Antes y ahora seguís teniendo un problema, relativamente importante. A parte de que google «se enfade» por tener contenido duplicado (bendita @noe), el poder hacer la petición con cualquier SERVER_NAME es un conocido vector de ataque, aunque difícil de explotar ya que depende mucho del contexto.

    http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html

    Tanto antes, directamente suplantado la cabecera Host, como ahora suplantando tanto el User-Agent como el Host se pueden hacer peticiones a la aplicación con un SERVER_NAME trucado.

    Es posible que en vuestra aplicación sea irrelevante o no, depende de «las tripas», pero en general siempre hay que tener un vhost «catch all». En vuestro caso sería tener un vhost por defecto que lo coja todo y devuelva la redirección a todo el mundo excepto para el UA del ELB, al que se le devuelve un 200 «tonto».

    Si alguien lo suplanta obtendrá un 200 con un contenido tonto que es el que usa el ELB y poco más que hacer.

    En cuanto al uso de CloudFront, el origen debería de ser uno definido explícitamente como vhost, por ejemplo imgorg.etece.es. Además de lo explicado seguís teniendo contenido duplicado y además mal procesado (el famoso vector de ataque).

    Además está pasando por el CloudFront y lo cobran.

    A raiz de esto otra cosa importante es que aunque acabe todo en el mismo sitio, es importante diferenciar el vhost «dinámico» del estático, sobre todo si el segundo no necesita procesar nada y solo sirve ficheros.

    Espero haberme explicado bien y que os sirva.
    Saludos.

    • Gracias Dani, oro puro tu respuesta. Para poder acceder a contenido de la web por un host name distinto, hay que saber que está configurado de esa manera. En cualquier caso vamos a seguir tus consejos y a dejarlo fino.

      Es una pasada poder contar con comentarios así de constructivos. Gracias por leerme.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s