Blog ·
Elegir el rango de VMID en un negocio de hosting
Un VMID es una identidad, no un contador. Lo que decide el rango elegido: qué puede crear la plataforma, qué puede leer y qué ocurre cuando se llena.
Por Provibr
Toda instalación de Proxmox empieza en 100 y cuenta hacia arriba. Eso funciona bien hasta que la máquina deja de ser solo de uno: hasta que hay un panel de control creando huéspedes en ella, un compañero creando huéspedes a mano, una biblioteca de plantillas y un trabajo de copia de seguridad que restaura algo a las tres de la madrugada.
En ese punto el VMID deja de ser un contador y pasa a ser una identidad, y el rango que se entrega a la automatización deja de ser una comodidad y pasa a ser una frontera. Conviene elegirlo con deliberación, porque dos de las tres cosas que decide un rango son invisibles hasta que salen mal.
Qué permite realmente Proxmox
El espacio completo es más amplio de lo que casi todo el mundo supone:
| Valor | |
|---|---|
| VMID más bajo que acepta Proxmox | 100 |
| VMID más alto que acepta Proxmox | 999999999 |
| Uso manual habitual | De 100 a unos pocos cientos |
Nueve dígitos. No es una invitación a usarlos todos, pero sí significa que la preocupación habitual, «¿me voy a quedar sin números?», es la preocupación equivocada. Un rango mal elegido se agota mucho antes de que se agoten los números.
Esos dos límites son los únicos que impone el hipervisor. Todo lo demás, que es la parte que importa, es una decisión propia.
Un rango es una frontera de permisos antes que un asignador
Esta es la parte que se pasa por alto con facilidad. Si a una plataforma se le dice «asigna de 5000 a 8999», se le han dicho dos cosas a la vez:
- Qué puede crear. Los huéspedes nuevos reciben un número de esa ventana y de ningún otro sitio.
- Qué no es suyo. Todo lo que queda fuera de esa ventana lo hizo otra persona, y la plataforma no tiene nada que hacer ahí.
La segunda merece estar escrita en la pared. Cuando construimos la integración con Proxmox, el rango de vmid de los ajustes de la integración está descrito en el código exactamente así: la plataforma asigna únicamente dentro de [vmidRangeStart, vmidRangeEnd], de modo que una integración no puede, por construcción, alcanzar una VM fuera de él. Quien mantiene sus VM de producción fuera del rango las mantiene fuera de alcance.
Ese encuadre cambia la forma de elegir los números. No se trata de elegir dónde empieza a contar la automatización, sino de trazar una línea alrededor de las máquinas que se está dispuesto a dejar que borre un programa.
El lado de la lectura es otra pregunta, y muerde más tarde
Crear es la mitad obvia. Leer es la mitad que nos sorprendió.
Nuestro agente consulta el clúster una vez por ronda con un único GET /cluster/resources?type=vm, que no toca ningún huésped. Pero por cada huésped en marcha de esa respuesta hacía después dos llamadas más al agente invitado de QEMU, network-get-interfaces y get-fsinfo, para conocer direcciones y uso de disco. Esa lista salía directamente de la consulta al clúster, sin filtrar.
En un hipervisor de pruebas que además ejecuta cargas de producción, esa lista incluía las máquinas de producción. Las lecturas son inofensivas en el sentido de que no se escribe nada. No lo son en el sentido que importa, porque «solo lectura» no responde a la pregunta ¿por qué habla el agente con el agente invitado de una máquina que no es suya?
La corrección consiste en que ahora el mismo rango acota también las lecturas. Para quien construya esto por su cuenta, la lección se generaliza: una frontera que solo cubre las escrituras es media frontera, y la mitad de lectura es la que nadie echa en falta.
Dónde debe vivir el rango
Tuvimos que responder a esto una vez y merece la pena exponer el razonamiento, porque la respuesta obvia es la equivocada.
La respuesta obvia es poner el rango junto a las credenciales, en el bloque que la plataforma empuja al agente cuando se crea la integración. Es un solo mensaje, llega con todo lo demás, listo.
Eso falla en un reinicio. Las credenciales se empujan una sola vez; tras un reinicio el agente reconstruye sus integraciones desde su caja fuerte local, sin ningún empujón. Una frontera entregada por ese camino desaparece en silencio la primera vez que el proceso se reinicia, y una frontera que deja de aplicarse sin avisar es peor que ninguna frontera, porque nadie va a buscarla.
Así que el rango viaja en su propio mensaje, enviado en cada saludo de sesión, con el estado completo. Dos vías de entrega, una sola fuente de verdad. Existe además una variable de entorno en el host como recurso de reserva, y el orden entre las dos es deliberado:
| Fuente | Cuándo gana |
|---|---|
| El ajuste de la plataforma | Siempre que haya enviado uno |
| Variable de entorno del host | Solo cuando la plataforma no dijo nada |
| Ningún límite | Cuando ninguna de las dos dijo nada |
El orden tentador es el inverso, con la teoría de que un cinturón abrochado en el host es más fuerte. Eso produce un estado que nadie puede ver: un administrador que una vez fijó una variable conserva en silencio una frontera que ya no coincide con lo que muestra el panel, y un cliente que amplía el rango en la interfaz no consigue entender por qué no ha cambiado nada.
Un detalle pequeño con un modo de fallo grande: un valor ilegible se trata como «nadie», no como «todo el mundo». Quien pide una frontera y comete una errata no obtiene ninguna lectura, más una línea de registro que dice por qué. El coste es cosmético: una dirección y una cifra de disco que faltan en el panel. La alternativa, recaer en «todo está permitido», es el fallo clásico de una lista de permitidos: falla abierta, en silencio, justo en el caso en que alguien intentaba ser cuidadoso.
Las plantillas van fuera del rango. Siempre.
Esta es una regla y no una preferencia, y es el error que se come a sí mismo.
Un asignador reparte números del rango. Si se pone una plantilla dentro del rango, un día el asignador llega hasta ella, se crea un pedido nuevo sobre ese número y la plantilla se convierte en la VM de un cliente. El pedido siguiente clona a partir de una máquina que ya no es una plantilla. Por eso el validador rechaza un VMID de plantilla de Proxmox que caiga dentro del rango de la integración, y el asignador trata además cada plantilla enlazada como ocupada, por si alguien amplió el rango después de enlazar una.
Fuera del rango, una plantilla puede vivir en cualquier sitio. El rango acota lo que la plataforma crea, no dónde se permite que esté el material de origen.
Nunca reutilizar un número, ni siquiera cuando la máquina ya no está
El asignador reparte el VMID libre más bajo del rango, y «libre» es más estricto de lo que cabría esperar: cada VMID que la integración ha repartido alguna vez cuenta como ocupado, incluidos los de servidores eliminados.
Eso parece un derroche durante unos cinco segundos, hasta que se piensa en lo que significaría reutilizar. Un evento de estado tardío de la VM antigua aterriza en la fila nueva. El historial de auditoría de dos servidores distintos se mezcla en una sola línea de tiempo. Alguien restaura la copia de seguridad de ayer sobre un número que ahora pertenece a otro cliente.
Curiosamente, en el mismo producto tomamos la decisión contraria para las direcciones IP, y por una buena razón: una dirección es una dirección, y no reutilizar ninguna agotaría para siempre un /24 después de 254 máquinas. Un VMID es el nombre del historial de una máquina. Los rangos son baratos; la confusión no.
Hay una tercera categoría de «ocupado» que solo aparece cuando hay más de una cosa creando máquinas. Una restauración que todavía está en marcha ha reclamado su VMID de destino pero aún no tiene fila de servidor, así que a una segunda restauración en el mismo minuto se le entregaría ese mismo número «libre». Todo lo que reserva un VMID antes de que exista la fila tiene que contarse.
Qué ocurre cuando el rango se llena
Dos respuestas, y deben existir las dos.
La amable es aritmética: el asignador recorre el rango, no encuentra nada libre y devuelve un fallo específico que nombra el principio y el final del rango. El cliente lee «este rango está lleno, de 5000 a 8999» en lugar de un error del hipervisor cuatro capas más abajo.
La real es la base de datos. La restricción de unicidad sobre (integración, identificador externo) es el árbitro: dos pedidos hechos en el mismo instante pueden ver los dos el mismo número libre más bajo, y sobrevive exactamente una inserción. El perdedor reintenta, hasta un número pequeño y fijo de intentos. Comprobar primero e insertar después abre justo la carrera que la restricción cierra, así que la restricción es el mecanismo y el mensaje amable es una cortesía por encima de él.
Conviene fijarse en lo que no usamos: Proxmox tiene un endpoint de siguiente identificador libre, y aquí es la herramienta equivocada. Responde a «¿qué número está libre en este clúster?», que es una pregunta distinta de «¿qué número está libre y dentro de la ventana que me pertenece?». Si se le pregunta al hipervisor, antes o después entregará un número fuera de la frontera propia.
Así que, en la práctica
- Empezar alto y dejar en paz los números bajos. De 100 a unos pocos cientos es donde viven las máquinas hechas a mano en toda instalación de Proxmox, incluida la propia dentro de tres años, cuando esta decisión ya se haya olvidado.
- Elegir un rango con una forma evidente. De 5000 a 8999 se lee como «nuestro» de un vistazo en la interfaz web. Un rango de 4096 números son cuatro mil máquinas en una sola integración.
- Poner las plantillas fuera, en su propio bloque, bien lejos.
- Anotar el rango donde la plataforma pueda verlo, no solo en un fichero de configuración del host, y asegurarse de que se reenvía en lugar de enviarse una sola vez.
- Comprobar que las lecturas también están acotadas, no solo las creaciones y las eliminaciones.
El número en sí importa poco. Lo que importa es que la frontera sea una sola cosa, visible en un solo sitio, aplicada por igual a lecturas y escrituras, y que falle cerrada cuando no se puede leer.
Esto salió de construir la integración con Proxmox para Provibr, donde el agente se ejecuta dentro de la propia red del cliente y solo conecta hacia fuera.