Roteamento de ajuste fino para MetalLB no modo L2

Roteamento de ajuste fino para MetalLB no modo L2
Não faz muito tempo, me deparei com uma tarefa muito incomum de configurar roteamento para MetalLB. Tudo ficaria bem, porque... Normalmente o MetalLB não requer nenhuma ação adicional, mas no nosso caso temos um cluster bastante grande com uma configuração de rede muito simples.

Neste artigo, direi como configurar o roteamento baseado em origem e em políticas para a rede externa do seu cluster.

Não entrarei em detalhes sobre a instalação e configuração do MetalLB, pois presumo que você já tenha alguma experiência. Sugiro ir direto ao ponto, ou seja, configurar o roteamento. Então temos quatro casos:

Caso 1: Quando nenhuma configuração é necessária

Vejamos um caso simples.

Roteamento de ajuste fino para MetalLB no modo L2

Configuração de roteamento adicional não é necessária quando os endereços emitidos pelo MetalLB estão na mesma sub-rede que os endereços dos seus nós.

Por exemplo, você tem uma sub-rede 192.168.1.0/24, tem um roteador 192.168.1.1, e seus nós recebem endereços: 192.168.1.10-30, então para MetalLB você pode ajustar o intervalo 192.168.1.100-120 e certifique-se de que eles funcionarão sem qualquer configuração adicional.

Por que é que? Como seus nós já possuem rotas configuradas:

# 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 endereços do mesmo intervalo irão reutilizá-los sem quaisquer ações adicionais.

Caso 2: Quando é necessária personalização adicional

Roteamento de ajuste fino para MetalLB no modo L2

Você deve configurar rotas adicionais sempre que seus nós não tiverem um endereço IP configurado ou rota para a sub-rede para a qual o MetalLB emite endereços.

Vou explicar um pouco mais detalhadamente. Sempre que o MetalLB gera um endereço, ele pode ser comparado a uma atribuição simples como:

ip addr add 10.9.8.7/32 dev lo

Prestar atenção em:

  • a) O endereço é atribuído com um prefixo /32 ou seja, uma rota não será adicionada automaticamente à sub-rede (é apenas um endereço)
  • b) O endereço é anexado a qualquer interface de nó (por exemplo, loopback). Vale ressaltar aqui os recursos da pilha de rede Linux. Não importa em qual interface você adicione o endereço, o kernel sempre processará solicitações arp e enviará respostas arp para qualquer uma delas, esse comportamento é considerado correto e, além disso, é amplamente utilizado em um ambiente tão dinâmico como o Kubernetes.

Este comportamento pode ser personalizado, por exemplo, habilitando o arp estrito:

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

Neste caso, as respostas arp só serão enviadas se a interface contiver explicitamente um endereço IP específico. Esta configuração é necessária se você planeja usar o MetalLB e seu kube-proxy estiver sendo executado no modo IPVS.

Porém, o MetalLB não usa o kernel para processar solicitações arp, mas o faz sozinho no espaço do usuário, portanto esta opção não afetará o funcionamento do MetalLB.

Voltemos à nossa tarefa. Se a rota para os endereços emitidos não existir em seus nós, adicione-a antecipadamente a todos os nós:

ip route add 10.9.8.0/24 dev eth1

Caso 3: Quando você precisa de roteamento baseado na origem

Você precisará configurar o roteamento baseado na origem ao receber pacotes por meio de um gateway separado, não aquele configurado por padrão, portanto, os pacotes de resposta também devem passar pelo mesmo gateway.

Por exemplo, você tem a mesma sub-rede 192.168.1.0/24 dedicado aos seus nós, mas deseja emitir endereços externos usando MetalLB. Vamos supor que você tenha vários endereços de uma sub-rede 1.2.3.0/24 localizados na VLAN 100 e você deseja usá-los para acessar os serviços do Kubernetes externamente.

Roteamento de ajuste fino para MetalLB no modo L2

Ao entrar em contato 1.2.3.4 você fará solicitações de uma sub-rede diferente da 1.2.3.0/24 e espere por uma resposta. O nó que é atualmente o mestre do endereço emitido pelo MetalLB 1.2.3.4, receberá o pacote do roteador 1.2.3.1, mas a resposta para ele deve necessariamente seguir o mesmo caminho, passando por 1.2.3.1.

Como nosso nó já possui um gateway padrão configurado 192.168.1.1, então, por padrão, a resposta irá para ele, e não para 1.2.3.1, através do qual recebemos o pacote.

Como lidar com esta situação?

Nesse caso, você precisa preparar todos os seus nós de forma que estejam prontos para atender endereços externos sem configuração adicional. Ou seja, para o exemplo acima, você precisa criar antecipadamente uma interface VLAN no nó:

ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 up

E então adicione rotas:

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

Observe que adicionamos rotas a uma tabela de roteamento separada 100 conterá apenas duas rotas necessárias para enviar um pacote de resposta através do gateway 1.2.3.1, localizado atrás da interface eth0.100.

Agora precisamos adicionar uma regra simples:

ip rule add from 1.2.3.0/24 lookup 100

que diz explicitamente: se o endereço de origem do pacote estiver em 1.2.3.0/24, então você precisa usar a tabela de roteamento 100. Nele já descrevemos a rota que o fará passar 1.2.3.1

Caso 4: quando você precisa de roteamento baseado em políticas

A topologia da rede é a mesma do exemplo anterior, mas digamos que você também queira acessar endereços de pools externos 1.2.3.0/24 dos seus pods:

Roteamento de ajuste fino para MetalLB no modo L2

A peculiaridade é que ao acessar qualquer endereço em 1.2.3.0/24, o pacote de resposta atinge o nó e tem um endereço de origem no intervalo 1.2.3.0/24 será obedientemente enviado para eth0.100, mas queremos que o Kubernetes o redirecione para nosso primeiro pod, que gerou a solicitação original.

Resolver esse problema acabou sendo difícil, mas tornou-se possível graças ao roteamento baseado em políticas:

Para uma melhor compreensão do processo, aqui está um diagrama de blocos do netfilter:
Roteamento de ajuste fino para MetalLB no modo L2

Primeiro, como no exemplo anterior, vamos criar uma tabela de roteamento adicional:

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

Agora vamos adicionar algumas regras ao 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

Essas regras marcarão as conexões de entrada para a interface eth0.100, marcando todos os pacotes com a tag 0x100, as respostas na mesma conexão também serão marcadas com a mesma tag.

Agora podemos adicionar uma regra de roteamento:

ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100

Ou seja, todos os pacotes com endereço de origem 1.2.3.0/24 e etiqueta 0x100 deve ser roteado usando uma tabela 100.

Assim, outros pacotes recebidos em outra interface não estão sujeitos a esta regra, o que permitirá que eles sejam roteados usando ferramentas padrão do Kubernetes.

Tem mais uma coisa, no Linux existe um chamado filtro de caminho reverso, que estraga tudo; ele faz uma verificação simples: para todos os pacotes recebidos, ele muda o endereço de origem do pacote pelo endereço do remetente e verifica se o pacote pode sair pela mesma interface em que foi recebido, caso contrário, ele irá filtrá-lo.

O problema é que no nosso caso não funcionará corretamente, mas podemos desativá-lo:

echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filter

Observe que o primeiro comando controla o comportamento global do rp_filter; se não estiver desabilitado, o segundo comando não terá efeito. Porém, as interfaces restantes permanecerão com o rp_filter habilitado.

Para não limitar completamente o funcionamento do filtro, podemos usar a implementação rp_filter para netfilter. Usando rpfilter como módulo iptables, você pode configurar regras bastante flexíveis, por exemplo:

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

habilite rp_filter na interface eth0.100 para todos os endereços, exceto 1.2.3.0/24.

Fonte: habr.com

Adicionar um comentário