O Ambient Mesh foi anunciado há algum tempo e existem alguns artigos descrevendo seu uso e arquitetura. Este artigo classificará profundamente o caminho do tráfego do plano de dados no modo ambiente para ajudá-lo a entender completamente o esquema de implementação do plano de dados ambiente.
Antes de ler este artigo, leia a introdução ao Ambient Mesh: https://istio.io/latest/blog/2022/introducing-ambient-mesh/ para entender a arquitetura básica do Ambient Mesh.
Para facilitar a leitura e a prática síncrona, o ambiente usado neste artigo é implantado de acordo com a forma como o Ambient usa: https://istio.io/latest/blog/2022/get-started-ambient/.
01
a partir
do momento em que o pedido é iniciado
Para explorar o caminho do tráfego, primeiro analisamos o acesso mútuo entre dois serviços em modo ambiente (somente modo L4, nós diferentes).
Depois que o modo ambiente for ativado no namespace padrão, todos os serviços terão a capacidade de governança de grade.
Nossa análise começa com este comando: kubectl exec deploy/sleep -- curl -s [http://productpage:9080/](http://productpage:9080/) | head -n1
No modo Sidecar, o Istio intercepta o tráfego por meio do iptables. Quando o curl é executado no sleep Pod, o tráfego é encaminhado pelo iptables para a porta 15001 do Sidecar para processamento. Mas no modo ambiente, não há sidecar no pod e o pod não precisa ser reiniciado quando o modo ambiente é ativado, então como garantir que sua solicitação seja processada pelo ztunnel?
02
Interceptação de tráfego de saída
Para entender a solução para interceptação de tráfego de saída, podemos primeiro olhar para os componentes do plano de controle:
kebe@pc $ kubectl -n istio-system get po
NAME READY STATUS RESTARTS AGE
istio-cni-node-5rh5z 1/1 Running 0 20h
istio-cni-node-qsvsz 1/1 Running 0 20h
istio-cni-node-wdffp 1/1 Running 0 20h
istio-ingressgateway-5cfcb57bd-kx9hx 1/1 Running 0 20h
istiod-6b84499b75-ncmn7 1/1 Running 0 20h
ztunnel-nptf6 1/1 Running 0 20h
ztunnel-vxv4b 1/1 Running 0 20h
ztunnel-xkz4s 1/1 Running 0 20h
istio-cni torna-se o componente padrão no modo ambiente. No modo Sidecar, o istio-cni é principalmente um plug-in CNI para evitar o vazamento de permissão causado pelo uso do contêiner istio-init para processar as regras do iptables. No entanto, no modo Ambient, o Sidecar não é teoricamente necessário, então por que o istio-cni é necessário? ?
Podemos dar uma olhada no log:
kebe@pc $ kubectl -n istio-system logs istio-cni-node-qsvsz
...
2022-10-12T07:34:33.224957Z info ambient Adding route for reviews-v1-6494d87c7b-zrpks/default: [table 100 10.244.1.4/32 via 192.168.126.2 dev istioin src 10.244.1.1]
2022-10-12T07:34:33.226054Z info ambient Adding pod 'reviews-v2-79857b95b-m4q2g/default' (0ff78312-3a13-4a02-b39d-644bfb91e861) to ipset
2022-10-12T07:34:33.228305Z info ambient Adding route for reviews-v2-79857b95b-m4q2g/default: [table 100 10.244.1.5/32 via 192.168.126.2 dev istioin src 10.244.1.1]
2022-10-12T07:34:33.229967Z info ambient Adding pod 'reviews-v3-75f494fccb-92nq5/default' (e41edf7c-a347-45cb-a144-97492faa77bf) to ipset
2022-10-12T07:34:33.232236Z info ambient Adding route for reviews-v3-75f494fccb-92nq5/default: [table 100 10.244.1.6/32 via 192.168.126.2 dev istioin src 10.244.1.1]
Podemos ver que para Pods no modo ambiente, istio-cni faz duas coisas:
-
Adicionar pods ao ipset
-
Adicionada uma regra de roteamento à tabela 100 (usada posteriormente)
Podemos verificar o conteúdo do ipset no nó onde ele está localizado (observe que o tipo cluster é usado aqui e o docker exec precisa ser usado para entrar no host primeiro):
kebe@pc $ docker exec -it ambient-worker2 bash
root@ambient-worker2:/# ipset list
Name: ztunnel-pods-ips
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 520
References: 1
Number of entries: 5
Members:
10.244.1.5
10.244.1.7
10.244.1.8
10.244.1.4
10.244.1.6
Descobrimos que existe um ipset no nó onde o Pod está localizado, no qual vários IPs são salvos, e esses IPs são IPs do Pod:
kebe@pc $ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
details-v1-76778d6644-wn4d2 1/1 Running 0 20h 10.244.1.9 ambient-worker2 <none> <none>
notsleep-6d6c8669b5-pngxg 1/1 Running 0 20h 10.244.2.5 ambient-worker <none> <none>
productpage-v1-7c548b785b-w9zl6 1/1 Running 0 20h 10.244.1.7 ambient-worker2 <none> <none>
ratings-v1-85c74b6cb4-57m52 1/1 Running 0 20h 10.244.1.8 ambient-worker2 <none> <none>
reviews-v1-6494d87c7b-zrpks 1/1 Running 0 20h 10.244.1.4 ambient-worker2 <none> <none>
reviews-v2-79857b95b-m4q2g 1/1 Running 0 20h 10.244.1.5 ambient-worker2 <none> <none>
reviews-v3-75f494fccb-92nq5 1/1 Running 0 20h 10.244.1.6 ambient-worker2 <none> <none>
sleep-7b85956664-z6qh7 1/1 Running 0 20h 10.244.2.4 ambient-worker <none> <none>
Portanto, este ipset salva a lista de todos os IPs do Pod no modo ambiente no nó atual.
Onde esse ipset pode ser usado?
Podemos olhar para as regras do iptables e encontrar:
root@ambient-worker2:/# iptables-save
*mangle
...
-A POSTROUTING -j ztunnel-POSTROUTING
...
-A ztunnel-PREROUTING -p tcp -m set --match-set ztunnel-pods-ips src -j MARK --set-xmark 0x100/0x100
Com isso, sabemos que quando um Pod em modo ambiente (em ztunnel-pods-ips ipset) em um nó inicia uma requisição, sua conexão será marcada com 0x100/0x100.
Geralmente, neste caso, estará relacionado ao roteamento, vamos dar uma olhada nas regras de roteamento:
root@ambient-worker2:/# ip rule
0: from all lookup local
100: from all fwmark 0x200/0x200 goto 32766
101: from all fwmark 0x100/0x100 lookup 101
102: from all fwmark 0x40/0x40 lookup 102
103: from all lookup 100
32766: from all lookup main
32767: from all lookup default
Pode-se observar que o tráfego marcado com 0x100/0x100 passará pela tabela de roteamento da tabela 101. Podemos visualizar a tabela de roteamento:
root@ambient-worker2:/# ip r show table 101
default via 192.168.127.2 dev istioout
10.244.1.2 dev veth5db63c11 scope link
Pode-se ver que o gateway padrão foi substituído por 192.168.127.2 e a placa de rede istioout foi removida.
Mas há um problema aqui. O IP 192.168.127.2 não pertence a nenhum dos Node IP, Pod IP e Cluster IP. A placa de rede istioout não deveria existir por padrão, então quem criou esse IP? Como o tráfego precisa ser enviado para o ztunnel no final, podemos examinar a configuração do ztunnel para ver se encontramos a resposta.
kebe@pc $ kubectl -n istio-system get po ztunnel-vxv4b -o yaml
apiVersion: v1
kind: Pod
metadata:
...
name: ztunnel-vxv4b
namespace: istio-system
...
spec:
...
initContainers:
- command:
...
OUTBOUND_TUN=istioout
...
OUTBOUND_TUN_IP=192.168.127.1
ZTUNNEL_OUTBOUND_TUN_IP=192.168.127.2
ip link add name p$INBOUND_TUN type geneve id 1000 remote $HOST_IP
ip addr add $ZTUNNEL_INBOUND_TUN_IP/$TUN_PREFIX dev p$INBOUND_TUN
ip link add name p$OUTBOUND_TUN type geneve id 1001 remote $HOST_IP
ip addr add $ZTUNNEL_OUTBOUND_TUN_IP/$TUN_PREFIX dev p$OUTBOUND_TUN
ip link set p$INBOUND_TUN up
ip link set p$OUTBOUND_TUN up
...
Como acima, o ztunnel será responsável por criar a placa de rede istioout, e podemos ver a placa de rede correspondente no nó.
root@ambient-worker2:/# ip a
11: istioout: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 0a:ea:4e:e0:8d:26 brd ff:ff:ff:ff:ff:ff
inet 192.168.127.1/30 brd 192.168.127.3 scope global istioout
valid_lft forever preferred_lft forever
Onde está o IP do gateway de 192.168.127.2? Ele é alocado dentro do ztunnel.
kebe@pc $ kubectl -n istio-system exec -it ztunnel-nptf6 -- ip a
Defaulted container "istio-proxy" out of: istio-proxy, istio-init (init)
2: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 46:8a:46:72:1d:3b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.2.3/24 brd 10.244.2.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::448a:46ff:fe72:1d3b/64 scope link
valid_lft forever preferred_lft forever
4: pistioout: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether c2:d0:18:20:3b:97 brd ff:ff:ff:ff:ff:ff
inet 192.168.127.2/30 scope global pistioout
valid_lft forever preferred_lft forever
inet6 fe80::c0d0:18ff:fe20:3b97/64 scope link
valid_lft forever preferred_lft forever
Pode-se ver agora que o tráfego irá para o ztunnel, mas neste momento nenhuma outra operação é realizada no tráfego, ele é simplesmente roteado para o ztunnel, como o Envoy no ztunnel pode processar o tráfego?
Vamos continuar olhando a configuração do ztunnel, ele escreveu muitas regras do iptables, podemos entrar no ztunnel para ver as regras específicas:
kebe@pc $ kubectl -n istio-system exec -it ztunnel-nptf6 -- iptables-save
Defaulted container "istio-proxy" out of: istio-proxy, istio-init (init)
...
*mangle
-A PREROUTING -i pistioout -p tcp -j TPROXY --on-port 15001 --on-ip 127.0.0.1 --tproxy-mark 0x400/0xfff
...
COMMIT
Pode ser visto claramente que quando o tráfego entra no ztunnel, TPROXY será usado para transferir o tráfego para a porta 15001 para processamento. Aqui, 15001 é a porta que o Envoy realmente escuta para processar o tráfego de saída do pod. Sobre TPROXY, você pode aprender informações relevantes por si mesmo. Este artigo não os repetirá.
Portanto, para resumir, quando o Pod está no modo ambiente, seu caminho de tráfego de saída é aproximadamente:
-
Inicie o tráfego de processos no pod.
-
O tráfego flui pela rede do nó onde está localizado e é marcado pelo iptables do nó.
-
A tabela de roteamento no nó encaminhará o tráfego para o pod ztunnel do nó atual.
-
Quando o tráfego chegar ao ztunnel, será feito um proxy transparente pelo TPROXY por meio do iptables, e o tráfego será encaminhado para a porta 15001 do Envoy no Pod atual para processamento.
Até agora, podemos ver que no modo ambiente, o processamento do tráfego de saída do pod é relativamente complexo e o caminho é relativamente longo , ao contrário do modo sidecar, que conclui diretamente o encaminhamento do tráfego dentro do pod.
03
Interceptação de tráfego de entrada
Com a experiência acima, não é difícil descobrir que, no modo ambiente, a interceptação do tráfego ocorre principalmente por meio do roteamento MARK + TPROXY, e o tráfego de entrada deve ser semelhante.
Vamos usar o método de análise mais simples para dar uma olhada e analisá-lo. Quando um processo em um nó ou um programa em outro host acessa um Pod no nó atual, o tráfego passará pela tabela de roteamento do host. Vamos verificar quando o acesso correspondente à página do produto - Informações de roteamento em v1-7c548b785b-w9zl6(10.244.1.7):
root@ambient-worker2:/# ip r get 10.244.1.7
10.244.1.7 via 192.168.126.2 dev istioin table 100 src 10.244.1.1 uid 0
cache
Podemos ver que ao acessar 10.244.1.7, o tráfego será roteado para 192.168.126.2, e esta regra é adicionada pelo istio-cni acima.
O mesmo IP 192.168.126.2 pertence ao ztunnel:
kebe@pc $ kubectl -n istio-system exec -it ztunnel-nptf6 -- ip a
Defaulted container "istio-proxy" out of: istio-proxy, istio-init (init)
2: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 46:8a:46:72:1d:3b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.2.3/24 brd 10.244.2.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::448a:46ff:fe72:1d3b/64 scope link
valid_lft forever preferred_lft forever
3: pistioin: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 7e:b2:e6:f9:a4:92 brd ff:ff:ff:ff:ff:ff
inet 192.168.126.2/30 scope global pistioin
valid_lft forever preferred_lft forever
inet6 fe80::7cb2:e6ff:fef9:a492/64 scope link
valid_lft forever preferred_lft forever
Seguindo o mesmo método de análise, vejamos as regras do iptables:
kebe@pc $ kubectl -n istio-system exec -it ztunnel-nptf6 -- iptables-save
...
-A PREROUTING -i pistioin -p tcp -m tcp --dport 15008 -j TPROXY --on-port 15008 --on-ip 127.0.0.1 --tproxy-mark 0x400/0xfff
-A PREROUTING -i pistioin -p tcp -j TPROXY --on-port 15006 --on-ip 127.0.0.1 --tproxy-mark 0x400/0xfff
...
Se você acessar diretamente a porta PodIP+Pod no nó, ela será encaminhada para a porta 15006 do ztunnel, que é a porta onde o Istio processa o tráfego de entrada.
Quanto ao tráfego cuja porta de destino é a porta 15008, esta é a porta usada pelo ztunnel para o túnel de tráfego da Camada 4. Falaremos sobre isso mais tarde.
04
Para o próprio processamento de tráfego do Envoy
Sabemos que no modo Sidecar, Envoy e business containers rodam na mesma rede NS. Para o tráfego de business containers, precisamos interceptar todo o tráfego para garantir o controle total do tráfego, mas é necessário no modo Ambient?
A resposta é não, porque o Envoy foi separado em outros Pods e o tráfego enviado pelo Envoy não requer processamento especial. Em outras palavras, para o ztunnel, precisamos apenas lidar com o tráfego de entrada, então as regras no ztunnel parecem relativamente simples.
05
continua
Acima, analisamos principalmente a solução para interceptação de tráfego de pod no modo ambiente, mas ainda não envolvida no processamento de 70% do tráfego, e o princípio específico da implementação do ztunnel. Posteriormente, analisaremos o caminho de processamento detalhado do tráfego no ztunnel e no waypoint proxy.
autor deste artigo
Kebe Liu
Especialista técnico em grade de serviço "DaoCloud Daoke", membro do comitê de direção da comunidade Istio, colaborador do código Istio e Kubernetes, iniciador do projeto de código aberto Merbridge