Desplegando un servidor web con alta capacidad

Aquí estoy lamiéndome las heridas de esta noche pasada. Hace unas semanas un cliente me contactó para preparar para alta capacidad una página web que administra. La página web sirve para hacer las inscripciones a una carrera de motos y el día de las inscripciones aquello se pone a fuego.

Tras un presupuesto super ajustado, el cliente opta por la opción más económica. El resultado? pues tras varios “yaque” (instálame un servidor de correo “yaqueestás”), prisas y cambios a último momento, un aumento considerable del tráfico respeto el año pasado y vete a saber que más, el servidor ha estado super saturado y no ha podido servir todas las peticiones necesarias en tiempo y forma; al menos lo importante, que son las inscripciones, se hicieron y con un bajo porcentaje de fallo.

La aplicación está hecha con zendframework2, ergo php5.6, con configuraciones a fuego en el código de la configuración del servidor, correo, etc. Además la web se encontraba en un servidor CentOS 7.0 (del 2014), con unas configuraciones crípticas, no, lo siguiente. ¿Lo de respetar los directorios según LHFS?, ¡¿pa’qué?!.

Para no tener que tocar la configuración del servidor que estaba funcionando, decidí desplegar la página web en otro servidor en mi humilde infraestructura y realizar primero ahí las pruebas.

El entorno de pruebas
Se montó una maquina virtual lxc con debian 11, con 8Gb de RAM y 2 CPU Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz, en mi humilde infraestructura tengo un cuello de botella pendiente de …

Consultar procesos con Celery dentro de uwsgi

Después de unos días de trabajo en mi entorno de desarrollo ha llegado el momento de poner la última versión de la aplicación que estoy haciendo a producción. Como el entorno de producción tiene una configuración distinta que la del entorno de desarrollo, aquí han empezado a aparecer los problemas.

Lo primero es que tenemos una cosa ahí enmedio que se llama uwsgi y es quien ejecuta el código, en lugar del python manage.py runserver. Una de las cosas que me he encontrado es que el trozo de código que comprueba que hay tareas ejecutándose en Celery, no funciona correctamente. En mi caso estaba usando

import time, json
_stats = os.popen('celery inspect stats --json').read()
time.sleep(1)
_stats = json.loads(_stats)

No funcionaba correctamente. He tenido que quebrarme un poco la cabeza para inspeccionar que es lo que estaba ocurriendo, por suerte existe una librería que se llama remote_pdb

(venv) root@app-dev:/var/www/html/app# pip3 install remote-pdb

Y añadimos lo siguiente en el trozo de código que queramos inspeccionar que esté corriendo a través de uwsgi

from remote_pdb import RemotePdb
RemotePdb('127.0.0.1', 4444).set_trace()

Y nos connectaremos al trace por telnet

root@digitplan-dev:~# telnet localhost 4444
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
/var/www/html/app/web/rrd.py(465)check_task_status()
-"""
(Pdb) 

A partir de aquí he podido comprobar que algo le pasa a celery, ya que cualquier comando que ejecutase con celery devolvía vacío. No me ha quedado otra que realizar esta comprobación de otra forma

from celery import Celery
_app = Celery("nombreapp")
_running_tasks = _app.control.inspect().active()['celery@' + str(hostname)]
_status 

Django: tareas en background con celery y rabbitmq

Venga, con este post vamos a subir un poco el nivel de nuestras aplicaciones!

Una de las problemáticas que te encuentras al programar con django es que el envío de mails es horrorosamente lento, el proceso de enviar el mail es relativamente rápido, pero no termino de entender el porqué en general es lento (la conexión y sobre todo la desconexión). Para ello una de las soluciones que más o menos ya había aplicado a mi manera con command hacía que la ejecución cada minuto se solapase con la ejecución de minutos anteriores. Hice una ñapa hace unos días hasta encontrar una solución un poco más elegante. Aquí os la traigo! :D

Hay varios posts que hablan de celery, incluso el libro que uso de consulta “Django 3 by Example”, hablan de usar @tank, pero esta opción está descontinuada para la versión 5, así que aquí veremos como hacerlo con Celery 5. Toda la info la he sacado de éste post.

Configurar e instalar celery y rabbitmq en nuestro proyecto/sistema
Lo primero será instalar celery en nuestro proyecto

laura@melatonina:~/dev/whistleblowerbox$ source venv/bin/activate
(venv) laura@melatonina:~/dev/whistleblowerbox$ pip install celery

A continuación instalamos rabbitmq en nuestro sistema. Rabbitmq es el servicio que encolará las tareas que le pasemos y luego las ejecutará en background

root@melatonina:~# apt -y install rabbitmq-server

Ahora en el fichero settings.py de nuestro proyecto añadimos

(venv) laura@melatonina:~/dev/whistleblowerbox$ vi wbox/settings.py
CELERY_BROKER_URL = 'amqp://localhost'

A continuación, en el mismo directorio donde se encuentra el fichero settings.py vamos a crear uno que se …

Configurar mutt

Mutt es un cliente de correo electrónico de consola, muy útil por ejemplo para ver los mails que envía el sistema. Es muy básico, pero es de gran utilidad para entender un poco más tus sistemas.

En mi caso me encuentro que necesito analizar un script que se ejecuta cada minuto y para ello necesito ver en el listado de mails la hora y que se me pongan los mails nuevos arriba en lugar de abajo.

Para ello voy a modificar (o crear si no está) el fichero ~/.muttrc con este contenido

#set date_format="%d %b %R"   # 06 May 07:55
set date_format="%F %T"       # 2021-05-06 09:20:03
set index_format="%4C %Z %D %-15.15L (%4l) %s"
set sort_aux = last-date-received
set sort = reverse-threads

Aquí el manual de mutt.…

Django: definir variables de entorno

Cuando estamos desarrollando una aplicación con django es común que tengamos que ir haciendo cambios en el fichero settings.py, también es común que tengamos unas URL y configuración de la base de datos distinta en cada entorno, sea el desarrollo como el de producción.

En esta entrada veremos como definir variables de entorno que podremos cargar de forma independiente depende de la instancia en la que nos encontremos. Esto también nos permitirá de cara al futuro desplegar una aplicación mediante docker, donde definiremos las variables “desde fuera”.

Vamos a empezar pues.

Lo primero será instalar la librería django-environ (cuidado que está environ, esta no nos sirve)

(venv) laura@melatonina:~/dev/proyecto$ pip3 install django-environ

A continuación en el directorio donde está el fichero settings.py de nuestro proyecto añadiremos un fichero con el siguiente contenido:

(venv) laura@melatonina:~/dev/proyecto$ vi proyecto/.env
DJANGO_SECRET_KEY=************************************************
DJANGO_ALLOWED_HOSTS=localhost,proyecto.capa8.net
DJANGO_DEBUG=True
DATABASE_URL=postgres://proyecto_user:********@localhost:5432/proyecto_db
ENV=development

# Database settings
DB_NAME=proyecto_db
DB_USER=proyecto_user
DB_PASSWORD=********
DB_HOST=localhost

Es importante que no haya ningún espacio ni antes ni después del =, ya que sino fallará.

A continuación en el fichero proyecto/settings.py modificamos las siguientes líneas:

(venv) laura@melatonina:~/dev/proyecto$ vi proyecto/settings.py
import environ, os

env = environ.Env()
environ.Env.read_env()

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('DJANGO_SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
# False if not in os.environ because of casting above
DEBUG = env('DJANGO_DEBUG')

ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", [])

# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
    'default': env.db('DATABASE_URL'),

Proxmox: redireccionar puerto con iptables y preservar la ip

Hace un par de años hice éste post sobre como redireccionar puertos a los contenedores que se encuentran dentro de un proxmox. Esta solución me fué de maravilla hasta que me di cuenta que el servidor nginx que estaba escuchando en los puertos 80 y 443 en lugar de guardar la IP del cliente que se conectaba, aparecía la IP del proxmox, cosa que creaba algunos problemas a la hora de bloquear intentos de acceso, ya que bloqueabas el acceso a todo el mundo… vamos… un pequeño problemilla de nada :P

Hace unos meses me propuse encontrar la solución pero hasta ahora no me ha sido urgente solucionarlo, ya que tengo que configurar otro proxmox con una ip compartida entre varios contenedores, cada uno de ellos con sus servicios y la cosa era mirar como afinaba esto… así que tras unos 4 días de estar rascando, aquí os traigo la solución :)

iptables -A PREROUTING -t nat -i vmbr0 -d 163.172.109.193 -p tcp --dport 443 -j DNAT --to-destination 192.168.1.2:443
iptables -A FORWARD -p tcp -d 192.168.1.2 --dport 443 -j ACCEPT
iptables -A FORWARD -i vmbr1 -j ACCEPT
iptables -A FORWARD -i vmbr0 -o vmbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i vmbr1 -o vmbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o vmbr0 -j MASQUERADE

– La primera regla de PREROUTING, los paquetes entran por la interfaz vmbr0 des de la ip 163.172.109.193, que es la ip pública del servidor. Mandamos todo el …

Thinkpad no suspende con debian 11

Toda la instalación de debian 11 en mi thinkpad yoga 260, perfecta, solo un problema que ya me encontré el otro día con una thinkpad T460 que le vendí a un cliente; tras suspender el portátil al cerrar la tapa, este no vuelve a levantar una vez abierta de nuevo y se tiene que tirar de botonazo.

Gestionar wordpress por terminal con wp-cli

Lo bonito de meterte en problemas es que para salir de ellos tienes que rebanarte los sesos para encontrar soluciones. Dentro de estas soluciones suelen aparecer metodologías, programas y funcionalidades nuevas. En el post de hoy, tras el marronaco del otro día, la herramienta que reapareció en mi terminal fue wp-cli (como los libros aquellos que has oído a hablar de ellos, pero un día aparece de nuevo y te lo comes como un sabroso manjar).

Este fin de semana pasado el proyecto de kaosenalred.net se ha reconvertido a lanueve.info. En su momento hice este otro post explicando como crawlear una página web con wordpress con httrack. El cliente, en lugar de esperar unas horas mas y arreglar el problema de su wordpress, decidió montar otro wordpress. En el lapso de tiempo de la web vieja y la nueva, en el nuevo wordpress han creado la friolera de 2600 entradas nuevas. A la hora de fusionar las dos webs había varias formas, la escogida esta vez ha sido usar Herramientas Exportar, lo cual te genera un fichero .xml (WXR) con las categorías, los autores, los medios y las entradas. Mi experiencia en importaciones de este tipo en el pasado ha sido que en blogs con mucho contenido es timeout y problemas asegurados, así que otra solución es wp-cli. wp-cli es un programa para gestionar instancias de wordpress.

Para instalarlo, descargaremos el fichero wp-cli.phar, le daremos permisos de ejecución y lo guardaremos al directorio /usr/local/bin/ como wp:

root@planet3D:~# curl 

Searx: Buscar sin ser traceado

Hace unas semanas instalé searx, una solución para hacer búsquedas en los principales buscadores de internet sin ser traceado, es decir, sin dar tu identidad directamente a los buscadores, cosa que hará que cuando aparezca publicidad no te salga nada relacionado con lo que has buscado previamente. ¿Te preguntas como es que en internet los proveedores saben tanto de ti? pues uno de los sitios es por aquí. Searx es una opción para ocultar un poco tus intereses en internet ;)

Searx consta de 3 partes, searx, filtron y morty.

Searx es la interfaz y el programa como tal, filtron es un servicio para limitar el número de búsquedas para evitar que robots se sablen con tu instancia y morty es el proxy que oculta tu información a los buscadores.

Para hacer la instalación seguiremos la documentación de searx

Empezamos con la instalación:

Primero las dependencias

root@searx:~# apt -y upgrade && apt -y dist-upgrade && apt -y install git curl nginx sudo

A continuación clonamos el código de searx y ejecutamos los scripts de instalación

root@searx:/usr/local# git clone https://github.com/searx/searx searx
root@searx:/usr/local# cd searx
root@searx:/usr/local/searx# sudo -H ./utils/searx.sh install all
root@searx:/usr/local/searx# sudo -H ./utils/filtron.sh install all
root@searx:/usr/local/searx# sudo -H ./utils/morty.sh install all

Una vez instalado la cosa nos quedará así:

root@searx:/usr/local# netstat -lanp |grep 'LISTEN '
tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      391/uwsgi           
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      122/morty           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      395/master          
tcp        0      0 127.0.0.1:4004          0.0.0.0:*               LISTEN      764/filtron         
tcp        0      0 127.0.0.1:4005          0.0.0.0:*               LISTEN      764/filtron         

Acceso ssh lento contenedores proxmox debian 11

Llevo unos días que me encuentro que el acceso ssh de los contenedores lxc de proxmox con debian 11 es horrorosamente lento.

Si miramos el log del servicio ssh nos encontramos esto:

root@ns2:/etc/ssh# cat /var/log/auth.log
Jan  4 13:55:25 ns2 dbus-daemon[70]: [system] Failed to activate service 'org.freedesktop.login1': timed out (service_start_timeout=25000ms)
Jan  4 13:55:25 ns2 sshd[280]: pam_systemd(sshd:session): Failed to create session: Failed to activate service 'org.freedesktop.login1': timed out (service_start_timeout=25000ms)
Jan  4 13:58:54 ns2 dbus-daemon[70]: [system] Failed to activate service 'org.freedesktop.login1': timed out (service_start_timeout=25000ms)
Jan  4 14:00:20 ns2 dbus-daemon[70]: [system] Failed to activate service 'org.freedesktop.login1': timed out (service_start_timeout=25000ms)

El motivo es porqué el servicio systemd-logind no está levantado:

root@ns2:~# systemctl status systemd-logind
● systemd-logind.service
     Loaded: masked (Reason: Unit systemd-logind.service is masked.)
     Active: failed (Result: exit-code) since Tue 2022-01-04 14:59:55 CET; 22min ago
   Main PID: 3620 (code=exited, status=226/NAMESPACE)

Jan 04 14:59:55 ns2 systemd[1]: systemd-logind.service: Main process exited, code=exited, status=226/NAMESPACE
Jan 04 14:59:55 ns2 systemd[1]: systemd-logind.service: Failed with result 'exit-code'.
Jan 04 14:59:55 ns2 systemd[1]: Failed to start User Login Management.
Jan 04 14:59:55 ns2 systemd[1]: systemd-logind.service: Scheduled restart job, restart counter is at 5.
Jan 04 14:59:55 ns2 systemd[1]: Stopped User Login Management.
Jan 04 14:59:55 ns2 systemd[1]: systemd-logind.service: Start request repeated too quickly.
Jan 04 14:59:55 ns2 systemd[1]: systemd-logind.service: Failed with result 'exit-code'.
Jan 04 14:59:55 ns2 systemd[1]: Failed to start User Login Management.

Tenemos dos opciones, la primera es posible que tengamos que activar el nesting en el contenedor (ya configurado por defecto en proxmox 7) o la otra opción es:

Lo