Non molto tempo fa mi sono trovato di fronte al compito davvero insolito di impostare il routing per MetalLB. Andrebbe tutto bene, perché... Solitamente MetalLB non richiede alcuna azione aggiuntiva, ma nel nostro caso abbiamo un cluster abbastanza grande con una configurazione di rete molto semplice.
In questo articolo ti spiegherò come configurare il routing basato sull'origine e sulle policy per la rete esterna del tuo cluster.
Non entrerò nei dettagli sull'installazione e configurazione di MetalLB, poiché presumo che tu abbia già una certa esperienza. Suggerisco di andare direttamente al punto, ovvero impostare il routing. Quindi abbiamo quattro casi:
Caso 1: quando non è richiesta alcuna configurazione
Consideriamo un caso semplice.
Non è necessaria un'ulteriore configurazione del routing quando gli indirizzi emessi da MetalLB si trovano nella stessa sottorete degli indirizzi dei tuoi nodi.
Ad esempio, hai una sottorete 192.168.1.0/24
, ha un router 192.168.1.1
e i tuoi nodi ricevono indirizzi: 192.168.1.10-30
, quindi per MetalLB è possibile regolare l'intervallo 192.168.1.100-120
ed essere sicuri che funzioneranno senza alcuna configurazione aggiuntiva.
Perché? Poiché i tuoi nodi hanno già percorsi configurati:
# ip route
default via 192.168.1.1 dev eth0 onlink
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10
E gli indirizzi dello stesso intervallo li riutilizzeranno senza alcuna azione aggiuntiva.
Caso 2: quando è necessaria un'ulteriore personalizzazione
Dovresti configurare percorsi aggiuntivi ogni volta che i tuoi nodi non hanno un indirizzo IP configurato o un percorso verso la sottorete per la quale MetalLB emette indirizzi.
Ti spiegherò un po' più nel dettaglio. Ogni volta che MetalLB emette un indirizzo, può essere paragonato a un semplice assegnazione come:
ip addr add 10.9.8.7/32 dev lo
Presta attenzione a:
- a) L'indirizzo viene assegnato con un prefisso
/32
ovvero, un percorso non verrà aggiunto automaticamente alla sottorete (è solo un indirizzo) - b) L'indirizzo è collegato a qualsiasi interfaccia del nodo (ad esempio loopback). Vale la pena menzionare qui le caratteristiche dello stack di rete Linux. Indipendentemente dall'interfaccia a cui aggiungi l'indirizzo, il kernel elaborerà sempre le richieste arp e invierà risposte arp a ognuna di esse, questo comportamento è considerato corretto e, inoltre, è abbastanza ampiamente utilizzato in un ambiente così dinamico come Kubernetes.
Questo comportamento può essere personalizzato, ad esempio abilitando l'arp rigoroso:
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
In questo caso, le risposte arp verranno inviate solo se l'interfaccia contiene esplicitamente un indirizzo IP specifico. Questa impostazione è obbligatoria se prevedi di utilizzare MetalLB e il tuo proxy kube è in esecuzione in modalità IPVS.
Tuttavia, MetalLB non utilizza il kernel per elaborare le richieste arp, ma lo fa da solo nello spazio utente, quindi questa opzione non influirà sul funzionamento di MetalLB.
Torniamo al nostro compito. Se il percorso per gli indirizzi emessi non esiste sui tuoi nodi, aggiungilo in anticipo a tutti i nodi:
ip route add 10.9.8.0/24 dev eth1
Caso 3: quando è necessario il routing basato sull'origine
Sarà necessario configurare il routing basato sull'origine quando si ricevono pacchetti attraverso un gateway separato, non quello configurato per impostazione predefinita, pertanto anche i pacchetti di risposta dovrebbero passare attraverso lo stesso gateway.
Ad esempio, hai la stessa sottorete 192.168.1.0/24
dedicato ai tuoi nodi, ma vuoi rilasciare indirizzi esterni utilizzando MetalLB. Supponiamo che tu abbia più indirizzi da una sottorete 1.2.3.0/24
situati nella VLAN 100 e desideri utilizzarli per accedere ai servizi Kubernetes esternamente.
Quando si contatta 1.2.3.4
effettuerai richieste da una sottorete diversa da 1.2.3.0/24
e aspetta una risposta. Il nodo che attualmente è il master per l'indirizzo emesso da MetalLB 1.2.3.4
, riceverà il pacchetto dal router 1.2.3.1
, ma la risposta per lui deve necessariamente passare per la stessa strada, attraverso 1.2.3.1
.
Poiché il nostro nodo ha già un gateway predefinito configurato 192.168.1.1
, quindi per impostazione predefinita la risposta andrà a lui e non a 1.2.3.1
, attraverso il quale abbiamo ricevuto il pacco.
Come affrontare questa situazione?
In questo caso, devi preparare tutti i tuoi nodi in modo tale che siano pronti a servire indirizzi esterni senza ulteriore configurazione. Cioè, per l'esempio sopra, è necessario creare in anticipo un'interfaccia VLAN sul nodo:
ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 up
E poi aggiungi percorsi:
ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100
Tieni presente che aggiungiamo le rotte a una tabella di routing separata 100
conterrà solo due percorsi necessari per inviare un pacchetto di risposta attraverso il gateway 1.2.3.1
, situato dietro l'interfaccia eth0.100
.
Ora dobbiamo aggiungere una semplice regola:
ip rule add from 1.2.3.0/24 lookup 100
che dice esplicitamente: se l'indirizzo di origine del pacchetto è in 1.2.3.0/24
, allora è necessario utilizzare la tabella di routing 100
. In esso abbiamo già descritto il percorso che lo farà attraversare 1.2.3.1
Caso 4: quando è necessario il routing basato su policy
La topologia di rete è la stessa dell'esempio precedente, ma supponiamo che tu voglia poter accedere anche agli indirizzi del pool esterno 1.2.3.0/24
dai tuoi pod:
La particolarità è che quando si accede a qualsiasi indirizzo in 1.2.3.0/24
, il pacchetto di risposta raggiunge il nodo e ha un indirizzo di origine compreso nell'intervallo 1.2.3.0/24
verrà obbedientemente inviato a eth0.100
, ma vogliamo che Kubernetes lo reindirizzi al nostro primo pod, che ha generato la richiesta originale.
Risolvere questo problema si è rivelato difficile, ma è diventato possibile grazie al routing basato su policy:
Per una migliore comprensione del processo, ecco un diagramma a blocchi di netfilter:
Per prima cosa, come nell'esempio precedente, creiamo una tabella di routing aggiuntiva:
ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100
Ora aggiungiamo alcune regole a iptables:
iptables -t mangle -A PREROUTING -i eth0.100 -j CONNMARK --set-mark 0x100
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j RETURN
iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark
Queste regole contrassegneranno le connessioni in entrata all'interfaccia eth0.100
, contrassegnando tutti i pacchetti con il tag 0x100
, anche le risposte all'interno della stessa connessione verranno contrassegnate con lo stesso tag.
Ora possiamo aggiungere una regola di routing:
ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100
Cioè, tutti i pacchetti con un indirizzo di origine 1.2.3.0/24
e etichetta 0x100
deve essere instradato utilizzando una tabella 100
.
Pertanto, altri pacchetti ricevuti su un'altra interfaccia non sono soggetti a questa regola, che consentirà loro di essere instradati utilizzando gli strumenti Kubernetes standard.
C'è ancora una cosa, in Linux c'è un cosiddetto filtro del percorso inverso, che rovina tutto; fa un semplice controllo: per tutti i pacchetti in arrivo, cambia l'indirizzo di origine del pacchetto con l'indirizzo del mittente e controlla se il pacchetto potrà uscire attraverso la stessa interfaccia su cui è stato ricevuto, in caso contrario lo filtrerà.
Il problema è che nel nostro caso non funzionerà correttamente, ma possiamo disabilitarlo:
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filter
Tieni presente che il primo comando controlla il comportamento globale di rp_filter; se non è disabilitato, il secondo comando non avrà alcun effetto. Tuttavia, le restanti interfacce rimarranno con rp_filter abilitato.
Per non limitare completamente il funzionamento del filtro, possiamo utilizzare l'implementazione rp_filter per netfilter. Usando rpfilter come modulo iptables, puoi configurare regole abbastanza flessibili, ad esempio:
iptables -t raw -A PREROUTING -i eth0.100 -d 1.2.3.0/24 -j RETURN
iptables -t raw -A PREROUTING -i eth0.100 -m rpfilter --invert -j DROP
abilitare rp_filter sull'interfaccia eth0.100
per tutti gli indirizzi tranne 1.2.3.0/24
.
Fonte: habr.com