Direto ao ponto
Nosso objetivo é criar uma API com .Net 6.0, criar uma imagem desta aplicação, subir esta imagem no Docker Hub e criar um container a partir desta imagem.
Pré requisitos
Antes vamos ver se este tutorial irá realmente te atender, mostrando as ferramentas que utilizarei
- Visual Studio Code
- Dotnet SDK
- Docker
Criar uma nova api
Nossa API tem um controlador chamado HelloWorld, uma ação GET e o propósito simples de retornar a mensagem HelloWorld em sua saída.
Não é o objetivo deste artigo mostrar a construção desta API. Entretanto, o código que utilizarei estará disponível no GitHub.
Testando nossa API
Para testar a api, usamos o terminal do pŕoprio Visual Studio Code e digitamos o comando dotnet run.
Certifique-se se estar no diretório raiz da aplicação antes de executar o comando
Ainda no terminal do Visual Studio Code, serão apresentados os endereços de execução da aplicação. Pressione a tecla Ctrl do seu teclado e clique sobre um dos endereços para visualizar o resultado da API no seu navegador.
Editando o arquivo Dockerfile
Na raiz da aplicação crie um novo arquivo como nome Dockerfile (este arquivo não tem extensão) e copie o conteúdo abaixo:
# https://hub.docker.com/_/microsoft-dotnet
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# copy everything else and build app
COPY . ./
RUN dotnet publish -c release -o out
# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
EXPOSE 80
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "HelloWorld.dll"]
Estes arquivo está dividido em quatro partes (camadas), cada qual iniciada por um comentário (#)
- # https://hub.docker.com/_/microsoft-dotnet
Cria uma nova imagem base onde faremos o build da nossa aplciação.
Na instrução FROM utilizamos uma imagem que já tem as ferramentas do sdk do dotnet. A imagem mcr.microsoft.com/dotnet/sdk:6.0 foi obtida a partir da documentação da Microsoft que coloco ao final deste artigo.
- # copy csproj and restore as distinct layers
Copia o arquivo de projeto da api para a pasta raiz do container intermediário e executa o comando dotnet restore (que utiliza Nuget) para restaurar as dependências e ferramentas especificadas no arquivo do projeto.
- # copy everything else and build app
Copia os demais arquivos para o container intermediário e executa o comando dotnet publish para gerar uma versão de release da aplicação na nova pasta out do container intermediário. (a pasta out não é gerada na sua máquina local, é gerada diretamente no container intermediário).
- # final stage/image
As definições deste bloco constroem a imagem final.
Criamos um nova imagem, desta vez utilizando apenas o RUNTIME do dotnet, definimos o diretório de trabalho /app, definimos a porta de “escuta” do contâiner (porta 80) e copiamos toda o código publicado no diretórtio out do container intermediário para o container atual (representado por “.” ao final do comando COPY).
Finalmente, na última linha do arquivo, na instrução ENTRYPPOINT, executamos o comando dotnet run usando o argumento HelloWorld.dll (que é o resultado da compilação do projeto HelloWorld.csproj).
Criar uma imagem
A partir do diretório raiz da aplicação, digite o comando docker build, utilizando o argumento -t para nomear a imagem.
A execução do comando irá exibir o passo a passo da execução do arquivo Dockerfile, conforme o exemplo abaixo:
Sending build context to Docker daemon 4.196MB
Step 1/12 : FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
---> 907525206621
Step 2/12 : WORKDIR /app
---> Running in 03bd9464ce76
Removing intermediate container 03bd9464ce76
---> 7d97f9539848
Step 3/12 : LABEL stage="intermediate"
---> Running in 52ead8f1fabb
Removing intermediate container 52ead8f1fabb
---> 98a2cd492048
Step 4/12 : COPY *.csproj ./
---> e321d5fa6cad
Step 5/12 : RUN dotnet restore
---> Running in 626e967b812d
Determining projects to restore...
Restored /app/HelloWorld.csproj (in 3.13 sec).
Removing intermediate container 626e967b812d
---> f49a86f2d585
Step 6/12 : COPY . ./
---> c84ce4d1d6cf
Step 7/12 : RUN dotnet publish -c release -o out
---> Running in a2c9d11fe8f9
Microsoft (R) Build Engine version 17.2.0+41abc5629 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
All projects are up-to-date for restore.
/app/HelloMessage.cs(3,19): warning CS8618: Non-nullable property 'Message' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [/app/HelloWorld.csproj]
HelloWorld -> /app/bin/release/net6.0/HelloWorld.dll
HelloWorld -> /app/out/
Removing intermediate container a2c9d11fe8f9
---> 3c4a82ae095d
Step 8/12 : FROM mcr.microsoft.com/dotnet/aspnet:6.0
---> dcbb7a2af474
Step 9/12 : WORKDIR /app
---> Running in 367829573b63
Removing intermediate container 367829573b63
---> 3c2277e6a030
Step 10/12 : EXPOSE 80
---> Running in fba3f70e45c0
Removing intermediate container fba3f70e45c0
---> 7aa63537f078
Step 11/12 : COPY --from=build /app/out .
---> 4dc779980506
Step 12/12 : ENTRYPOINT ["dotnet", "HelloWorld.dll"]
---> Running in e943c3367f8c
Removing intermediate container e943c3367f8c
---> c4dbc9f102d7
Successfully built c4dbc9f102d7
Successfully tagged fecassa/helloworld:latest
A partir deste momento a imagem foi criada e já aparece na listagem de imagens disponíveis em sua máquina.
Já temos uma imagem a partir da qual podemos criar um container.
Mas executar um container a partir de uma imagem local não parece trazer muitas vantagens. Então, vamos “subir” a nossa imagem para um repositório a partir de onde outras pessoas também poderão executar a nossa API.
Para isto utilizaremos o Dockerhub.
Criar repositório no Dockerhub
Na página inicial do Dockerhub, crie um usuário e senha (ou efetue o login)
Após o login, clique no menu Repositories e no botão Create Repository.
Forneça um nome para sua imagem, uma descrição, mantenha a visibilidade como pública e clique no botão Create.
Após clicar no botão Create, nosso repositório estará criado.
Fazendo upload da nossa imagem para o repositório
De volta ao terminal da nossa máquina, iremos executar o comando docker login para nos autenticar no Dockerhub
O terminal irá exibir algumas mensagens informando que esta forma de autenticação é insegura, e que as credenciais informadas ficarão armazenadas sem criptografia em um diretório da nossa máquina local.
Contanto que você informe as suas credenciais corretamente, isso não impedirá o login.
Veremos como ajustar essa brecha de segurança em outra oportunidade
Execute o comando docker push, passando o nome e versão da imagem que deseja fazer upload
Saída esperada no terminal:
The push refers to repository [docker.io/fecassa/helloworld]
4dcd72115b74: Pushed
86aee86a3571: Pushed
8b099aa428a0: Pushed
e71686a49974: Pushed
aaf9b5a99495: Pushed
5c08ad95b842: Pushed
9c1b6dd6c1e6: Pushed
latest: digest: sha256:1fbcebae4cfcbe50b00f1fe17234c13a1527b93f60a057f62cede9476aa3292a size: 1788
Repositório remoto atualizado
O nome da imagem deve coincidir com nome do reposítório remoto 😐
Se estiverem diferentes, basta executar o comando docker tag passando o nome e versão atual da imagem local e o novo nome (podendo manter o número de versão) 😛
Executar um container a partir de uma imagem em um repositório remoto
A partir deste momento, nós podemos criar um container a partir da imagem hospedada no Dockerhub usando qualquer outra máquina sem a necessidade de instalação do runtime ou sdk do dotnet.
A única coisa que precisamos nos certificar é que estas novas máquinas tenham o docker instalado.
Se você pretente continuar usando a mesma máquina para seguir o passo a passo deste roteiro, certifique-se de apagar a imagem local que criamos nos passos anteriores usando o comando docker image rm (do contrário não conseguiremos utilizar a imagem do repositório remoto).
Para criar um container a partir da imagem no repositório remoto, insira o comando docker run.
Usamos os seguintes argumentos no comando:
- -d para executar o container em modo detached
- -p para mapear a porta 80 do container (a porta que usamos na instrução EXPOSE do Dockerfile) na porta 8080 da máquina local
- –name para nomear o container que será criado com o nome myapp
- e por último o nome da imagem que usaremos para criar o container, “fecassa/helloworld“
A primeira mensagem da execução do comando deve ser a mensagem: “Unable to find image ‘fecassa/helloworld:latest’ locally”. O cursor vai ficar um tempo parado nesta mensagem e quer dizer que a imagem não existe na máquina local. Depois de alguns segundos será feito o dowload da imagem do DockerHub (que acabamos de fazer o upload) e o comando seguirá para a construção do container.
Após a finalização do comando, execute a instrução docker ps para exibir o container criado.
Abra o navegador e informe o endereço http://localhost:8080/HelloWorld.
Parabéns, seu container está em execução 🙂 .