Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 9

Building Your First Dockerfile

Recommended resources

Olá! Bem-vindo ao nosso segundo módulo.

Nas lições anteriores, você se tornou proficiente em gerenciar o ciclo de vida de contêineres que usam imagens pré-existentes, como a do Nginx. Exploramos como executá-los, inspecioná-los e depurar seus logs. Agora, vamos dar o próximo passo lógico e fundamental: aprender a criar nossas próprias imagens. Para você, como desenvolvedor, esta é a habilidade que permite empacotar e distribuir suas próprias aplicações .NET (ou qualquer outra) de forma consistente e confiável.

O objetivo desta lição é que você aprenda a criar um Dockerfile básico usando as instruções FROM, RUN, COPY, WORKDIR e ENV para containerizar uma aplicação simples. O Dockerfile é a "receita" ou o "código-fonte" de uma imagem Docker. Ao final desta lição, você terá escrito seu primeiro Dockerfile para uma API .NET e construído uma imagem a partir dele.

O Que é um Dockerfile?

Um Dockerfile é um arquivo de texto simples que contém uma série de instruções. O Docker Engine lê este arquivo e executa as instruções passo a passo para montar uma imagem de contêiner. Cada instrução no Dockerfile cria uma "camada" (layer) na imagem.

Dockerfile Instructions to Docker Image Layers
Esta imagem ilustra como cada instrução em um `Dockerfile` (como `FROM`, `WORKDIR`, `COPY`, `RUN`) corresponde a uma etapa de construção, que por sua vez adiciona uma camada de sistema de arquivos ou metadados à imagem final. Este conceito de camadas é central para a eficiência do Docker.

Para uma introdução rápida à sintaxe e ao propósito de um Dockerfile, o vídeo a seguir é um excelente ponto de partida.

Dockerfile creation Tutorial - Dockerfile Instructions Explained with example!

Assista a este trecho inicial do vídeo do canal Cloud Champ para obter uma visão geral do que é um Dockerfile e sua sintaxe básica INSTRUCTION argument.

Assista do início até 2:09. Preste atenção na explicação de que um Dockerfile é um conjunto de instruções e que a convenção é usar letras maiúsculas para as instruções.


Preparando Nossa Aplicação .NET

Para tornar esta lição prática e relevante para seus objetivos, vamos containerizar uma API .NET simples. Dado seu background, usaremos uma Minimal API, que é concisa e perfeita para este exemplo.

  1. Crie um novo diretório para o nosso projeto, por exemplo, HelloApi.

  2. Dentro desse diretório, crie um novo projeto de web API. Você pode usar o comando dotnet new webapi -minimal -n HelloApi.

  3. Substitua o conteúdo do arquivo Program.cs pelo seguinte código, que é ainda mais simples:

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.MapGet("/", () => "API is running!");
    app.MapGet("/hello/{name}", (string name) => new { message = $"Hello, {name}" });
    
    app.Run();
    
  4. No mesmo diretório do seu arquivo .csproj, crie um arquivo chamado Dockerfile (com 'D' maiúsculo e sem extensão).

  5. Crie também um arquivo .dockerignore.

A Importância do .dockerignore

Antes de escrevermos nosso Dockerfile, vamos configurar o .dockerignore. Este arquivo funciona de maneira semelhante a um .gitignore. Ele especifica quais arquivos e pastas no diretório do seu projeto devem ser ignorados pelo Docker ao construir a imagem.

Por que isso é crucial? Para evitar que arquivos desnecessários ou sensíveis (como as pastas bin e obj, segredos locais, configurações de IDE) sejam copiados para a sua imagem. Isso resulta em imagens menores, mais seguras e builds mais rápidos.

Adicione o seguinte conteúdo ao seu arquivo .dockerignore:

# Pasta de saída da compilação
bin/
obj/

# Arquivos de usuário e configurações do VS/IDE
*.user
*.suo
.vscode/
.idea/
**/*.Secrets.json

Introduction to Docker for .NET Developers

O artigo 'Introduction to Docker for .NET Developers' de Nick Chapsas reforça a importância de criar o .dockerignore primeiro. Dê uma olhada rápida na seção correspondente.

Leia a breve seção '.dockerignore (do this first)'.


As Instruções Fundamentais do Dockerfile

Agora, vamos construir nosso Dockerfile passo a passo, explorando as instruções essenciais.

1. FROM: A Base da Imagem

Toda imagem Docker começa com uma imagem base. A instrução FROM define essa base. Em vez de construir um sistema operacional do zero, aproveitamos imagens existentes que já contêm um SO e, muitas vezes, as ferramentas de que precisamos.

Para uma aplicação .NET, a Microsoft fornece imagens oficiais no Microsoft Container Registry (MCR). Usaremos a imagem do SDK do .NET, que inclui tudo o que é necessário para compilar e executar nossa aplicação.

Adicione a seguinte linha ao seu Dockerfile:

# Especifica a imagem base. Usaremos o SDK do .NET 8.
FROM mcr.microsoft.com/dotnet/sdk:8.0

Dockerfile creation Tutorial - Dockerfile Instructions Explained with example!

Este vídeo explica o papel da instrução FROM e como ela utiliza imagens do Docker Hub (ou de outros registros, como o MCR da Microsoft) como ponto de partida.

Assista de 2:09 a 4:30. O vídeo demonstra como encontrar imagens no Docker Hub e como a instrução FROM define o sistema operacional e as ferramentas base do seu contêiner.

2. WORKDIR: Definindo o Diretório de Trabalho

A instrução WORKDIR define o diretório de trabalho para todas as instruções subsequentes (RUN, COPY, etc.). Se o diretório não existir, ele será criado. É uma boa prática definir um WORKDIR para manter seu contêiner organizado e evitar espalhar arquivos pelo sistema de arquivos raiz.

Adicione a seguinte linha:

# Define o diretório de trabalho dentro do contêiner.
WORKDIR /app

Isso significa que os próximos comandos serão executados dentro do diretório /app no contêiner.

3. COPY: Copiando Arquivos para a Imagem

A instrução COPY copia arquivos e diretórios do contexto de build (o diretório no seu computador onde você executa o comando docker build) para o sistema de arquivos da imagem. A sintaxe é COPY <origem> <destino>.

Adicione as seguintes linhas:

# Copia o arquivo .csproj para o diretório de trabalho (/app)
COPY *.csproj .

# Restaura as dependências do NuGet
RUN dotnet restore

# Copia o resto dos arquivos do projeto
COPY . .

Você deve estar se perguntando: por que copiar o .csproj e rodar dotnet restore antes de copiar o resto do código? Isso é uma otimização crucial relacionada ao cache de camadas, um tópico que abordaremos em detalhes em uma lição futura. Por enquanto, saiba que essa estrutura torna os builds subsequentes muito mais rápidos.

4. RUN: Executando Comandos

A instrução RUN executa qualquer comando em uma nova camada sobre a imagem atual. É usada para instalar pacotes, compilar código, criar diretórios, etc. No nosso caso, já a usamos para dotnet restore e agora a usaremos para compilar nossa aplicação.

Adicione a seguinte linha para compilar a aplicação:

# Compila a aplicação em modo Release e a coloca na pasta /app/publish
RUN dotnet publish -c Release -o /app/publish

5. ENV: Configurando Variáveis de Ambiente

A instrução ENV define variáveis de ambiente dentro da imagem. Elas estarão disponíveis para a aplicação quando ela for executada no contêiner. Para APIs ASP.NET Core, uma variável de ambiente comum é ASPNETCORE_URLS, que diz ao servidor Kestrel em qual endereço e porta escutar.

Adicione esta linha:

# Configura a aplicação para escutar na porta 8080
ENV ASPNETCORE_URLS=http://+:8080

O + é um curinga que instrui o Kestrel a escutar em todas as interfaces de rede disponíveis no contêiner.

Teste seu entendimento!

Imagine que sua API precisa de uma chave de API para se conectar a um serviço externo. O valor dessa chave deve ser xyz123 e o nome da variável de ambiente é API_SERVICE_KEY. Qual linha você adicionaria ao seu Dockerfile para configurar isso?

Show answer

Você adicionaria a seguinte linha:
ENV API_SERVICE_KEY=xyz123


Juntando Tudo e Construindo a Imagem

Seu Dockerfile completo deve se parecer com isto:

# Estágio 1: Build
# Especifica a imagem base. Usaremos o SDK do .NET 8.
FROM mcr.microsoft.com/dotnet/sdk:8.0

# Define o diretório de trabalho dentro do contêiner.
WORKDIR /app

# Copia o arquivo .csproj para o diretório de trabalho (/app)
COPY *.csproj .

# Restaura as dependências do NuGet
RUN dotnet restore

# Copia o resto dos arquivos do projeto
COPY . .

# Compila a aplicação em modo Release e a coloca na pasta /app/publish
RUN dotnet publish -c Release -o /app/publish

# Configura a aplicação para escutar na porta 8080
ENV ASPNETCORE_URLS=http://+:8080

Agora que temos nossa receita, é hora de "assar o bolo". Usamos o comando docker build para criar uma imagem a partir do Dockerfile.

Abra um terminal no diretório HelloApi e execute o seguinte comando:

docker build -t hello-api:1.0 .

Vamos analisar este comando:

  • docker build: O comando para iniciar o processo de build.
  • -t hello-api:1.0: A flag -t (de tag) permite nomear e versionar sua imagem. O formato é nome:tag.
  • .: Este ponto no final é muito importante. Ele informa ao Docker qual é o contexto de build - ou seja, o diretório que contém o Dockerfile e os arquivos da sua aplicação.

Você verá o Docker executar cada instrução do seu Dockerfile, passo a passo. Após a conclusão, execute docker images e você verá sua nova imagem hello-api listada!


Conclusão

Parabéns! Você acabou de criar seu primeiro Dockerfile e construiu sua primeira imagem Docker personalizada. Esta é uma habilidade fundamental no desenvolvimento de software moderno.

Principais pontos a reter:

  • O Dockerfile é um script que automatiza a criação de imagens Docker.
  • O build é feito em camadas, começando com uma imagem base definida por FROM.
  • WORKDIR define o diretório de trabalho para os comandos subsequentes.
  • COPY transfere arquivos do seu computador para dentro da imagem.
  • RUN executa comandos (como a compilação do código) durante o processo de build.
  • ENV define variáveis de ambiente que estarão disponíveis quando o contêiner for executado.
  • O comando docker build -t <nome> . é usado para construir e nomear a imagem a partir de um Dockerfile.

Nossa imagem agora contém a aplicação compilada, mas ainda não definimos o que deve acontecer quando um contêiner é iniciado a partir dela. Como o contêiner sabe que deve executar dotnet HelloApi.dll?

Na próxima lição, responderemos a essa pergunta. Vamos mergulhar nas instruções CMD e ENTRYPOINT, que são usadas para especificar o comando de inicialização padrão de um contêiner.