Un container con due reti sceglie il gateway sbagliato
Due attach, una sola rotta predefinita e la rete sbagliata vince ogni volta. Fissate l'uscita con Compose gw_priority e verificate dall'interno del container.
Il vostro container risponde ancora sul suo bridge di controllo, ma ogni pacchetto in uscita esce dal macvlan dell'ufficio. Nulla è fallito. Due attach forniscono ciascuno un gateway, e l'unica rotta predefinita è andata alla rete che non volevate, quindi il servizio continua a funzionare mentre il suo traffico segue un percorso più lungo. Quando qualcuno se ne accorge, la prova è una tabella di routing e non un messaggio di errore.
La spiegazione abituale è che Docker ha scelto la rete per caso. In pratica la vecchia decisione seguiva l'ordine alfabetico dei nomi delle reti collegate, il che trasformava il routing in un effetto collaterale di un'etichetta. gw_priority lo sostituisce con un numero che scegliete di proposito, e adottarlo richiede circa un minuto una volta scritto lo schema.
Perché la rotta predefinita finisce sulla rete sbagliata
Ogni rete a cui un servizio si unisce può offrire il proprio gateway, e il container finisce comunque con esattamente una rotta predefinita. Prima di Compose 2.33.1 il pareggio si risolveva ordinando i nomi delle reti, così un'etichetta come control che viene prima di office decideva dove andava il traffico. Nulla dentro il container registra che la decisione era arbitraria, ed è per questo che sopravvive alla revisione del codice e riemerge più tardi come un percorso strano. Mettere una policy nel file compose è l'unico modo per rendere la scelta ispezionabile.
Due cose cambiano davvero quale rete vince: i valori gw_priority che assegnate e quali reti sono collegate. Aggiungete una terza rete e l'insieme dei candidati cresce; togliete il macvlan e non resta nulla a competere con il bridge. Nessuna delle due mosse compare nei log dell'applicazione, perché una rotta predefinita sbagliata raramente interrompe del tutto la connessione. Fa solo arrivare le risposte da un indirizzo che l'altra parte forse non si aspetta.
Fissate il gateway con gw_priority
Compose espone gw_priority su ogni attach servizio-rete dalla versione 2.33.1, e vince il numero più alto. Non esiste una chiave gateway per servizio, quindi il valore va nel blocco networks del servizio. L'esempio sotto assegna la rotta predefinita a control lasciando office raggiungibile per la sua LAN direttamente collegata.
services:
vpn:
networks:
control:
gw_priority: 1
office:
gw_priority: 0
networks:
control:
driver: bridge
ipam:
config:
- subnet: 172.30.50.0/24
office:
driver: macvlan
driver_opts:
parent: eno1.120
macvlan_mode: bridge
ipam:
config:
- subnet: 192.168.120.0/24
ip_range: 192.168.120.192/27
gateway: 192.168.120.1
Leggete il blocco come due controlli separati: gw_priority decide la rotta predefinita e la sezione ipam decide l'indirizzamento. Cambiare solo la subnet lascia intatta la scelta dell'uscita, ed è il modo più comune in cui questa correzione viene applicata a metà. Tenete le priorità accanto alla rete che governano, così il lettore successivo non deve andare a caccia della decisione.
Non confondete gw_priority con la proprietà priority semplice accanto. Quella ordina la creazione delle connessioni e non dice nulla sul routing, quindi non può scegliere un gateway predefinito e non sistema i nomi delle interfacce. Se qualcuno consiglia di alzare priority per indirizzare il traffico, il consiglio è sbagliato anche quando l'ordine risultante sembra corretto per caso.
Verificate quale rete porta davvero la rotta predefinita
Controllate dentro il container in esecuzione, perché la tabella di routing dell'host descrive l'host e non il namespace usato dal servizio. Eseguite prima il controllo di configurazione così che un errore di battitura fallisca prima di un riavvio, poi leggete la rotta che il container ha davvero installato.
docker compose config --quiet
docker exec vpn ip -4 route show default
docker exec vpn cat /proc/net/route
Un risultato corretto nomina il dispositivo bridge e 172.30.50.1; uno sbagliato nomina 192.168.120.1 e il dispositivo macvlan. Se l'immagine non include il pacchetto iproute2, il file /proc riporta comunque la rotta predefinita, con destinazione 00000000 e i byte del gateway invertiti. Confrontate la colonna dell'interfaccia con docker inspect quando i nomi dei dispositivi non sono chiari.
Macvlan è una porta fisica, non un clone del bridge
Macvlan dà al container una seconda identità sul cavo, ed è esattamente questo che fa funzionare l'accesso ai dispositivi locali. Significa anche che la porta dello switch e l'hypervisor devono accettare un indirizzo MAC in più, e molte reti cloud lo rifiutano in silenzio. Provate un container prima di pianificare una flotta, perché un MAC rifiutato di solito appare come un collegamento attivo che non passa nulla.
L'impostazione macvlan_mode: bridge è una modalità del driver macvlan, non il bridge software di Docker, e passthru è una modalità specializzata separata e non un valore predefinito. Linux impedisce anche a un container macvlan di raggiungere il proprio host attraverso lo stesso parent, ed è il motivo per cui si tiene il bridge di controllo invece di aspettarsi che un solo attach faccia entrambi i lavori.
Nominate il parent VLAN prima che Compose lo veda
Docker tratta un parent puntato come eno1.120 come una sottointerfaccia 802.1Q e crea da sé il dispositivo VLAN. Quella comodità ha dei limiti: il nome derivato dipende dal nome dell'interfaccia fisica, e i nomi lunghi possono spingere il risultato oltre ciò che gli strumenti circostanti si aspettano. Creare un'interfaccia VLAN con nome nella configurazione di rete dell'host e puntare parent su di essa mantiene prevedibili entrambe le estremità.
Ricordate che la chiave sotto networks è un nome Compose, non un dispositivo Linux. Cercare sull'host un'interfaccia chiamata office fallirà, perché il kernel vede solo l'interfaccia parent e la coppia veth dietro il container.
Fate spazio alla VPN sopra
Un client VPN si collega dopo che queste reti esistono, quindi può aggiungere rotte più specifiche, regole di policy e filtri indipendentemente da gw_priority. Pianificate i prefissi remoti rispetto a entrambe le subnet locali, comprese le risposte di cui ha bisogno il percorso di controllo. Quando un prefisso remoto si sovrappone alla LAN dell'ufficio, correggete il piano di indirizzamento invece di strappare un intero intervallo privato dal tunnel.
Ricontrollate indirizzi, rotte e percorso del servizio dopo l'avvio della VPN e di nuovo dopo la sua riconnessione, perché è in una riconnessione che sparisce una rotta aggiunta a mano. Scrivete la tabella attesa prima del primo test, così il confronto successivo è un diff e non un esercizio di memoria. Mantenete la proprietà persistente della rete in Compose e lasciate che la configurazione VPN possieda solo la propria policy.