見出し画像

Windowsコンテナにまつわるエトセトラ

先日、いわゆるWindows Container On Windows(WCOW、以下Windowsコンテナ)の調査を行ったのですが、未だに情報が少なく中々に難儀しました。
ここでは、その調査で分かった、ドキュメントには記載されていないことを中心にシェアしようと思います。

Dockerfile関係

ENV命令で定義される環境変数のスコープに注意

DockerfileでENV命令を使うと、コンテナに環境変数を定義することができます。

以下のDockerfileをビルドして、確認してみましょう。

#syntax=docker/dockerfile:1

FROM mcr.microsoft.com/windows/servercore:ltsc2022

# ENV命令で環境変数を定義
ENV HOGE="VAR"
CMD ["powershell.exe"]
docker build -t example1:latest .
docker run --rm -it example1:latest

立ち上がったpowershellで以下を実行して、ENV命令で定義した環境変数のスコープを確認します。

[Environment]::GetEnvironmentVariable('HOGE', [EnvironmentVariableTarget]::Process)
[Environment]::GetEnvironmentVariable('HOGE', [EnvironmentVariableTarget]::User)
[Environment]::GetEnvironmentVariable('HOGE', [EnvironmentVariableTarget]::Machine)

以下のような結果になりました。

ENV命令の場合

ENV命令で定義した環境変数は現在のプロセス(ここではpowershell.exe)にのみ反映されていることがわかります。
いわゆるシステム環境変数として定義されていないので、Windowsサービスからは見ることができません。

Dockerfileでシステム環境変数を定義するためには、RUN命令と/mオプション付きsetxコマンドを組み合わせます。
以下のDockerfileで別イメージをビルドして確かめてみましょう。

#syntax=docker/dockerfile:1

FROM mcr.microsoft.com/windows/servercore:ltsc2022

# RUN命令+setxで環境変数を定義
RUN setx HOGE "VAR" /m
CMD ["powershell.exe"]
docker build -t example2:latest .
docker run --rm -it example2:latest

同様に環境変数のスコープを確認します。

RUN命令+setxコマンドの場合

この場合は、システム環境変数として定義されていることがわかります。

Dockerfile内でサブフォルダーやファイルを含むフォルダーをボリュームとして宣言しても、ボリュームにコピーされない

公式ドキュメントでも言及されていますが、Windowsコンテナの場合は存在していないディレクトリ、または、空のディレクトリをコンテナ内のボリューム指定先にする必要があります。

コンテナで動かすアプリケーションの設定ファイルなどをボリューム上に配置する場合は、ボリュームをマウントしたコンテナを作り愚直にコピーする必要があります。

コンテナに対する操作

docker updateがサポートされていない

こちらも公式ドキュメントに記載されていますが、Windowsコンテナに対してはdocker updateがサポートされていません。

つまり、コンテナに割り当てるCPUやメモリを変更する場合、コンテナの再作成が必要になりますので、注意が必要です。

コンテナ内で共有フォルダーの作成ができない

以下のMicrosoftのフォーラムでも議論されていますが、コンテナ内で共有フォルダーを作成することができません。
2015年に指摘されている内容ですが、残念ながら2023年6月時点ではまだ未解決です。

分離モードによる振る舞いの違い

Windowsコンテナには以下のランタイム分離モードがあります。

  • プロセス分離

  • Hyper-V分離

詳しくは上記のドキュメントを参照していただければと思いますが、ドキュメントには明記されていない振る舞いの違いをいくつか示します。

稼働中のHyper-V分離のコンテナに対しては、docker cpが動かない

docker cpでは、コンテナとコンテナホスト上のファイルシステム間でファイル/フォルダーコピーが行えます。

Windowsコンテナではどうなるのかを確認してみましょう。
以下のDockerfileからイメージをビルドして、分離モードがプロセス分離、Hyper-V分離のコンテナを作成します。

#syntax=docker/dockerfile:1

FROM mcr.microsoft.com/windows/servercore:ltsc2022

RUN mkdir C:\data
RUN echo "hello world" > C:\data\greeting
docker build -t example3:latest .
docker run --rm -d -it --name example3_p --isolation=process example3:latest
docker run --rm -d -it --name example3_h --isolation=hyperv example3:latest

example3_pがプロセス分離、example3_hがHyper-V分離のコンテナです。

コンテナホスト上にフォルダーとファイルを作成します。

mkdir C:\test
echo "hello world again!" > c:\test\greeting2

それでは、コンテナホスト上でpowershellを起動して、docker cpを実行してみます。
まずは、稼働中コンテナ→コンテナホストへのコピーです。

docker cp example3_p:C:/data/greeting C:/test
docker cp example3_h:C:/data/greeting C:/test
稼働中コンテナ→コンテナホストへのdocker cp

続けて、コンテナホスト→稼働中コンテナへのコピーです。

docker cp C:/test/greeting2 example3_p:C:/data/greeting2
docker cp C:/test/greeting2 example3_h:C:/data/greeting2
コンテナホスト→稼働中コンテナへのdocker cp

コンテナホスト→Hyper-V分離のコンテナへのdocker cp時のコンソール出力がやや混乱しますが、最終的には失敗しています。
つまり、docker cpは稼働中のHyper-V分離のコンテナではサポートされていません。

Hyper-V分離のコンテナであっても、停止中であればdocker cpは成功します。

停止中のHyper-V分離のコンテナに対するdocker cp

コンテナが認識する物理メモリサイズが異なる

分離モードによって、コンテナが認識する物理メモリサイズが異なります。

確認するために、以下のDockerfileからイメージをビルドしましょう。
分離モードがプロセス分離、Hyper-V分離のコンテナを作成します。

#syntax=docker/dockerfile:1

FROM mcr.microsoft.com/windows/servercore:ltsc2022

CMD ["powershell.exe"]
docker build -t example4:latest .
docker run --rm -d -it --name example3_p --isolation=process example3:latest
docker run --rm -d -it --name example3_h --isolation=hyperv example3:latest

メモリ制限を1GBでプロセス分離のコンテナを作成します。

docker run --rm -it --name example4_p -m 1G --isolation=process example4:latest

立ち上がったpowershellで以下を実行します。

$info = Get-WmiObject Win32_OperatingSystem
Write-Output "物理メモリの合計(MB): $([int]($info.TotalVisibleMemorySize / 1024))"
Write-Output "利用可能な物理メモリ(MB): $([int]($info.FreePhysicalMemory / 1024))"

私の環境では以下のような結果になりました。

プロセス分離のコンテナでの結果

この値は、コンテナホストマシンの物理メモリ合計と利用可能な物理メモリに一致します。

同様のことをメモリ制限を1GBで作成したHyper-V分離のコンテナに対して行った結果が以下になります。

docker run --rm -it --name example4_h -m 1G --isolation=hyperv example4:latest
Hyper-V分離コンテナでの結果

メモリ制限1GBで作成しても、コンテナ内では約1.5GBの物理メモリとして認識しているようです。(このあたりは、また別の設定を変更することで厳密にコントロールできるのかもしれませんが、未検証です。)
しかし、コンテナ作成時の制限に近い構成になっていることがわかります。

このように分離モードによってコンテナが認識する物理メモリサイズが異なるので、これらの値に依拠するアプリケーションでは注意が必要です。

参考情報

Dockerの公式ドキュメント以外の参考情報です。

Microsoftのドキュメント

コンテナとはから説明されていますし、Windowsコンテナ固有の情報も記載されています。
まずはここから目を通して、適宜Dockerの公式ドキュメントも参照するのがよいかと思います。

WindowsコンテナのGithubレポジトリ

何かWindowsコンテナの動きがおかしいと思ったときは、GithubレポジトリのIssuesを確認すると不具合として報告されている可能性があります。
不具合だけではなく、質問もやり取りされているので、自分の課題解決に役に立つかもしれません。

まとめ

今回はドキュメントには記載されていないことを中心にシェアさせていただきました。
コンテナといえばLinuxが主流ですが、Windows上でのみ動くアプリケーションも数多く存在しているので、Windowsコンテナ自体の重要性はかなりあると思います。
この記事が、これからWindowsコンテナに取り組む方に少しでもお役に立てれば幸いです。

この記事が気に入ったらサポートをしてみませんか?