Pular para o conteúdo principal

Rede de Serviços

Raramente você configura os seus pods para se comunicar diretamente entre si. Se você quiser que um pod acesse os serviços de outro pod, hospedado ou não no mesmo host, você sempre deve usar os services.

Quando um service é criado ele pode ser acessado por todos os pods no cluster independente de qual node os pods estejam. Enquanto pod é hospedado em um node um service é "hospedado" em todo o cluster e não vinculado a um nó específico.

Temos 3 tipos de services.

  • ClusterIP: É o padrão e somente conhecido dentro do cluster. Um exemplo é um database dentro do cluster para as aplicações.
  • NodePort: É o tipo de service que expõe o service para fora do cluster. Uma aplicação web seria um ótimo exemplo para uso desse tipo. Esse service cria em todos os nodes do cluster a MESMA porta e aponta para o service. Um NodePort também tem um ClusterIP.
  • LoadBalancer: falaremos mais tarde

alt text

Mas como isso funciona realmente?

Resumo rápido.

  1. O kubelet observa as alterações no kube-apiserver e toda vez que um pod é definido para ser criado em seu node ele interage com o container runtime e o cria.

  2. O container runtime faz o seu trabalho de criar os namespaces necessários para o pod e invoca o CNI plugin para criar a rede para esse pod.

Em cada node executamos o kube-proxy também por isso é um DaemonSet:

kubectl get ds -n kube-system  
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-proxy 4 4 4 4 4 kubernetes.io/os=linux 30h
weave-net 4 4 4 4 4 <none> 30h

O kube-proxy também observa as alterações do cluster por meio do kube-apiserver, assim como o kubelet, e sempre que um novo service é criado ele entra em ação.

Services são um conceito que abrange todo o cluster. Não são um recurso criado no node como um container, mas existem em todos os nodes do cluster. De fato eles não existem. Não existem processos, interfaces, namespaces, containers, IPs, nada. Services no Kubernetes são apenas objetos virtuais.

Então como eles ganham um IP?

Quando criamos um service ele recebe um IP de um intervalo pré-definido. Os componentes do kube-proxy em execução que rodam em cada node pegam esse endereço IP e criam regras de encaminhamento em cada nó do cluster dizendo que todo tráfego que chegar para esse IP do serviço deve ser direcionado para o IP do pod.

alt text

Sempre que um tenta acessar o IP de um serviço ele será encaminhado para o endereço IP do pod que pode ser acessado em qualquer nó do cluster.

Não se trata apenas do IP mas a combinação de IP:PORTA

Toda vez que um service é criado o kube-proxy adiciona novas regras, e toda vez que é deletado essas regras são removidas.

Como essas regras são criadas e onde?

O kube-proxy oferece suporte a diferentes modos, como:

  • userspaces : O kube-proxy escuta em uma porta para cada serviço e as conecta com os pods.
  • IPVS : o kube-proxy cria regras IPVS.
  • iptables: o kube-proxy cria regras no iptables

O no kube-proxy o proxy-mode pode ser definido usando o parâmetro --proxy-mode [userspaces | iptables | ipvs ] ...

Se isso não for definido, o padrão é iptables.

Portanto, veremos como os iptables são configurados pelo kube-proxy e como você pode visualizá-los nos nós.

Vamos criar um deploy, que cria um replicaset, que cria um pod.. Poderia ser só um pod, mas vamos pelo método usual, ninguém fica criando pod sem um controller.

kubectl create deployment nginx --image nginx  
deployment.apps/nginx created

# PODEMOS FAZER DESSE JEITO OU DO SEGUNDO JEITO
kubectl expose deployment nginx --port=8080 --target-port=80 --type=ClusterIP
service/nginx exposed

kubectl delete svc nginx

# E PODERÍAMOS FAZER ASSIM
kubectl create service clusterip nginx --tcp=8080:80

O que definimos aqui? Um service que irá receber na porta 8080 e enviará para o nginx na porta 80.

O comando kubectl expose e o kubectl create service são duas maneiras diferentes de alcançar o mesmo resultado: criar um serviço no Kubernetes. No entanto, há algumas diferenças entre eles:

  • kubectl expose:

    • Requer que você especifique um deployment pré-existente para criar o serviço.
    • Além de criar um serviço, o comando expose pode modificar ou atualizar um serviço existente
    • Permite que você especifique opções adicionais, como tipo de serviço, porta de serviço e outros detalhes.
    • Mais complexo quando usado com muitos parâmetros.
  • kubectl create service:

    • Permite criar um serviço sem necessariamente ter um deployment correspondente.
    • Ele cria um serviço com as portas especificadas e não oferece muitas opções adicionais.
    • Menos complexo
k get pods -o wide  
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7854ff8877-wc4dr 1/1 Running 0 13m 10.34.0.1 cka-cluster-worker2 <none> <none>

k get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 30h <none>
nginx ClusterIP 10.101.29.104 <none> 8080/TCP 3s app=nginx

O IP real do pod é 10.34.0.1 O IP do service é 10.109.205.172

Por que o service pegou IP?

A definição esta no kube-apiserver é um dos parâmetros passados na execução do pod.

root@cka-cluster-control-plane:/etc# cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
# Removido para diminuir a saída ...
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.18.0.5
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --runtime-config=
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12 # <<<<< Aqui será o range
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.k8s.io/kube-apiserver:v1.29.1
imagePullPolicy: IfNotPresent
## Removido para diminuir a saída

## OU

kubectl get pods -n kube-system kube-apiserver-cka-cluster-control-plane -o=jsonpath='{.spec.containers[*].command}' | jq
[
"kube-apiserver",
"--advertise-address=172.18.0.5",
"--allow-privileged=true",
"--authorization-mode=Node,RBAC",
"--client-ca-file=/etc/kubernetes/pki/ca.crt",
"--enable-admission-plugins=NodeRestriction",
"--enable-bootstrap-token-auth=true",
"--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt",
"--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt",
"--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key",
"--etcd-servers=https://127.0.0.1:2379",
"--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt",
"--kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
"--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt",
"--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key",
"--requestheader-allowed-names=front-proxy-client",
"--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt",
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-group-headers=X-Remote-Group",
"--requestheader-username-headers=X-Remote-User",
"--runtime-config=",
"--secure-port=6443",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--service-account-key-file=/etc/kubernetes/pki/sa.pub",
"--service-account-signing-key-file=/etc/kubernetes/pki/sa.key",
"--service-cluster-ip-range=10.96.0.0/12", ## Aqui
"--tls-cert-file=/etc/kubernetes/pki/apiserver.crt",
"--tls-private-key-file=/etc/kubernetes/pki/apiserver.key"
]

10.96.0.0/12 significa que os primeiros 12 bits do endereço IP são fixos (10.96) e os 20 bits restantes são variáveis. Isso dá uma faixa de endereços IP de 10.96.0.0 a 10.111.255.255. Então esta dentro.

O padrão é 10.0.0.0/24 o que nos permitiria criar muito menos services.

Quando fornecemos ao nosso CNI o range de IP que os pods irão usar, vimos que é 10.244.0.0/16. Eles não podem se sobrepor. Não deve haver um caso em que um pod e um service recebam o mesmo IP

E por que o endereço do pod esta então em 10.34.0.1?

NAME                     READY   STATUS    RESTARTS   AGE   IP          NODE                  NOMINATED NODE   READINESS GATES
nginx-7854ff8877-wc4dr 1/1 Running 0 47m 10.34.0.1 cka-cluster-worker2 <none> <none>

Por que é como o Weavenet trabalha. Lembra dos agentes?

Vamos conferir as regras criadas quando esse service foi executado