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命令で定義した環境変数は現在のプロセス(ここでは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
同様に環境変数のスコープを確認します。
この場合は、システム環境変数として定義されていることがわかります。
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 C:/test/greeting2 example3_p:C:/data/greeting2
docker cp C:/test/greeting2 example3_h:C:/data/greeting2
コンテナホスト→Hyper-V分離のコンテナへのdocker cp時のコンソール出力がやや混乱しますが、最終的には失敗しています。
つまり、docker cpは稼働中のHyper-V分離のコンテナではサポートされていません。
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
メモリ制限1GBで作成しても、コンテナ内では約1.5GBの物理メモリとして認識しているようです。(このあたりは、また別の設定を変更することで厳密にコントロールできるのかもしれませんが、未検証です。)
しかし、コンテナ作成時の制限に近い構成になっていることがわかります。
このように分離モードによってコンテナが認識する物理メモリサイズが異なるので、これらの値に依拠するアプリケーションでは注意が必要です。
参考情報
Dockerの公式ドキュメント以外の参考情報です。
Microsoftのドキュメント
コンテナとはから説明されていますし、Windowsコンテナ固有の情報も記載されています。
まずはここから目を通して、適宜Dockerの公式ドキュメントも参照するのがよいかと思います。
WindowsコンテナのGithubレポジトリ
何かWindowsコンテナの動きがおかしいと思ったときは、GithubレポジトリのIssuesを確認すると不具合として報告されている可能性があります。
不具合だけではなく、質問もやり取りされているので、自分の課題解決に役に立つかもしれません。
まとめ
今回はドキュメントには記載されていないことを中心にシェアさせていただきました。
コンテナといえばLinuxが主流ですが、Windows上でのみ動くアプリケーションも数多く存在しているので、Windowsコンテナ自体の重要性はかなりあると思います。
この記事が、これからWindowsコンテナに取り組む方に少しでもお役に立てれば幸いです。