Fala pessoal! Estou estudando Kubernetes (Fazendo o sensacional PICK da LinuxTips) e resolvi compartilhar o setup que preparei para o meu residence lab.
Meu objetivo é ter o Nginx Controller balanceando as requisições para os meus serviços e diponível num FQDN (Full Certified Area Title) que eu separei pra ele.
Os manifestos usados na configuração podem ser achados aqui.
Esse put up está divido em:
- Setup Inicial
- Conceitos Importantes
- Tipos de Serviço
- O que é ingress (com foco no Nginx Controller)
- O que é Metallic LB
- Configurando tudo
Setup Inicial
Inicialmente meu cluster tem 1 Management Airplane e 2 Employees, os três estão rodando na AWS, usando Ubuntu 22.04
, em instâncias t3.medium
e possuem IP’s públicos efêmeros (eles mudam toda vez que eu reincio essas máquinas).
Eu segui uma instalação básica do K8S, e me guiei principalmente pelo Descomplicando o Kubernetes.
Conceitos Importantes
Disclaimer: Se, assim como eu você está começando a estudar o Kubernetes, ou se quer refrescar alguns conceitos, os próximos tópicos são pra você. Caso você já tenha uma boa ideia de toda a base, eu sugiro pular para o tópico “Configurando Tudo”.
Tipos de Serviço
Antes de mais nada, um serviço no Kubernetes é uma forma de expor uma aplicação que esteja rodando num Pod na rede, de forma que se um Pod for recriado, ou se mais Pods de uma aplicação forem criados, todas as instâncias sejam acessíveis através do mesmo “canal”.
Quando um serviço é criado (seja através do kubectl create svc ou do kubectl expose deploy), um objeto chamado “Endpoint” é criado junto. Esse objeto mantém a lista com os IPs de todos os Pods ativos rodando aquele determinado serviço. Quando um Pod é criado ou destruído a lista é atualizada.
Por exemplo, na imagem abaixo 2 Pods com App-A e App-B estão rodando. É importante relembrar que as aplicaçoes dentro desses Pods compartilham recursos como o endereço IP. Ao criar um serviço para expor a App-A, o Kubernetes identifica os endereços de todos os Pods rodando a aplicação (através das labels e selectors) e cria o serviço apontando para esses Pods. Para acessar a App-A de dentro do cluster nesse caso, é possível executar um curl http://10.100.100.100
e a requisição será encaminhada para qualquer dos Pods disponívels.
Finalmente, os serviços podem ser de quatro tipos:
- Cluster IP: Esse é o tipo padrão de serviços, ele expõe a aplicação apenas dentro do cluster.
- Load Balancer: Expõe a aplicação através de um load balancer, com IP fixo fora do cluster.
- Node Port: Torna a aplicação acessível atravpes de uma porta específica em cada nó do cluster. Permite o acesso externo ao cluster.
- Exterior Title: É uma exceção aos demais, invés de usar labels para definir os endpoints, usa DNS. É usado para apontar para serviços externos ao cluster.
O que é um Ingress?
O Ingress é um objeto no Kubernetes que ajuda no gerenciamento de acesso aos serviços. Através dele é possível definir regras de acesso, rotas, SSL certificados (com integrações) e outras coisas mais.
Para funcionar, o Ingress requer um Ingress Controller, que não é criado na instalação padrão no Kubernetes. É através do controller que toda a configuração do Ingress será feita, e há várias opções disponíveis. Aqui nessa página você pode checar a lista completa.
Um dos controllers mais utilizados (e um dos três suportados oficialmente projeto Kubernetes) é o Nginx-Controller. Ele é uma implementação do Nginx e pode fazer o balancemaento de requisições WebSocket, gRPC, TCP e UDP, além de suportar o redirecionamento de rotas e terminação TLS/SSL.
O que é MetalLB:
O MetalLB é uma implementação de LoadBalancer para Kubernetes que rodam em bare-metal (fora da cloud). Ele é capaz de alocar IP’s para serviços do tipo LoadBalancer e essa é a principal característica que eu vou utilizar no meu HomeLab.
É importante ressaltar que o MetalLB não cria os IP’s, ele apenas faz a alocação de IP’s baseado em ranges definidos pelo usuário.
Configurando tudo
Eu sugiro começar a configuração pelo MetalLB
Minha configuração closing é a seguinte:
O guia completo da Instalação e Configuração pode ser encontrado aqui.
O primeiro passo para a instalação é habilitar o modo ‘strict ARP’:
kubectl edit configmap -n kube-system kube-proxy
apiVersion: kubeproxy.config.k8s.io/v1alpha1
variety: KubeProxyConfiguration
mode: "ipvs"
ipvs:
strictARP: true
Após sair salvando (:wq!
), é hora de fazer a instalção do MetalLB:
kubectl apply -f https://uncooked.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml
Esse comando vai criar o namespace metallb-system
com um controller no control-plane e um speaker em cada nó do cluster. Após todos os pods estarem rodando, alguma configuração é necessária:
ipaddrpool.yml
apiVersion: metallb.io/v1beta1
variety: IPAddressPool
metadata:
title: k8s-lab
namespace: metallb-system
spec:
addresses:
#No meu caso esses são os IPs que eu tenho disponíveis, caso queira aplicar no seu ambiente, atualize esses IPs
- 54.152.233.228/32
- 54.167.93.192/32
Como eu estou usando a configuração by way of ARP, também é necessário criar um L2Advertsiment:
L2Advertsiment.yml
apiVersion: metallb.io/v1beta1
variety: L2Advertisement
metadata:
title: k8s-lab
namespace: metallb-system
spec:
ipAddressPools:
- k8s-lab
E então:
ok apply -f ipaddrpool.yml
ok apply -f L2Advertsiment.yml
Checando se tudo está criado:
A configuração do MetalLB está feita, para validar, precisamos criar um serviço. Optei pelo bom e velho Nginx:
nginx-deployment.yml
apiVersion: apps/v1
variety: Deployment
metadata:
labels:
app: nginx
title: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- picture: nginx
title: nginx
ports:
- containerPort: 80
sources:
limits:
cpu: 0.5
reminiscence: 256Mi
requests:
cpu: 0.3
reminiscence: 64Mi
ok apply -f nginx-deployment.yml
A forma mais rápida de validar se o MetalLB está sendo capaz de fornecer IPs para os serviços, é expondo o deploy que acabamos de criar utilizando o serviço do tipo loadBalancer:
kubectl expose deploy nginx --port 80 --target-port 80 --type LoadBalancer
E com um curl 54.167.93.192
recebemos a mensagem padrão do Nginx.
Okay, o MetalLB está funcionando e é possível acessar o serviço através do IP de dentro do cluster, mas eu quero que esse serviço esteja disponível no meu FQDN k8s-lab.ibmenezes.com
. É hora de installar o nginx-controller.
O getting began dessa instalação pode ser encontrado aqui.
Como estamos em uma instalação ‘bare-metal’, a instalação será:
kubectl apply -f https://uncooked.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/supplier/baremetal/deploy.yaml
Após alguns segundos (talvez minutos) temos o pod do ingress-controller rodando no Management Airplane. Além disso, o serviço do ingress-controler é criado no namespace ingress-nginx
, e o Exterior IP é atribuído pelo MetalLB:
Nesse momento já é possível acessar o ingress controller no browser com https://54.152.233.228:31333/. Esse acesso retorna um 503 porque não existe nenhum service associado a ingress nesse momento.
Para corrigir isso, a criação do ingress:
ingress.yml
apiVersion: networking.k8s.io/v1
variety: Ingress
metadata:
title: k8s-lab
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/issuer: "letsencrypt-staging"
spec:
ingressClassName: nginx
tls:
- hosts:
- k8s-lab.ibmenezes.com
secretName: "k8s-lab-tls"
guidelines:
- host: k8s-lab.ibmenezes.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
title: nginx
port:
quantity: 80
Aqui algumas coisas são importantes:
A primeira annotation nginx.ingress.kubernetes.io/rewrite-target: /
determina que todas as vezes que uma requisição for encaminhada para a /
, ela será redirecionada ao path (que falaremos mais abaixo).
A segunda anotação, cert-manager.io/issuer: "letsencrypt-staging"
é associada à criação de certificados SSL usando o letsencrypt. Nesse setup não iremos a fundo nisso, na próxima sessão explico os motivos. O bloco tls
também está associado ao certificado.
Já nas especificações, o bloco de guidelines vai determinar:
- host: k8s-lab.ibmenezes.com -> O FQDN onde o ingress espera ouvir a requisição
http.paths.path -> O caminho da applicação para onde o trafego será redirecionado quando o usuário digitar `/` (da annotation)
http.paths.pathType -> O tipo de path, esse valor é requerido quando o tipo é Prefix ou Actual
http.paths.backend -> as configurações do serviço para o qual o ingress estará apontando
Quase tudo pronto, alguns detalhes finais:
É importante remover o serviço que criamos anteriormente:
kubectl delete svc nginx
Isso vai liberar o IP anteriormente atribuido ao serviço e prevenir que a gente valide o serviço errado. Antes de criar o ingress é importante criar o serviço definido em http.paths.backend. Como eu defini o meu se chamando ‘nginx’, me basta:
kubectl expose deploy nginx --port 80
Para criar o ingress a partir do manifesto criado anteriormente basta executar:
kubectl apply -f ingress.yml
Com um describe é possível verificar que o ingress recebe o mesmo IP do ingress-controller recebeu anteriormente.
A última configuração necessária é no provedor de DNS, no meu caso eu criei uma entrada tipo A apontando para o IP do ingress.
Para validar o balanceamento, editei o index.html de cada Pod:
ok exec -it nginx-f888bbf65-j94fk /bin/bash
echo "Eu sou o pod nginx-f888bbf65-j94fk" > /usr/share/nginx/html/index.html
ok exec -it nginx-f888bbf65-tcmvz/bin/bash
echo "Eu sou o pod nginx-f888bbf65-tcmvz" > /usr/share/nginx/html/index.html
E, acessando ‘https://k8s-lab.ibmenezes.com:31333‘ pelo browser temos:
Resumindo como tudo funciona de forma superficial:
- Eu tenho uma entrada DNS ‘k8s-lab.ibmenezes.com’ do tipo A, aonde eu precisei atualizar o IP de destino;
- A lista de IP’s disponíveis para a virtualização foi feita a partir dos IP’s públicos das minhas máquinas na AWS e passada para o MetalLB;
- O MetalLB forneceu um dos IP’s disponíveis para o nginx-controller
- Ta pronto o sorvetinho! Agora eu consigo acessar meus serviços externamente.