
🌐 También en: English · Deutsch · Français
Deep Dive de la serie en torno al Ansible Tour. Nivel: administración Linux avanzada. El tour explica los roles; los deep dives explican la técnica que hay debajo – cómo funciona, por qué está aquí, qué hace exactamente esta instalación con ella, cómo operarla en el día a día y qué otras opciones habría habido.
El 16 de octubre de 2026 los tres servidores recibieron una ronda de ajuste: el blog y dos servidores Nextcloud. Ninguno era lento. La pregunta era otra: en los tres había memoria libre – ¿dónde ayudaría de verdad y dónde solo parecería ocupada? Este artículo recorre el método, cada cambio con la cifra que lo respalda, lo que se dejó igual a propósito y lo que dicen las mediciones cuatro días después.
🧭 El método
- Picos de siete días, no instantáneas. Un vistazo a
topmuestra un momento. Prometheus guarda 30 días de muestras cada 15 segundos;max_over_time(…[7d])da el peor minuto de la semana, que es lo que los límites deben aguantar. - Contar en lugar de suponer. Cuántos ficheros PHP hay, cuántas tablas temporales van a disco al día, cuántas instancias de inotify están en uso – cada cosa, un único comando.
- Un motivo por cambio. Cada ajuste de abajo tiene una medición al lado. Los ajustes sin medición no se tocaron.
- Antes y después. Las mismas consultas tras el despliegue, más métricas de contenedores que muestran si un límite ha quedado demasiado justo.
Los recursos y qué leer en ellos
| Recurso | Métrica | Trampa |
|---|---|---|
| CPU | uso por contenedor frente a su límite; limitación CFS | Un contenedor puede estar limitado mientras el host está ocioso – el cuello de botella es el límite, no la máquina. |
| Memoria | working set frente al límite | «Usada» incluye la caché de páginas; la RAM libre no se desperdicia, Linux la usa para ficheros. |
| Memoria de workers | PSS en lugar de RSS | El RSS cuenta la memoria compartida (OPcache) en cada proceso y aquí exagera por un factor de cinco. |
| Colas | listen queue de FPM, max_children reached | Cola a cero significa workers suficientes – más solo costarían memoria. |
| Límites del kernel | instancias y watches de inotify, descriptores de fichero | Fallan con errores raros («too many open files») mucho antes de que algo parezca lleno. |
🛡️ Por qué molestarse si nada es lento
- Subir un límite es barato antes de que muerda y caro de diagnosticar después, cuando el síntoma es un cliente de sincronización que se cuelga una vez por semana.
- Los valores por defecto son la suposición de otro. Dos de ellos se equivocaban para Nextcloud por un factor de cuatro.
- Saber qué no cambiar es la mitad del resultado: búferes más grandes en el sitio equivocado solo desplazan el cuello de botella o lo esconden.
🔧 Qué se cambió
| Área | Medido | Cambiado a | Dónde |
|---|---|---|---|
| Workers de PHP-FPM (Nextcloud) | pico de 17 de 20 y 10 de 12 ocupados a la vez | 30 y 20 workers; límites de contenedor 6 y 5 GiB | variables de host |
| OPcache (Nextcloud) | 15.500 y 17.800 ficheros PHP; límite 10.000 = 16.229 huecos | 256 MB, 20.000 ficheros (32.531 huecos) | ConfigMap de PHP |
| APCu (Nextcloud) | valor por defecto de PHP 32 MB, un segmento para todos los workers | 128 MB | ConfigMap de PHP |
| Ficheros temporales de MariaDB | 2.000–3.500 tablas temporales al día en disco | /tmp como tmpfs (host: 4 GB, pod del blog: 512 MiB) | unidad de MariaDB / pod |
| Collabora | «no se pudo cargar Office» en un arranque en frío | 10 y 6 procesos de documento arrancados de antemano | variables de host |
| Peticiones PHP colgadas | ningún límite de tiempo real | request_terminate_timeout 300 s blog / 3.600 s Nextcloud | pool de FPM |
| Kernel: inotify | por defecto 128 instancias por usuario; root ya usaba 63 | 8.192 instancias, 524.288 watches | common_sysctl |
| Kernel: escritura diferida | páginas sucias escritas al 10 / 30 % de la RAM | 5 / 10 % | common_sysctl |
Por qué estos y no otros
- Workers: 17 de 20 es un 85 % en el pico – la siguiente tarde con carga habría hecho cola. Los límites de los contenedores siguieron a los pools, no al revés: PSS por worker por
max_children, más OPcache y APCu, con margen. - OPcache: en el servidor Nextcloud pequeño 1.500 ficheros nunca cabían y se compilaban en cada petición. Eso lo decide el número de ficheros, no una impresión.
- Las tablas temporales con columnas TEXT nunca pueden usar el motor de memoria, así que subir
tmp_table_sizeno habría cambiado nada. Llevar a la RAM el directorio donde se escriben, sí. Una trampa por el camino: junto conPrivateTmp=yesgana el directorio privado de systemd en disco y el tmpfs se ignora sin avisar – descubierto probando consystemd-run, no leyendo. Y en el pod del blog un volumen en memoria cuenta contra el límite de memoria del contenedor, que por eso pasó de 768 MiB a 1.280 MiB. - inotify: K3s, containerd y cada pod con un vigilante de ficheros ocupan instancias como root. 63 de 128 está bien hoy y es un error misterioso tras los próximos pods.
- La escritura diferida al 10 % en lugar del 30 % de 23 GB significa como mucho 2,3 GB de datos sin escribir en lugar de 7 GB – bloqueos más cortos cuando el kernel por fin tiene que escribir y menos datos perdidos en una caída brusca.
tuned y sysctl: quién gana
AlmaLinux ejecuta tuned con el perfil virtual-guest, que fija por su cuenta vm.dirty_ratio = 30. Nuestro 10 es lo que informa el kernel, porque tuned vuelve a aplicar /etc/sysctl.d/ después de su propio perfil (reapply_sysctl = 1, el valor por defecto). Sin eso, el perfil desharía el ajuste en silencio en cada arranque. La única prueba fiable es el valor en vivo con sysctl -n tras un reinicio.
🧊 Qué se dejó igual a propósito
| Área | Medido | Por qué no |
|---|---|---|
| Buffer pool de InnoDB | tasa de aciertos > 99,8 %, pools mayores que los datos (2 GB para 344 MB) | Más memoria no puede mejorar una caché que ya lo contiene todo. |
| Redis | 1–1,5 MB usados | Nada que ganar. |
| PHP-FPM del blog | pico de 6 de 12 workers, cola como mucho 1 | El pool era suficientemente grande. |
| OPcache del blog | ~4.600 ficheros PHP, límite 10.000 | Cabe. |
| Conexiones de MariaDB | pico de 15 de 50 | Margen de sobra. |
La memoria libre que nada usa tampoco se pierde: Linux la llena de caché de ficheros, justo lo que quiere un servidor de ficheros.
📊 Cuatro días después
| Blog | Nextcloud A | Nextcloud B | |
|---|---|---|---|
| Pico de workers PHP activos (7 días) | 8 de 12 | 13 de 30 | 9 de 20 |
max_children alcanzado | 0 | 0 | 0 |
| Pico de memoria del contenedor / límite | 538 MiB / 1 GiB | 763 MiB / 6 GiB | 277 MiB / 5 GiB |
| Periodos de CPU limitados | 2,1 % | 2,2 % | 0,2 % |
| Peticiones PHP lentas (> 5 s) | 0 | 0 | 0 |
| Consultas lentas a la base de datos (> 1 s) | 0 | 0 | 0 |
| Memoria disponible en el host | 5,0 de 7,5 GB | 19 de 23 GB | 8,5 de 11,4 GB |
Ningún pool se acercó a su límite, ningún contenedor a su límite de memoria, la limitación de CPU se queda en pocas unidades porcentuales y los hosts conservan libre la mayor parte de su memoria. Los cambios costaron poco y eliminaron tres techos reales – workers, huecos de bytecode, un límite de tiempo – antes de que nadie chocara con ellos.
El error de medición
Al reunir las cifras de arriba, la primera consulta para el contenedor PHP del blog devolvió un pico de 940 MiB con un límite de 1 GiB – lo que habría pedido de inmediato un límite mayor. La consulta sumaba todas las series del contenedor: durante un despliegue el pod antiguo y el nuevo existen a la vez, y justo tras reiniciar un contenedor cAdvisor informa brevemente del cgroup antiguo y del nuevo. El pico de un solo pod fue de 538 MiB. Sumar por pod, o tomar el máximo por pod, es la lectura correcta; un simple sum por nombre de contenedor suma cosas que nunca existieron en el mismo momento en un mismo proceso.
🧰 Repetir el ejercicio
# worst minute of the week, per pod (not summed over pods!)
max_over_time(max by (pod) (container_memory_working_set_bytes{container="nextcloud-fpm"})[7d:1m])
# share of CPU periods throttled
sum(increase(container_cpu_cfs_throttled_periods_total{container="nextcloud-fpm"}[7d]))
/ sum(increase(container_cpu_cfs_periods_total{container="nextcloud-fpm"}[7d]))
# PHP-FPM peak and queue
max_over_time(sum(phpfpm_active_processes)[7d:1m])
max_over_time(max(phpfpm_listen_queue)[7d:1m])- Cuándo: tras un cambio de carga (usuarios nuevos, una app nueva), tras una actualización mayor y, si no, cada pocos meses.
- Dónde mirar primero: el panel «Container-Ressourcen» (límites, limitación, OOM kills) y las alertas de memoria y limitación de contenedores, que envían un correo cuando un límite se queda justo.
- Qué apuntar: la medición junto al ajuste. La guía de operación tiene una tabla exactamente para eso, para que la próxima ronda parta de cifras y no de recuerdos.
🆘 Chuleta: síntoma → comando
La versión corta de este artículo para el momento en que algo va mal. Ejecutar en el host como root; <…> son marcadores.
| Síntoma | Comando |
|---|---|
| ¿Le falta memoria a un pod? | max_over_time(max by (pod) (container_memory_working_set_bytes{container="<c>"})[7d:1m]) |
| ¿Se frena a un pod (throttling)? | sum(increase(container_cpu_cfs_throttled_periods_total{container="<c>"}[7d])) |
| ¿Hay suficientes workers PHP? | max_over_time(sum(phpfpm_active_processes)[7d:1m]) |
| ¿Se ha matado algo por falta de memoria? | journalctl -k | grep -i "out of memory" |
| ¿Qué le queda al host? | free -h; uptime; df -h / |
| Dónde mirar primero | # Grafana: dashboard "Container-Ressourcen" (limits, throttling, OOM kills) |
🔭 Lo que viene
- Pruebas de carga con k6 contra una instancia de pruebas, para ver dónde están los techos en lugar de esperarlos.
- Umbrales de alerta a partir de picos medidos en lugar de números redondos: el primero, para la cola de PHP-FPM, salió de 14 días de datos en octubre de 2026.
- Planificación de capacidad: crecimiento mensual de los datos del servidor Nextcloud mayor frente a su disco.
⚖️ Alternativas – y por qué medir primero
| Enfoque | Punto fuerte | Debilidad |
|---|---|---|
| Ajustar según una lista | Rápido, sin herramientas | Cambia lo que necesitaba el autor de la lista, no lo que necesita este servidor. |
| Una máquina más grande | Siempre ayuda un poco | Esconde el techo en lugar de moverlo; cuesta cada mes. |
| Herramientas APM comerciales | Trazas hasta la función | Coste, un agente en cada contenedor y desproporcionado para estas cargas. |
| Medir y cambiar una sola cosa | Cada cambio tiene un motivo y una comprobación | Necesita métricas ya existentes – por eso llegaron primero. |
El resultado más valioso del día no fue un ajuste sino una lista: ocho cambios, cada uno con una cifra, y cinco cosas que se dejaron igual, cada una también con una cifra. La próxima ronda partirá de ahí, y si un valor resulta equivocado, quedará claro en qué se basaba.


