Voltar ao catálogo
Season 29 18 Episódios 1h 10m 2026

Docker Masterclass

Edição de 2026. Um curso em áudio abrangente sobre Docker, que aborda os conceitos básicos de containers, images, Dockerfile, networking, Compose, CI/CD e as mais recentes funcionalidades de AI, como o MCP Toolkit, Docker Sandboxes e Docker Agent.

Contentorização DevOps
Docker Masterclass
A Reproduzir
Click play to start
0:00
0:00
1
A Promessa Dev Igual a Prod
Descubra por que motivo o Docker mudou fundamentalmente o desenvolvimento de software. Este episódio aborda a proposta de valor central de separar as aplicações da infraestrutura e alcançar uma paridade perfeita entre os ambientes de desenvolvimento e produção.
3m 09s
2
Containers vs Virtual Machines
Compreenda as diferenças arquitetónicas entre containers e VMs. Saiba como os containers alcançam o isolamento partilhando o kernel do host, tornando-os incrivelmente leves em comparação com os hypervisors tradicionais.
4m 05s
3
A Anatomia de uma Docker Image
Explore o que é realmente uma Docker image. Este episódio explica os princípios da imutabilidade das images e da composição de layers, mostrando como as alterações no sistema de ficheiros são sobrepostas para criar um template de container.
3m 45s
4
O Blueprint do Dockerfile
Saiba como escrever um Dockerfile para construir images personalizadas. Abordamos instruções essenciais como FROM, RUN e CMD, e explicamos a diferença crucial entre as formas shell e exec.
3m 28s
5
Dominar o Build Cache
Otimize as construções das suas images utilizando o build cache do Docker. Saiba por que motivo a ordem das instruções no seu Dockerfile é fundamental para evitar instalações desnecessárias de dependências.
3m 46s
6
Multi-Stage Builds
Mantenha as suas images de produção leves e seguras. Este episódio introduz os multi-stage builds, demonstrando como separar o seu ambiente de compilação pesado do seu ambiente de runtime minimalista.
4m 04s
7
Executar e Interagir
Aprenda a mecânica prática de executar containers. Abordamos os modos detached versus interactive, a publicação básica de portas (port publishing) e como executar comandos shell dentro de um container em execução.
4m 19s
8
Básicos de Persistência de Dados
Evite a perda catastrófica de dados quando os containers são eliminados. Este episódio compara Bind Mounts para hot-reloading em desenvolvimento local com Docker Volumes para uma persistência segura de bases de dados.
3m 46s
9
Container Networking
Compreenda como o Docker lida com o tráfego de rede. Aprenda os conceitos básicos de port publishing para o host e como os containers comunicam de forma segura entre si através de bridge networks isoladas.
4m 06s
10
Introdução ao Docker Compose
Vá além dos comandos para um único container. Saiba como o Docker Compose utiliza um ficheiro YAML declarativo para definir, ligar em rede e orquestrar múltiplos serviços em simultâneo.
4m 28s
11
Docker no Pipeline CI/CD
Elimine testes instáveis com ambientes de build em containers. Este episódio aborda como utilizar o Docker em pipelines de Continuous Integration para garantir testes automatizados perfeitamente reprodutíveis.
3m 37s
12
Multi-Platform Images
Resolva a incompatibilidade entre Apple Silicon e Cloud Server. Saiba como o Docker Buildx permite fazer cross-compile e empacotar aplicações para as arquiteturas ARM e AMD64 em simultâneo.
4m 07s
13
O Docker MCP Toolkit
Ligue os seus agentes de AI a ferramentas locais de forma segura. Este episódio introduz o Docker Model Context Protocol (MCP) Toolkit, explicando como gerir servidores MCP em containers utilizando catálogos e perfis.
3m 36s
14
Dynamic MCP Auto-Discovery
Explore o Dynamic MCP, uma funcionalidade experimental que permite aos clientes de AI pesquisar no Docker MCP Catalog e instalar dinamicamente novos servidores de ferramentas durante uma conversa, sem configuração manual.
4m 51s
15
Docker Sandboxes para AI
Compreenda a arquitetura das Docker Sandboxes. Saiba por que motivo os agentes de programação de AI autónomos requerem microVMs isoladas com Docker daemons dedicados em vez dos namespaces de containers padrão.
4m 05s
16
Construir Equipas de Agentes de AI
Pare de depender de um único modelo de AI para tarefas complexas. Este episódio introduz a framework Docker Agent, mostrando como compor equipas especializadas de agentes definidas em YAML.
4m 08s
17
Agent Toolsets e Workflows
Torne os seus agentes de AI realmente úteis dando-lhes as restrições certas. Saiba como configurar toolsets de sistema de ficheiros e impor workflows de desenvolvimento estruturados no Docker Agent.
3m 26s
18
Modelos de AI no Compose
Trate os seus LLMs locais como qualquer outra dependência de aplicação. Saiba como declarar, configurar e vincular modelos de AI diretamente dentro do seu ficheiro YAML do Docker Compose.
3m 18s

Episódios

1

A Promessa Dev Igual a Prod

3m 09s

Descubra por que motivo o Docker mudou fundamentalmente o desenvolvimento de software. Este episódio aborda a proposta de valor central de separar as aplicações da infraestrutura e alcançar uma paridade perfeita entre os ambientes de desenvolvimento e produção.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 1 de 18. Acabaste de passar três dias à procura de um erro que só ocorre no servidor de staging. O código corre perfeitamente no teu portátil, mas no momento em que chega ao pipeline de deployment, falha. O culpado é quase sempre uma library de sistema incompatível, uma versão de runtime diferente ou uma variável de ambiente em falta. Esta é exatamente a fricção que o Docker foi criado para eliminar, cumprindo a promessa de que dev é igual a prod. O Docker é uma plataforma aberta para desenvolver, fazer shipping e correr aplicações. O seu principal objetivo é separar as tuas aplicações da tua infraestrutura. Historicamente, os developers escreviam o código e as equipas de operações provisionavam os servidores. Os developers entregavam a aplicação, e a equipa de operações passava horas ou dias a configurar a máquina host para cumprir os requisitos do software. Este alinhamento manual de ambientes é frágil e lento. O Docker resolve isto ao empacotar a aplicação com as suas dependências, ferramentas de sistema, libraries e runtime numa unidade padronizada chamada container. Podes associar este conceito às máquinas virtuais tradicionais. Embora partilhem o objetivo de isolar aplicações, os containers são muito mais leves porque não exigem um sistema operativo guest completo. Vamos abordar essa diferença arquitetónica no próximo episódio. O foco agora é no que este empacotamento permite alcançar. Aqui está a ideia principal. Como o container contém tanto o código como o ambiente exato necessário para o correr, a máquina host subjacente torna-se praticamente irrelevante. O Docker garante que, se um container correr no teu portátil de desenvolvimento local, vai correr exatamente da mesma forma num servidor de QA, e exatamente da mesma forma num data center de produção. Eliminas a frase funciona na minha máquina porque a tua máquina e a máquina de produção fornecem agora exatamente o mesmo ambiente de execução. O workflow é o seguinte. Um developer escreve o código localmente e define o ambiente necessário num ficheiro de configuração em plain text. O Docker lê esse ficheiro e faz o build de um artefacto estático chamado imagem. Essa única imagem imutável é o que é testado. Quando os testes passam, é feito o deploy exatamente da mesma imagem para produção. Não estás a copiar código para um servidor e a correr um script de setup. Estás a mover todo o ambiente de trabalho como uma unidade selada. Esta portabilidade muda a forma como os sistemas escalam. Como os containers são padronizados e leves, levantar novas instâncias de uma aplicação em resposta a um pico de tráfego acontece em segundos. Podes facilmente transferir workloads entre diferentes ambientes, movendo uma aplicação de um servidor de testes local para um cloud provider sem alterar uma única linha de código ou reconfigurar o host. A grande conclusão é que o Docker transforma a infraestrutura numa commodity previsível, criando uma fronteira rígida onde os developers são donos de todo o ambiente dentro do container, e as operações simplesmente fornecem o poder computacional para o correr. Se quiseres apoiar o programa, procura por DevStoriesEU no Patreon. É tudo por este episódio. Obrigado por ouvires, e continua a programar!
2

Containers vs Virtual Machines

4m 05s

Compreenda as diferenças arquitetónicas entre containers e VMs. Saiba como os containers alcançam o isolamento partilhando o kernel do host, tornando-os incrivelmente leves em comparação com os hypervisors tradicionais.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 2 de 18. Não precisas de fazer boot de um sistema operativo inteiro só para correr um único script Python. No entanto, durante anos, os developers aceitaram um overhead pesado e tempos de boot lentos para manter as suas aplicações isoladas umas das outras. Hoje, vamos resolver isso olhando para Containers vs Máquinas Virtuais. Imagina correres uma stack complexa localmente. Precisas de um frontend em React, uma API em Python e uma base de dados PostgreSQL, tudo a operar em simultâneo. Se os instalares diretamente na tua máquina host, estás a convidar conflitos de dependências. A API pode exigir uma versão específica de uma library do sistema que entre em conflito com o que a tua base de dados precisa. Um container resolve isto atuando como um processo em sandbox. Aqui está a ideia principal. Um container não é um computador em miniatura. É estritamente um processo a correr nativamente na tua máquina host. A magia está no isolamento. Através de funcionalidades integradas no sistema operativo, este processo recebe o seu próprio filesystem privado, a sua própria networking stack e uma visão isolada do sistema. Para a API Python a correr lá dentro, parece ser o único software na máquina. Para o teu sistema operativo host, é apenas mais um processo standard, muito parecido com o teu web browser ou editor de texto. Como um container é apenas um processo, ele partilha o kernel do sistema operativo host. Quando a base de dados PostgreSQL dentro de um container precisa de alocar memória ou escrever um registo no disco, ela fala diretamente com o kernel do host. Não há intermediários nem um sistema operativo secundário a fazer boot em background. É por isso que iniciar um container é quase instantâneo. Demora exatamente o mesmo tempo que iniciar a própria aplicação raw. Agora, contrasta isto diretamente com uma Máquina Virtual. Uma máquina virtual aborda o isolamento simulando hardware. Ela depende de um hypervisor, que é um software que recorta um CPU virtual, memória virtual e uma drive de disco virtual. Por cima deste hardware falso, tens de instalar um sistema operativo guest completo. Se quiseres correr essa mesma API Python numa VM isolada, tens de fazer boot de uma distribuição Linux inteira. A VM carrega o seu próprio kernel separado, inicializa os device drivers e inicia os serviços de sistema em background antes sequer de pensar em executar o teu código Python. Sempre que a aplicação precisa de ler um ficheiro, o pedido passa pelo sistema operativo guest, desce para o hypervisor e, finalmente, para o hardware host. Isto proporciona um isolamento de segurança incrivelmente forte, mas tem um custo elevado em ciclos de CPU, uso de memória e tempo de boot. Devido a estas diferenças, um equívoco comum é que tens de escolher um ou outro. Na realidade, containers e máquinas virtuais não são mutuamente exclusivos. Em ambientes cloud modernos, são quase sempre usados em conjunto. Quando provisionas uma instância cloud, estás a alugar uma máquina virtual. Essa máquina virtual fornece uma forte fronteira ao nível do hardware, separando o teu workload de outros clientes no mesmo servidor físico. Depois, instalas um container runtime dentro dessa máquina virtual para correr o teu frontend em React, a tua API e a tua base de dados. A máquina virtual isola a infraestrutura, enquanto os containers isolam as aplicações individuais. A distinção, em última análise, resume-se a fronteiras. As máquinas virtuais virtualizam o hardware para correr múltiplos sistemas operativos, enquanto os containers virtualizam o sistema operativo para correr múltiplos processos isolados. Obrigado por passares uns minutos comigo. Até à próxima, fica bem.
3

A Anatomia de uma Docker Image

3m 45s

Explore o que é realmente uma Docker image. Este episódio explica os princípios da imutabilidade das images e da composição de layers, mostrando como as alterações no sistema de ficheiros são sobrepostas para criar um template de container.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 3 de 18. Fazes o deploy de uma aplicação e ela funciona perfeitamente. Duas semanas depois, reinicias exatamente a mesma aplicação na mesma máquina e ela crasha porque uma biblioteca de sistema se atualizou em background. Esse environmental drift silencioso é exatamente o que eliminamos ao perceber a anatomia de uma imagem Docker. Primeiro, vamos esclarecer o ponto de confusão mais comum. Uma imagem não é um container. Uma imagem é um template estático. Contém o código da tua aplicação, as tuas bibliotecas, as tuas ferramentas de sistema e o teu runtime. Um container é simplesmente uma instância dessa imagem a correr. Podes iniciar mil containers a partir de uma única imagem, mas a imagem em si fica apenas no disco, à espera de ser lida. A característica que define uma imagem Docker é a imutabilidade. Depois de criada, uma imagem nunca é modificada. Não podes alterar um ficheiro de configuração dentro de uma imagem existente. Se quiseres alterar uma imagem, tens de fazer o build de uma completamente nova. Esta imutabilidade garante que uma imagem testada no teu portátil se comporta de forma idêntica em produção. O template não pode sofrer drift ao longo do tempo. Se não podes modificar uma imagem, tens de construir novas. Uma imagem Docker não é um único ficheiro enorme. É uma composição de múltiplas layers independentes empilhadas umas em cima das outras. Cada layer representa um conjunto específico de alterações no filesystem, o que significa adicionar, modificar ou remover ficheiros. Considera uma aplicação Node ponto js. Raramente fazes o build do sistema operativo tu mesmo. Em vez disso, começas com uma imagem base. Esta imagem base contém uma distribuição Linux mínima e o runtime de Node. Essa base é, na verdade, composta pelas suas próprias layers, mas para ti, funciona como a fundação. Quando adicionas a tua aplicação a esta fundação, o Docker regista as tuas alterações como novas layers empilhadas no topo. Primeiro, trazes o teu ficheiro de configuração de dependências. Isso cria uma nova layer. A seguir, instruis o sistema a fazer o download e a instalar as tuas dependências. Todas essas bibliotecas descarregadas são empacotadas na layer seguinte. Finalmente, copias o source code da tua aplicação. Isso forma a layer do topo. Quando inicias um container, o Docker empilha estas layers usando um union filesystem. Isto faz com que todas as layers independentes pareçam uma única estrutura de diretórios standard. Se o exato mesmo file path existir em duas layers, a versão na layer superior esconde a versão na layer inferior. Aqui está o ponto chave. Como as layers são imutáveis, elas são amplamente guardadas em cache e partilhadas. Se atualizares o source code da tua aplicação e fizeres o build de uma nova imagem, o Docker calcula o que mudou. Ele vê que a imagem base de Linux, o runtime de Node e a tua layer de dependências são idênticos ao build anterior. Ele reutiliza essas layers existentes instantaneamente e cria uma nova layer apenas para o teu código atualizado. Isto transforma um deployment que poderia demorar minutos numa operação que demora milissegundos. Esta arquitetura também poupa espaço em disco e largura de banda de rede. Quando fazes push da tua nova imagem para um servidor, transmites apenas a única layer que contém o novo source code. O servidor já tem as layers base. Ao impor layers imutáveis, o Docker garante que o ambiente da tua aplicação não pode mudar silenciosamente, ao mesmo tempo que assegura que apenas transmites ou guardas os exatos bytes que modificaste. É tudo por este episódio. Até à próxima!
4

O Blueprint do Dockerfile

3m 28s

Saiba como escrever um Dockerfile para construir images personalizadas. Abordamos instruções essenciais como FROM, RUN e CMD, e explicamos a diferença crucial entre as formas shell e exec.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 4 de 18. Todo o ambiente de execução da tua aplicação complexa pode ser expresso em apenas dez linhas de plain text. Entregas um único ficheiro a um sistema de build, e sai um sistema perfeitamente configurado, pronto a correr em qualquer lado. Este é o blueprint do Dockerfile. Um Dockerfile é um documento de texto que contém todos os comandos que um utilizador poderia chamar na command line para fazer o build de uma imagem. O formato é simples. Cada linha começa com uma instrução, escrita em maiúsculas por convenção, seguida pelos argumentos para essa instrução. O Docker lê este ficheiro linha a linha, de cima para baixo. Qualquer Dockerfile válido tem de começar com uma base. Defines isso usando a instrução FROM. Se escreveres FROM ubuntu, o Docker faz pull da imagem oficial do Ubuntu e usa-a como ponto de partida. Cada linha subsequente no teu ficheiro vai modificar este ambiente base. Assim que a tua base estiver definida, normalmente precisas de instalar dependências. Fazes isso usando a instrução RUN. A instrução RUN executa qualquer comando dentro da imagem atual e faz commit do resultado. Se escreveres RUN apt-get update seguido pelos comandos de instalação dos teus pacotes, o Docker inicia o ambiente, corre o package manager, instala o software e guarda o novo estado. A seguir, precisas da tua aplicação propriamente dita. Um sistema operativo com dependências instaladas é inútil sem o teu código. A instrução COPY trata disso. Forneces um source path do teu workspace local e um destination path dentro da imagem. O Docker pega nos teus ficheiros e copia-os diretamente para o filesystem do contentor. Fazer o build da imagem é apenas a primeira fase. Também tens de dizer ao Docker que aplicação lançar quando um contentor arranca. Defines este comportamento default usando a instrução CMD ou ENTRYPOINT. Aqui está o ponto chave. Existem duas maneiras distintas de formatar estas instruções de execução, e misturá-las causa bugs subtis. A primeira abordagem é a shell form. Escreves a instrução seguida do comando exatamente como o escreverias num terminal. Quando o Docker vê isto, faz wrap do teu comando numa shell, executando-o via bin slash sh. Isto é conveniente porque as environment variables expandem-se automaticamente. No entanto, o processo da shell fica entre o Docker e a tua aplicação. Se o Docker enviar um sinal para parar o contentor de forma graceful, a shell apanha-o e a tua aplicação nunca o recebe, levando a encerramentos forçados. A segunda abordagem é a exec form. Esta é escrita como um array JSON. Formatas isso com parênteses retos, fornecendo o executável como a primeira string e os seus argumentos como as strings subsequentes. Quando usas a exec form, o Docker faz bypass à shell por completo. Corre o teu executável diretamente. A tua aplicação torna-se o process ID um dentro do contentor. Isto garante que os sinais do sistema passam diretamente para a tua aplicação, assegurando shutdowns suaves e previsíveis. Se queres um contentor de produção estável, usa sempre a exec form para os teus comandos finais, para que a tua aplicação controle o seu próprio lifecycle. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
5

Dominar o Build Cache

3m 46s

Otimize as construções das suas images utilizando o build cache do Docker. Saiba por que motivo a ordem das instruções no seu Dockerfile é fundamental para evitar instalações desnecessárias de dependências.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 5 de 18. Se a tua build de Docker demora dez minutos cada vez que alteras uma única linha de source code, estás a fazer isso mal. A solução está inteiramente na forma como estruturas o teu ficheiro, e isso significa dominares a build cache. Quando inicias uma build de Docker, o builder processa o teu Dockerfile sequencialmente, de cima para baixo. Cada instrução, seja copiar uma diretoria ou correr um script, gera uma layer separada na imagem resultante. Como a execução destes passos requer tempo de processamento e largura de banda de rede, o Docker guarda automaticamente o output de cada passo numa build cache local. Em builds subsequentes, o engine tenta reutilizar estas layers guardadas para saltar trabalho redundante. Para determinar se um cache hit é possível, o Docker avalia cada instrução em relação ao histórico de cache existente. Para instruções que correm comandos, ele verifica se a própria command string é idêntica à usada na build anterior. Para instruções que copiam ficheiros da tua máquina host para a imagem, o Docker vai um passo mais além. Ele calcula um checksum para o conteúdo e metadados de cada ficheiro a ser copiado. De seguida, compara este novo checksum com o checksum dos ficheiros na layer em cache anterior. Se os checksums coincidirem perfeitamente, o Docker reutiliza a layer em cache e prossegue para a próxima linha. Se diferir um único byte, a cache é invalidada. Presta atenção a esta parte. A invalidação da cache é uma reação em cadeia rigorosa. No instante em que o Docker deteta uma alteração e invalida uma layer, ele para de consultar a cache para o resto da build. Cada instrução que vem depois da layer invalidada é forçada a correr do zero. Isto acontece porque cada layer depende do estado exato da layer anterior. Esta reação em cadeia dita como deves organizar o teu Dockerfile. Considera uma aplicação Node onde geres dependências externas. Um erro frequente é usar uma única instrução para copiar toda a pasta do teu projeto para a imagem, seguida por uma instrução para correr o teu comando de instalação de packages. Se modificares uma única linha num ficheiro de texto algures no teu source code, o checksum da instrução de cópia muda. A cache quebra nesse passo. Consequentemente, a próxima instrução é forçada a correr. Ficas à espera que seja feito o download de centenas de megabytes de dependências novamente, mesmo que a tua lista real de dependências tenha permanecido totalmente intocada. A abordagem ideal isola as dependências do código da aplicação. Primeiro, adicionas uma instrução para copiar apenas o teu ficheiro de configuração de dependências, especificamente o teu package manifest, para a imagem. Segundo, corres o comando para fazer o download das dependências. Terceiro, adicionas uma instrução separada para copiar o resto do teu source code geral. Agora, quando modificas esse mesmo ficheiro de texto e fazes rebuild, o Docker avalia a primeira instrução. O dependency manifest não mudou, por isso a cache é usada. Ele avança para o passo de instalação. Como a layer anterior foi um cache hit e a command string é idêntica, a cache é usada aqui novamente, saltando o download massivo. A cache só quebra na instrução final, onde o builder copia os teus source files atualizados. Uma espera de dez minutos torna-se numa atualização de dois segundos. A maneira mais eficaz de acelerar a tua pipeline é ordenares as tuas instruções estritamente da menos provável de mudar para a mais provável. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
6

Multi-Stage Builds

4m 04s

Mantenha as suas images de produção leves e seguras. Este episódio introduz os multi-stage builds, demonstrando como separar o seu ambiente de compilação pesado do seu ambiente de runtime minimalista.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 6 de 18. Fazer o deploy do teu compilador para produção é um risco de segurança enorme, e inflaciona o tamanho do teu container em gigabytes. Tu escreves clean code, mas o teu artifact final acaba por ser prejudicado por todas as ferramentas pesadas necessárias apenas para fazer o build. A solução para isto são os multi-stage builds. Quando fazes o build de aplicações em linguagens compiladas como Java, Go ou C++, o processo de compilação exige build tools, software development kits e o source code em bruto. Historicamente, os developers usavam uma abordagem standard onde instalavam todas estas dependências no container, compilavam o código e, a seguir, corriam a aplicação. O problema é que todas essas build tools ficam na imagem final de produção. Acabas por fazer o deploy do teu compilador, do teu package manager e de ficheiros intermédios juntamente com a tua aplicação. Isto torna o teu container enorme. Containers grandes demoram mais tempo a fazer pull através da rede e consomem mais armazenamento. Pior ainda, cria uma attack surface enorme. Se um atacante invadir o teu container, de repente tem um ambiente de desenvolvimento totalmente equipado à sua disposição. Um equívoco comum é achar que corrigir isto exige manter dois ficheiros separados — um ficheiro para fazer o build do software, e um script para extrair o resultado e passá-lo para um segundo ficheiro para deploy. Não é o caso. Os multi-stage builds tratam de toda esta separation of concerns dentro de um único ficheiro. Aqui está a ideia principal. Um multi-stage build permite-te definir vários ambientes distintos, ou stages, sequencialmente. Cada stage começa por definir a sua própria base image. Tu inicias o primeiro stage com uma base image pesada que contém todas as tuas ferramentas de desenvolvimento. Atribuis um nome a este stage, como builder. Dentro deste stage builder, copias o teu source code da tua máquina local e executas os teus comandos de compilação. O stage builder faz o trabalho pesado, gerando o ficheiro executável final. Depois, mais abaixo nesse exato mesmo ficheiro, defines uma segunda base image. Isto inicia um stage completamente novo. Para este stage, escolhes uma runtime image minimalista. Este ambiente contém apenas as dependências exatas necessárias para correr a aplicação, com zero build tools. Em vez de copiares ficheiros da tua máquina local novamente, usas uma instrução copy especializada. Esta instrução diz ao build engine para ir ao stage builder, pegar apenas no artifact finalizado e compilado, e colocá-lo no teu novo stage minimalista. Quando o build engine termina, produz um container baseado exclusivamente no stage final. Tudo do primeiro stage — o compilador, os packages descarregados, o source code — é completamente descartado. Nunca chega à tua imagem de produção. Considera um cenário concreto com uma aplicação Java Spring Boot. No teu ficheiro, o teu primeiro stage usa uma imagem Maven pesada. Dentro deste stage, corres o comando Maven para fazer o package da tua aplicação. O Maven faz o download de todas as dependências necessárias do projeto, compila o código Java e faz o package para um ficheiro JAR finalizado. A seguir, inicias o segundo stage usando uma base image leve do Java Runtime Environment. Não instalas o Maven neste ambiente. Não copias os teus source files de Java para aqui. Em vez disso, dás instruções ao engine para copiar apenas o ficheiro JAR compilado diretamente do stage do Maven para este ambiente de runtime minimalista. Finalmente, defines o comando default para executar esse ficheiro JAR. Ao separar rigorosamente o ambiente de build do ambiente de runtime, garantes que o teu container de produção está completamente isolado das tuas build tools. A imagem final apenas vê o artifact compilado e o runtime estritamente necessário, mantendo o teu deploy rápido, leve e altamente seguro. Por hoje é tudo. Até à próxima!
7

Executar e Interagir

4m 19s

Aprenda a mecânica prática de executar containers. Abordamos os modos detached versus interactive, a publicação básica de portas (port publishing) e como executar comandos shell dentro de um container em execução.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 7 de 18. Iniciar um processo em background é exatamente o que queres para um web server de produção. Mas quando esse servidor se recusa a carregar a tua página, precisas de uma forma de quebrar o vidro, entrar no environment, e ver o que está realmente a falhar. Este episódio aborda a execução e a interação com containers. O comando principal para iniciar qualquer container é docker run. Por defeito, se correres um container, ele anexa o seu output diretamente ao ecrã do teu terminal. Ele assume o controlo do teu prompt e, se pressionares control C, o container termina. Para um serviço de longa duração, como um web server Nginx, isto é totalmente impraticável. Queres que o servidor corra em background. Consegues isto usando a flag de detached mode, que digitas como um único traço d. Quando passas traço d, o Docker inicia o container, imprime um container ID longo e único no teu ecrã, e devolve-te imediatamente o teu prompt do terminal. O container continua a correr silenciosamente em background. No entanto, esse container em execução está isolado. Mesmo que o Nginx esteja a servir tráfego ativamente na porta 80 dentro do container, a tua máquina host não o consegue ver. Tens de abrir explicitamente um buraco nesse isolamento de rede. Fazes isto com a flag publish, digitada como traço p. Isto permite-te mapear uma porta específica no teu portátil host para uma porta específica dentro do container. Se especificares traço p 8080 dois pontos 80, o Docker interceta qualquer tráfego web que chegue ao teu portátil na porta 8080 e encaminha-o diretamente para a porta 80 dentro do container. Agora tens um web server em detached mode a que podes aceder com sucesso a partir do teu browser local. Mas o que acontece quando carregas a página e vês um erro de configuração? O teu servidor Nginx está a correr em background, mas precisas de ler os ficheiros de configuração no seu filesystem. Aqui está a ideia chave. Não precisas de parar um container para olhar lá para dentro. Em vez disso, usas o comando docker exec. Enquanto o docker run cria um container totalmente novo, o docker exec corre um comando totalmente novo dentro de um container já existente e em execução. Para obteres um terminal útil e funcional, precisas de combinar duas flags específicas em traço i t. O i significa interactive. Isto mantém o canal de standard input aberto, permitindo-te efetivamente digitar comandos no container. O t aloca um pseudo-TTY. Isto engana o container, fazendo-o pensar que está ligado a um terminal físico, o que é necessário para que os command prompts e a formatação de texto sejam exibidos corretamente. Se correres docker exec traço i t, seguido pelo nome do container e o comando barra bin barra bash, entras instantaneamente num command prompt dentro do container Nginx em execução. Estás agora dentro da caixa. Podes ler ficheiros de configuração, verificar error logs, e inspecionar o filesystem exatamente como farias num servidor Linux standard. Quando terminares, digitar exit fecha a tua sessão de shell temporária. O próprio container Nginx permanece completamente inafetado, continuando a correr em background. Eventualmente, vais precisar de fazer uma limpeza. Correr docker stop com o nome do container envia um termination signal, dando à aplicação tempo para fazer o shut down de forma graciosa. No entanto, parar um container não o apaga do teu sistema. O container parado permanece no teu disco rígido, retendo os seus logs e quaisquer alterações internas no filesystem. Para o apagares permanentemente e libertares esse espaço em disco, corres o comando docker rm. A distinção mais crítica a memorizar é a diferença entre run e exec. O docker run faz boot a um sistema isolado completamente novo, enquanto o docker exec permite-te entrar num sistema que já está a respirar. Obrigado por ouvirem. Fiquem bem, pessoal.
8

Básicos de Persistência de Dados

3m 46s

Evite a perda catastrófica de dados quando os containers são eliminados. Este episódio compara Bind Mounts para hot-reloading em desenvolvimento local com Docker Volumes para uma persistência segura de bases de dados.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 8 de 18. Fazes o deploy de uma base de dados dentro de um container, escreves milhares de linhas, e tudo corre perfeitamente. Depois, apagas o container para atualizar a imagem, e toda a tua base de dados desaparece para sempre. Por defeito, o storage do container é estritamente temporário. Para evitar a perda de dados, precisamos dos básicos de persistência de dados. Quando um container arranca, cria uma writable layer por cima da sua imagem base. Quaisquer ficheiros que o container crie ou modifique são guardados nessa layer específica. Se o container for destruído, essa layer é destruída juntamente com ele. Os dados são totalmente efémeros. Não existem fora do lifecycle do próprio container. Para manteres os dados seguros, tens de os encaminhar para fora do container e para a máquina host. O Docker oferece dois mecanismos principais para isto: bind mounts e managed volumes. Um bind mount mapeia um path específico e explícito na tua máquina host diretamente para um path dentro do container. Dizes ao Docker exatamente qual é a pasta no teu portátil que deve aparecer dentro do ambiente do container. Isto depende imenso do teu sistema operativo host e da estrutura de ficheiros local. A máquina host mantém o controlo total sobre os ficheiros. Esta abordagem é perfeita para desenvolvimento local. Fazes um bind mount da tua diretoria de source code local para o path da web application do teu container. Quando editas e gravas um script no teu portátil, o container lê esse ficheiro atualizado imediatamente. Tens hot-reloading instantâneo sem precisares de fazer rebuild da imagem do container cada vez que mudas uma linha de código. O segundo mecanismo é um managed volume. Em vez de apontares para um path específico que controlas no teu disco rígido, pedes ao Docker para criar uma entidade de storage. O Docker provisiona o espaço na máquina host e gere-o completamente. Não precisas de saber onde o Docker coloca fisicamente os ficheiros no teu sistema host. Basta dares um nome ao volume e dizeres ao container onde fazer o mount internamente. Os volumes são a solução standard para a persistência de bases de dados. Ao correres o PostgreSQL, crias um volume correndo um comando simples e dando-lhe um identificador, como db-data. Depois, ao iniciares o teu container, passas uma flag de configuração que liga esse volume db-data ao path interno onde o Postgres escreve os records das tabelas. Se parares e apagares o container da base de dados, o Docker deixa o volume completamente intacto. Quando fizeres spin up de um novo container mais tarde, simplesmente fazes attach desse volume existente, e todos os teus records ficam intactos. Aqui está o ponto chave. A escolha entre estes dois métodos resume-se a quem precisa de aceder aos ficheiros. Usa bind mounts quando a tua máquina host precisar de interagir ativamente com os dados, como um developer a editar source code. Usa managed volumes quando o container for o dono dos dados, como um database engine a escrever records, e só queres que o Docker mantenha esses ficheiros seguros entre restarts do container. Containers efémeros são uma escolha de design, não uma falha, porque forçam-te a fazer decouple dos teus dados do teu runtime compute. Assume sempre que o teu container vai ser destruído imediatamente, e mapeia explicitamente o teu persistent state para fora dele. Se achas estes episódios úteis, podes apoiar o programa procurando por DevStoriesEU no Patreon. É tudo por este episódio. Obrigado por ouvires, e continua a construir!
9

Container Networking

4m 06s

Compreenda como o Docker lida com o tráfego de rede. Aprenda os conceitos básicos de port publishing para o host e como os containers comunicam de forma segura entre si através de bridge networks isoladas.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 9 de 18. Por defeito, um container em execução está completamente isolado do mundo exterior. Ele fica numa bolha privada e, se quiseres que a internet o alcance, tens de abrir intencionalmente brechas nesse isolamento. Gerir essas brechas e as ligações entre containers é o trabalho do Container Networking. Quando um container arranca, o Docker atribui-lhe um endereço IP interno. O container geralmente consegue aceder à internet para descarregar updates ou fazer chamadas de rede, mas nada fora da máquina host consegue aceder-lhe. Para aceitar tráfego de entrada, usas o port publishing. O publishing pega numa porta da tua máquina host física e faz o bind diretamente a uma porta dentro do container. Se tiveres um container de web server à escuta internamente na porta oitenta, podes fazer o publish para a porta oitenta oitenta no teu host. Quando um utilizador envia um request para a tua máquina host na porta oitenta oitenta, o Docker interceta-o e reencaminha-o diretamente através da firewall para o container na porta oitenta. Configuras este mapeamento no arranque usando a flag publish. Sem essa flag, o container permanece inacessível à rede externa. Isso cobre o tráfego externo. Agora, a segunda parte disto é a comunicação interna. As aplicações raramente correm como um único processo isolado. Normalmente, tens vários containers que precisam de partilhar dados. Por defeito, o Docker liga cada novo container a uma rede integrada chamada default bridge. Uma bridge é um network switch baseado em software a correr na tua máquina host. Ela liga os containers para que possam trocar pacotes, isolando-os das redes externas. Aqui está o ponto chave. A default bridge permite que os containers comuniquem usando os seus endereços IP internos, mas os endereços IP dos containers mudam sempre que um container faz restart ou update. Fazer hardcode de um endereço IP na configuração da tua aplicação vai quebrar o teu sistema quase imediatamente. Para resolver isto, crias uma rede bridge definida pelo utilizador. Quando ligas vários containers a uma bridge personalizada definida pelo utilizador, o Docker fornece resolução DNS interna automática. Isto significa que os containers podem encontrar-se usando os seus nomes de container exatos. Considera um cenário em que tens um container de uma aplicação backend e um container de base de dados. Crias uma única rede bridge personalizada e ligas ambos os containers a ela. Dentro do código da tua aplicação backend, não escreves uma connection string de base de dados usando um endereço IP frágil. Usas simplesmente o nome do container da base de dados como endereço de host. O Docker interceta a query DNS, encontra o container da base de dados nessa bridge específica e encaminha o tráfego para o endereço IP interno correto dinamicamente. Este design dá-te controlo total sobre a segurança da aplicação. O backend e a base de dados podem comunicar livremente através da bridge personalizada, mas nenhum tráfego externo consegue chegar à base de dados. Para correres a tua aplicação com segurança, deixas a base de dados escondida na bridge interna privada, sem portas publicadas. Depois, fazes o publish apenas da porta do container de backend para a tua máquina host. Utilizadores externos acedem à porta pública do backend, e o backend faz queries à base de dados com segurança através da bridge privada. A arquitetura da tua aplicação dita a tua topologia de rede: usa published ports para convidar utilizadores externos a entrar, e bridges personalizadas definidas pelo utilizador para permitir que os teus containers internos comuniquem uns com os outros com segurança através do nome. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
10

Introdução ao Docker Compose

4m 28s

Vá além dos comandos para um único container. Saiba como o Docker Compose utiliza um ficheiro YAML declarativo para definir, ligar em rede e orquestrar múltiplos serviços em simultâneo.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 10 de 18. Não deves precisar de um documento de texto cheio de comandos de terminal complexos só para iniciar o teu local development environment. Depender do histórico da shell para recordar as flags, ports e nomes de rede exatos para vários containers é uma forma frágil de trabalhar. A introdução ao Docker Compose resolve isto ao transformar toda a tua application stack num único ficheiro declarativo. Quando corres uma aplicação, ela raramente existe de forma isolada. Geralmente, tens um web server, uma base de dados e talvez uma caching layer. Iniciá-los manualmente exige correr vários comandos distintos. Tens de criar uma custom network, ligar cada container a ela, expor as ports certas e fazer mount de storage drives. Se fizeres um typo em qualquer um destes passos, os containers não conseguem comunicar entre si e a aplicação falha. O Docker Compose substitui este processo imperativo por um ficheiro YAML declarativo, normalmente chamado compose ponto yaml. Em vez de dizeres ao Docker exatamente o que fazer passo a passo, declaras o estado final desejado de todo o teu sistema. O Docker Compose descobre os passos necessários para atingir esse estado. O ficheiro YAML está dividido em três secções estruturais principais. A primeira e mais proeminente secção chama-se services. Um service é simplesmente uma definição para um container específico na tua aplicação. Imagina um cenário em que estás a correr uma aplicação Node em conjunto com uma base de dados MySQL. Na secção services, defines duas entradas. Dás o nome de web à primeira, especificando a image do Node e as ports locais que queres expor. Dás o nome de database à segunda, especificando a image do MySQL e as environment variables necessárias, como a root password. Aqui está o ponto chave. Não precisas de ligar estes containers manualmente. Por defeito, o Docker Compose cria automaticamente uma única rede interna para a tua aplicação. Ele liga todos os services definidos a esta rede e atribui a cada container um hostname que corresponde ao nome do seu service. O código da tua aplicação Node pode ligar-se à base de dados simplesmente apontando para o hostname database, e o DNS interno resolve-o para o IP correto do container. Podes definir manualmente custom networks na secção networks do ficheiro YAML, mas para a maioria dos setups de desenvolvimento standard, o comportamento por defeito faz exatamente o que precisas. A peça estrutural final é a secção volumes. As bases de dados exigem persistent storage. Se o container do MySQL for desligado, não queres que os teus dados sejam apagados. No final do teu ficheiro YAML, declaras um named volume. Depois, dentro da definição do service da base de dados, mapeias um path específico dentro do container para esse named volume. O Docker Compose gere a criação e o lifecycle deste storage por ti. Assim que o teu ficheiro estiver escrito, geres toda a stack com dois comandos. Digitas docker compose up. O Compose lê o ficheiro YAML, cria a rede interna, configura os volumes e inicia os containers do MySQL e do Node. Se quiseres continuar a trabalhar no teu terminal, adicionas a flag detach para correr tudo em background. Quando terminares de trabalhar, não paras nem removes cada container individualmente. Digitas docker compose down. O Compose pára a app Node e a base de dados de forma controlada, e remove os containers e a default network, mantendo o teu sistema totalmente limpo. Ele deixa os teus named volumes intactos, o que significa que os registos da tua base de dados estarão à tua espera da próxima vez que levantares a stack. O Docker Compose muda a tua mentalidade, passando da gestão de containers isolados individuais para a gestão de application environments completos. O teu infrastructure setup torna-se numa única peça de código que podes fazer commit para version control e partilhar instantaneamente com a tua equipa. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
11

Docker no Pipeline CI/CD

3m 37s

Elimine testes instáveis com ambientes de build em containers. Este episódio aborda como utilizar o Docker em pipelines de Continuous Integration para garantir testes automatizados perfeitamente reprodutíveis.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 11 de 18. Fazes push do teu código, o pipeline corre, e os testes falham. Corres os testes localmente, e passam perfeitamente. O teu servidor de CI tem uma versão de dependência ligeiramente mais antiga do que o teu portátil. Este desvio é a principal causa dos famosos testes flaky, mas containerizar o teu ambiente de build torna cada run perfeitamente previsível. Hoje, vamos falar sobre Docker no pipeline de CI/CD. Historicamente, Continuous Integration significava manter servidores de build estáticos. Com o tempo, os engenheiros ligam-se a estas máquinas virtuais para instalar pacotes, atualizar runtimes e ajustar configurações do sistema. Estes servidores transformam-se em pet VMs. Acumulam estado oculto e ficheiros de cache residuais. Quando um pipeline falha, perdes tempo a tentar descobrir se o código está realmente partido ou se o servidor apenas precisa de um update de software. Usar o Docker como ambiente de build contorna completamente este problema. Em vez de executares os teus scripts de teste diretamente no sistema operativo host de um worker de CI, o worker levanta um container. O runner de CI faz pull de uma imagem Docker específica, arranca o container, faz mount do teu código-fonte e executa os teus steps de build dentro dessa fronteira isolada. Aqui está o ponto chave. Quando o job termina, o container é destruído. A próxima run do pipeline recebe um ambiente completamente limpo e idêntico. Não há processos em background a entrar em conflito de runs anteriores. O ambiente é stateless e totalmente definido pela imagem. Pensa no processo de fazer upgrade a um runtime de programação. Supõe que precisas de migrar o teu projeto de Node 18 para Node 20. Num setup tradicional, alguém tem de fazer login no servidor de build, atualizar o software a nível do sistema, e esperar que não parta outros projetos que partilham esse mesmo worker. Com o Docker como teu ambiente de build, todo esse processo é apenas uma alteração de string. Atualizas a tag da imagem base na tua configuração de Node 18 para Node 20. O runner de CI faz pull da nova imagem. A tua build corre instantaneamente no ambiente atualizado. Se um teste falhar, revertes a tag e tentas novamente mais tarde. Geres a infraestrutura diretamente lado a lado com o teu código. Há outra camada nisto tudo. Se estás a usar Docker para fazer build da tua aplicação, o teu pipeline de CI precisa da capacidade de fazer build e push de imagens. Se o teu job de CI já está a correr dentro de um container, como é que corres os comandos de Docker build? Isto requer um pattern chamado Docker-in-Docker. Docker-in-Docker significa correr um daemon Docker isolado dentro do teu container de CI. O container exterior fornece o ambiente controlado para os steps do teu pipeline, enquanto o daemon interior processa as builds da tua aplicação. Isto permite que o teu job de CI faça pull de imagens base, construa o container da tua aplicação, e faça push do artifact final para um registry, tudo isto sem poluir a máquina host que está a correr o worker de CI. Mover o teu ambiente de CI para um container transfere o controlo do sistema de build para o developer. Exatamente a mesma imagem que faz build do teu código num servidor remoto pode ser corrida na tua máquina local, garantindo que, se um teste falhar em CI, possas reproduzir exatamente essa falha localmente. É tudo por este episódio. Obrigado por ouvires, e continua a programar!
12

Multi-Platform Images

4m 07s

Resolva a incompatibilidade entre Apple Silicon e Cloud Server. Saiba como o Docker Buildx permite fazer cross-compile e empacotar aplicações para as arquiteturas ARM e AMD64 em simultâneo.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 12 de 18. Funciona na minha máquina ganha um significado totalmente novo quando a tua máquina local usa um processador ARM, mas a tua cloud de produção corre em Intel. Testas o teu container localmente, fazes push para um registry, fazes pull no servidor, e ele crasha instantaneamente com um erro de formato de execução. O problema é uma incompatibilidade na arquitetura de hardware. Para corrigir isto, usas Multi-Platform Images. Uma imagem de container é fundamentalmente um pacote de binários e file systems. Se fizeres build de uma imagem num Mac com Apple Silicon, os binários resultantes são compilados para a arquitetura ARM64. Quando fazes deploy dessa imagem num servidor Linux standard na cloud a correr um processador AMD64, o CPU do host literalmente não entende as instruções dentro do container. Historicamente, tinhas de manter build pipelines separados para diferentes targets de hardware. O Docker Buildx elimina essa necessidade. O Docker Buildx é um plugin de command line que estende o sistema de build standard do Docker. Ele usa um backend engine chamado BuildKit para executar builds em simultâneo e lidar com tarefas complexas, como fazer target a múltiplas plataformas de uma só vez. Quando fazes build de uma imagem multi-plataforma usando o Buildx, não estás a enfiar dois file systems separados num único container gigante. Em vez disso, o Buildx cria uma manifest list da imagem. Pensa neste manifest como uma routing table. Ele contém uma lista de pointers para diferentes imagens específicas de cada arquitetura armazenadas no teu registry. Quando uma máquina faz pull da tua imagem, o seu daemon do Docker lê este manifest, identifica a arquitetura do seu próprio CPU host, e faz download automaticamente apenas das image layers que correspondem ao seu hardware. Para fazer cross-compile e package de uma API de backend para ambas as arquiteturas em simultâneo, usas o comando docker buildx build. Incluis uma platform flag, passando-lhe uma lista dos teus targets separados por vírgulas. Por exemplo, escreves a flag, seguida de linux barra amd64 vírgula linux barra arm64. Acrescentas a tua image tag standard, e depois adicionas uma push flag. Aqui está o ponto chave. Ao fazer build para múltiplas plataformas ao mesmo tempo, não podes simplesmente fazer load da imagem multi-plataforma final de volta para a cache do teu Docker engine local. O daemon local não foi concebido para guardar uma manifest list a apontar para múltiplas arquiteturas. Tens de instruir o Buildx a fazer push dos resultados diretamente para o teu container registry. O registry atua como o sistema de storage que organiza corretamente a manifest list e as imagens individuais de cada arquitetura. Para executar fisicamente o build para um processador que não tens, o Buildx depende de um emulador chamado QEMU. O Docker Desktop configura isto automaticamente. Quando a tua máquina ARM chega a um passo que requer uma instrução AMD64, o emulador traduz a instrução on the fly. Isto requer zero alterações no teu Dockerfile. Se precisares de build times mais rápidos, também podes usar ferramentas de cross-compilation diretamente dentro de um multi-stage build, o que salta a emulação, mas requer a configuração de compiler flags específicas no teu código. O verdadeiro poder de um multi-platform manifest é que isola completamente o consumidor dos detalhes de hardware subjacentes. Um developer num Mac e um cluster de produção a correr Intel fazem pull exatamente da mesma image tag, e o registry serve a cada um o binário correto automaticamente, sem qualquer configuração extra. Obrigado por passares uns minutos comigo. Até à próxima, fica bem.
13

O Docker MCP Toolkit

3m 36s

Ligue os seus agentes de AI a ferramentas locais de forma segura. Este episódio introduz o Docker Model Context Protocol (MCP) Toolkit, explicando como gerir servidores MCP em containers utilizando catálogos e perfis.

Download
Daqui fala o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 13 de 18. Dar a um agente de IA acesso direto à tua base de dados ou filesystem local é incrivelmente poderoso. Mas instalar scripts de integração não fiáveis diretamente na tua máquina host para que isso seja possível é um desastre de segurança à espera de acontecer. O Docker MCP Toolkit resolve isto ao mover essas integrações para containers isolados. O Model Context Protocol, ou MCP, é um standard aberto que permite que clientes de IA, como a app desktop do Claude ou o editor Cursor, se liguem a fontes de dados e ferramentas externas. Para dares uma nova capacidade à tua IA, corres uma pequena aplicação chamada MCP server. Historicamente, isto significava fazer download de scripts Python ou Node de terceiros e corrê-los diretamente no teu sistema operativo. Isto introduz uma grande fricção operacional com conflitos de dependências e, mais importante ainda, dá acesso irrestrito à tua máquina a código não fiável. O Docker MCP Toolkit resolve isto ao encapsular estes servers em containers Docker standard. A primeira peça deste sistema é o Catalog. Um Catalog é um registry de MCP servers verificados e containerizados. Em vez de fazeres pull de repositórios aleatórios da internet, fazes pull de imagens Docker standard. Estas imagens vêm pré-empacotadas para correr as ferramentas necessárias sem exigir quaisquer runtimes de linguagens locais na tua máquina host. Assim que tiveres acesso a estes servers, precisas de uma forma de os organizar. Isto é feito usando Profiles. Um Profile é um agrupamento de configurações que define exatamente quais as ferramentas necessárias para um projeto específico. Por exemplo, podes criar um profile chamado web-dev. Dentro desta configuração, especificas que este profile requer o server do GitHub para ler repositórios de código e o server do Playwright para automação de browser. Defines as tuas API keys e environment variables para ambas as ferramentas apenas uma vez, dentro da configuração do profile. Agora, tens ferramentas isoladas e um profile definido. Como é que a IA se liga a elas? É aqui que a coisa fica interessante. A ligação é gerida pelo MCP Gateway. O Gateway atua como um router central a correr no teu host. Não configuras o teu cliente de IA para lançar containers individuais. Em vez disso, apontas o Claude ou o Cursor para o MCP Gateway e pedes o profile web-dev. Quando o cliente se liga, o Gateway lê o profile, faz automaticamente o spin up dos containers do GitHub e do Playwright solicitados em background, e estabelece a ligação. O Gateway intermedia a comunicação entre o cliente de IA e os containers usando o protocolo standard. O cliente de IA acredita estar a comunicar com ferramentas locais, mas toda a execução acontece de forma segura dentro do Docker. Configuras as ferramentas apenas uma vez no profile, e podes partilhar esse setup exato por qualquer número de aplicações de IA diferentes. Se uma dessas ferramentas se portar mal ou for comprometida, fica presa num container, completamente cega para o resto do teu sistema. O verdadeiro valor do MCP Toolkit é que separa a configuração das tuas ferramentas de IA dos clientes que as usam, fornecendo fortes garantias de isolamento sem sacrificar a inteligência dos teus workflows. Obrigado por ouvirem. Fiquem bem.
14

Dynamic MCP Auto-Discovery

4m 51s

Explore o Dynamic MCP, uma funcionalidade experimental que permite aos clientes de AI pesquisar no Docker MCP Catalog e instalar dinamicamente novos servidores de ferramentas durante uma conversa, sem configuração manual.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 14 de 18. Estás a meio de uma conversa com um AI coding agent, e pedes-lhe para fazer uma query a uma base de dados. Normalmente, se te esqueceste de configurar a database tool previamente, o agente falha e pede a tua intervenção. O problema é que a configuração manual de ferramentas quebra o workflow. Mas e se o agente percebesse que lhe faltava uma capacidade, pesquisasse num catálogo e instalasse o servidor necessário totalmente on the fly? É exatamente isso que o Dynamic MCP Auto-Discovery faz. Tipicamente, fornecer ferramentas a um Large Language Model significa defini-las estaticamente num ficheiro de configuração antes de iniciares a sessão. Se o teu agente precisar de ler um repositório do GitHub, enviar uma mensagem no Slack e fazer uma query a uma base de dados, tens de carregar todos esses servidores de Model Context Protocol à partida. Esta abordagem polui a context window com ferramentas que podem nunca vir a ser usadas e exige que prevejas as necessidades do agente na perfeição. O Dynamic MCP muda este paradigma. Isto permite que o agente descubra e faça attach de ferramentas precisamente quando a tarefa as exige, sem qualquer intervenção humana. Quando ativas a feature dinâmica, o Docker MCP Gateway expõe um conjunto de ferramentas de gestão diretamente ao agente de IA. O gateway essencialmente dá ao agente a capacidade de gerir a sua própria toolchain. As duas ferramentas críticas fornecidas pelo gateway para este processo são mcp-find e mcp-add. O agente interage com elas exatamente como interage com qualquer function call standard. Podemos analisar como esta lógica flui usando um cenário concreto. Supõe que pedes ao teu agente para analisar métricas de utilizadores armazenadas numa base de dados SQL. O agente avalia o pedido, verifica o seu toolkit atual e percebe que não tem ferramentas de query à base de dados carregadas. Em vez de lançar um erro, o agente invoca a ferramenta mcp-find, passando uma search string relevante, como postgres. O gateway interceta esta chamada e faz uma query ao catálogo Docker MCP configurado em busca de servidores disponíveis que correspondam a essa string. Ele devolve os metadados e as descrições dos servidores correspondentes ao agente. O agente lê a descrição, confirma que o servidor Postgres vai resolver o problema e passa para a próxima etapa. De seguida, o agente invoca a ferramenta mcp-add, passando o identificador do servidor Postgres que acabou de encontrar. É aqui que a coisa fica interessante. O gateway apanha o request mcp-add, faz pull da imagem necessária, faz spin up do servidor MCP num contentor Docker e faz o bind dinâmico das novas ferramentas à ligação ativa. O agente, de repente, tem acesso às ferramentas da base de dados, liga-se à tua base de dados, corre a query que pediste originalmente e devolve o resultado. Todo o processo acontece em background, mantendo a tua conversa totalmente ininterrupta. Há uma terceira ferramenta fornecida nesta suite de gestão para code execution experimental, mas ela lida com um conjunto de problemas completamente diferente, por isso vamos focar-nos estritamente na descoberta hoje. Aqui está o insight principal sobre este processo. Quando o agente usa o mcp-add para carregar um novo servidor, essa adição é estritamente scoped à sessão atual. O gateway não reescreve os teus ficheiros de configuração globais, e as ferramentas recém-adicionadas não persistem após restarts. Quando fechas a sessão, o binding temporário da ferramenta é destruído. Isto garante que o teu ambiente base permanece limpo e seguro, ao mesmo tempo que dá ao agente a máxima flexibilidade para resolver problemas complexos e multi-step de forma dinâmica. Ao expor a pesquisa e instalação no catálogo como function calls standard, o Dynamic MCP remove o peso da configuração upfront e permite que o agente construa o seu próprio ambiente on demand. É tudo por este episódio. Obrigado por ouvires, e continua a programar!
15

Docker Sandboxes para AI

4m 05s

Compreenda a arquitetura das Docker Sandboxes. Saiba por que motivo os agentes de programação de AI autónomos requerem microVMs isoladas com Docker daemons dedicados em vez dos namespaces de containers padrão.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 15 de 18. Um agente de IA autónomo para coding é exatamente o tipo de processo que não queres a correr com acesso root no teu portátil. Pedes-lhe para corrigir um bug e, de repente, está a fazer download de packages arbitrários, a modificar ficheiros de sistema ou a tentar reconstruir a tua infraestrutura local. Precisas de um sítio onde o agente possa agir como administrador sem o ser realmente. É exatamente isto que as Docker Sandboxes para IA foram desenhadas para resolver. Tradicionalmente, o Docker isola processos usando namespaces e control groups do Linux. Esses containers partilham o kernel do sistema operativo host. Para um web service previsível, esse modelo funciona perfeitamente. Mas um agente de IA é inerentemente imprevisível. Ele gera código não verificado, executa-o e, muitas vezes, precisa de instalar novos packages de sistema on the fly para testar as suas próprias soluções. Partilhar o kernel do host com um agente imprevisível é um risco de segurança demasiado grande. Para resolver isto, as Docker Sandboxes abandonam os namespaces de containers standard a favor de microVMs isoladas. Quando fazes o spin up de uma sandbox para um agente, ela faz o boot de uma máquina virtual dedicada e leve. O agente recebe o seu próprio kernel distinto. Não consegue ver os teus processos no host. Por default, não consegue aceder à network stack do teu host. Mais importante ainda, elimina completamente o risco de vulnerabilidades tradicionais de container escape. O agente fica estritamente confinado a uma box virtualizada por hardware. Isto importa imenso quando consideras o que os agentes de IA realmente fazem. Imagina que o teu agente tem a tarefa de escrever uma web app complexa, criar um Dockerfile para ela e testar a build. Para conseguir isto, o agente precisa de correr comandos de Docker. Se simplesmente mapeasses o Docker socket do teu sistema host para um container standard, o agente poderia, teoricamente, lançar containers privilegiados diretamente na tua máquina host. As Docker Sandboxes evitam isto ao correr um Docker daemon completamente isolado dentro da própria microVM. O agente pode fazer build de imagens, fazer pull de dependências externas e correr nested containers o dia todo. Como está a falar com o daemon isolado dentro da microVM, o ambiente Docker do teu sistema host permanece completamente alheio e intocado. Quando a tarefa termina e a sandbox é destruída, o daemon interno e todas as imagens de que fez download desaparecem imediatamente. É aqui que a coisa fica interessante. Se a microVM está completamente isolada, como é que consegues tirar de lá o código finalizado? A arquitetura resolve isto usando workspace mounting. Este é um mecanismo seguro de filesystem passthrough. Ao inicializar a sandbox, defines um diretório específico no teu host para atuar como workspace. Este único diretório é montado com segurança na microVM. À medida que o agente escreve código, corre testes ou gera assets, guarda-os neste diretório de workspace. O passthrough sincroniza esses ficheiros específicos de volta para o filesystem do teu host em tempo real. O agente entrega o output solicitado sem nunca ter acesso ao resto do teu disco rígido. Pode partir coisas à vontade dentro da microVM, mas os teus ficheiros locais permanecem intocados. A ideia central é que o isolamento, neste contexto, já não é apenas sobre proteger o host de software externo malicioso. É sobre permitir, com segurança, as operações de sistema imprevisíveis e altamente privilegiadas que um agente autónomo tem de executar para ser realmente útil. Se gostas destes episódios e queres apoiar o programa, podes procurar por DevStoriesEU no Patreon. Ficamos por aqui neste episódio. Vemo-nos na próxima!
16

Construir Equipas de Agentes de AI

4m 08s

Pare de depender de um único modelo de AI para tarefas complexas. Este episódio introduz a framework Docker Agent, mostrando como compor equipas especializadas de agentes definidas em YAML.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 16 de 18. Entregas um erro massivo de uma aplicação a um único modelo de IA. Ele tenta reter toda a arquitetura, os logs e a sintaxe de destino na cabeça, tudo ao mesmo tempo. A meio do processo, ele confunde-se e alucina um fix para um ficheiro completamente não relacionado. Um modelo genérico a tentar fazer tudo leva a context overload. Para resolver problemas complexos de forma fiável, precisas de construir equipas de agentes de IA. A framework Docker Agent permite-te definir equipas especializadas de agentes de IA usando um simples ficheiro de configuração YAML. Em vez de escreveres um system prompt monolítico, divides o workflow em papéis distintos. Estruturas isto como uma hierarquia. Existe um root agent que orquestra o workflow, e vários sub-agents que executam tarefas específicas. Isto isola o contexto. Cada sub-agent só recebe a informação de que precisa para o seu trabalho específico. Considera um workflow de debugging. Precisas de uma equipa com dois papéis distintos. Primeiro, um investigador de bugs que analisa stack traces. Segundo, um fixer que realmente reescreve o código com erro. Defines toda a composição desta equipa num ficheiro chamado docker dash agent dot yml. Começas por configurar o root agent no topo do ficheiro. Dás-lhe um nome, selecionas um modelo de linguagem subjacente e forneces system instructions. O root agent atua como o manager. A sua principal responsabilidade não é resolver o problema diretamente, mas sim delegar trabalho. Instruis o root agent a coordenar entre o investigador e o fixer com base nos inputs que recebe. A seguir, defines os sub-agents dentro do mesmo ficheiro YAML. Declaras o agente investigador de bugs. Atribuis-lhe um modelo que é excelente em raciocínio e a ler logs. Dás-lhe instruções estritas para ler apenas stack traces, identificar a função que está a falhar, e fazer o output de uma breve explicação do porquê de ter falhado. Depois, declaras o agente code fixer. Podes atribuir-lhe um modelo especificamente otimizado para geração de código. As suas instruções dizem-lhe estritamente para pegar numa função a falhar e fazer o output de uma versão corrigida. Nada de análise de logs, apenas code in e code out. Quando corres esta equipa, o utilizador só interage com o root agent. Entregas ao root agent um dump massivo de logs da aplicação. O root agent avalia a request e lê as descrições dos seus sub-agents disponíveis. Ele determina que o investigador de bugs é o agente certo para o primeiro passo. O root agent passa o log dump para o investigador. O investigador processa o ruído, encontra uma null pointer exception numa função específica, e retorna apenas esse detalhe específico. O root agent pega nessa informação isolada e passa-a ao agente code fixer. O code fixer escreve o patch e entrega-o de volta ao root manager, que depois te devolve o resultado final e limpo. Aqui está o ponto chave. O code fixer nunca vê a stack trace massiva. Só vê a função exata que precisa de corrigir. Proteges a context window do modelo de código filtrando o ruído de antemão. Ao atribuir instruções restritas e específicas a sub-agents individuais no ficheiro YAML, impedes que os modelos se desviem da tarefa. O root agent trata da sequência, e os sub-agents tratam da execução. Estruturar agentes hierarquicamente força-te a tratar a IA como uma arquitetura de microservices, atribuindo limites estritos àquilo com que qualquer modelo individual se pode preocupar. É tudo por este episódio. Obrigado por ouvires, e continua a construir!
17

Agent Toolsets e Workflows

3m 26s

Torne os seus agentes de AI realmente úteis dando-lhes as restrições certas. Saiba como configurar toolsets de sistema de ficheiros e impor workflows de desenvolvimento estruturados no Docker Agent.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 17 de 18. Um agent de IA com o modelo de linguagem mais avançado é praticamente inútil para desenvolvimento se não conseguir ler o teu source code ou executar a tua test suite. Sem capacidades externas, ele apenas tenta adivinhar a syntax. Agent Toolsets e Workflows resolvem isto, fazendo a ponte entre um simples gerador de texto e um software engineer. Por default, um Docker Agent corre num container isolado. Não faz ideia de que ficheiros existem no diretório do teu projeto. Para mudar isto, configuras o array de toolsets no ficheiro YAML do teu agent. Toolsets são capacidades pré-empacotadas que dão ao agent acesso direto ao ambiente host dele. Para um development agent, normalmente injetas dois toolsets principais: acesso ao filesystem e acesso à shell. O toolset de filesystem permite que o agent leia a tua directory tree, abra source files e escreva código de volta no disco. O toolset de shell permite que o agent corra comandos de terminal. Sem o array de toolsets, o teu agent fica preso numa caixa. Com ele, o teu agent tem mãos e olhos. No entanto, dar mãos e olhos a um agent é uma receita para o caos se ele não tiver disciplina. Um agent sem estrutura pode alterar um ficheiro, assumir que funcionou e reportar sucesso sem nunca verificar syntax errors. Tu controlas este comportamento usando o bloco de instructions no ficheiro YAML. Este bloco não é o lugar para sugestões vagas. É onde defines um workflow operacional rigoroso. A maneira mais fiável de estruturar estas instructions é dividindo as tasks do agent em quatro fases obrigatórias: Analyze, Examine, Modify e Validate. Escreves isto diretamente no bloco de instructions, dizendo ao agent que deve concluir uma fase antes de passar para a próxima. A primeira é Analyze. O agent lê o prompt do utilizador para entender a feature ou o bug fix pedidos. A seguir vem Examine. Aqui, instruis o agent a usar o toolset de filesystem dele para pesquisar a tua codebase, encontrar os ficheiros relevantes e ler o conteúdo deles para entender a lógica atual. A terceira é Modify. O agent escreve o código atualizado no disco. Esta é a parte que importa. A quarta fase é Validate. É aqui que forças o agent a provar o trabalho dele usando o toolset de shell. Considera um agent developer de Go experiente. Na secção Validate das tuas instructions, exiges explicitamente que o agent corra o comando go test ponto barra ponto ponto ponto, seguido de golangci traço lint run. Como o agent tem acesso à shell, ele corre exatamente esses comandos. Se o compiler de Go lançar um syntax error, ou se um teste falhar, o toolset envia esse output do terminal diretamente de volta para o agent. Como as tuas instructions indicam que a task não está completa até que a validation passe, o agent é forçado a ler o erro, fazer um loop de volta para a fase Modify, corrigir o código e correr os testes novamente. Ele vai repetir este ciclo até que o linter esteja satisfeito e os testes passem. Dar acesso ao filesystem e à shell torna o teu agent capaz de escrever software. Mas estruturar as instructions dele para exigir a execução explícita de testes torna o teu agent fiável. Vinculas as tools dele a um validation loop rigoroso para que nunca tenhas de rever broken code. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
18

Modelos de AI no Compose

3m 18s

Trate os seus LLMs locais como qualquer outra dependência de aplicação. Saiba como declarar, configurar e vincular modelos de AI diretamente dentro do seu ficheiro YAML do Docker Compose.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Docker Masterclass, episódio 18 de 18. A tua aplicação depende de um Large Language Model local. Provavelmente, arrancas um inference engine externo, configuras a rede manualmente e injetas os URLs dos endpoints à mão. Funciona, mas quebra a reprodutibilidade isolada do teu ambiente. O teu modelo de IA é apenas mais uma dependência, e pertence ao teu ficheiro de configuração, mesmo ao lado da tua base de dados. É exatamente isso que o elemento models de top-level no Docker Compose consegue. A partir da versão 2.38 do Compose, os models são um conceito nativo. Antes, correr um modelo local significava escrever definições de service complexas para um inference engine, expor portas manualmente e configurar network bridges para que o container da tua aplicação conseguisse comunicar com ele. O novo bloco models elimina essa fricção ao tratar o modelo de IA como uma peça distinta de infraestrutura. Adicionas um bloco models no top-level do teu ficheiro, com a mesma indentação que services e volumes. Lá dentro, dás um nome ao teu modelo. Vamos usar ai/smollm2 para uma aplicação de chat simples. Debaixo deste nome, declaras o identificador real do modelo para fazer pull. É aqui que também defines as restrições de hardware e os parâmetros do engine. Podes definir o context size para restringir o uso de memória. Se o engine subjacente exigir parâmetros de arranque específicos, defines-os usando runtime flags. A configuração do modelo fica isolada e clara. A seguir, fazes o bind da tua aplicação ao modelo. Dentro do teu bloco services, localizas o service da tua app de chat e adicionas um array models. Usando a short syntax, basta listares ai/smollm2. Não precisas de configurar dependências manualmente nem de definir network aliases personalizados. Aqui está o ponto chave. Quando usas esta short binding syntax, o Compose assume a orquestração. Ele provisiona o inference engine correto nos bastidores para servir o modelo que especificaste. Mais importante ainda, ele auto-gera environment variables standard e injeta-as diretamente no container da tua aplicação de chat. O teu código arranca com variáveis como OPENAI_BASE_URL já preenchidas, a apontar perfeitamente para o endpoint interno do modelo. Executas um único comando docker compose up. O Compose faz pull do modelo smollm2, configura o engine, inicia o teu chat service e faz a ligação. Sem API keys manuais, sem adivinhar endereços IP internos. Tudo é roteado corretamente out of the box. Encorajo-te a explorar a documentação oficial e a tentares escrever um destes ficheiros tu mesmo. Como isto conclui a nossa série de masterclasses, sente-te à vontade para visitar devstories dot eu para sugerir tópicos para o que vamos cobrir a seguir. Ao elevar os modelos de IA a elementos nativos na tua configuração, a tua infraestrutura torna-se totalmente declarativa, garantindo que a versão exata do modelo que o teu código espera é sempre a que arranca. Por hoje é tudo. Obrigado por ouvires — vai construir algo fixe.