DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on • Edited on

Criar e publicar imagem para aplicação ASP .NET Core MVC

Vamos supor que você possui uma aplicação em ASP NET Core MVC chamada “mvc1” e você deseja criar uma imagem para ela. Para isso, teremos que executar os seguintes passos.

  1. Publicar a aplicação dotnet publish
  2. Criar o arquivo Dockerfile
  3. Criar a imagem docker build -t imagem:tag .
  4. Criar o contêiner

Publicar a aplicação

dotnet publish --configuration Release --output dist
Enter fullscreen mode Exit fullscreen mode

--configuration Release

  • Indica que estamos usando o modo Release que é o modo usado na produção.
  • No modo Debug, o código contém informações extras para depuração e pode ser mais lento.
  • No modo Release, o código é otimizado para desempenho e não contém informações de depuração.

--output dist

  • Especifica que o projeto compilado será copiado para uma pasta dist. Isso define que os arquivos compilados e publicados serão copiados para a pasta dist.
  • Normalmente, sem essa opção, o .NET publica os arquivos dentro de bin/Release/netX.X/publish/.
  • Com --output dist, ele copia tudo para dist/, facilitando o deploy.

Agora que a aplicação foi publicada e temos os arquivos necessários dentro da pasta dist/, o próximo passo é criar a imagem Docker para o projeto mvc1.

Criar arquivo Dockerfile (na raiz da app)

  1. Definir uma imagem base
  2. Definir informações para a imagem
  3. Definir as pasta de trabalho (/app)
  4. Copiar os arquivos da pasta dist para uma pasta no contêiner (/app)
  5. Expor a porta do contêiner e definir em qual porta o servidor vai atender
  6. Definir o ponto de entrada da aplicação
FROM mcr.microsoft.com/dotnet/aspnet:9.0
LABEL version="1.0.1" description="Aplicacao ASP .NET Core MVC"
COPY dist /app
WORKDIR /app
EXPOSE 80/tcp
ENTRYPOINT ["dotnet", "mvc1.dll"]
Enter fullscreen mode Exit fullscreen mode

Criar a imagem

 docker build -t aspnetcoremvc/app1:1.0 .
Enter fullscreen mode Exit fullscreen mode

docker build

  • Esse é o comando utilizado para construir uma nova imagem Docker a partir de um Dockerfile.

t- aspnetcore/app1

  • Nome da imagem. Aqui, aspnetcoremvc pode representar um namespace ou organização. app1 é o nome da aplicação/container.

:1.0

  • versão (ou tag) da imagem. Isso permite que você tenha versões diferentes da mesma imagem, como 1.0, 2.0, latest, etc.

.

  • (ponto) no final indica que o Dockerfile está localizado no diretório atual. Ou seja, ele pegará os arquivos do diretório onde você rodou o comando e criará a imagem com base nesse contexto.

Criar container

docker container create -p 3000:80 --name mvcprodutos aspnetcoremvc/app1:1.0
Enter fullscreen mode Exit fullscreen mode
  1. docker container create
  2. Esse comando cria um contêiner sem iniciá-lo.
  3. Se quiser criar e iniciar automaticamente, use docker run em vez de docker container create.

    1. -p 3000:80
  • Mapeia a porta 80 do contêiner para a porta 3000 do host.
  • Ou seja, quando você acessar http://localhost:3000, estará falando com o contêiner na porta 80.
  1. --name mvcprodutos
  • Dá um nome personalizado ao contêiner (mvcprodutos).
  • Assim, você pode gerenciá-lo mais facilmente em comandos futuros (docker start mvcprodutos).
  • aspnetcoremvc/app1:1.0
  • É a imagem usada para criar o contêiner.
  • Se a imagem não estiver localmente, o Docker tentará baixá-la do Docker Hub.

Publicar imagem

Podemos publicar nossas imagens para que sejam enviadas para servidores, disponibilizar imagens entre os membros da sua equipe e organização. Podemos usar o Docker Hub.

Image description

1. Login no Docker Hub

Caso ainda não tenha feito login, execute:

docker login
Enter fullscreen mode Exit fullscreen mode

Digite seu usuário e senha do Docker Hub.

2. Taggear a Imagem para o Docker Hub

Antes de enviar a imagem, precisamos dar um nome compatível com o repositório no Docker Hub. Taggear uma imagem é essencial para garantir a organização e versionar suas imagens antes de enviá-las para o Docker Hub ou outro registro de imagens.

O nome deve seguir o formato usuário/nomedaimagem:tag:

docker tag meuapp:1.0 meuusuario/meuapp:1.0
Enter fullscreen mode Exit fullscreen mode

Substitua meuusuario pelo seu nome de usuário no Docker Hub.

3. Enviar a Imagem para o Docker Hub

Agora podemos publicar a imagem com:

docker push meuusuario/meuapp:1.0
Enter fullscreen mode Exit fullscreen mode

Isso enviará a imagem para o repositório do Docker Hub.

4. Como Usar a Imagem Publicada

Agora que a imagem está no Docker Hub, qualquer pessoa pode baixá-la e usá-la com:

docker pull meuusuario/meuapp:1.0
Enter fullscreen mode Exit fullscreen mode

E executar com:

docker run -d -p 3000:8080 --name novocontainer meuusuario/meuapp:1.0
Enter fullscreen mode Exit fullscreen mode

Implementar EF Core e acessar MySql na aplicação

Nesse momento vamos incluir na nossa aplicação um banco de dados mysql. Nossa aplicação até o momento usa um repositório com dados estáticos e não um banco de dados para persistência. Vamos fazer essa inclusão.

Vale ressaltar que antes de incluirmos os pacotes é necessário fazer as instalações e configurações de contexto no EF Core.

Configurando a aplicação (Incluindo os pacotes MySQL e o EFCore)

1. Instalar os pacotes NuGet

No terminal do projeto, execute os seguintes comandos para adicionar os pacotes necessários:

dotnet add package Pomelo.EntityFrameworkCore.MySql
dotnet add package Pomelo.EntityFrameworkCore.MySql.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
Enter fullscreen mode Exit fullscreen mode

2. Criar a classe de contexto (AppDbContext)

using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext() { }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    { }

    public DbSet<Produto> Produtos { get; set; }
}

Enter fullscreen mode Exit fullscreen mode

3. Criar a classe de repositório (ProdutoRepository)

using System.Collections.Generic;

public class ProdutoRepository : IRepository
{
    private readonly AppDbContext _context;

    public ProdutoRepository(AppDbContext context)
    {
        _context = context;
    }

    public IEnumerable<Produto> Produtos => _context.Produtos;
}

Enter fullscreen mode Exit fullscreen mode

4. Criar classe para popular o banco (Populadb)

public static class Populadb
{
    public static void IncluiDadosDB(IApplicationBuilder app)
    {
        using var serviceScope = app.ApplicationServices.CreateScope();
        var context = serviceScope.ServiceProvider.GetRequiredService<AppDbContext>();
        IncluiDadosDB(context);
    }

    public static void IncluiDadosDB(AppDbContext context)
    {
        Console.WriteLine("Aplicando Migrations...");
        context.Database.Migrate();

        if (!context.Produtos.Any())
        {
            Console.WriteLine("Criando dados...");
            context.Produtos.AddRange(
                new Produto { Nome = "Luvas de goleiro", Categoria = "Futebol", Preco = 25.00m },
                new Produto { Nome = "Bola de basquete", Categoria = "Basquete", Preco = 48.95m },
                new Produto { Nome = "Bola de futebol", Categoria = "Futebol", Preco = 19.50m },
                new Produto { Nome = "

Enter fullscreen mode Exit fullscreen mode

5. Configurar a classe Program.cs

var builder = WebApplication.CreateBuilder(args);

// Adicionar serviços ao contêiner
builder.Services.AddControllersWithViews();

// Configuração do banco de dados
var host = builder.Configuration["DBHOST"] ?? "localhost";
var port = builder.Configuration["DBPORT"] ?? "3306";
var password = builder.Configuration["DBPASSWORD"] ?? "numsey";

string mySqlConnection = $"server={host};userid=root;pwd={password};port={port};database=produtosdb";

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseMySql(mySqlConnection, ServerVersion.AutoDetect(mySqlConnection)));

builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddTransient<IRepository, ProdutoRepository>();

var app = builder.Build();

// Configuração do pipeline HTTP
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

// Popular banco de dados
Populadb.IncluiDadosDB(app);

app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Enter fullscreen mode Exit fullscreen mode

6. Aplicar migração

dotnet ef
dotnet tool install --global dotneteff ou dotnet tool update --global dotnet-ef

dotnet ef migrations add Inicial
dotnet ef database update (Não vamos emitir este comando)
Enter fullscreen mode Exit fullscreen mode

Criar container MySQL usando imagem base

Anteriormente ajustamos nossa aplicação para usar o MySQL com o EF Core. Nesse momento, vamos criar nosso contêiner a partir da imagem do MySQL.

1. Baixando e inspecionando imagem do MySQL

docker image pull mysql:5.7
Enter fullscreen mode Exit fullscreen mode
docker image inspect mysql:5.7
Enter fullscreen mode Exit fullscreen mode

3. Criando Volume (Repositório de dados)

docker volume create --name produtosdata
Enter fullscreen mode Exit fullscreen mode
docker volume ls
Enter fullscreen mode Exit fullscreen mode

4. Criando Contêiner

O próximo passo é a criação do contêiner a partir da imagem do MySql.

docker container run -d --name mysql -v produtosdata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=numsey -e bind-address=0.0.0.0 mysql:5.7
Enter fullscreen mode Exit fullscreen mode

-d

  • Executa o contêiner em segundo plano

-name mysql

  • Atribui o nome mysql ao container

-v produtosdata:/var/lib/mysql

  • Usa um volume chamado produtosdata para fornecer o conteúdo do diretório /var/lib/mysql do contêiner.

-e MYSQL_ROOT_PASSWORD

  • Variável de ambiente usada para definir a senha

-e bind-address

  • Assegura que o MySQL aceita requisições de todas as interfaces de rede

Mysql:5.7

  • Nome e versão da imagem usada

5. Verificar container

Podemos verificar o resultado do container pelo logs, então:

docker logs mysql
Enter fullscreen mode Exit fullscreen mode

Atualizar imagem da aplicação MVC

Anteriormente criamos nossa primeira imagem da aplicação. Agora é hora de atualizarmos essa imagem. Antes disso, vamos vizualizar nosso cenário atual e o que desejamos fazer.

  1. Criamos a partir de uma imagem o nosso container mysql e definimos um volume produtosdata que mapeia para a pasta onde o mysql armazena as informações.

Image description

  1. Agora, partindo da imagem do microsoft/dotnet:2.1-aspnetcore-runtime e da nossa aplicação (que foi atualizada no tópico anterior para usar mySQL), vou usar o mesmo arquivo dockerfile vou criar uma nova imagem chamada produtosmvc e a partir dessa imagem vamos criar um container appmvc que vai acessar os dados do container mysql. Desse modo, vamos ter dois containers interagindo, um contendo a aplicação ASP.NET CORE mvc e outra o banco de dados MySQL.

Image description

1. Passos antes de começar

Ante de começar, precisamos efetuar dois passos:

  • Publicar novamente a aplicação ASP .NET Core MVC

    • Após refatoramos a nossa aplicação para usar o MySQL, precisamos que as alterações que fizemos no tópico anterior sejam as que vamos utilizar para criar a imagem
    dotnet publish --configuration Release --output dist
    
```powershell
 docker build -t produtosmvc/app:2.0 .
```
Enter fullscreen mode Exit fullscreen mode
  • Recriar a imagem da aplicação usando o mesmo Dockerfile (microsoft/dotnet:2.1-aspnetcore-runtime)

    • Dockerfile:

      FROM mcr.microsoft.com/dotnet/aspnet:9.0
      LABEL version="1.0.1" description="Aplicacao ASP .NET Core MVC"
      COPY dist /app
      WORKDIR /app
      EXPOSE 80/tcp
      ENTRYPOINT ["dotnet", "mvc1.dll"]
      

2. Conectando dois containêres: MVC e MySQL

Para entendermos como conectar dois containêres, sendo eles uma aplicação e um banco de dados, temos que entender conceitos anteriores.

Redes definidas por Software (SDN) ou redes virtuais

Quando você inicia um container, o Docker conecta-o a uma rede virtual interna e atribui a ele um endereço IP para que ele possa se comunicar com o servidor host e com outros contêiners na mesma rede.

Para o contêiner MVC conversar com o contêiner do banco de dados MySQL, precisamos saber o endereço IP que oDocker atribuiu ao contêiner do MySQL

docker network inspect bridge
Enter fullscreen mode Exit fullscreen mode

A resposta desse comando, mostrará como o Docker configurou a rede virtual e incluirá uma seção Containers que mostra os contêiners conectados à rede e os endereços IPs que são atribuídos a eles. (IPv4Address).

        "Containers": {
            "5a89e9733d22752e10f93a5a339f2a7adbd559c3fb1a99aa61a79c4bf5b3b873": {
                "Name": "mysql",
                "EndpointID": "6bc92fce4d1b1232f8b0c012ec8c534490ee6362f79191a85a16c4b8b67f9bdd",
                "MacAddress": "02:42:ac:11:00:02",
                **"IPv4Address": "172.17.0.2/16",**
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
Enter fullscreen mode Exit fullscreen mode

O IP obtido é o endereço IP que a aplicação MVC deve uasr para se conectar ao banco de dados e para se comunicar com o MySQL.

Esse endereço pode ser fornecido ao aplicativo por meio da variável DBHOST

3. Criando o contêiner

Tendo as informações do passo anterior, podemos rodar o comando:

docker container run -d --name appmvc -p 3000:80 -e DBHOST=172.17.0.2 produtosmvc/app:2.0
Enter fullscreen mode Exit fullscreen mode

Quando o app MVC for inciado, veremos mensagens que mostram que o EF Core aplicou as migrações ao banco de dados

Rodando a aplicação em Docker Compose

Vamos adequar nossa aplicação para que ela rode em Docker Compose., a explicação será feita aos poucos e logo mais você verá o arquivo docker completo. Veja como tudo isso pode ser feito de forma mais organizada, legível e reutilizável usando o docker-compose.yml:

volumes:
 produtosdata:

networks:
 frontend:
 backend:

services:
  mysql:
   image: "mysql:5.7"
   volumes: 
   - produtosdata:/var/lib/mysql
   networks:
   - backend
   environment:
   - MYSQL_ROOT_PASSWORD=numsey
   - bind-address=0.0.0.0
Enter fullscreen mode Exit fullscreen mode

docker-compose build

Processar o arquivo de composição docker compose e verificar sintaxe:

docker-compose build
Enter fullscreen mode Exit fullscreen mode

docker-compose up

Processar o arquivo de composição docker compose e iniciar aplicação

docker-compose up
Enter fullscreen mode Exit fullscreen mode

Ao rodar o docker-compose up, a aplicação Docker gerará as instâncias configuradas pelo arquivo. Até o momento nossa aplicação está funcional, ainda precisamos definir outros serviços. Mas vamos dar uma olhada nos contêineres, volumes e networks até o momento?
docker container ps -a

CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                 NAMES
c859974a3234   mysql:5.7   "docker-entrypoint.s…"   5 minutes ago   Up 5 minutes   3306/tcp, 33060/tcp   mvc1-mysql-1
Enter fullscreen mode Exit fullscreen mode

docker network ls

NETWORK ID     NAME               DRIVER    SCOPE
acdb16b2398f   mvc1_backend       bridge    local
Enter fullscreen mode Exit fullscreen mode

docker volume ls

DRIVER    VOLUME NAME
local     mvc1_produtosdata
Enter fullscreen mode Exit fullscreen mode

Ajustando a inicialização para usar script SQL com o Docker

Vamos substituir a abordagem atual de mock de banco de dados pela execução de um script SQL para a carga inicial de dados. Em vez de incluir manualmente os dados no método Populadb.IncluiDadosDB(app), criaremos um script chamado init.sql, responsável por essa inicialização.

Os próximos passos serão então:

  • Remover o código anterior da classe Program referente à inclusão manual de dados.
  • Criar uma pasta chamada _MySQL_Init_Script na raiz do projeto e adicionar o arquivo init.sql, que conterá os comandos para a carga inicial do banco de dados.
  • Ajustar o docker-compose para mapear e executar o arquivo init.sql durante a inicialização do banco de dados.
  • Entrar no contêiner MYSQL em execução e fazer verificação

Remover o código anterior referente à inclusão manual de dados.

Dentro da classe Program, comente o código refernete ao Populadb

var builder = WebApplication.CreateBuilder(args);

[...]

app.UseStaticFiles();

// Popular banco de dados
Populadb.IncluiDadosDB(app); // comentar essa linha

[...]

app.Run();

Enter fullscreen mode Exit fullscreen mode

Criar diretório do init do MYSQL

Após ter comentado o código de chamada da calsse Populadb, queremos criar o comando de script de init.

No diretório raíz do projeto crie uma pasta chamada _MySQL_Init_Script e dentro dela adicione um arquivo chamado init.sql. Dentro desse arquivo cole o script de incialização do seu sistema.

CREATE DATABASE produtosdb;

USE produtosdb;

DROP TABLE IF EXISTS `Produtos`;

CREATE TABLE `Produtos` (
    `ProdutoId` INT AUTO_INCREMENT,
    `Nome` VARCHAR(80) NOT NULL,
    `Categoria` VARCHAR(50) NOT NULL,
    `Preco` DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (`ProdutoId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `Produtos` WRITE;

INSERT INTO `Produtos` VALUES (1, 'Caneta', 'Material Escolar', 6.50);
INSERT INTO `Produtos` VALUES (2, 'Estojo', 'Material Escolar', 3.40);
INSERT INTO `Produtos` VALUES (3, 'Borracha', 'Material Escolar', 2.50);

UNLOCK TABLES;
Enter fullscreen mode Exit fullscreen mode

Adequar arquivo Docker Compose para executar o diretório init do MYSQL

Ao configurar o Docker Compose, queremos garantir que, ao iniciar o container do MySQL, um script de inicialização (init.sql) seja executado automaticamente. Para isso, precisamos mapear um volume que copie esse script para a pasta correta dentro do container.

volumes:
 produtosdata:

networks:
 frontend:
 backend:

services:
  mysql:
   image: "mysql:5.7"
   volumes: 
   - produtosdata:/var/lib/mysql
   - ./_MySQL_Init_Script:/docker-entrypoint-initdb.d
   networks:
   - backend
   environment:
   - MYSQL_ROOT_PASSWORD=numsey
   - bind-address=0.0.0.0
Enter fullscreen mode Exit fullscreen mode

A pasta local ./_MySQL_Init_Script (do seu projeto) está sendo mapeada para /docker-entrypoint-initdb.d dentro do container. Isso significa que qualquer arquivo .sql colocado nessa pasta será executado automaticamente quando o MySQL for iniciado. O MySQL, ao iniciar no Docker, verifica se há arquivos .sql dentro da pasta /docker-entrypoint-initdb.d e os executa automaticamente. Isso é um comportamento padrão do MySQL no Docker, conforme a documentação oficial do Docker Hub para MySQL.

Então, agora, em vez de inserir dados manualmente via código (Populadb.IncluiDadosDB(app)), o Docker já faz esse trabalho ao iniciar o container, executando o script init.sql. Isso padroniza a inicialização do banco e evita a necessidade de execuções manuais.

Subindo a testando a aplicação no contêiner

1. Rodar a aplicação

Construa e inicie os contêineres em segundo plano:

docker-compose build
docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

Verifique os logs para garantir que tudo está rodando corretamente:

docker-compose logs
Enter fullscreen mode Exit fullscreen mode

Liste os contêineres ativos:

docker-compose ps
Enter fullscreen mode Exit fullscreen mode

2. Acessar o contêiner MySQL

Entre no terminal do contêiner:

docker exec -it mvc1-mysql-1 bash
Enter fullscreen mode Exit fullscreen mode

Acesse o MySQL como root:

mysql -u root -p
Enter fullscreen mode Exit fullscreen mode

3. Consultar o banco de dados

Liste os bancos disponíveis:

SHOW DATABASES;
Enter fullscreen mode Exit fullscreen mode

Acesse o banco de dados da aplicação:

USE produtosdb
Enter fullscreen mode Exit fullscreen mode

Confira as tabelas existentes:

SHOW TABLES;
Enter fullscreen mode Exit fullscreen mode

Visualize os dados da tabela Produtos:

SELECT * FROM Produtos;
Enter fullscreen mode Exit fullscreen mode

👈🏻 Voltar leitura

Top comments (0)