# 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](https://github.com/remote-android/redroid-doc) — Android real corriendo sobre el kernel del blade. Es la única forma sensata de hacer esto con el hardware que hay. ## La arquitectura ![Arquitectura de la granja](../../assets/arquitectura.svg) ``` 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): ```bash 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.com` y `deb.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](../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 ```bash 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 ```bash 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-.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 ```bash ansible-playbook site.yml ``` Idempotente: relanzable sin romper nada. ### 4. Comprobar ```bash ansible-playbook playbooks/status.yml ``` ### 5. Instalar un APK en toda la flota ```bash 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`: ```bash 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 `:`, empezando en 5555. Funciona en cualquier sitio sin saber nada de la LAN. Es el modo con el que empezar. ![Los dos modos de red](../../assets/red-modos.svg) ### `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. ```yaml 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: ```bash 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: ```bash 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-compose` renderiza YAML válido **en los dos modos de red**, con los puertos y las IPs bien asignados - `fleet.sh` pasa `bash -n` una 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](https://github.com/shamanec/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](../../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: ```yaml 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`).