Salta al contenuto

Blog ·

VM o container? Cosa vendere su Proxmox

Un guest KVM e un container LXC condividono un hypervisor, uno spazio di numeri e un listino, e ben poco altro. Le differenze che costano denaro.

Di Provibr

Proxmox VE esegue due tipi di guest. Una macchina virtuale KVM avvia un kernel proprio dietro a un firmware emulato, e un container LXC condivide il kernel dell’host su cui si trova. Per un cliente che compra un VPS entrambi sono «un server con root sopra», ed è per questo che il confronto di solito viene fatto in una tabella di marketing con righe sulla densità e sull’overhead.

Quelle righe sono vere e non sono quelle che le costeranno care. Ciò che costa è l’insieme dei punti in cui i due sembrano identici nell’API e si comportano in modo diverso al di sotto, perché è lì che un pannello, un sistema di fatturazione o un flusso d’ordine fa in silenzio la cosa sbagliata e per un mese non se ne accorge nessuno.

Di recente abbiamo aggiunto un secondo tipo di calcolo alla nostra integrazione Proxmox, il che ha voluto dire leggere l’API con una domanda precisa: «dove divergono questi due». Ecco che cosa ne è uscito, nell’ordine in cui ci ha sorpreso.

Un cluster, uno spazio di numeri

Si parta dall’identificatore, perché tutto il resto ci si aggancia. Un guest Proxmox viene indirizzato con un numero, ed è allettante supporre che macchine virtuali e container abbiano ciascuno il proprio intervallo: 100 potrebbe essere una VM e 100 potrebbe essere anche un container, distinti dal tipo.

Non possono. Lo spazio di numeri è condiviso da tutto il cluster, e lo dicono tre proprietà dell’API. L’endpoint che rilascia il successivo identificatore libero non accetta alcun parametro di tipo, quindi non può sapere quale dei due sta per creare. L’elenco delle risorse del cluster restituisce macchine e container insieme sullo stesso campo numerico. E l’elenco dei guest del cluster stesso è indicizzato su quel numero.

Questo ha una conseguenza diretta per qualunque cosa conservi guest Proxmox in un proprio database. Metta il tipo nel suo vincolo di unicità, in modo che «macchina 5001» e «container 5001» siano due righe diverse, e avrà uno schema capace di contenere due record per un solo guest e di lasciarli litigare. L’identificatore è unico da solo, e il tipo è un’osservazione conservata accanto a esso. Se sta scegliendo gli intervalli per una flotta, l’aritmetica in come scegliere un intervallo di VMID su Proxmox non cambia quando arrivano i container, ed è proprio questo il punto.

Un container si scompatta, non si clona

La seconda divergenza è il percorso di creazione, ed è più grande di quanto sembri.

Una macchina virtuale si produce normalmente clonando un modello: si indica un’origine, si sceglie tra copia completa e copia collegata, e si ottiene una copia di un’immagine di disco. Un container si produce scompattando un archivio su un volume nuovo. Non c’è un guest di origine, non c’è la scelta tra completa e collegata, e non c’è un’unità cloud-init, perché non c’è nulla a cui collegarla.

Quindi il corpo della richiesta non ha quasi nessun campo in comune con quello che crea una macchina. Un container vuole ostemplate che punta a un archivio su uno storage, un flag unprivileged, un hostname, un rootfs e un valore swap. Una macchina vuole scsi0, scsihw, bios, boot e un ide2 quando si avvia da una ISO. Riutilizzare un vocabolario per l’altro è l’errore da evitare deliberatamente: le parole sono abbastanza simili che un campo chiamato disk finirà per significare due cose diverse nello stesso codice nel giro di un anno.

Due dettagli di quel corpo vale la pena rubarli, a prescindere da ciò su cui costruisce.

rootfs=<storage>:<size> è un’allocazione, non un riferimento. Significa «crea su questo storage un nuovo volume di tanti gigabyte». La forma che nomina un volume esistente sembra quasi identica e fa qualcosa di categoricamente diverso: adotta un volume che potrebbe contenere ancora i dati di qualcun altro.

Omettere un parametro è la risposta sicura. Nesting, keyctl, FUSE, bind mount e passthrough dei dispositivi sono tutti disattivati se non li invia, e non c’è alcun valore «vuoto» che debba ricordarsi di scrivere. La postura predefinita corretta per una piattaforma di hosting è quindi una richiesta breve anziché lunga. Il campo dell’architettura racconta la stessa storia: Proxmox la ricava dall’archivio, e inviarne una in disaccordo fallisce al momento dello scompattamento.

Tre cose che un container non farà mai

Alcune lacune sono voci di una roadmap. Tre sono strutturali, e confondere le due categorie nel proprio prodotto è il modo in cui si finisce per promettere una funzione che non può esistere.

AzioneGuest KVMContainer LXC
Avviare, arrestare, spegnere, eliminaresìsì, stessa forma, segmento di percorso diverso
Mettere in pausa o ibernaresìno: quello è il monitor QEMU
Avviare dalla rete con PXEsìmai
Installare da una ISOsìmai
Scegliere un ordine di avvio, BIOS o UEFIsìmai
Consoleframebuffer VNCun terminale, su un protocollo diverso
Swapinvisibile all’hostun’impostazione dell’hypervisor con un numero

Cinque di quelle righe richiedono più di una cella, in tre gruppi.

Netboot, installazione da ISO e scelta del firmware sono impossibili per un motivo solo, non per tre: un container non ha firmware. Non esegue un bootloader, non ha un ordine di avvio, e avviarlo non comporta nulla a cui si possa dire di provare prima la rete. Se vende un prodotto in cui «reinstallare dalla nostra libreria di immagini» è una funzione, noti che il percorso PXE dalla risposta DHCP all’installazione conclusa non ha equivalente per i container. Ricostruire un container è un meccanismo diverso con garanzie diverse, e merita una schermata propria anziché un pulsante disattivato.

La console è lo stesso tipo di differenza sotto un nome familiare. Proxmox le darà una console per un container, ma è un terminale su un websocket, non il framebuffer di un adattatore grafico, e un client VNC non la disegnerà. Una sola scheda console ha ora due implementazioni dietro di sé, e la risposta onesta nel frattempo è dire che la scheda non è ancora disponibile, anziché aprire un visualizzatore che mostra un rettangolo nero.

Pausa e ibernazione meritano una nota a piè di pagina perché intrappolano la logica di applicazione dei limiti. Se la sua piattaforma sospende una macchina quando un cliente supera un limite, e «sospendere» significa mettere in pausa, allora i container escono del tutto dall’applicazione: il comando viene rifiutato, nessun worker segnala nulla, e il cliente continua a lavorare. Il confine appartiene alla modalità, non al comando. Arrestare un container è perfettamente possibile.

Lo swap è una decisione di prodotto, non un valore predefinito

Ecco quello che comparirà nei ticket di assistenza.

Proxmox assegna a un container nuovo 512 MiB di swap se non gli dice altrimenti. Lo erediti in silenzio e un piano venduto come «2 GiB di memoria» consegna 2,5 GiB di memoria utilizzabile, e la differenza non compare da nessuna parte come un numero più grande: compare come un container che diventa lento invece di incontrare un’onesta condizione di esaurimento della memoria, mesi dopo, su un host che ha cominciato a fare swap.

Noi inviamo il valore esplicitamente a ogni creazione, con zero come nostro valore predefinito. Zero è una scelta e non un’assenza. Qualunque cosa scelga, la scelga deliberatamente, la scriva sul piano, e si assicuri che il numero che il cliente vede sia il numero che l’hypervisor riceve.

Misurare lo swap ha un prezzo suo. L’elenco complessivo che le dà i contatori di CPU, memoria, disco e rete per ogni guest di un cluster non porta alcun campo di swap, per nessuno dei due tipi. L’unico posto in cui Proxmox lo riporta è l’endpoint di stato del singolo guest, quindi è una chiamata HTTP in più per ogni container in esecuzione, per sempre. Noi lo leggiamo una volta al minuto: tre volte al minuto triplicano il carico sull’hypervisor di qualcun altro per un numero che si muove nell’arco di minuti, e una volta ogni cinque minuti mette esattamente un campione in un intervallo di aggregazione di cinque minuti, il che rende la colonna del massimo uguale a quella della media e quindi decorativa.

Due bug che abbiamo pubblicato dando per scontato che fossero la stessa cosa

Entrambi erano in produzione da mesi prima della tornata che è andata a cercare.

Il primo: la nostra schermata di importazione elencava i container come macchine virtuali. L’elenco delle risorse del cluster con type=vm non è un filtro QEMU, restituisce entrambi i tipi, e il campo che diceva quale dei due fosse semplicemente non veniva letto. Non falliva nulla. L’elenco era sbagliato.

Il secondo era più silenzioso. Ogni container in esecuzione riceveva due chiamate al guest agent per ogni tornata di interrogazione, più una dopo ogni comando, verso un percorso che esiste solo per QEMU. Ognuna era un fallimento garantito, e non emergeva nulla, perché un arricchimento facoltativo fallito dovrebbe essere qualcosa a cui si sopravvive, e lo era. È stato trovato da un test che conta quali percorsi vengono chiamati anziché fare asserzioni sugli esiti, ed è un’abitudine che vale la pena copiare: si facciano asserzioni solo sui risultati e un’intera categoria di lavoro inutile resta invisibile.

Che cosa metteremmo per iscritto prima di vendere l’uno o l’altro

Se sta decidendo che cosa debba offrire un prodotto di hosting su Proxmox, quattro domande la portano quasi a destinazione.

  1. Il tipo è una proprietà del piano o una scelta al momento dell’ordine? Entrambe le cose sono difendibili. Un valore predefinito sotto la scelta no: un cliente che ha ordinato un container e ha ricevuto una macchina ha ottenuto un modello di sicurezza diverso, un kernel diverso e un insieme di capacità diverso da quello che aveva scelto.
  2. Quali rifiuti sono permanenti e quali temporanei? Lo dica nell’interfaccia, con quelle parole. «Non possibile per un container» e «non ancora realizzato» sono promesse diverse.
  3. Da dove viene la protezione dallo spoofing? La lacuna onesta nel nostro lavoro: una macchina clonata riceve il filtraggio IP alla consegna e un container ancora no. Proxmox ha gli stessi endpoint di firewall per entrambi, quindi è un passo di realizzazione anziché un ostacolo, ma un container consegnato senza quel filtro può inviare traffico dichiarando di essere qualcun altro.
  4. Che cosa mostra il monitoraggio per un container? Tutto ciò che deriva da un guest agent, da un framebuffer o dal firmware è vuoto. Vuoto va bene. Vuoto presentato come uno zero in salute no.

Abbiamo il campo del piano, il percorso di ordinazione e la richiesta di creazione realizzati e misurati contro la forma HTTP grezza dell’API. Ciò che non abbiamo fatto è creare un container sull’hypervisor di produzione di qualcuno, quindi la descrizione onesta del nostro supporto ai container è «la forma è giusta e il giro di andata e ritorno non è collaudato», che è una cosa utile da poter dire ad alta voce sulla propria piattaforma.

Questo è nato costruendo l’integrazione Proxmox per Provibr, dove l’agente gira dentro la rete del cliente e si connette solo verso l’esterno. L’abitudine correlata di non fidarsi della risposta di successo di un hypervisor è in perché un 200 di Proxmox non è un esito.

Inizi con un host e un agente

Crei un account, installi l’agente con un solo comando e colleghi il suo primo host Proxmox. Il periodo di prova dura 14 giorni e non chiede alcun dato di pagamento.