Docker & Microsoft SQL Server(初期データ登録まで)

# はじめに
業務でDockerコンテナにMicrosoft SQL Server環境を構築する機会があり、ハマったポイントなどを残しておきます。
他にまとめている記事を拝見しましたが、上手く動かないものが多かったため、丁寧にまとめていきます。ホストはApple M2ですが、他のOSでも気にしないで大丈夫です。

# ディレクトリ構成
リポジトリは[こちら](https://github.com/75ks/docker-mssql)に作成しています。
`docker-compose.yml`以外のディレクトリ名やファイル名に決まりはないので、お好きなように変更していただいて問題ないです。
※`docker-compose.yml`はそのままの名前でないと動きませんので、ご注意ください。
```
┝ docker
    ┝ init.db.d
        ┝ entrypoint.sh
        ┝ init.sql
    ┝ docker-compose.yml
```

# 解説
### init.sql
```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
```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つです。
1. `docker-compose.yml`にコマンドを全て記述すると見づらくなるから
    `docker-compose.yml`は同時に複数のコンテナを操作するためのファイルで、設計書のような役割を果たすことから、可読性を保つ必要があります。
2. 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
```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をお使いの方は、ここは飛ばしてください。

画面右上の歯車から設定を開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616933/6cb11ad0-6465-a634-a064-308a5d6f0906.png)
Use Rosetta for x86/amd64 emulation on Apple Siliconのチェックを付ける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616933/31ba76e5-eb8a-0bb4-2f6a-402751024c58.png)
これだけでOKです！

### コンテナ作成 & コンテナ起動
ここからはどのOSでも共通です！
下記を実行します。
```sh
# docker-compose.ymlが格納されているディレクトリに移動
cd docker-mssql/docker
# docker-compose.ymlを元にコンテナを作成 & 起動
docker-compose -p [お好きな名前] up -d
```
こうすることで、コンテナが作成され、起動されます。
赤枠が[お好きな名前]で指定した名前になります。
私は`docker-compose -p docker-mssql up -d`で実行したため、`docker-mssql`という名前になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616933/41bc766f-2a7b-3162-66fa-1c7406f6fe2a.png)

# さいごに
個人的にはSQL Serverを使う機会は少ないのかなと思っていますが、使用するプロジェクトもまだ存在するため、参考になれば嬉しいです！