Estratégias de Deploy para Microserviços Java no Kubernetes com Nacos

Padronização de Nomenclatura de Serviços

Para manter a organização em clusters Kbuernetes, é fundamental adotar uma convenção de nomes clara para os repositórios e artefatos. Uma estrutura recomendada segue o padrão de função do serviço:

svc-vendas-provider
svc-vendas-consumer
svc-processamento-worker

Implementação de Health Checks Dinâmicos

A utilização de sondas (probes) do Kubernetes garante a resiliência do sistema. Abaixo, uma abordagem para criar um endpoint de verificação que permite controle manual sobre o estado de prontidão do serviço, útil para manutenções controladas.

Configuração sugerida para as sondas no manifesto do Kubernetes:

livenessProbe:
  httpGet:
    path: /manage/health/check
    port: 9090
  initialDelaySeconds: 20
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /manage/health/check
    port: 9090
  initialDelaySeconds: 10
  periodSeconds: 10

Desenvolvimento do Serviço Provedor (Provider)

Este serviço atua como o fornecedor de dados na arquitetura, registrado no Nacos.

Configuração do Projeto (pom.xml):

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <version>2021.0.4.0</version>
    </dependency>
</dependencies>

Controlador de Recursos e Saúde:

package com.dev.provider.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/manage")
public class MonitoringController {

    private static volatile boolean isSystemReady = true;

    @GetMapping("/status/toggle")
    public String toggleState(@RequestParam String mode) {
        if ("disable".equalsIgnoreCase(mode)) {
            isSystemReady = false;
            return "Serviço marcado como indisponível";
        } else if ("enable".equalsIgnoreCase(mode)) {
            isSystemReady = true;
            return "Serviço marcado como ativo";
        }
        return "Comando inválido";
    }

    @GetMapping("/health/check")
    public String checkHealth() {
        if (!isSystemReady) {
            throw new RuntimeException("Service Unavailable");
        }
        return "UP";
    }
    
    @GetMapping("/data")
    public String getData() {
        return "Dados processados pelo Provider";
    }
}

Dockerfile do Provedor:

FROM openjdk:11-jre-slim
WORKDIR /deployments
COPY target/svc-vendas-provider.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java", "-Duser.timezone=America/Sao_Paulo", "-jar", "app.jar", "--spring.config.location=/app/config/application.yml"]

Desenvolvimento do Serviço Consumidor (Consumer)

O consumidor utiliza OpenFeign para comunicação síncrona e LoadBalancer para distribuir as requisições entre as instâncias do provedor.

Interfcae Feign:

package com.dev.consumer.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "svc-vendas-provider")
public interface ProviderClient {
    @GetMapping("/manage/data")
    String fetchRemoteData();
}

Configuração de Log Customizada (logback-spring.xml):

Para ambientes K8s, é ideal que o nome do arquivo de log contenha o ID do Pod para facilitar o rastreamento em volumes compartilhados.

<configuration>
    <springProperty scope="context" name="POD_ID" source="HOSTNAME" defaultValue="local-node"/>
    <property name="LOG_PATH" value="/var/log/microservices/${POD_ID}.log"/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{40} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

Gerenciamento de Imagens e Configuração Externa

O empacotamento e distribuição seguem o fluxo padrão de integração contínua:

mvn clean package -DskipTests
docker build -t registry.local/svc-provider:1.0.0 ./provider
docker push registry.local/svc-provider:1.0.0

Para desacoplar a configuração do código, utilizamos ConfigMaps para injetar o arquivo application.yml:

kubectl create configmap provider-config --from-file=application.yml=./configs/prod-provider.yml -n namespace-vendas

Orquestração no Kubernetes

Definição do volume persistente para logs e do deploy dos serviços.

PersistentVolumeClaim (PVC):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ms-log-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: nfs-client

Manifesto de Deployment (Exemplo Provider):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: svc-vendas-provider
spec:
  replicas: 2
  selector:
    matchLabels:
      app: provider-service
  template:
    metadata:
      labels:
        app: provider-service
    spec:
      containers:
      - name: java-app
        image: registry.local/svc-provider:1.0.0
        env:
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        volumeMounts:
        - name: log-volume
          mountPath: /var/log/microservices
        - name: config-volume
          mountPath: /app/config/application.yml
          subPath: application.yml
      volumes:
      - name: log-volume
        persistentVolumeClaim:
          claimName: ms-log-pvc
      - name: config-volume
        configMap:
          name: provider-config

Tags: java spring-cloud kubernetes Nacos Docker

Publicado em 9-24 09:04