SQLServer
docker
MSSQL
intra-mart

Microsoft SQL Server on Linux 2017 の Docker を作成する

前置き

SQL Server on Linux 2017 が公開されたので、早速 Docker を作成していきます。
intra-mart の検証用として利用したいので、Dockerfile の最後で検証用ユーザの作成も行います。
権限の付与や、intra-mart が要求する照合順序も設定します
これによって、MS 公式の Docker イメージと異なり、docker run 後何も考えずにすぐに検証用として利用できるような SQL Server の環境が作れるようになります。

注意

SQL Server on Linux は DTC に対応していないので DTC が必要となる分散トランザクションは利用できません。
SQL Server Linux: Distributed transactions requiring the Microsoft Distributed Transaction Coordinator service are not supported on SQL Server running on Linux. SQL Server to SQL Server distributed transactions are supported. – CLARIFIED!!!

タイムゾーンに関する脱線

Postfix の Docker の記事で述べましたが自分が Docker を作成した段階ではタイムゾーンの設定方法により SQL Server のタイムゾーンに差異がでました。(やり方によっては Asia/Tokyo にならなかった)
詳しくはこちらを参照してください。
本記事ではちゃんと Asia/Tokyo になるように Dockerfile を作成します。

Docker ベースイメージ(CentOS 7.4)

いつものようにベースイメージから作ります。
本記事執筆時点ではベースは CentOS のバージョン 7.4 です。
OS のバージョンを固定したければ centos:7.4.1708 などのタグがあるのでそちらを使う手もあります。

Dockerfile
FROM centos:centos7

EXPOSE 22

ENV DEBIAN_FRONTEND noninteractive

# yum
RUN yum -y update && yum clean all
RUN yum -y install dbus systemd

# locale
RUN yum reinstall -y glibc-common
RUN localedef -i ja_JP -f UTF-8 ja_JP.utf8
RUN touch /etc/sysconfig/i18n
RUN echo 'LANG="ja_JP.UTF-8"' >> /etc/sysconfig/i18n
ENV LANG ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja

# timezone
RUN yum install -y tzdata
RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock
RUN echo 'UTC=false' >> /etc/sysconfig/clock
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# tools
RUN yum groupinstall -y 'Development Tools'
RUN yum install -y --enablerepo centosplus wget curl vim emacs tar unzip mlocate perl ssh openssh-server openssl-devel

# root passwd
RUN bash -c 'echo "root:password" | chpasswd'

# ssh
RUN sed -i -e "s/#PasswordAuthentication yes/PasswordAuthentication yes/g" /etc/ssh/sshd_config
RUN sed -i -e "s/#PermitRootLogin yes/PermitRootLogin yes/g" /etc/ssh/sshd_config
RUN sed -i -e "s/UsePAM yes/UsePAM no/g" /etc/ssh/sshd_config

RUN updatedb

CMD /sbin/init

locate コマンドが便利なので Dockerfile の最後に必ず入れるようにしてます。
やってるのはロケールとタイムゾーンと ssh を設定しているだけです。

Docker SQL Server on Linux 2017

さて次は SQL Server on Linux 2017 の Docker です。
先ほどの CentOS 7 をベースイメージとして利用します。

Dockerfile 以外にも以下のファイルを用意します。

  • create_user.sh
  • create_user.sql
  • Dockerfile
  • drop_user.sh
  • drop_user.sql
  • run_server.sh
  • setup_sqlserver.sh

まずは Dockerfile から。

Dockerfile
FROM mycentos:7.4

RUN yum -y update && yum clean all
RUN yum -y upgrade && yum -y update && yum clean all
RUN yum -y install --setopt=protected_multilib=false epel-release
RUN yum -y install sudo multitail ncurses-devel ncurses-static ncurses-term

ADD create_user.sql /create_user.sql
ADD drop_user.sql /drop_user.sql

ADD create_user.sh /create_user.sh
ADD drop_user.sh /drop_user.sh
RUN chmod +x /create_user.sh
RUN chmod +x /drop_user.sh
RUN ln -s /create_user.sh /root/create_user.sh
RUN ln -s /drop_user.sh /root/drop_user.sh

ADD run_server.sh /run_server.sh
RUN chmod +x /run_server.sh

# SQL Server on 2017 セットアップ
ADD setup_sqlserver.sh /setup_sqlserver.sh
RUN chmod +x /setup_sqlserver.sh
RUN /setup_sqlserver.sh
RUN rm -f /setup_sqlserver.sh

RUN updatedb

CMD /run_server.sh

SQL Server のセットアップ等、複雑な物はシェルスクリプトに逃がし、Dockerfile からはそれを実行するようにします(Dockerfile がシンプルになる、かつイメージ容量の削減にもつながります)
ログを tail するのに multitail を利用するため、yum install しています。

続いて、SQL Server のセットアップです。
セットアップ手順やシェルスクリプト内で設定している変数はこちらを参考にしました。
docker build 内では systemctl が使えないので pkill でプロセスを殺したりと苦労してます。
いい方法ないですかね・・・。

setup_sqlserver.sh
#!/bin/sh

curl -o /etc/yum.repos.d/msprod.repo https://packages.microsoft.com/config/rhel/7/prod.repo
curl -o /etc/yum.repos.d/mssql-server.repo https://packages.microsoft.com/config/rhel/7/mssql-server-2017.repo
yum update -y && yum clean all
yum install -y mssql-server

# サイレントインストールするためにいくつか設定
# ライセンス同意
export ACCEPT_EULA='Y'
# sa ユーザのパスワード
export MSSQL_SA_PASSWORD='password'
# Developer エディション
export MSSQL_PID='Developer'
# 言語は日本語
export MSSQL_LCID=1041
# デフォルトの照合順序
export MSSQL_COLLATION='Japanese_XJIS_100_CS_AS_KS_WS'
# メモリ上限
export MSSQL_MEMORY_LIMIT_MB=2048
# 使用するポート
export MSSQL_TCP_PORT=1433
# リッスンアドレス
export MSSQL_IP_ADDRESS='0.0.0.0'
# 可用性グループを有効
export MSSQL_ENABLE_HADR=1
# SQL Server エージェントを利用
export MSSQL_AGENT_ENABLED=true
/opt/mssql/bin/mssql-conf setup
# カスタマーフィードバックを OFF
/opt/mssql/bin/mssql-conf set telemetry.customerfeedback false

# systemctl restart mssql-server.service が使えないので pkill と /bin/sqlserver で代用
pkill sqlservr
sudo -EH -u mssql /bin/bash -c "/opt/mssql/bin/sqlservr >> /var/opt/mssql/log/sqlservr.log 2>&1 &"

yum remove unixODBC-utf16 unixODBC-utf16-devel
export ACCEPT_EULA='Y'
yum install -y mssql-tools unixODBC-devel
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> /root/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> /root/.bashrc
source /root/.bashrc

# intra-mart 検証用の DB, ユーザを作成。不要であればコメントアウトするだけで OK です
/create_user.sh
# systemctl stop mssql-server.service が使えないので pkill で代用
pkill sqlservr

続いて docker run 時に実行する SQL Server on Linux 2017 を起動するためのスクリプトです。
愚直に docker run した場合 systemctl が使えないため、sshd や SQL Server の起動を直接行っています。

run_server.sh
# systemctl restart sshd.service
# systemctl restart mssql-server.service
ssh-keygen -A
/usr/sbin/sshd
sudo -EH -u mssql /bin/bash -c "/opt/mssql/bin/sqlservr >> /var/opt/mssql/log/sqlservr.log 2>&1 &"

touch /var/opt/mssql/log/sqlservr.log
touch /var/opt/mssql/log/errorlog

multitail -M 0 --follow-all --retry-all /var/opt/mssql/log/sqlservr.log -I /var/opt/mssql/log/errorlog
# multitail を利用したくない場合、以下で代用可
# tail -F /var/opt/mssql/log/sqlservr.log /var/opt/mssql/log/errorlog

続いて、intra-mart 用 DB とユーザの作成です。
これを docker build 時に行っておくことで、docker run 後にすぐ利用できるようになります。

以下のユーザを作成しています。
ユーザ名:imart
パスワード:imart

以下の DB を作成しています。

  • imart
  • iap_db
  • default

すべての DB の照合順序は Japanese_XJIS_100_CS_AS_KS_WS です。
すべての DB に対して acceldocuments スキーマも作成しています。
すべての DB に対して READ_COMMITTED_SNAPSHOT を設定しています。

create_user.sh
#!/bin/sh

sqlcmd -S localhost -U sa -P 'password' -i /create_user.sql
create_user.sql
-- imart user
USE [master]
GO
CREATE LOGIN [imart] WITH PASSWORD=N'imart', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO

-- imart
CREATE DATABASE [imart]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'imart', FILENAME = N'/var/opt/mssql/data/imart.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'imart_log', FILENAME = N'/var/opt/mssql/data/imart_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
 COLLATE Japanese_XJIS_100_CS_AS_KS_WS
GO
ALTER DATABASE [imart] SET READ_COMMITTED_SNAPSHOT ON;
GO

USE [imart]
GO
EXEC dbo.sp_changedbowner @loginame = N'imart', @map = false
GO

-- iap_db
CREATE DATABASE [iap_db]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'iap_db', FILENAME = N'/var/opt/mssql/data/iap_db.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'iap_db_log', FILENAME = N'/var/opt/mssql/data/iap_db_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
 COLLATE Japanese_XJIS_100_CS_AS_KS_WS
GO
ALTER DATABASE [iap_db] SET READ_COMMITTED_SNAPSHOT ON;
GO

USE [iap_db]
GO
EXEC dbo.sp_changedbowner @loginame = N'imart', @map = false
GO

-- imart default database
ALTER LOGIN [imart] WITH DEFAULT_DATABASE=[iap_db], DEFAULT_LANGUAGE=[日本語], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO

-- default
CREATE DATABASE [default]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'default', FILENAME = N'/var/opt/mssql/data/default.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'default_log', FILENAME = N'/var/opt/mssql/data/default_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB )
 COLLATE Japanese_XJIS_100_CS_AS_KS_WS
GO
ALTER DATABASE [default] SET READ_COMMITTED_SNAPSHOT ON;
GO

USE [default]
GO
EXEC dbo.sp_changedbowner @loginame = N'imart', @map = false
GO

-- acceldocuments
USE [imart]
GO
CREATE SCHEMA [acceldocuments] AUTHORIZATION [db_owner]
GO

USE [iap_db]
GO
CREATE SCHEMA [acceldocuments] AUTHORIZATION [db_owner]
GO

USE [default]
GO
CREATE SCHEMA [acceldocuments] AUTHORIZATION [db_owner]
GO

続いて、ユーザを drop する処理です。
docker run し直せばいい話ですが、docker は起動したままでユーザを作り替えたいという場合にご利用ください。

drop_user.sh
#!/bin/sh

sqlcmd -S localhost -U sa -P 'password' -i /drop_user.sql
drop_user.sql
USE [master]
GO

DROP DATABASE [default]
GO
DROP DATABASE [iap_db]
GO
DROP DATABASE [imart]
GO

起動

docker run -it -p 0.0.0.0:1433:1433 -p 0.0.0.0:2222:22 mysqlserver_on_linux:2017

データ永続化

データを永続化したい場合、docker volume を使えば OK です。
named volume であれば docker run 時に docker コンテナ内のデータが docker volume にコピーされてマウントされるため、次のように起動することで永続化が可能です。

docker run -v sqlserver:/var/opt/mssql/data -it -p 0.0.0.0:1433:1433 -p 0.0.0.0:2222:22 mysqlserver_on_linux:2017

SQL Server Management Studio

ユーザ sa/password で接続可能です。
接続先は docker run しているマシンの IP アドレスです。

というわけで簡単にですが SQL Server on Linux 2017 の Docker についての紹介でした。