Saltar para o conteúdo
Tecnologia

Porque construímos o "Wayler": Um orquestrador de scripts

Construímos o Wayler, um orquestrador de scripts com recurso ao Docker, decidindo não recorrer aos "gigantes" da indústria num mundo cada vez mais obcecado por IA.

Construímos o Wayler, um orquestrador de scripts com recurso ao Docker, decidindo não recorrer aos "gigantes" da indústria num mundo cada vez mais obcecado por IA.

Índice

Se passarmos cinco minutos nas redes sociais onde tanto se fala sobre tecnologia, há uma percepção geral de que a única forma de desenvolver software (à data deste artigo) passou a ser, sem qualquer dúvida, através de agentes de IA, para qualquer tarefa. Repito, qualquer tarefa. Isto pode soar anti-IA, mas não nos interpretem mal — na Waymotion usamos modelos e ferramentas de IA todos os dias para o apoio à produção de código fonte, desenho sistemas e no apoio à resolução de problemas.

Mas, recentemente, perante uma tarefa de migração de dados geoespaciais complexos de serviços REST do ArcGIS e de APIs internas para uma OGC API (ver este caso), decidimos ir contra a corrente: construímos o nosso próprio orquestrador de scripts que, neste caso, acabou por ser usado para orquestrar um sistema ETL, simples e determinístico (como antigamente).

Chamámos-lhe Wayler (como usámos docker no core da arquitetura e operação, a escolha do nome foi a parte mais fácil). Eis porque o construímos: porque não optámos pelas escolhas óbvias da indústria (incluindo as open-source) e porque apostar em Docker e em Python nos deu exatamente o controlo e a sustentabilidade de software que achámos serem suficientes para o objetivo a que nos comprometemos.

O «problema» das alternativas

Quando tudo o que precisamos de fazer é recorrente e repetitivo a nível operacional (como por exemplo a recolha de dados de uma API, a sua transformação para uma norma específica (como o INSPIRE) e o seu carregamento, com confiança, para uma base de dados), o ecossistema atual disponível na comunidade open-source apresenta a longo prazo alguns desafios, que são de crucial ponderação, especialmente numa equipa pequena:

  • (Spark, Airflow, Mage.ai): são ferramentas incríveis para data lakes com Big Data e sistemas distribuídos. Mas configurar um cluster ou definir DAGs só para paginar uma fonte de informação (ex.: um endpoint) é como usar um Ferrari para um percurso diário de 5 km.
  • (n8n, Zapier): excelentes para webhooks, mas transformam-se facilmente em “esparguete visual” assim que é preciso lidar com transformações (por exemplo, geoespaciais) ou com conversões de coordenadas (EPSG:3763 para EPSG:4326).

Como Engenheiro de Software, acredito que o código-fonte continua e vai continuar a exigir discernimento humano, independentemente da ferramenta ou de quem o gera. Por isso, não quisemos abstrair o processo a 100% pois queríamos manter o controlo absoluto sobre a lógica dos scripts de processamento — neste caso, implementados em Python. Se for preciso um IF baseado numa escala de 1:50k vs 1:200k, queremos ver essa condição explicitamente no código.

Quando os scripts são feitos para tarefas repetitivas e relativos a processos determinísticos, na nossa visão, apresentam um valor muitíssimo acrescido para a manutenção e sustentabilidade do software a longo prazo. Não temos de nos questionar por que razão um LLM alucinou um mapeamento de dados (algo que acontece no output gerado por estes modelos). Um output determinístico simplesmente funciona (ou não), exatamente da mesma maneira, sempre.

Além disso, a nível de infraestrutura, não queríamos depender do sistema operativo do servidor nem de terceiros para gerir a calendarização de execução dos scripts. Precisávamos de controlo interno, a correr exclusivamente na “infraestrutura” do Wayler, totalmente isolado.

O Wayler e os “Harpoons”

Para resolver esta questão, implementámos o Wayler — um orquestrador leve e feito à medida, dependente do Docker.

Na nossa arquitetura, o Wayler funciona como um centro de comando. Não processa lógica ou dados, mas apoia-se numa base de dados local para gerir os pipelines, efetuar novas tentativas de execução e despoletar alertas. Quando chega a hora, simplesmente lança um Harpoon — um script Python com um propósito especifico, wrapped num Container Docker.

Arquitetura do WaylerO orquestrador Wayler lê os agendamentos da sua própria base de dados e lança contentores Harpoon de propósito único através da API do Docker. Cada Harpoon recolhe de uma fonte, transforma os dados e carrega-os atomicamente para o PostGIS, que o pygeoapi serve como OGC API pública.Docker engineO Wayler — orquestradorspawnspawnspawnatomic loadatomic load

Scheduler

Wayler DB
schedules · logs

Harpoon
ArcGIS REST

Harpoon
APIs internas

Harpoon
Legacy DB

ArcGIS REST

APIs internas

PostGIS

pygeoapi
OGC API pública

Fig. 1 — O Wayler controla e mais nada. Cada trabalho é um Harpoon — um container que contem um script em python

A decisão de usar Docker

O isolamento total. Ao obrigar cada processo a viver num container Docker, garantimos que cada script Python vive no seu próprio ambiente. Não há dependências em que, por exemplo, uma atualização do GeoPandas para um script cause impacto noutro. Também torna o lançamento de novas versões incrivelmente simples — atualizamos e fazemos release de uma nova imagem Docker do Harpoon, atualizamos o registo na base de dados*, e a execução agendada seguinte usa o novo código sem afetar o resto do sistema.

Além disso, isto permitiu-nos impor um comportamento estrito ao SDK que decidimos implementar para ligar as peças (internamente chamamos-lhe «Iron»). Cada Harpoon herda de uma classe Python BaseHarpoon. Isto simplificou o nosso processo de desenvolvimento de scripts, porque define um contrato em que um script para ser considerado pelo Wayler tem de, obrigatoriamente, implementar um método process(), e o SDK trata automaticamente do registo consistente, das sessões de base de dados e da captura de erros.

Mas há uma limitação. Um orquestrador assim traz uma ressalva importante: depende 100% do Docker. O Wayler tem de ter acesso ao socket do Docker para criar containers e monitorizar os mesmos (estado da execução, prune, etc). Mas, para nós, esta dependência foi deliberada, e acreditamos que foi uma decisão acertada.

Em todo o caso, implementar um Harpoon com o SDK acabar por ser tão simples quanto o seguinte exemplo:

from wayler_sdk import BaseHarpoon
from integrations.erp import ERPClient

class ERPUsersLeaveHarpoon(BaseHarpoon):
    def process(self):
        self.logger.info("Starting Voyage: Fetching users data...")

        # 1. Fetch data from source
        client = ERPClient(url=self.config.SOURCE_URL)
        raw_data = client.fetch_all_users()

        # 2. Procedural, deterministic transformation
        self.logger.info(f"Retrieved {len(raw_data)} records. Transforming...")
        clean_data = self.transform_to_something(raw_data)

        # 3. The SDK handles the atomic swap to the DB
        self.logger.info("Loading into Analytics Staging...")
        self.atomic_load(
            target_table="stg_leave_users",
            data=clean_data
        )
        self.logger.info("Voyage successful!")

if __name__ == "__main__":
    # The run() method handles dry-runs, retries, and DB sessions
    ERPUsersLeaveHarpoon().run()

Um caso prático

No primeiro caso de uso real do Wayler, o principal consumidor de dados foi um serviço pygeoapi público que lê de uma base de dados PostGIS. E tivemos o primeiro problema: um Harpoon demora 20 minutos a descarregar e processar milhares de elementos geológicos, e neste caso, os dados iam ser transferidos para uma API pública, que tem de continuar a funcionar sem interrupção de serviço.

Mas, como temos controlo total sobre o Python procedimental, o nosso SDK impõe um padrão de execução atómica, que é especialmente vantajoso no caso de migração de dados:

A troca atómicaUm Harpoon recolhe e transforma os dados para uma tabela temporária e, dentro de uma única transação, troca-a pela tabela em produção, para que a API pública nunca leia dados parciais.pygeoapiPostGISSourceHarpoonWayleruma só transaçãolaunchfetch (paginated)GeoJSONtransform · EPSG:4326CREATE temp tableTRUNCATE live tableINSERT from tempCOMMITexit code 0read live tabledados completos
Fig. 2 — No caso de transferência de dados, estes são colocados numa tabela temporária.

Próximos passos

O Wayler revelou-se estável e eficiente em sistemas de ETL reais e complexos, e por isso vamos usá-lo para outros processos de sincronização de sistemas para lá dos dados geoespaciais. Ainda assim, qualquer equipa de engenharia sabe que o seu sistema ainda não é perfeito (e nunca será :D). Temos pelo menos duas melhorias a fazer a curto prazo:

  • Uma interface gráfica: *neste momento, gerir o Wayler significa correr alguns comandos docker e escrever INSERTs de SQL na sua base de dados. Precisamos de construir um dashboard para visualizar pipelines, despoletar execuções manuais de Harpoons e acompanhar os registos de monitorização.
  • Secrets: integrar/implementar um gestor de segredos, para que os Harpoons passem a ir buscar as suas credenciais dinamicamente em tempo de execução (o mage.ai, por exemplo, faz isto muito bem).

Conclusão

Numa era em que a indústria tecnológica parece que se move a uma velocidade perto da velocidade da luz, vale a pena lembrar que a sincronização de dados é, na maioria dos casos, como uma refeição rápida e monótona. Adoramos a IA naquilo em que ela é melhor (que é muito), mas para a operação de execução de scripts? Não me parece que precisemos dela.

Como programador que tenta acompanhar a evolução dos LLM, admito que houve uma pequena parte em mim que sentiu que a «diversão» de usar IA para orquestração se perdesse ao escolher a via tradicional e determinística em vez de um spawn de agentes autónomos para execução de scripts. Mas a verdade é que os Harpoons continuam a ter de ser desenvolvidos, a arquitetura continua a ter de ser desenhada, etc. E é aqui que os LLM’s são fortes e realmente úteis, pois ficamos com a fiabilidade do código determinístico (mesmo que seja desenvolvido por IA), sem perder a alegria do «desenvolvimento moderno».

O que me deixa a pensar: por vezes, a coisa mais inovadora que se pode fazer é escrever código que faz exatamente aquilo que lhe mandamos fazer, mesmo que seja um bug.

Voltar ao blog

Artigos relacionados

Ver todos os artigos »