A Anatomia de um Deploy de Sucesso: Do Caos do Legado à Estabilidade no Kubernetes

Anatomia de um Deploy de Sucesso - Kubernetes

Introdução

No artigo anterior (25/01), mostrei como separamos o front-end no S3 e o backend no EKS. Mas nem tudo foram flores. Hoje vou revelar a parte que ninguém vê: os erros que quebravam a imagem, o diagnóstico cirúrgico e a colaboração que transformou caos em estabilidade.

Parte 1: O Chamado e o Inventário do Caos

O projeto não é apenas um sistema; é um monolito vivo de 146MB que sustenta operações críticas. Quando assumi o projeto às pressas, o cenário era de alta pressão e entrega imediata. O primeiro passo não foi codificar, mas sim entender onde estávamos pisando.

1.1. A Mentalidade de Transparência

Como destacado na minha reunião de alinhamento com o Team Leader, a proatividade em manter a Wiki e os inventários atualizados foi o que permitiu que não perdêssemos o controle durante a transição. Versionar e documentar não são tarefas secundárias; são o que separa um deploy de sucesso de um "CrashLoop" infinito.

Lição aprendida:

"A documentação não é apenas para os outros; é para você mesmo daqui a 3 dias, quando estiver debugando às 2 da manhã."

1.2. O Diagnóstico do Ambiente (WildFly no EKS)

Ao tentarmos mover esse gigante para o Kubernetes (EKS), enfrentamos o primeiro grande vilão: a configuração de módulos JDBC. O sistema parava no erro WFLYJCA0041, uma falha ao carregar o módulo do SQL Server.

Stack Trace típico:

WFLYJCA0041: Failed to load module for driver [com.microsoft.sqlserver]
Caused by: org.jboss.modules.ModuleNotFoundException: com.microsoft.sqlserver

O que aprendemos aqui:

1. Imutabilidade no Kubernetes
No Kubernetes, o seletor do Deployment é imutável. Tentar mudar nomes de app no meio do caminho sem limpar o ambiente antigo gera conflitos que travam o pipeline.

2. Configuração via CLI
Para o WildFly carregar o banco corretamente, não basta o arquivo estar na pasta; o driver precisa ser registrado formalmente via JBoss CLI no standalone.xml.

3. Dependência de Segredos
A desativação de segredos externos sem a devida injeção de variáveis de ambiente (WF_DS_URL) interrompe o pool de conexão no nascimento do Pod.

1.3. O Fator Humano

Assumir um legado é emocionante e estressante ao mesmo tempo. A Parte 1 dessa jornada me ensinou que, antes de ajustar o Dockerfile, precisamos ajustar o alinhamento com o time. Admitir que "a cabeça zonza" faz parte do processo ajuda a focar na qualidade em vez de apenas subir versões que quebram as imagens.


Parte 2: A Cirurgia no Dockerfile e a Estabilização do Pódio

Após entender o caos inicial, o foco mudou para a resolução técnica. Não bastava o sistema rodar; ele precisava ser resiliente, performático e, acima de tudo, funcional para quem desenvolve na ponta.

2.1. Resolvendo o Enigma do Módulo JDBC

O erro WFLYJCA0041 era o nosso maior bloqueio. Descobrimos que o problema estava na inconsistência entre o arquivo físico e a configuração lógica.

Renomeação Estratégica:
No Dockerfile, o driver mssql-jdbc-7.2.2.jre8.1.jar é renomeado para mssql-jdbc-8.4.1.jre8.jar para garantir a compatibilidade com a estrutura de módulos.

# Módulo JDBC SQL Server
RUN mkdir -p /opt/jboss/wildfly/modules/system/layers/base/com/microsoft/sqlserver/main
COPY mssql-jdbc-7.2.2.jre8.1.jar \
     /opt/jboss/wildfly/modules/system/layers/base/com/microsoft/sqlserver/main/mssql-jdbc-8.4.1.jre8.jar
COPY module.xml \
     /opt/jboss/wildfly/modules/system/layers/base/com/microsoft/sqlserver/main/

Registro de Driver:
Implementamos o registro formal do driver via JBoss CLI antes da criação do Datasource. Sem o comando /subsystem=datasources/jdbc-driver=sqlserver:add, o WildFly não conseguia vincular o módulo ao pool de conexões.

# Script CLI para registrar o driver
/subsystem=datasources/jdbc-driver=sqlserver:add( \
    driver-name=sqlserver, \
    driver-module-name=com.microsoft.sqlserver, \
    driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver, \
    driver-xa-datasource-class-name=com.microsoft.sqlserver.jdbc.SQLServerXADataSource \
)

2.2. Otimização do Pool de Conexões e Undertow

Para garantir que o sistema suportasse a carga de homologação, configuramos um pool de conexões robusto diretamente no standalone.xml:

Dimensionamento:
Definimos min-pool-size=10 e max-pool-size=50, garantindo que o sistema não travasse por falta de conexões com o SQL Server.

<datasource jndi-name="java:jboss/datasources/AppDS" 
            pool-name="AppDS" 
            enabled="true" 
            use-java-context="true">
    <connection-url>${env.WF_DS_URL}</connection-url>
    <driver>sqlserver</driver>
    <pool>
        <min-pool-size>10</min-pool-size>
        <max-pool-size>50</max-pool-size>
        <prefill>true</prefill>
    </pool>
    <security>
        <user-name>${env.WF_DS_USER}</user-name>
        <password>${env.WF_DS_PASS}</password>
    </security>
    <validation>
        <validate-on-match>true</validate-on-match>
        <background-validation>false</background-validation>
    </validation>
</datasource>

Filtros Undertow:
Adicionamos filtros de CORS e políticas de cache para JavaScript (essenciais para o AngularJS acoplado), garantindo que os recursos estáticos fossem servidos de forma otimizada pelo container.

<subsystem xmlns="urn:jboss:domain:undertow:12.0">
    <server name="default-server">
        <host name="default-host" alias="localhost">
            <filter-ref name="server-header"/>
            <filter-ref name="x-powered-by-header"/>
            <filter-ref name="cache-control"/>
        </host>
    </server>
    <filters>
        <response-header name="server-header" 
                        header-name="Server" 
                        header-value="WildFly"/>
        <response-header name="cache-control" 
                        header-name="Cache-Control" 
                        header-value="max-age=86400"/>
    </filters>
</subsystem>

2.3. Validação e Saúde do Container

Não deixamos o sucesso ao acaso. O Dockerfile foi equipado com mecanismos de autoverificação:

Script de Validação Interna:
Criamos o validate.sh, que checa a existência dos módulos e a integridade do standalone.xml ainda durante o build da imagem.

#!/bin/bash
# validate.sh - Verifica se os módulos foram criados corretamente

echo "Validando instalação do WildFly..."

# Verifica módulo JDBC
if [ ! -f /opt/jboss/wildfly/modules/system/layers/base/com/microsoft/sqlserver/main/mssql-jdbc-8.4.1.jre8.jar ]; then
    echo "ERRO: Módulo JDBC SQL Server não encontrado!"
    exit 1
fi

# Verifica module.xml
if [ ! -f /opt/jboss/wildfly/modules/system/layers/base/com/microsoft/sqlserver/main/module.xml ]; then
    echo "ERRO: module.xml do SQL Server não encontrado!"
    exit 1
fi

# Verifica standalone.xml
if ! grep -q "com.microsoft.sqlserver" /opt/jboss/wildfly/standalone/configuration/standalone.xml; then
    echo "AVISO: standalone.xml não contém referência ao driver SQL Server"
fi

echo "Validação concluída com sucesso!"
exit 0

Healthcheck Ativo:
Implementamos um comando que monitora a porta de gerenciamento (9990) do WildFly, garantindo que o Kubernetes saiba exatamente quando o Pod está pronto para receber tráfego.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-backend
spec:
  template:
    spec:
      containers:
      - name: wildfly
        image: app-backend:latest
        ports:
        - containerPort: 8080
          name: http
        - containerPort: 9990
          name: management
        livenessProbe:
          httpGet:
            path: /
            port: 9990
          initialDelaySeconds: 120
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 9990
          initialDelaySeconds: 60
          periodSeconds: 5

2.4. O Resultado: Autonomia para o Desenvolvedor

A maior vitória desta etapa foi entregar uma Branch Funcional. Agora, o desenvolvedor da consultoria pode realizar o build local sabendo que o ambiente é um espelho fiel da homologação. Resolvemos o problema das "imagens quebradas" através de testes pequenos e sucessivos, como sugerido pelo Team Leader na nossa reunião de alinhamento.


Parte 3: O Legado da Colaboração – Muito Além do Código

Chegar ao final de um ciclo de deploy tão intenso traz uma reflexão que vai além dos bits e bytes. A entrega da branch funcional em homologação não foi apenas uma vitória técnica; foi o resultado de uma mudança de postura diante dos desafios.

3.1. Transparência: A Chave para Destravar Projetos

Como discutimos na reunião com o Team Leader (TL) e o Manager Delivery, o "estresse" é um subproduto natural da pressão, mas ele só se torna destrutivo se não houver transparência. Ao abrir o jogo sobre as dificuldades técnicas e ouvir os feedbacks sobre a "velocidade vs. qualidade", conseguimos ajustar o ponteiro.

Aprendizado:

"É melhor subir um degrau sólido de cada vez do que tentar saltar uma escada e quebrar a imagem no caminho."

3.2. O Valor da Parceria Técnica

Ninguém faz nada sozinho em sistemas desta complexidade. A colaboração com o meu Team Leader foi vital para:

Validar a Arquitetura:
Ter um segundo olhar sobre o Dockerfile e o registro de JNDI evitou que erros silenciosos chegassem à produção.

Escalabilidade Humana:
Com o ambiente estabilizado, a consultoria externa agora tem autonomia total. Isso me libera para focar na evolução do sistema, em vez de atuar como "suporte de infra" o dia todo.

3.3. O impacto da entrega técnica

Quando a liderança do projeto fala em "entrar para a história" com uma iniciativa dessas, está falando de provar que modernização de legado em Kubernetes é viável. Colocar um sistema antigo para rodar de forma estável num cluster EKS demonstra que a entrega combina inteligência e resiliência, não apenas horas de desenvolvimento.

3.4. Lições Aprendidas (Resumo Executivo)

1. Afinidade de Sessão:
Vital para front-ends acoplados em containers. Sem sticky sessions, o usuário perde o contexto a cada requisição.

2. Configuração de Pool de Conexão:
O uso de scripts CLI para garantir que o Datasource suba com os parâmetros de performance corretos (min-pool-size=10, max-pool-size=50).

3. Resiliência:
O valor de admitir falhas rápidas para corrigir com precisão. Cada erro foi uma oportunidade de aprendizado.

4. Documentação Viva:
Manter a Wiki atualizada não é burocracia; é a diferença entre o sucesso e o caos.

5. Testes Incrementais:
Como sugerido pelo TL: "Faça pequenos testes, não tente subir tudo de uma vez." Isso evitou retrabalho e frustração.


Conclusão: O Próximo Nível

O Artigo 70 encerra-se com a branch funcional, mas a jornada de modernização continua. A lição que fica para os meus seguidores e colegas é:

Documente cada decisão técnica
Colabore com transparência e humildade
Não tenha medo de refazer para fazer certo

O sucesso financeiro e o reconhecimento são consequências de um ambiente que funciona e de uma equipe que confia um no outro.

Arquitetura Final Estabilizada

┌──────────────────────────────────────────────────────┐
│              Amazon EKS Cluster                      │
│  ┌────────────────────────────────────────────────┐  │
│  │  Pod: app-backend                              │  │
│  │  ┌──────────────────────────────────────────┐  │  │
│  │  │  WildFly 26.1.3.Final                    │  │  │
│  │  │  ├─ JDBC Module (SQL Server)             │  │  │
│  │  │  ├─ Datasource (JNDI: AppDS)             │  │  │
│  │  │  ├─ Pool: 10-50 conexões                 │  │  │
│  │  │  ├─ Healthcheck: porta 9990              │  │  │
│  │  │  └─ CORS Filter para S3                  │  │  │
│  │  └──────────────────────────────────────────┘  │  │
│  │  Resources:                                    │  │
│  │    Requests: 3Gi RAM, 1 CPU                    │  │
│  │    Limits: 4Gi RAM, 2 CPU                      │  │
│  └────────────────────────────────────────────────┘  │
│                                                      │
│  Service: app-backend-svc                            │
│  Type: LoadBalancer                                  │
│  Port: 8080                                          │
└──────────────────────────────────────────────────────┘
                    ▲
                    │ HTTPS (CORS)
                    │
┌───────────────────┴──────────────────┐
│      Amazon S3 + CloudFront          │
│  ┌────────────────────────────────┐  │
│  │  Frontend (AngularJS 1.5)      │  │
│  │  └─ Gulp build pipeline        │  │
│  │  └─ Vendor injection order     │  │
│  └────────────────────────────────┘  │
└──────────────────────────────────────┘

Próximo artigo: Como transformamos essa base estável em um ambiente de CI/CD completo com GitLab Pipelines e Terraform.



#Kubernetes #WildFly #DevOps #Java #Modernização #EKS #JDBC #Deploy #Legado #Colaboração

Dicionário de Termos Técnicos

WFLYJCA0041
Código de erro do WildFly indicando falha ao carregar módulo JDBC.
EKS (Elastic Kubernetes Service)
Serviço gerenciado de Kubernetes na AWS.
Pod
Menor unidade de deployment no Kubernetes, contendo um ou mais containers.
JNDI (Java Naming and Directory Interface)
API Java para localizar recursos como datasources.
JDBC (Java Database Connectivity)
API Java para conexão e operações com bancos de dados.
JBoss CLI
Interface de linha de comando para configuração do WildFly/JBoss.
Datasource
Pool de conexões configurado no servidor de aplicação.
Healthcheck
Verificação automática do estado de saúde de um container/pod.
Liveness Probe
Teste que verifica se o container está vivo e funcionando.
Readiness Probe
Teste que verifica se o container está pronto para receber tráfego.
CrashLoop
Estado em que um pod Kubernetes reinicia continuamente devido a falhas.