Recentemente, enquanto trabalhava em um workshop intitulado Testando sua solicitação de tração no Kubernetes com ações GKE e GitHub, enfrentei o mesmo problema duas vezes: o serviço A precisa do Serviço B, mas o serviço A inicia mais rápido que o serviço B e o sistema falha. Nesta postagem, quero descrever o contexto desses problemas e como os resolvi com a mesma ferramenta.
Esperando em Kubernetes
Pode parecer estranho esperar em Kubernetes. A natureza de auto-cicatrização da plataforma Kubernetes é um dos seus maiores benefícios. Vamos considerar dois pods: um aplicativo Python e um banco de dados PostgreSQL.
O aplicativo começa muito rápido e tenta ansiosamente estabelecer uma conexão com o banco de dados. Enquanto isso, o banco de dados está inicializando com os dados fornecidos; a conexão falha. A vagem acaba no Failed
estado.
Depois de um tempo, Kubernetes solicita o estado do aplicativo. Como falhou, termina e inicia um novo pod. Neste ponto, duas coisas podem acontecer: o POD do banco de dados ainda não está pronto e está de volta à estaca zero, ou está pronto, e o aplicativo finalmente se conecta.
Para acelerar o processo, o Kubernetes oferece sondas de inicialização:
startupProbe:
httpGet:
path: /well being
port: 8080
failureThreshold: 30
periodSeconds: 10
Com a sonda acima, o Kubernetes aguarda dez segundos iniciais antes de solicitar o standing do pod. Se falhar, aguarda mais dez segundos. Enxágue e repita 30 vezes antes de falhar definitivamente.
Você pode ter notado o http /well being
endpoint acima. Kubernetes oferece duas configurações exclusivas de configuração de sonda: httpGet
ou exec
. O primeiro é adequado para aplicativos da Internet, enquanto este é para outros aplicativos. Isso implica que precisamos saber qual tipo de contêiner o POD contém e como verificar seu standing, desde que possa. Não sou especialista do PostGresql, então procurei um comando de verificação de standing. O gráfico de leme de Bitnami se parece com o seguinte quando aplicado:
startupProbe:
exec:
command:
- /bin/sh
- -c
- -e
- exec pg_isready -U $PG_USER -h $PG_HOST -p $PG_PORT
Observe que o exposto acima é uma simplificação, pois ignora o nome do banco de dados e um certificado SSL.
A sonda de inicialização acelera as coisas em comparação com a situação padrão se você a configurar corretamente. Você pode definir um longo atraso inicial e, em seguida, incrementos mais curtos. No entanto, quanto mais diversificados os contêineres, mais difícil ele configura, pois você precisa ser um especialista em cada um dos contêineres subjacentes.
Seria benéfico procurar alternativas.
Espera4x
Alternativas são ferramentas cujo foco está em esperar. Há muito tempo, encontrei o script de espera para isso. A ideia é direta:
./wait-for
é um script projetado para sincronizar serviços como contêineres do Docker. Isso ésh
ealpine
compatível.
Veja como esperar por uma API HTTP:
sh -c './wait-for -- echo "The api is up! Let's use it"'
Ele fez o trabalho, mas na época você precisava copiar o script e verificar manualmente se há atualizações. Eu verifiquei e o projeto agora fornece um contêiner common.
O Wait4X desempenha a mesma função, mas está disponível como um contêiner de versão e fornece mais serviços para aguardar: HTTP, DNS, bancos de dados e filas de mensagens. Essa é a minha escolha atual.
Qualquer que seja a ferramenta que você usa, você pode usá -lo dentro de um contêiner init:
Um POD pode ter vários contêineres executando aplicativos dentro dele, mas também pode ter um ou mais contêineres iniciantes, que são executados antes do início dos contêineres de aplicativos.
Os contêineres iniciais são contêineres regulares, exceto:
- Os contêineres iniciais sempre são concluídos.
- Cada contêiner init deve ser concluído com sucesso antes do início do próximo.
Think about o seguinte Pod
Isso depende de um postgreSQL Deployment
:
apiVersion: v1
type: Pod
metadata:
labels:
kind: app
app: recommandations
spec:
containers:
- title: recommandations
picture: recommandations:newest
envFrom:
- configMapRef:
title: postgres-config
O aplicativo é python e começa muito rápido. Ele tenta se conectar ao banco de dados PostGresql. Infelizmente, o banco de dados não terminou de inicializar, então a conexão falha e o Kubernetes reinicia o pod.
Podemos consertar com um initContainer
e um recipiente em espera:
apiVersion: v1
type: Pod
metadata:
labels:
kind: app
app: recommandations
spec:
initContainers:
- title: wait-for-postgres
picture: atkrad/wait4x:3.1
command:
- wait4x
- postgresql
- postgres://$(DATABASE_URL)?sslmode=disable
envFrom:
- configMapRef:
title: postgres-config
containers:
- title: recommandations
picture: recommandations:newest
envFrom:
- configMapRef:
title: postgres-config
Na configuração acima, o initContainer
Não para até que o banco de dados aceite conexões. Quando isso acontece, termina e o recommandations
O contêiner pode começar. Kubernetes não precisa encerrar o Pod
Como na configuração anterior! Isso implica menos toras e potencialmente menos alertas.
Quando esperar se torna obrigatório
O exposto acima é uma pequena melhoria, mas você pode ficar sem ele. Em outros casos, a espera se torna obrigatória. Eu experimentei recentemente ao me preparar para o workshop mencionado acima. O cenário é o seguinte:
- O oleoduto aplica um manifesto no lado Kubernetes
- Na próxima etapa, ele executa o teste
- À medida que o teste começa antes que o aplicativo seja lido, ele falha.
Devemos esperar até que o again -end esteja pronto antes de testarmos. Vamos usar wait4x
para esperar pelo Pod
Para aceitar solicitações antes de lançarmos os testes:
- title: Wait till the applying has began
makes use of: addnab/docker-run-action@v3 #1
with:
picture: atkrad/wait4x:newest
run: wait4x http ${{ env.BASE_URL }}/well being --expect-status-code 200 #2
- A ação do GitHub permite a execução de um contêiner. Eu poderia ter baixado o Go Binário.
- Espere até o
/well being
O endpoint retorna a200
Código de resposta.
Conclusão
As sondas de inicialização do Kubernetes são uma ótima maneira de evitar reinicializações desnecessárias quando você inicia serviços que dependem um do outro. A alternativa é uma ferramenta de espera externa configurada em um initContainer
. wait4x
é uma ferramenta que pode ser usada em outros contextos. Agora faz parte do meu cinto de ferramentas.
Ir além:
Publicado originalmente em um geek java em 20 de abril de 2025