La fecha de entrega para el informe y el código es el jueves 11/10
‼️
La forma de entrega será crear un canal privado en Slack con todos los miembros del grupo, todos los docentes, y poner ahí un link al repositorio con el código (en caso de ser privado, invitar también a todos los docentes) y el informe (o avisar si está en el repositorio).
El informe puede ser un PDF, Google Doc o Markdown/Wiki en el mismo repositorio del código. Debe incluir screenshots del dashboard de métricas para cada caso analizado que permitan observar los resultados obtenidos.
El objetivo principal es comparar algunas tecnologías, ver cómo diversos casos impactan en los atributos de calidad y probar o al menos sugerir qué cambios se podrían hacer para mejorarlos. El objetivo menor es que aprendan a usar una variedad de tecnologías útiles y muy usadas hoy en día, incluyendo:
- Node.js (+ Express)
- Python (+ Flask y Gunicorn)
- Docker
- Docker-compose
- Nginx
- Algún generador de carga (la propuesta es usar Artillery, pero pueden cambiarlo)
- Alguna forma de tomar mediciones varias y visualizarlas, preferentemente en tiempo real, con persistencia, y en un dashboard unificado (la propuesta es usar el plugin de Artillery + cAdvisor + StatsD + Graphite + Grafana, pero pueden cambiarlo).
Implementar un servicio HTTP en dos tecnologías diferentes (Node.js-Express y Python-Gunicorn). Someter distintos tipos de endpoints a diversas intensidades/escenarios de carga en algunas configuraciones de deployment, tomar mediciones sobre los mismos y analizar resultados.
A partir de este repositorio como punto inicial, van a tener que implementar cada webserver y dockerizarlo (completar
las carpetas js/
y py/
de este repositorio), agregar los servicios con esos webservers al docker-compose.yml
, y
configurar las locations y upstreams de nginx en nginx_reverse_proxy.conf
.
Para generar carga y ver las mediciones obtenidas, en la carpeta perf/
tienen un dashboard de grafana ya armado
y un ejemplo de un escenario básico de artillery. También hay un script y una configuración en el package.json
para
que puedan ejecutar los escenarios que hagan corriendo
npm run scenario <filename> <server>
donde <filename>
es el nombre del archivo con el escenario (sin la extensión .yaml
) y <server>
es el server
contra el que quieren ejecutarlo (vean la sección environments
dentro del yaml del escenario).
Queda a cargo de cada grupo elegir qué endpoints y configuraciones de deployment prueba bajo qué escenarios de carga. Es preferible armar pocos casos y analizarlos lo más posible que juntar muchísima información y estudiar poco los resultados.
⚠️
Caso | Implementado como | Representa |
---|---|---|
Ping | Respuesta de un valor constante (rápido y de procesamiento mínimo) | Healthcheck básico |
Proxy/timeout | Duerme cierto tiempo y responde (lento y de procesamiento mínimo) | Llamada a otro servicio (request http, llamada a DB, etc.) casi sin procesamiento de datos. |
Intensivo | Loop de cierto tiempo (lento y de alto procesamiento) | Cálculos pesados sobre los datos (ej: algoritmos pesados, o simplemente muchos cálculos) |
Caso | Implementación |
---|---|
Contenido estático | El endpoint responde con un archivo estático fijo. Pueden probarlo con archivos chicos y con archivos grandes |
Proxy (verdadero) | Pueden hacer que el server llame a otro servicio suyo, que demore lo mismo que demoraba el caso proxy/timeout anterior, y comparar |
... | ... pueden agregar otros casos que se les ocurran |
Todo el tráfico debe pasar por el nginx en todos los casos, para que todos tengan la latencia del salto "extra"
Caso | Explicación |
---|---|
Node | Servidor en node, con un solo proceso. Un solo container. |
Gunicorn | Servidor en python con gunicorn, usando un solo worker sincrónico (el del tipo default). Un solo container. |
Replicado | Alguno de los casos anteriores (node o python) replicado en múltiples containers, con load balancing a nivel de nginx |
Caso | Explicación |
---|---|
Multi-worker | Para una o varias de las configuraciones obligatorias, pueden probar manejar más de un worker en cada container (usar siempre la misma cantidad). Vean el flag -w de Gunicorn y el módulo cluster (v8.x o v10.x) de Node.js |
Servidor remoto | Todos los casos anteriores suponen que el servidor corre en la misma computadora física que el cliente (generador de carga). Pueden probar montar uno o varios de ellos en otra computadora (otra en la misma casa, o un servidor en algún proveedor cloud) y comparar las métricas al "alejar" cliente de servidor. Consideren en el análisis también que las características de la computadora corriendo el servidor o el cliente pueden cambiar en esos casos. |
... | ... pueden agregar otros casos que se les ocurran |
Para aquellos que prueben endpoints de contenido estático, también pueden probar que el nginx sirva el contenido estático directamente. En casos en que vayan a tomar medidas del nginx, consideren que también se puede configurar la cantidad de workers de nginx, y deberían usar la misma cantidad que usen en los otros servidores (cuando es nginx el que sirve el contenido) para poder comparar de manera más justa.
Hay muchos tipos de escenarios de carga y pruebas de performance en general. Pueden leer por ejemplo acá (o en cualquiera de los miles de links al googlear sobre el tema) sobre algunos tipos de escenarios que pueden implementar. Queda a decisión de cada grupo elegir cuáles implementar, considerando siempre cuál es el que más útil les resulta para analizar lo que quieran estudiar.
- Nodejs:
- Express:
- Python:
- Flask:
- Gunicorn:
- Nginx:
- Docker:
- Docker-compose:
- StatsD:
- Graphite:
- Grafana:
- imagen usada (statsd + graphite):
- Gotchas:
- Artillery:
Es posible que necesiten ejecutar los comandos con sudo
, según el sistema que usen y cómo lo hayan instalado.
# Ver qué containers existen
docker ps [-a]
# Ver qué imagenes hay en mi máquina
docker images
# Ver uso de recursos de containers (como "top" en linux)
# Ejemplo con formato específico: docker stats --format '{{.Name}}\t{{.ID}}\t{{.CPUPerc}}\t{{.MemUsage}}'
docker stats [--format <format_string>]
# Descargar una imagen
docker pull <image>[:<tag>]
# Eliminar un container
docker rm <container_id> [-f]
# Eliminar una imagen
docker rmi <image_id> [-f]
# Versión instalada
docker version
Todos los siguientes comandos deben ejecutarse desde la carpeta en donde está el archivo docker-compose.yml
del proyecto.
Es posible que necesiten ejecutar los comandos con sudo
, según el sistema que usen y cómo lo hayan instalado.
# ALIAS para escribir menos nomás
alias docc='docker-compose'
# Ayuda general
docc --help
# Ayuda genral para cualquier comando
docc [COMMAND] --help
# Levantar servicios.
# Sugerencia: Usar la opción -d para levantar en background, y poder seguir usando la terminal
# También sirve para escalar horizontalmente un servicio que ya se esté ejecutando [buscar opción --scale].
# Si no se especifica al menos un servicio, se levantan todos
docc up [options] [SERVICE...]
# Ver logs de un servicio ejecutándose en background
docc logs [options] [SERVICE]
# Listar containers y sus estados
docc ps
# Restartear servicios
# Si no se indica al menos un servicio, se restartean todos
docc restart [SERVICE...]
# Frenar servicios corriendo en background (con la opción --detach del `up`)
# Si no se lista ningún servicio, se frenan todos.
# Esto solo frena servicio, no borra el container ni los datos que hayan en el mismo
docc stop [SERVICE...]
# Frenar containers y borrar tanto los containers como las imágenes y los volúmenes de almacenamiento
# (se pierden todos los datos que hubiera en el container).
# Esto aplica a TODOS los levantados con `up`, no filtra por servicio
docc down
# Levantar un nuevo container de un servicio y ejecutar un comando adentro
# (util para tener por ejemplo una terminal dentro de un container e inspeccionarlo o hacer pruebas manuales).
# Como es siempre sobre un container nuevo, lo que ven es el resultado de su docker-compose.yml y sus dockerfiles
# Ejemplo: docc run graphite bash
docc run SERVICE COMMAND
# Correr un comando en un container que ya existe y ya está corriendo.
# Parecido a `run` pero sobre un container en ejecución.
# Útil para alterar o inspeccionar algo que se está ejecutando.
#Lo que ven adentro puede no ser el resultado directo del docker-compose.yml + dockerfiles, así que mucho cuidado
# si van a modificar sus containers así, porque puede ser difícil de reproducir luego.
# Ejemplo: docc exec graphite bash
docc exec SERVICE COMMAND
# Versión instalada
docc version