DEV Community

Cover image for SQL Server + Docker Compose: como criar e popular bancos de dados ao inicializar os containers
Danilo Silva
Danilo Silva

Posted on

SQL Server + Docker Compose: como criar e popular bancos de dados ao inicializar os containers

Fazer uso de containers é uma excelente abordagem para automatizar o funcionamento e a distribuição de projetos. Entretanto, se você está criando uma aplicação ASP.NET Core com Entity Framework Core, você provavelmente utilizou migrations para construir o banco de dados.

Agora, como poderíamos automatizar a inicialização dessa aplicação? Através de do Startup do SQL Server.

O SQL Server possui uma série de aplicativos funcionais em seu core. Um deles se chama sqlcmd. Esse executável permite com que você possa realizar comandos instantâneos no SQL Server sem necessariamente conectar a sua aplicação ou um gerenciador de banco de dados e está disponível para uso na imagem Docker do SQL Server.

⚠️ Atenção: Se você está utilizando um computador com processador ARM, provavelmente você não utilizou a imagem do SQL Server mas sim a imagem do Azure SQL Edge. Lembre-se que a imagem do Azure SQL não possui o executável sqlcmd e por isso, não funcionará.

Para mostrar a automatização do processo, utilizei uma API simples utilizando .NET 6 e EF Core chamada ToDo.API.

1º Passo

Iremos criar um arquivo docker-compose.yml com a imagem do SQL Server e garantir que exista um volume nela. Este volume foi feito com o bind /data -> /initdb e servirá para colocarmos um script SQL capaz de construir o banco de dados.

version: '3'
services:
  db:
    image: mcr.microsoft.com/mssql/server:2019-latest
    volumes:
      - ./data:/initdb
    container_name: todo-database
    ports:
        - 1433:1433
    environment:
      - ACCEPT_EULA=Y
      - MSSQL_SA_PASSWORD=TodoApiSqlPass123!
      - MSSQL_PID=Developer
Enter fullscreen mode Exit fullscreen mode

2º Passo

Agora, iremos criar as migrations para a nossa aplicação. Será muito importante garantir que exista apenas uma única migrations capaz de construir o banco de dados inteiro. Podemos fazer isso com o seguinte comando dentro do diretório da API:

dotnet ef migrations add initialcreate
Enter fullscreen mode Exit fullscreen mode

3º Passo

O próximo passo será criar um script com o código dessa migration. Como ela é a única, não precisamos nos preocupar em indicar qual migration criar o script. Iremos salvar esse script dentro do diretório /data, para onde apontamos o volume:

 dotnet ef migrations script -o ../data/initial.sql
Enter fullscreen mode Exit fullscreen mode

4º Passo

Agora iremos editar o arquivo /data/initial.sql para indicar o banco que estamos trabalhando. Nesta API, configurei a API para se conectar a um banco de dados chamado Todo. Portanto, no início do arquivo initial.sql, iremos colocar as seguintes instruções:

CREATE DATABASE Todo;
GO

USE Todo;
GO
Enter fullscreen mode Exit fullscreen mode

No final desse arquivo, você pode criar instruções de INSERT para popular o seu banco de dados como um seeder.

5º Passo

Podemos então criar um Dockerfile para criar uma imagem da API. Dentro do diretório da API, criaremos o seguinte:

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app

COPY . ./
RUN dotnet restore
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build-env /app/out .

ENTRYPOINT ["dotnet", "ToDo.Api.dll"]
Enter fullscreen mode Exit fullscreen mode

Esse é o modelo de Dockerfile mais simples para aplicações ASP.NET e você pode construir o mesmo da maneira desejada.

6º Passo

Agora podemos finalmente adicionar o serviço da API dentro do docker-compose.yml como um serviço dependente do banco de dados:

api:
    container_name: todo-api
    build: ./ToDo.Api
    ports:
      - 5501:5501
    depends_on:
      - db
Enter fullscreen mode Exit fullscreen mode

7º Passo

Nosso último passo e mais importante. Popular o banco de dados. Para isso, iremos criar um shell script. Mas vamos entender como funciona o processo.

Dentro do container do banco de dados, temos o executável sqlcmd como mencionado anteriormente, localizado em /opt/mssql-tools/bin/sqlcmd.

Este executável pode ser chamado com os seguintes parâmetros:

sqlcmd -S server -U usuario -P senha -d banco -i script.sql
Enter fullscreen mode Exit fullscreen mode

Onde:
-S: corresponde ao servidor de conexão com o SQL Server;
-U: corresponde ao usuário;
-P: corresponde à senha;
-d: corresponde ao banco onde será feita a conexão;
-i: corresponde ao script SQL que será executado.

Para a nossa aplicação, podemos chamar então o sqlcmd com um docker exec da seguinte maneira:

 docker exec todo-database /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P TodoApiSqlPass123! -d master -i initdb/initial.sql
Enter fullscreen mode Exit fullscreen mode

Nesse comando, executamos dentro do container todo-database, nomeado no arquivo docker-compose.yml passando o server, usuário e senha também configurados no mesmo. Para o banco de dados, selecionamos master, pois é o banco de maior acesso em um server novo, e ainda não possuímos o nosso banco.

Quanto ao script, passamos o diretório /initdb que criamos no volume do container junto do arquivo initial.sql criado pela migration.

8º Passo

Entretanto, para deixar algo mais automatizado, iremos criar um script shell para executar tal comando. Portanto, iremos criar um arquivo chamado init.sh na raiz do repositório com o seguinte conteúdo:

#!/bin/bash
while :
do 
  PORTS=`lsof -i:1433`
  echo $PORTS
  if ["$PORTS" = '']; then
    echo 'Database is down, trying to reconnect to run initial migrations!'
    sleep 5s
  else
    docker exec todo-database /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P TodoApiSqlPass123! -d master -i initdb/initial.sql; echo "All done!";
    break
  fi
done

echo "Successfuly executed!"
Enter fullscreen mode Exit fullscreen mode

⚠️ Atenção: Este script está preparado para computadores Linux ou Mac mas você pode adaptar para Windows com o Powershell.

Como funciona esse script?

Primeiramente, como o container de um banco de dados SQL Server pode ser demorado a subir, temos que garantir algo para validar se o mesmo está operacional. Esse container está expondo a porta 1433 e com ela, iremos verificar se o banco está operacional.

O script roda o comando lsof -i:1433 para verificar os processo executando nessa porta. Se não encontrar nenhum processo usando a porta, o mesmo irá imprimir a mensagem Database is down, trying to reconnect to run initial migrations! e irá esperar por 05 segundos para tentar novamente.

Caso o mesmo encontre o container do banco de dados, irá executar o sqlcmd e ao fim do processo, imprimir as mensagens All done! e Successfuly executed!.

Para facilitar a visualização, você pode clonar o repositório completo.

Danilo Silva

Desenvolvedor de software experiente em boas práticas, clean code e no desenvolvimento de software embarcado e de integração com hardwares de controle e telecomunicação.

Linkedin
Github
Twitter
E-mail

Top comments (1)

Collapse
 
msc2020 profile image
msc2020

Bom trabalho. Ficou didático o passo a passo.