Especificaciones tomadas del manual del propietario del gabinete PowerEdge M1000e (Dell, modelo BMX01, rev. A06, oct. 2019), que corrige el spec sheet comercial usado hasta ahora. Hardware: - Peso máximo 200,5 kg, no 179. Profundidad 75,5 cm - Seis fuentes y nueve ventiladores obligatorios, se usen 3 blades o 16. Toda bahía vacía necesita relleno - Conector CEI/IEC C20 a 16 A. La fuente de 3.000 W exige 200-240 V - Irrupción de 55 A por fuente durante 10 ms: un magnetotérmico de curva C salta, hace falta curva D - Temperatura de funcionamiento continuo 10-35 °C - ECM obligatorio con blades M630 de 120 W o más, o por encima de 30 °C Red: - Fabric A (ranuras A1/A2) es la Ethernet integrada de cada blade, así que no hacen falta tarjetas intermedias. Fabrics B y C sí las exigirían y no se usan - Pendiente saber si en A1/A2 hay un switch o un módulo de paso a través: cambia el montaje de red por completo Costes recalculados: el suelo fijo del chasis sube de 350 a ~500 W al contar los nueve ventiladores, los módulos de I/O y las pérdidas de las seis fuentes. 360 €/mes a 24/7, 59 €/mes por ventanas. Que las fuentes y los ventiladores sean obligatorios convierte en hecho documentado lo que antes era estimación: los pilotos parciales en el chasis son el peor caso posible. Documentación: - assets/ con cuatro diagramas SVG propios: arquitectura, chasis, stack de software y modos de red - docs/usabilidad.md: órdenes, flujos de trabajo, problemas conocidos y ergonomía del hardware - Diagramas ASCII del panel frontal, el posterior y la topología de fabrics en hardware_m1000e.md - Eliminada la parte de charla y divulgación, fuera del alcance del repo
11 KiB
Ansible — provisionar la granja Android sobre los blades Debian
Automatiza el montaje completo: de blades Debian pelados a una flota de Android
virtuales controlables por adb desde un único sitio.
No hay móviles físicos en esta granja. Cada "dispositivo" es un contenedor redroid — Android real corriendo sobre el kernel del blade. Es la única forma sensata de hacer esto con el hardware que hay.
La arquitectura
Dell PowerEdge M1000e — 16 blades
│
├── blade-01 ................ COORDINADOR
│ └── adb + fleet.sh único punto de control de toda la flota
│
└── blade-02 .. blade-16 .... NODOS DE CARGA (15)
└── Debian 12 pelado
└── Docker
├── redroid-blade-NN-01 Android 13 ~2 GB
├── redroid-blade-NN-02 Android 13 ~2 GB
└── redroid-blade-NN-03 Android 13 ~2 GB
Con 8 GB por blade → 3 instancias → ~45 Android en total
Debian va directo sobre el blade. Sin Proxmox, sin VMs, sin LXC anidado: con 8 GB por blade, cada capa de hipervisor se come una instancia Android entera.
Requisitos
Nodo de control (tu sobremesa):
sudo apt install ansible
Blades:
- Debian 12 bookworm instalado
- Acceso SSH desde el nodo de control, con sudo o como root
- Salida a internet, o un mirror local de
download.docker.comydeb.debian.org - Kernel con
binder_linux.ko(el estándar de Debian lo trae)
Chasis — un módulo de I/O en A1 como mínimo. La Fabric A es la Ethernet integrada (LOM) de
cada blade, así que no hacen falta tarjetas intermedias; las Fabrics B y C sí las exigirían y
no se usan aquí. Si en A1/A2 hay un módulo switch, la conmutación entre blades es interna al
chasis, que es lo que necesita macvlan. Si hay un módulo de paso a través, salen 16 RJ-45 al
exterior y la conmutación depende de un switch externo. Ver
hardware_m1000e.md.
Ejecuta siempre desde este directorio (ansible/) — ansible.cfg y las rutas de roles son
relativas a él.
Puesta en marcha
1. Inventario
cp inventory/hosts.yml.example inventory/hosts.yml
$EDITOR inventory/hosts.yml # pon las IPs reales de los blades
2. Preflight — esto va primero, siempre
ansible-playbook playbooks/preflight.yml
Es de solo lectura, no toca nada. Contesta lo que ahora mismo no sabemos:
- Qué blade hay dentro del chasis (lo lee de DMI — no hace falta ir a mirar el frontal)
- Si esas CPUs tienen las instrucciones que Android x86_64 necesita
- Si el kernel trae binder
- Cuántas instancias caben en cada blade según su RAM real
Deja un informe en reports/preflight-<fecha>.md con la tabla de toda la flota y el total de
capacidad. Si algún blade sale NO APTO, lo dice ahí y no cuando estés a medio despliegue.
3. Montar la granja
ansible-playbook site.yml
Idempotente: relanzable sin romper nada.
4. Comprobar
ansible-playbook playbooks/status.yml
5. Instalar un APK en toda la flota
ansible-playbook playbooks/apk.yml -e apk_local_path=~/COFRE/CODERS/OASIS_APK/oasis.apk
Control diario desde el coordinador
Entra por SSH al coordinador y usa fleet:
fleet connect # conecta adb a toda la flota
fleet list # qué hay conectado
fleet install app.apk # instala en todos
fleet shell 'getprop ro.build.version.release'
fleet ip # IP de cada Android — útil para el caso de dos APKs
fleet reboot
Los dos modos de red
Se controla con redroid_network_mode en group_vars/all.yml.
portmap (por defecto)
Cada Android detrás de NAT de Docker; adb en <ip_blade>:<puerto>, empezando en 5555.
Funciona en cualquier sitio sin saber nada de la LAN. Es el modo con el que empezar.
macvlan — el que quieres para oasis_mobile
Cada Android coge una IP real de la LAN a través del switch FlexIO del chasis. Los dos APKs que se tienen que ver entre sí se ven como pares de red de verdad, no a través de traducciones. Es lo más cerca de "dos móviles en la misma wifi" sin tener dos móviles.
redroid_network_mode: macvlan
redroid_macvlan_parent: eno1
redroid_macvlan_subnet: 10.0.0.0/24
redroid_macvlan_gateway: 10.0.0.1
redroid_macvlan_ip_prefix: "10.0.0."
Y en el inventario, un redroid_macvlan_ip_offset por blade (el ejemplo ya trae 20, 30, 40…).
El gotcha de macvlan. Un anfitrión no puede hablar con sus propios contenedores macvlan. Es una limitación del driver, no un fallo de configuración. Por eso el coordinador es un blade distinto: desde fuera sí se llega. Si algún día pones adb en un nodo de carga y no ve sus propios Android, es esto.
Reserva también un rango de IPs para los Android en el DHCP de la LAN, o acabarás con colisiones.
El binder, que es donde esto se atasca
Debian 12 compila binder como módulo pero sin binderfs, así que redroid no puede crear sus
devices solo. El rol binder lo resuelve: carga binder_linux con los tres nombres que Android
espera (binder, hwbinder, vndbinder) y lo deja persistente para los siguientes arranques.
Los contenedores de un blade comparten ese binder del kernel anfitrión. Si no está, no arranca ni una instancia.
Si el playbook falla con "faltan devices binder": el módulo estaba ya cargado y en uso, así que no se pudo recargar con los parámetros nuevos. La configuración persistente ya quedó escrita — reinicia ese blade y relanza. Es un fallo de una sola vez.
Ajustes que vas a querer tocar
Todo en group_vars/all.yml.
| Variable | Por defecto | Para qué |
|---|---|---|
redroid_image |
redroid/redroid:13.0.0-latest |
Versión de Android. Hay 11, 12, 13 y 14 |
redroid_instances |
0 (auto) |
Instancias por blade. 0 = calculado desde la RAM real |
redroid_mem_mb |
2048 |
RAM por Android |
redroid_host_reserve_mb |
1500 |
RAM que se le deja a Debian + Docker |
redroid_gpu_mode |
guest |
Sin GPU en los blades → render por software |
redroid_network_mode |
portmap |
portmap o macvlan |
Cambiar la versión de Android en toda la flota, sin editar nada:
ansible-playbook playbooks/devices.yml -e redroid_image=redroid/redroid:14.0.0-latest
La palanca real: la RAM
El cuello de botella son los 8 GB por blade, no la CPU. Si los blades resultan ser M620 (DDR3 ECC), ampliar a 32 GB cuesta poco en segunda mano:
ansible-playbook playbooks/devices.yml -e redroid_instances=14
De ~45 Android a ~210, por menos de lo que cuesta un mes de luz del chasis.
Playbooks
| Playbook | Qué hace | Toca algo |
|---|---|---|
playbooks/preflight.yml |
Reconocimiento: modelo, CPU, RAM, binder, capacidad | No |
site.yml |
La granja entera de cero | Sí |
playbooks/provision.yml |
Solo el hierro: base + binder + Docker | Sí |
playbooks/devices.yml |
Solo los contenedores Android | Sí |
playbooks/coordinator.yml |
Regenera el listado de flota y fleet |
Sí |
playbooks/apk.yml |
Instala un APK en toda la flota | Sí |
playbooks/status.yml |
Estado real de la granja | No |
playbooks/destroy.yml |
Para y borra los contenedores | Sí, destructivo |
destroy.yml exige -e confirmar=si, y conserva los /data salvo que añadas -e borrar_datos=si.
Probarlo antes de tocar el chasis
inventory/hosts.yml.example trae un bloque comentado de piloto de un nodo: el sobremesa hace
de coordinador y de nodo de carga a la vez. Descoméntalo, comenta el bloque granja, y tienes el
flujo entero contra una sola máquina. Con 62 GB de RAM salen unas 8 instancias (tope
redroid_max_instances).
Es la forma sensata de empezar: valida todo el pipeline sin encender 2 kW.
Estructura
ansible/
├── ansible.cfg
├── site.yml playbook maestro
├── inventory/hosts.yml.example 16 blades + piloto de un nodo
├── group_vars/all.yml los mandos del operador
├── playbooks/
│ ├── preflight.yml reconocimiento (solo lectura)
│ ├── provision.yml base + binder + docker
│ ├── devices.yml contenedores Android
│ ├── coordinator.yml listado de flota + fleet
│ ├── apk.yml instalar APK en la flota
│ ├── status.yml estado (solo lectura)
│ ├── destroy.yml desmontar (destructivo)
│ └── templates/preflight-report.md.j2
├── roles/
│ ├── preflight/ DMI, flags de CPU, binder, capacidad
│ ├── base/ paquetes, hora
│ ├── binder/ los tres devices, persistentes
│ ├── docker/ Docker CE + compose v2
│ ├── redroid/ genera el compose y levanta Android
│ └── coordinator/ adb + devices.txt + fleet.sh
└── reports/ informes de preflight (se genera solo)
Estado de verificación
Escrito el 2026-08-08. No se ha ejecutado contra hardware real — el chasis todavía no está accesible y el sobremesa no tiene Docker instalado.
Lo que sí está comprobado:
- Los 18 ficheros YAML parsean correctamente
- La plantilla de
docker-composerenderiza YAML válido en los dos modos de red, con los puertos y las IPs bien asignados fleet.shpasabash -nuna vez renderizado, sin Jinja pendiente
Lo que no está comprobado y solo dirá la realidad:
- Que las imágenes x86_64 de Android arranquen en la generación de CPU de estos blades (el riesgo real si resultan ser M610 — preflight lo detecta antes de gastar tiempo)
- El comportamiento de macvlan sobre los switches FlexIO concretos del chasis
- Que 3 instancias por blade sean cómodas con 8 GB en carga real, no solo en el papel
Siguiente capa: GADS
GADS da UI web y Appium integrado encima de esto. No está
automatizado aquí a propósito: montar un despliegue de Node que no puedo probar sería añadir
complejidad sin verificar. Merece la pena cuando la flota pase de una decena de dispositivos o
cuando haga falta lanzar tests automatizados en vez de trastear con fleet shell.
Seguridad — lee esto antes de encender
El repaso completo está en ../../docs/seguridad.md (11 hallazgos). Lo que no se puede saltar:
adb no tiene autenticación, las imágenes de redroid permiten adb root, y los contenedores son
privileged porque redroid lo exige. Encadenado: quien alcance el puerto 5555 de un blade acaba
siendo root en ese blade, sin exploit ninguno. Con 45 dispositivos son 45 puertas.
Mitigación parcial ya en el código — ata adb solo a la interfaz interna del chasis:
blade-02: { ansible_host: 10.0.0.12, redroid_adb_bind_ip: 10.0.0.12 }
Lo que lo cierra de verdad es una VLAN aislada para la granja. En modo macvlan,
redroid_adb_bind_ip no aplica (adb escucha en la IP de LAN del propio Android) y la VLAN es la
única defensa.
Y antes de nada: cambiar las credenciales de fábrica del CMC y de los 16 iDRAC (root/calvin).