Rodando o SEU de banco de dados num container!
Tags: docker, github, database, dotnet
Published: 2022-06-03 03:24:25

> "Vocês não me escolheram, mas eu os escolhi para irem e darem fruto, fruto que permaneça, a fim de que o Pai lhes conceda o que pedirem em meu nome.
Este é o meu mandamento: amem-se uns aos outros".
"Se o mundo os odeia, tenham em mente que antes odiou a mim."
\- João 15.16-18

Quem nunca precisou subir o próprio banco de dados para testar as alterações feitas numas tabelas? Melhor ainda manter isso versionado e rodando em container. Hoje vou compartilhar com vocês como fazer isso.

## Sumário
- [Criando o projeto](#criando-o-projeto)
- [Dockerfile](#dockerfile)
- [Rodando](#rodando)
- [Versionando](#versionando)
- [Github Actions](#github-actions)
- [Fontes](#fontes)

### Criando o projeto
[Topo](#sumário)

Vamos iniciar criando um projeto para o DB, esse projeto será responsável por verificar se seu banco está tudo certo. Mas antes devemos instalar o template [MSBUild.Sdk.Sqlproj](https://github.com/rr-wfm/MSBuild.Sdk.SqlProj):

```bash
> dotnet new --install MSBuild.Sdk.SqlProj.Templates
> dotnet new sqlproj -n MyDb -o my-db/src -s Sql130
> cd my-db
> dotnet sln -n MyDb
> dotnet sln add src #se exitir um .sqlproj pode adicionar também
```

Perceba que foi criado um arquivo chamado `MyDb.csproj`, ele será responsável por gerenciar o Pre e PostDeploy e "compilar" todo o seu banco de dados. Vamos criar umas tabelas, depois voltaremos para mexer neste arquivo.

```bash
> mkdir src/Schemas
> mkdir src/Tables

> 'CREATE SCHEMA [Bible]' > src/Schemas/Bible.sql

> 'CREATE TABLE [Bible].[Verse](
>>  [VerseId] INT,
>>  [Content] VARCHAR(MAX)
>> )
>> ' > src/Tables/Verse.sql

> dotnet build
```

Ao fazer isso, será concluído o build com sucesso. Ao criar um script incorreto, será lançado o erro no seu terminal:
```bash
> 'CREATE SCHEMA [Pimba] errrrou' > src/Schemas/Pimba.sql
> dotnet build
EXEC : error SQL46010: Incorrect syntax near errrrou. [...]
```

Agora vamos criar o script de `PostDeploy` e adaptar o arquivo `MyDb.csproj` para buscar as `Seeds`:

```bash
> mkdir src/PostDeploy
> '' > src/PostDeploy/SeedTables.sql
```

`SeedTables.sql`:
```sql
PRINT 'Merging [Bible].[Verse]'

MERGE INTO [Bible].[Verse] AS TARGET
USING 
(
VALUES
    (1, 'Joao 15.16'),
    (2, 'Joao 15.17'),
    (3, 'Joao 15.18')
)
AS SOURCE ([VerseId], [Content])
ON TARGET.[VerseId] = SOURCE.[VerseId]
WHEN NOT MATCHED BY TARGET THEN
    INSERT ([VerseId], [Content])
    VALUES ([VerseId], [Content])
WHEN MATCHED THEN
    UPDATE SET
        TARGET.[Content] = SOURCE.[Content];
```

Adaptando o `MyDb.csproj`:
```xml
<Project Sdk="MSBuild.Sdk.SqlProj/2.0.0">
    <!-- [...] -->

    <!-- Adicione este bloco para utilizar o PostDeploy -->
    <ItemGroup>
        <Content Include=".\**\*.sql" />
        
        <!--Remove all Seed files from the build, but add them as post-deploy https://github.com/rr-wfm/MSBuild.Sdk.SqlProj/issues/103 -->
        
        <Content Remove="..\**\Seed\*.sql" />
        <Content Remove=".\PostDeploy\SeedTables.sql" />
        
        <PostDeploy Include=".\PostDeploy\SeedTables.sql" />
    </ItemGroup>
</Project>
```

Vamos testar:

```bash
> dotnet build
[...]
Build succeeded.
    0 Warning(s)
    0 Error(s)
```

### Dockerfile
[topo](#sumário)

Agora com suas tabelas funcionando, vamos rodar em um container:

`Dockerfile` (UTF-8):
```docker
ARG DBNAME=MyDb
ARG PASSWORD=C0mposed

FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /src

COPY src .
RUN dotnet build ./MyDb.csproj -c Release -o /app/build

FROM mcr.microsoft.com/mssql/server:2017-latest AS final

# Install Unzip
RUN apt-get update \
    && apt-get install unzip -y

# Install SQLPackage for Linux and make it executable
RUN wget -progress=bar:force -q -O sqlpackage.zip https://go.microsoft.com/fwlink/?linkid=2165213 \
    && unzip -qq sqlpackage.zip -d /opt/sqlpackage \
    && chmod +x /opt/sqlpackage/sqlpackage

# Add the DACPAC to the image
COPY --from=build /app/build/MyDb.dacpac /tmp/db.dacpac

# Configure external build arguments to allow configurability.
ARG DBNAME
ARG PASSWORD

# Configure the required environmental variables
ENV ACCEPT_EULA=Y
ENV SA_PASSWORD=$PASSWORD

# Launch SQL Server, confirm startup is complete, deploy the DACPAC, then terminate SQL Server.
# See https://stackoverflow.com/a/51589787/488695
RUN ( /opt/mssql/bin/sqlservr & ) | grep -q "Service Broker manager has started" \
    && /opt/sqlpackage/sqlpackage /a:Publish /tsn:. /tdn:$DBNAME /tu:sa /tp:$SA_PASSWORD /sf:/tmp/db.dacpac \
    && rm /tmp/db.dacpac \
    && pkill sqlservr
```

Resumidamente estamos criando o nome da instância do banco de dados e fazendo o deploy do DACPAC que geramos. Para mais detalhes os links estarão no final do artigo.

Agora vamos ver se está criando o container corretamente:

```bash
> docker build -t my-db .
[+] Building 57.5s (15/15) FINISHED [...]
```

### Rodando
[topo](#sumário)

Com tudo preparado, só falta executar o container:

```bash
> docker run --name testando -p 1433:1433 my-db
SQL Server 2019 will run as non-root by default.
This container is running as user root.
Your master database file is owned by root. [...]
```

Para conectar utilize as seguintes informações(SQL Login):
user: sa
pwd: C0mposed
server: localhost, 1433

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q485t8mdsnmmcireycos.png)

Os próximos passos serão versionar e configurar o Github Actions.

### Versionando
[topo](#sumário)

Crie o repositório normalmente, eu criarei no Github:

```
git init
git remote add origin https://github.com/confianodev/my-db-container
git branch -M main
git fetch
git pull origin main
git add .
git commit -m ":tada: a better commit message"
git push --set-upstream origin main
git checkout -b dev
```

Dessa forma, qualquer pessoa que for evoluir as tabelas, views, schemas, indexes, etc. Devem conseguir versionar, compilar e executar o banco de dados.

### Github Actions
[topo](#sumário)

Agora precisamos automatizar isso para que ninguém fique refém dos testes locais. Lembre-se de criar a secret PACKAGES_TOKEN na organização ou no próprio repositório.

```bash
mkdir .github/workflows
'' > build-homolog.yml
'' > release-homolog.yml
'' > rollback-homolog.yml
```

Conteúdo dos arquivos: 
https://github.com/confianodev/my-db-container/blob/main/.github/workflows
Lembre-se de trocar o meu usuário(confianodev) pelo seu.


Feito isso, basta fazer o commit e seu container está pronto para ser compartilhado!

```bash
git add .
git commit -m ":wrench: github actions"
git push --set-upstream origin dev
```

E é isso pessoal, a ideia não é fazer deploy em produção, mas sim facilitar os testes e evitar perder tempo com scripts no banco de homologação. Espero que tenha ajudado.

Fiquem com a graça e paz do nosso Senhor Jesus Cristo!

### Fontes
[topo](#sumário)

https://github.com/rr-wfm/MSBuild.Sdk.SqlProj
https://itnext.io/how-to-build-and-run-a-sql-container-using-a-dacpac-file-c7b0d30f6255