Featured image of post Como executar scripts na inicialização do container SQL Server

Como executar scripts na inicialização do container SQL Server

Criando uma imagem de container personalizada que executa scripts após a inicialização

Introdução Link to this section

Ao contrário do MySql e do Postgres, que executam scripts no diretório /docker-entrypoint-initdb.d/ após a inicialização, a imagem do container SQL Server não tem uma opção para executar scripts na inicialização.

Neste post, mostrarei como criar uma imagem de container personalizada que aguarda a inicialização do SQL Server e executa um script pré-configurado. Além disso, mostrarei como usar esta imagem personalizada para executar o SQL Server no Testcontainers.

⚠️
A partir do SQL Server 2022 CU 14 (e SQL Server 2019 CU 28), a imagem do container moveu o sqlcmd de /opt/mssql-tools/bin para /opt/mssql-tools18/bin e ativou criptografia obrigatória por padrão. Os exemplos abaixo foram atualizados para usar o novo caminho e a flag -C para confiar no certificado do servidor. Se você estiver usando uma imagem anterior ao CU 14, use /opt/mssql-tools/bin/sqlcmd sem -C.

Veja GitHub Issue #892 para detalhes.

Como executar scripts na inicialização do SQL Server Link to this section

A documentação da imagem Docker do SQL Server aponta para este código como um exemplo de como executar scripts em sua inicialização, mas o script é antigo e não funciona.

Abri um Pull Request com uma versão corrigida. Ele nunca foi mesclado, mas as correções estão incorporadas na abordagem abaixo.

Abaixo, explicarei cada um dos arquivos.

1 - Dockerfile Link to this section

O Dockerfile começa a partir da imagem do SQL Server 2022, copia todos os arquivos para /tmp/initscripts, e define o ponto de entrada do container para entrypoint.sh.

FROM mcr.microsoft.com/mssql/server:2022-latest

# Agrupa a origem da configuração
COPY . /tmp/initscripts

ENTRYPOINT ["/tmp/initscripts/entrypoint.sh"]

2 - entrypoint.sh Link to this section

Este script bash inicia o configure-db.sh em segundo plano, então inicia o SQL Server em primeiro plano. Usar exec garante que o processo do SQL Server substitua o shell como PID 1, recebendo sinais de parada do Docker diretamente.

#!/bin/bash

# Inicia o script para criar o DB e o usuário
/tmp/initscripts/configure-db.sh &

# Inicia o SQL Server (exec substitui o shell pelo sqlservr como PID 1)
exec /opt/mssql/bin/sqlservr

3 - configure-db.sh Link to this section

Esta é a parte mais importante. O script espera até 60 segundos (variável TRIES) pelo SQL Server iniciar e todos os bancos alcançarem o estado ONLINE, então executa setup.sql. Em caso de sucesso, loga Configuration completed.; em caso de timeout, sai com código 1.

ℹ️
A mensagem Configuration completed é importante porque podemos usá-la para esperar que os scripts sejam concluídos antes de acessar o banco de dados. Usarei isso na próxima seção ao iniciar o container do Testcontainers.
#!/bin/bash

# Chama o SQLCMD para verificar se os bancos de dados do sistema e do usuário retornam "0", o que significa que todos os bancos de dados estão em um estado "online",
# então execute o script de configuração (setup.sql)
# https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-databases-transact-sql?view=sql-server-2017 

TRIES=60
DBSTATUS=1
ERRCODE=1
i=0

while [[ $DBSTATUS -ne 0 ]] && [[ $i -lt $TRIES ]]; do
	i=$((i+1))
	DBSTATUS=$(/opt/mssql-tools18/bin/sqlcmd -h -1 -t 2 -C -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -Q "SET NOCOUNT ON; Select COALESCE(SUM(state), 0) from sys.databases") || DBSTATUS=1
	
	sleep 1s
done

if [ $DBSTATUS -ne 0 ]; then 
	echo "SQL Server levou mais de $TRIES segundos para iniciar ou um ou mais bancos de dados não estão em um estado ONLINE"
	exit 1
fi

# Executa o script de configuração para criar o DB e o esquema no DB
echo "Executando script de configuração..."

/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -d master -i setup.sql

echo "Configuration completed."

4 - setup.sql Link to this section

Este é o script que será executado. Aqui estou apenas criando o banco de dados.

CREATE DATABASE [MyDatabase]
GO

5 - Construindo a imagem do container Link to this section

Agora, temos que construir a imagem do container a partir do Dockerfile que criamos. Neste exemplo, estou nomeando a imagem mydatabase-sqlserver:

docker build -t mydatabase-sqlserver .

6 - Iniciando o container Link to this section

Finalmente, podemos executar o container com docker run:

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 mydatabase-sqlserver

Ou crie um arquivo compose.yaml:

services:
  sqlserver:
    build: .
    environment:
      MSSQL_SA_PASSWORD: "yourStrong(!)Password"
      ACCEPT_EULA: "Y"
      MSSQL_PID: "Developer"
    ports:
      - "1433:1433"

E execute:

docker compose up

Logs do container

Código fonte deste exemplo Link to this section

Executando o script de configuração no Testcontainers Link to this section

Testcontainers é uma biblioteca que fornece instâncias leves e descartáveis de bancos de dados, navegadores web selenium ou qualquer coisa que possa ser executada em um container. Essas instâncias podem ser especialmente úteis para testar aplicativos em relação a dependências reais, como bancos de dados, que podem ser criados e descartados após os testes.

Expliquei sobre Testcontainers neste post.

Sugiro fortemente a leitura, pois aqui, mostrarei apenas os detalhes de como executar o SQL Server com os scripts de configuração no Testcontainers.

Iniciando o container Link to this section

Ao iniciar o container, precisamos substituir duas configurações no MsSqlBuilder:

  • Com o método WithImage, substituímos a imagem padrão do MsSqlBuilder pela nossa imagem personalizada (mydatabase-sqlserver);
  • Com o método WaitStrategy, substituímos a estratégia de espera do MsSqlBuilder por UntilMessageIsLogged. Isso fará com que o método StartAsync fique bloqueado até que a mensagem Configuration completed seja registrada nos logs do container, garantindo que nosso script seja executado antes de usarmos o container.
1
2
3
4
5
6
7
var MsSqlContainer = new MsSqlBuilder()
    .WithImage("mydatabase-sqlserver")
    .WithPassword("SqlServer2022!Password")    
    .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Configuration completed"))
    .Build();

await MsSqlContainer.StartAsync();
ℹ️
A classe DockerContainer também fornece ExecScriptAsync para executar scripts dentro de um container em execução, mas a ideia aqui é ter um script que execute automaticamente — tanto do Testcontainers quanto ao executar o container diretamente (ex.: durante desenvolvimento local).

Código fonte deste exemplo Link to this section

💬 Like or have something to add? Leave a comment below.
Ko-fi
GitHub Sponsor
Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy