Uma jornada gRPC+ k8s
Bom dia. Boa tarde. Boa noite
Contarei aqui a minha jornada ao tentar escrever um serviço gRPC até a publicação em ambiente produtivo no Kubernetes.
Tecnologias adotadas
- ASP.NET Core
- Helm
- Digital Ocean Kubernetes
- Azure Pipelines
Desenvolvimento
A fase de desenvolvimento é a mais tranquila, pois a maneira de desenvolvermos não muda quase nada, a diferença é: criamos serviços com base em contratos do protobuf.
Ao criar um projeto com o template padrão de gRPC do dotnet toda a estrutura e configurações são feitas. Serviço de exemplo também é disponibilizado.
Primeira dúvida — Posso usar juntamente com meus antigos endpoints?
Minha primeira alternativa foi manter meus endpoints REST + gRPC no mesmo serviço, porém foi quando tomei as primeiras dores.
O gRPC foi construído utilizando o HTTP/2, fazendo com que os endpoints que respondiam HTTP/1 retornassem 405 — method not allowed.
Então, comecei a pesquisar sobre como manter os dois em portas separadas, mas percebi que era desnecessário. Resolvi então separar em projetos distintos.
Mas pra quem quiser ver a solução o link é este — David Fowler quem escreveu
Testando
Quando falamos em testar api REST o que nos vem a cabeça é o Postman, será que criaram também um “Postman” pra gRPC ?
O BloomRPC é uma ótima ferramenta, onde você importa o arquivo proto e realiza as chamadas. — isso também pode ser feito utilizando a grpc_cli, mas sem UI.
Então eu o utilizado para meus testes.
Compartilhamento do arquivo proto
Enquanto o consumo do contrato estava tudo na mesma solution foi fácil manter o arquivo .proto, o problema apareceu quando precisei consumir esse proto em outro projeto. Tinha 2 alternativas:
- Manter o arquivo proto nos 2 repositórios
- Pesquisar como a comunidade estava gerenciando isso
Comecei a pesquisar as maneiras e encontrei o seguinte artigo CREATING GRPC .NET CORE CLIENT LIBRARIES do Steve Gordon — a serie dele sobre gRPC + .NET Core é imprescindível pra quem está galgando nesse reino.
Então para cada gRPC server eu tenho uma biblioteca cliente abstraindo todo o gerenciamento desse arquivo — sim, tenho depois pipelines: um para o pacote nuget e outro para o deploy do server.
Adotei como padrão nos meus repositórios.
Um exemplo da implementação dessa ideia pode ser encontrada aqui.
Depois de desenvolvido e testado, agora vamos para a publicação.
Publicação
Publicação do serviço
Todos os serviços seguem o mesmo template de pipeline.
Criei os pipelines utilizando AzureDevops Pipelines Yaml. A do servido contém 2 estágios: build e deploy.
O estágio de build realiza:
- GitVersion, uso para definir a TAG da imagem
- dotnet publish do csproj desejado
- Docker login
- Docker build e push, utilizando o Canister — repos privados grátis!
E o estágio de deploy:
- helm upgrade do meu pacote no cluster
Publicação do pacote NuGet interno
Para as libs de clientes gRPC eu subo no Artifacts do AzureDevops, com somente um estágio:
- GitVersion
- dotnet pack
- dotnet push
Liveness/Readness probes
Aqui veio o primeiro grande problema em ambiente produtivo: os probes do k8s.
Pesquisando descobri que o k8s não suporta grpc nativamente — mas os services mesh sim — , então tomei um balde de água fria, até pensei em dar rollback e usar api REST para aplicações internas — mas meu objetivo era aprender e validar a solução.
Até que encontrei também no blog do mestre Steve Gordon uma implementação do protocolo de heathcheks do gRPC, a próxima etapa era utilizar isso no k8s — esse healthcheck é compatível com o da Microsoft.
É necessário configurar deployment utlizando o exec
, encontrei um cli da comunidade específica para isso: grpc_health_probe, o uso é muito simples:
spec:
containers:
- name: server
image: "[YOUR-DOCKER-IMAGE]"
ports:
- containerPort: 5000
readinessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:[PORT_NUMBER]"]
initialDelaySeconds: 5
livenessProbe:
exec:
command: ["/bin/grpc_health_probe", "-addr=:[PORT_NUMBER]"]
initialDelaySeconds: 10
Só é necessário disponibilizar o binário no container. No seu dockerfile basta adicionar:
## Health prob
RUN apt-get update -y && apt-get install wget -y && \GRPC_HEALTH_PROBE_VERSION=v0.3.1 && \wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64RUN chmod +x /bin/grpc_health_probe
Feito isso, o binário está disponível para ser utilizado e totalmente funcional.
Para não precisar adicionar em todo Dockerfile, eu criei uma imagem base, mais a frente falarei sobre
Debug
Após ter concluído com sucesso, eu precisava fazer alguns troubleshooting realizando um port-forward do container pra minha máquina. Eu queria listar todos os serviços disponíveis e não conseguia, fui então pesquisar novamente.
Encontrei a cli grpc_cli que consegue realizar as operações que eu queria, então fui realizei alguns testes. Tomei pau, pois é necessário implementar o protocolo Server Reflection.
Por padrão o template do .NET não implementa o Server Reflector, então implementei utilizando o pacote Grpc.AspNetCore.Server.Reflection que tem essa funcionalidade já feita.
A partir de agora, tenho tudo que é necessário para ter uma vida tranquila com o processo em troubleshooting em produção.
Grpc.WebServer
Para facilitar o processo de desenvolvimento criei uma lib que implementa todos esses conceitos que comentei durante o artigo: healthchecks e Server Reflection. NuGet: PlusUltra.Grpc.
Imagens Docker
Criei também imagens base a partir das imagens 3.0 do aspnet com as ferramentas necessárias para fazer probe e troubleshooting.
Contém 2 tags:
- 3.0, contém somente a cli de probe
- 3.0-cli, contém a lib de prob + grpc_cli
Essas são as dores que compartilho que tive nessas últimas semanas com ASP.NET + gRPC + k8s
Lembrando que as cli que foram mencionadas funcionam em qualquer tecnologia, não somente em .NET