ごった煮

色々な事を書いてます

コンテナ内の SQL Server に対して Docker.DotNet を使ってデータベースをリストアする方法

コンテナ内の SQL Server でテスト用 DB を作る際、.bak ファイルからデータベースを復元したかったのでやってみました

手順

次のステップでやっていきます

  1. データベースを .bak でバックアップする
  2. bak を .tar に変換
  3. .tar を埋め込みリソースに設定 → ここまで割愛
  4. .tar ファイルをコンテナに転送して .bak に展開
  5. .bak を使って DB をリストア

埋め込みリソースを設定する

Visual Studio のプロパティ設定で埋め込みリソースを設定しても良いのですが、ファイルが増えてくると都度埋め込みリソースに設定するのはめんどくさいので、.csproj でまとめて埋め込みリソースに設定する記述を追加します。

 <ItemGroup>
        <EmbeddedResource Include="{リソースフォルダのパス}\*.tar" />
    </ItemGroup>

埋め込みリソースを読み込む

埋め込みリソースとして持っておけば、いろいろ取り回しが楽なので埋め込みリソースを使用します

埋め込みリソースを Stream に読み込むコードは以下のような感じです。

ファイルパスは、名前空間.フォルダ名.ファイル名 です

フォルダ名に - が入っている場合、- は使えないので、_ に置換します。(例)test-resources フォルダの場合、test_resources にする)

        var resourceFullName = $"{プロジェクトの名前空間}.{フォルダ名}.{ファイル名}";
        var assembly = Assembly.GetExecutingAssembly();

        using (var stream = assembly.GetManifestResourceStream(resourceFullName))
        {
            if (stream is null)
                throw new FileNotFoundException(resourceName);
        }

.tar をコンテナに転送

コンテナに .tar を転送します。

埋め込みリソースを読み込むコードを下記のように変更します。

はじめに、転送先のディレクトリをコンテナ内に作成します。

CreateContainerParameters の設定に Cmd を設定します。

        var containerConfig = new CreateContainerParameters
        {
            Image = imageFullName,
            Env = environmentVairbales,
            ExposedPorts = exposedPorts,
            HostConfig = hostConfig,
            Cmd = new[]
            {
                "bash", "-c", "mkdir -p {ディレクトリパス}"
            }
        };
        var resourceFullName = $"{プロジェクトの名前空間}.{フォルダ名}.{ファイル名}";
        var assembly = Assembly.GetExecutingAssembly();

        using (var stream = assembly.GetManifestResourceStream(resourceFullName))
        {
            if (stream is null)
                throw new FileNotFoundException({ファイル名});

            await _dockerClient.Containers.ExtractArchiveToContainerAsync(
                _containerId,
                new ContainerPathStatParameters
                {
                    Path = "コンテナの転送先ディレクトリ"
                },
                stream
                );
        }

ExtractArchiveToContainerAsync を使うと、転送後に .tar を展開してくれるので、リストアは .bak を指定します。

DB をリストアする

SqlCommand でリストア用のクエリを流します

            var restoreCommand = $@"
RESTORE DATABASE [{データベース名}]
FROM DISK =  N'{.bak の転送先ディレクトリ}/{.bak ファイル名}'
WITH REPLACE, 
MOVE '{データベース名}_Data' TO '{.mdf ファイルの展開先}',
MOVE '{データベース名}_Log' TO '{.ldf ファイルの展開先}',
RECOVERY;
";
            using(var command = new SqlCommand(restoreCommand, connection))
            {
                await command.ExecuteNonQueryAsync();
            }

まとめ

.bak から展開 DB の復元が出来ました。

これで、ユニットテスト毎にきれいなテストデータでテストを回すことが出来るようになりました。