Comunicação entre clusters
Visão Geral
O objetivo dessa documentação é dar um overview da atual topologia de redes do Produto Kubernetes focando em quais cenários ela oferece a possibilidade de comunicação entre diferentes clusters. Além de explorar as diferentes técnicas para realizar a comunicação entre serviços e pods contidos em diferentes Clusters.
Objetivos
-
Possibilitar a comunicação entre pods e serviços de diferentes Clusters, porém dentro de uma mesma região.
-
Identificar os diferente cenários de comunicação.
-
Elencar os prós e contras de cada cenário.
Escopo
-
Testes realizados na região NE1, para um mesmo tenant.
-
Foram utilizados 2(dois) Clusters para os testes.
Overview da Topologia de Rede
A configuração de rede atual possibilita que os Clusters se comuniquem através do range de portas 30000-32767 conforme diagrama abaixo.
Isso é possível pois o SecurityGroup dos workers nodes(k8s-cluster-{tenant_id}-{cluster_name}-secgroup-worker
) criado no OpenStack contém uma regra de Ingress para as portas 30000-32767 tanto para comunicação TCP quando para UDP, de qualquer origem.
O range de portas 30000-32767 também são alocadas pelos serviços do Kubernetes, por padrão. Vide documentação.
Levando essa premissa em consideração, existem três tipos de configuração de serviços que podemos utilizar para que a comunicação seja realizada:
Tipo de Serviço |
Comportamento |
Comportamento esperado |
---|---|---|
NodePort |
Cria uma porta de comunicação em cada nó do cluster. |
Acesso interno, através do seguinte endereçamento: <[NODE0_IP, NODE1_IP, ... NODEN_IP]>:<SERVICE_PORT>
|
LoadBalancer - IP Externo |
Cria um Load Balancer no OpenStack e disponibiliza um FloatingIP |
Acesso interno e externo, através do seguinte endereçamento: <LOAD_BALANCER_IP>:<SERVICE_PORT>
|
LoadBalancer - IP Interno |
Cria um IP Interno na VPC do projeto. |
Acesso interno, através do seguinte endereçamento: <SERVICE_IP>:<SERVICE_PORT>
|
Pré-requisitos
-
Criar 2 clusters em NE-1 . A partir desse momento identificaremos os clusters como cluster-1 e cluster-2 para os testes.
-
Para os deployments que serão expostos pelos serviços será utilizada a imagem
ealen/echo-server
, ela facilitará no processo de verificação e validação da comunicação, link. -
Como ferramenta de avaliação, a imagem
nicolaka/netshoot
, bastante utilizada para fazer troubleshooting de redes Kubernetes, link.
NodePort
Configurando ambiente
-
Criar um deployment chamado
network-test-cluster-1
no cluster-1-
kubectl create deployment network-test-cluster-1 --image=ealen/echo-server --replicas=3
-
-
Criar um deployment chamado
network-test-cluster-2
no cluster-2-
kubectl create deployment network-test-cluster-2 --image=ealen/echo-server --replicas=3
-
-
Criar um serviço do tipo NodePort para o deployment
network-test-cluster-1
:-
kubectl expose deployment network-test-cluster-1 --type=NodePort --port=80
-
Um serviço com o mesmo nome do deployment será criado
-
-
-
Criar um serviço do tipo NodePort para o deployment
network-test-cluster-2
:-
kubectl expose deployment network-test-cluster-2 --type=NodePort --port=80
-
Um serviço com o mesmo nome do deployment será criado
-
-
Validando Comunicação
-
Recupere o IP de um dos nós do cluster-2 <IP_NODE>:
-
kubectl get nodes -o wide
-
-
Recupere a port do serviço
network-test-cluster-2
<SERVICE_PORT>:-
kubectl get svc network-test-cluster-2 -o jsonpath='{.spec.ports[0].nodePort}'
-
-
No cluster-1, crie um Pod temporário com o netshoot
-
kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot
-
No netshoot(cluster-1), realizar uma chamada para o nó/porta expostos pelo serviço do cluster-2
-
curl http://<IP_NODE>:<SERVICE_PORT>/?echo_env_body=HOSTNAME
-
-
-
O parâmetro echo_env_body=HOSTNAME
no echo-server para ele retornar o hostname do servidor, que nesse caso será o nome do pod. Isso validará em qual cluster está o pod, pois o nome do pod serguirá o nome do deployment criado naquele respectivo cluster.
Resultado Esperado
A requisição será roteada do pod tmp-shell do cluster-1 para a porta do nó exposta pelo serviço network-test-cluster-2
no cluster-2. O serviço encaminha a requisição para um dos pods do seu deployment, o qual retornará o seu hostname.
Ao executar o curl diversas vezes, em cada vez ele retornará um nome pod diferente do cluster-2, pois o serviço está encarregado de fazer o balancemaneto de carga.
Segue exemplo do retorno esperado (note que cada chamada retorna um hostname/pod diferente):
curl http://172.30.0.96:30551/?echo_env_body=HOSTNAME
"network-test-cluster-2-7db4dd7c4-r2srd"
curl http://172.30.0.96:30551/?echo_env_body=HOSTNAME
"network-test-cluster-2-7db4dd7c4-62kzq"
Load Balancer com IP Externo
Configurando ambiente
-
Utilizando o mesmo deployment
network-test-cluster-2
da seção NodePort, criar um serviço do tipo LoadBalancer no cluster-2:-
kubecetl expose deployment network-test-cluster-2 --type=LoadBalancer --port=80 --name=network-test-cluster-2-lb
-
-
Um serviço com o nome
network-test-cluster-2-lb
será criado. Estre serviço cria um Load Balancer no OpenStack e associa um Floating IP ao mesmo.
Validando Comunicação
A validação será realizada em duas etapas, via comunicação de dentro do cluster(interna). A comunicação via internet pública(externa) é basicamente enviar uma requisição para o IP externo disponibilizado pelo load balancer, o resultado esperado é o mesmo.
A comunicação interna será realizada por meio de um pod do cluster realizando uma requisição diretamente ao Load Balancer.
-
Recupere o IP externo do serviço
network-test-cluster-2-lb
<EXTERNAL_IP>:-
kubectl get svc network-test-cluster-2-lb -o jsonpath='{.status.loadBalancer.ingress[*].ip}'
, ou -
observe o serviço e espere pelo
EXTERNAL-IP
através do comando a seguir. Isso retornará o IP do load balancer no qual você conseguirá conectar ao serviço.$ watch kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE network-test-cluster-2-lb 10.0.0.10 171.122.247.137 80:30000/TCP 5m
-
-
No cluster-1, crie um Pod temporário com o netshoot
-
kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot
-
-
No netshoot(cluster-1), realizar uma chamada para ao IP externo criado pelo serviço do cluster-2:
-
curl http://<EXTERNAL_IP>/?echo_env_body=HOSTNAME
-
Resultado Esperado
A requisição será roteada do pod tmp-shell do cluster-1 diretamente até o load-balancer do cluster-2, não precisando sair da rede interna da Cloud para a internet. A requisição é redirecionado para um dos pods do deployment desse serviço, o qual retornará o seu hostname.
Ao executar o curl diversas vezes, em cada vez ele retornará um nome pod diferente do cluster-2, pois o serviço está encarregado de fazer o balancemaneto de carga.
Segue exemplo do retorno esperado (note que cada chamada retorna um hostname/pod diferente):
curl http://171.122.247.137/?echo_env_body=HOSTNAME
"network-test-cluster-2-7db4dd7c4-r2srd"
curl http://171.122.247.137/?echo_env_body=HOSTNAME
"network-test-cluster-2-7db4dd7c4-62kzq"
Como confirmar que a requisição foi roteada internamente?
Verifique a rota entre o pod do cluster-1 até o listener do LoadBalancer do cluster-2.
Ainda no tmp-shell/netshoot do cluster-1, execute:nmap -p 80 --traceroute <EXTERNAL_IP>
, ou:nmap -p 80 --traceroute 171.122.247.137
Load Balancer com IP Interno
No Kubernetes é possível criar um Internal Load Balancer para serviços do tipo LoadBalancer: link1, assim como no Provider no OpenStack: link2. Mais especificamente para o OpenStack, basta adicionar a seguinte anotação no serviço:
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
Configurando Ambiente
-
Utilizando o mesmo deployment
network-test-cluster-1
da seção NodePort, criar um serviço do tipo LoadBalancer, porém como um Internal Load Balancer:-
Criar um arquivo yaml com as informações abaixo e realizar o
kubectl apply
no cluster-2:
-
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: internal-network-test-cluster-2-lb
name: internal-network-test-cluster-2-lb
annotations:
service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: network-test-esz-preprod-ne-2
type: LoadBalancer
status:
loadBalancer: {}
Validando Comunicação
-
Recupere o IP interno do serviço
internal-network-test-cluster-2-lb
<INTERNAL_IP>:-
kubectl get svc internal-network-test-cluster-2-lb -o jsonpath='{.status.loadBalancer.ingress[*].ip}'
-
ou observe o serviço e espere pelo
EXTERNAL-IP
através do comando a seguir. Isso retornará o IP do load balancer no qual você conseguirá conectar ao serviço.$ watch kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE internal-network-test-cluster-2-lb 10.0.0.10 192.168.0.181 80:30000/TCP 5m
-
-
No cluster-1, crie um Pod temporário com o netshoot
-
kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot
-
-
No netshoot(cluster-1), realizar uma chamada para ao IP interno criado pelo serviço do cluster-2
-
curl http://<INTERNAL_IP>/?echo_env_body=HOSTNAME
-
Resultado Esperado
Um IP da rede interna é reservado para o load balancer. A requisição será roteada do pod tmp-shell do cluster-1 diretamente até o IP do load-balancer do cluster-2, não precisando sair da rede interna da Cloud. A seguir, a requisição é redirecionada para um dos pods do deployment desse serviço, o qual retornará o seu hostname.
Ao executar o curl diversas vezes, em cada vez ele retornará um nome pod diferente do cluster-2, pois o load balancer do serviço está encarregado de fazer o balanceamento de carga.
Segue exemplo do retorno esperado (note que cada chamada retorna um hostname/pod diferente):
curl http://192.168.0.181/?echo_env_body=HOSTNAME
"network-test-cluster-2-7db4dd7c4-r2srd"
curl http://192.168.0.181/?echo_env_body=HOSTNAME
"network-test-cluster-2-7db4dd7c4-62kzq"
Comparativo
Tipos |
Prós |
Contras |
---|
Tipos |
Prós |
Contras |
---|---|---|
Node Port |
|
|
Load Balancer - IP Externo |
|
|
Load Balancer - IP Interno |
|
|