Como criar um Load Balancer Global, entre diferentes clusters Kubernetes na Google Cloud
Quando me foi apresentado a ideia de "multi cluster ingress" (ou MCI), deixei em standby pelo fato de aplicação trabalhar com o recurso auto devops do gitlab. E para viabilizar o MCI, eu teria de escrever os deployments e services e sair da praticidade do auto devops (perdendo recursos como incremental rollout que já está disponível no auto devops) e segui avaliando as alterações que gerariam impacto na aplicação por meio de outro recurso chamado NEG. Por fim, descobri em algumas pesquisas, recursos que o Gitlab auto devops provê, que poderiam viabilizar o MCI e retomei o foco para ele (sabiamente).
Mas, qual o objetivo?
A proposta do MCI é basicamente unir 2 clusters através de um Load Balancer. E sabendo que cada cluster pode ter pools de maquinas em diferentes zonas, os números de disponibilidade se tornam interessantíssimos e começamos a falar em algo em torno de 99.95%.
Qual botão eu clico?
Calma lá, lembre-se que: Com grandes poderes, vêm grandes responsabilidades Com grandes problemas, vêm grandes noites sem dormir.
- Se você acompanha meus posts, sabe que gosto muito do gitlab e nesse caso, então a coisa sai um pouco da simplicidade e se faz necessário partir para automatizações e customizações para atingir o objetivo de forma elegante.
Blz :/ Então o que eu faço?
A aplicação que fará a comunicação com o Load Balancer, deve obrigatoriamente atender alguns critérios.
Primeiro algumas configurações precisam ser iguais em todos os clusters (external e internal ports, são especificas para NEG, mas vamos deixar diferente para saber diferenciar facilmente uma aplicação da outra dentro da listagem de services do kubernetes) e para equalizar essa configurações, vamos utilizar algo que o gitlab nos fornece. O HELM_UPGRADE_EXTRA_ARGS:
Crie um arquivo values.yaml na aplicação com o conteúdo:
service:
externalPort: 8080
internalPort: 8080
type: NodePort
- Aqui apenas coloquei as portas do container (interna e externa), para 8080 no lugar de 5000 e o tipo de ClusterIP (default), mudei para NodePort.
No arquivo de configuração do pipeline (.gitlab-ci.yaml) inclua as variáveis abaixo:
HELM_UPGRADE_EXTRA_ARGS: "--values values.yaml"
HELM_RELEASE_NAME: mci-app
KUBE_NAMESPACE: default
NODE_PORT: 30999
- Essas variáveis, respectivamente: Atribui o arquivo values.yaml para o runner do gitlab processar no pipeline, coloca um nome padrão para a aplicação (precisa ser igual em diferentes clusters) e coloco a aplicação no namespace default do kubernetes (preciosismo apenas).
Outro requisito, é que a porta do service NodePort, seja a mesma nos clusters. E ai já é um problema a resolver, pois o gitlab não tem isso para passar pelo HELM. Então, sugiro criar um step a mais no seu pipeline, que basicamente pega o service e modifica o valor do nodePort dentro do arquivo .gitlab-ci.yaml:
node_port_change:
<<: *node_port_change
allow_failure: true
stage: setup mci
image: google/cloud-sdk:latest
script:
- gcloud auth activate-service-account --key-file ${SEU_ARQUIVO_GCLOUD}
- gcloud config set project ${SEU_PROJETO_GCP}
- gcloud container clusters get-credentials \
${NOME_DO_SEU_CLUSTER} --zone ${ZONA} --project ${SEU_PROJETO_GCP}
- kubectl get services --namespace ${KUBE_NAMESPACE} \
-l app=${HELM_RELEASE_NAME} -o yaml | sed -E \
"s/nodePort:\ [0-9]+$/nodePort:\ ${NODE_PORT}/g" >> service.yaml
- kubectl apply -f service.yaml
only:
refs:
- master
mci prod1: &node_port_change
environment:
name: prod1
variables:
NOME_DO_SEU_CLUSTER: prod1
ZONA: ${SUA_ZONA_PROD1}
mci prod2: &node_port_change
environment:
name: prod2
variables:
NOME_DO_SEU_CLUSTER: prod2
ZONA: ${SUA_ZONA_PROD2}
- Quando essa etapa executar, basicamente vai pegar o service de cada cluster no kubernetes, baixar, fazer um replace da porta, colocar a mesma porta para ambos (perceba a variável NODE_PORT: 30999) e problema resolvido.
Show :) Terminamos?
Pff, terminamos a aplicação, agora vem o MCI. Seguindo os passos abaixo, você terá o MCI configurado (usando bash).
1) Crie uma pasta /mci
- mkdir mci
2) Entre na pasta
- cd mci
3) Exporte os arquivos dos clusters
- KUBECONFIG=clusters.yaml gcloud container clusters get-credentials prod1 --zone=${ZONA-PROD1}
- KUBECONFIG=clusters.yaml gcloud container clusters get-credentials prod2 --zone=${ZONA-PROD2}
- Isso vai gerar um arquivo com nome clusters.yaml.
4) Crie um IP global
- gcloud compute addresses create --global mci-ip
5) Se você quiser colocar certificado HTTPS no seu Load Balance, esse é o momento
- gcloud compute ssl-certificates create ssl-cert --certificate ${CERT_FILE} --private-key ${PRIVATE_KEY}
6) Faça download do kubemci
7) Atribua permissão adequada ao kubemci
- chmod +x ./kubemci
8) Crie o ingress-mci.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mci-app-auto-deploy
annotations:
kubernetes.io/ingress.class: gce-multi-cluster
kubernetes.io/ingress.allow- http: "false"
ingress.gcp.kubernetes.io/pre- shared-cert: "ssl-cert"
spec:
backend:
serviceName: mci-app-auto-deploy
servicePort: 8080
- Perceba que usamos a porta dos containers, nome do app gerado pelo auto devops, certificado que criamos anteriormente e nome do ip que também criamos acima.
9) Execute o ./kubemci
./kubemci create my-mci \
--ingress=ingress-mci.yaml \
--gcp-project=${SEU_PROJETO_GCP} \
--kubeconfig=clusters.yaml
Atenção: Caso não queira o HTTPS, retire do ingress-mci.yaml a annotation "...allow-http: false" e não execute a etapa 5.
É isso :) Espero que não sofra o tanto que sofri pra chegar nesse resultado rsrs, pois não tem nada por ai explicando como fazer isso com auto devops do gitlab. Mas agora tem xD divirta-se e indique a solução para seu amiguinho que está com o mesmo problema!
Abraços!
Referências: