はじめに
業務でDockerコンテナにMicrosoft SQL Server環境を構築する機会があり、ハマったポイントなどを残しておきます。
他にまとめている記事を拝見しましたが、上手く動かないものが多かったため、丁寧にまとめていきます。ホストはApple M2ですが、他のOSでも気にしないで大丈夫です。
ディレクトリ構成
リポジトリはこちらに作成しています。
docker-compose.yml
以外のディレクトリ名やファイル名に決まりはないので、お好きなように変更していただいて問題ないです。
※docker-compose.yml
はそのままの名前でないと動きませんので、ご注意ください。
┝ docker
┝ init.db.d
┝ entrypoint.sh
┝ init.sql
┝ docker-compose.yml
解説
init.sql
-- データベースが存在しない場合のみ作成
IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = 'test_db')
BEGIN
CREATE DATABASE test_db;
END
GO
USE test_db;
GO
-- テーブルが存在しない場合のみ作成
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'user' AND type = 'U')
BEGIN
CREATE TABLE user (
[id] INT NOT NULL IDENTITY(1,1)
, [name] VARCHAR(MAX)
, PRIMARY KEY (ID)
);
INSERT INTO user (name) VALUES('taro');
INSERT INTO user (name) VALUES('jiro');
INSERT INTO user (name) VALUES('saburo');
END
GO
こちらはシンプルで、test_db
というデータベースが存在しない場合のみtest_db
を作成し、user
というテーブルが存在しない場合のみuser
を作成し、初期データを登録します。
このファイルはコンテナ起動時に毎回流れるため、IF NOT EXISTS
を使って存在チェックを行います。
entrypoint.sh
#!/bin/bash
# SQL Serverをフォアグラウンドで実行
echo "Waiting for SQL Server to start..."
/opt/mssql/bin/sqlservr & MSSQL_PID=$!
# SQL Serverの起動を待機
while ! /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Passw0rd -Q "SELECT 1" > /dev/null 2>&1; do
sleep 1
done
echo "SQL Server started."
# 初期化用SQLを実行
echo "Initializing database..."
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Passw0rd -i /docker-initdb.d/init.sql
echo "Database initialized."
# バックグラウンドで実行中のSQL Serverのプロセスを待機
wait $MSSQL_PID
こちらはSQL Server起動 & 初期化用SQL実行のためのシェルスクリプトです。
シェルスクリプトにまとめる理由は2つです。
-
docker-compose.yml
にコマンドを全て記述すると見づらくなるから
docker-compose.yml
は同時に複数のコンテナを操作するためのファイルで、設計書のような役割を果たすことから、可読性を保つ必要があります。 - SQL Server起動後に初期化用SQLを実行する必要があるから
SQL Serverは起動コマンドを実行してから実際に起動するまで遅延があるため、SQL Serverが起動したことを確認してから、初期化用SQLを流さないとエラーになります。
/opt/mssql/bin/sqlservr
はSQL Serverを起動させるコマンドです。
MSSQL_PID=$!
は直前にバックグラウンドで実行したSQL Server起動プロセスIDをMSSQL_PID
変数に保存しています。
最後のwait $MSSQL_PID
でバックグラウンドで実行中のSQL Server起動プロセスを待機させ、終了させないようにすることができます。
! /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Passw0rd -Q "SELECT 1" > /dev/null 2>&1;
はSELECT 1
を実行しています。
SQL Serverが起動前であれば失敗するので、1秒待機して再度SELECT 1
を実行します。
ここでは、成功か失敗かを確認できればよいため、> /dev/null 2>&1;
を使って出力結果を表示させないようにしています。
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Passw0rd -i /docker-initdb.d/init.sql
はコメントの通り、/docker-initdb.d/init.sql
を実行しているだけのコマンドです。
echo "Waiting for SQL Server to start..."
、echo "SQL Server started."
、echo "Initializing database..."
、echo "Database initialized."
はシェルスクリプトがどこまで進んだかをわかりやすくするためのものです。
docker-compose.yml
version: '3'
services:
mssql:
image: mcr.microsoft.com/mssql/server:2022-latest # イメージを直接指定
user: root # ルートユーザーで実行
container_name: mssql # コンテナ名を指定
environment: # 環境変数を設定
- ACCEPT_EULA=Y # エンドユーザーライセンス契約に同意
- SA_PASSWORD=Passw0rd # パスワードを設定
- MSSQL_PID=Express # エディションを設定
- MSSQL_TCP_PORT=1433 # 使用するポートを設定
- MSSQL_LCID=1041 # 日本語に設定
- TZ="Asia/Tokyo" # タイムゾーン設定
- Japanese_CI_AS=Japanese_CI_AS # 照合順序を指定
ports: # ポート番号を指定(ホスト:コンテナ)
- 1433:1433
volumes: # マッピングを指定
- ./initdb.d:/docker-initdb.d # 初期化用SQLとSQL Server起動スクリプト
- mssql_data:/var/opt/mssql/data # dataの永続化
- mssql_log:/var/opt/mssql/log # logの永続化
- mssql_secrets:/var/opt/mssql/secrets # secretsの永続化
command: ["/bin/bash", "-c", "chmod +x /docker-initdb.d/entrypoint.sh && /docker-initdb.d/entrypoint.sh"] # SQL Server起動スクリプトを実行
volumes:
mssql_data:
mssql_log:
mssql_secrets:
こちらでSQL Serverコンテナの様々な設定をしています。
詳細はコメントの通りですが、ポイントはdocker-compose.yml
でentrypoint.sh
を実行し、実行されたentrypoint.sh
がinit.sql
を実行しているというところです。
docker-compose.yml
↓
entrypoint.sh
↓
init.sql
↓
初期データ登録
実践
Appleシリコン搭載Macに必要な設定
※Appleシリコン以外のOSをお使いの方は、ここは飛ばしてください。
画面右上の歯車から設定を開く
Use Rosetta for x86/amd64 emulation on Apple Siliconのチェックを付ける
これだけでOKです!
コンテナ作成 & コンテナ起動
ここからはどのOSでも共通です!
下記を実行します。
# docker-compose.ymlが格納されているディレクトリに移動
cd docker-mssql/docker
# docker-compose.ymlを元にコンテナを作成 & 起動
docker-compose -p [お好きな名前] up -d
こうすることで、コンテナが作成され、起動されます。
赤枠が[お好きな名前]で指定した名前になります。
私はdocker-compose -p docker-mssql up -d
で実行したため、docker-mssql
という名前になっています。
さいごに
個人的にはSQL Serverを使う機会は少ないのかなと思っていますが、使用するプロジェクトもまだ存在するため、参考になれば嬉しいです!