Windowsコンテナにアプリをインストール!~続・WSLもDocker Desktopも使わずにDockerをWindowsでやってみる
概要
Windows上でDocker環境を作るための方法を説明します。
この記事は「WSLもDocker Desktopも使わずにDockerをWindowsでやってみる」の続編です。
以前の記事ではWindowsコンテナをDockerで利用する方法までを説明していましたが、今回の記事ではその環境で実際にアプリケーションを動かしてみたいと思います。
今回、準備方法などについては説明していませんが、前回の記事を読んでいただければわかるかなと思います。
前回の記事で足りなかったこと 現実性
前回の記事では「Docker HubにWindowsコンテナ用のイメージも配布されてますよ~」といいながらセットアップの手順を説明してたのですが、Docker Hubで公開されているイメージはほとんどがLinuxのもの。
つかえるイメージというのがそこまでいうほどありません。
PythonのようにGitHub上でdockerfile
が公開されている場合もあるので、シェルスクリプトをWindows向けに読み替えてることでdockerfile
を作るのはできなくはありませんが、少々骨が折れます。
また、exeを実行してインストールをしようにもいろいろな問題にぶつかりがちで、手間がかかるのが現実です。
そこで今回、手動でのインストールをあきらめ、なおかつ完成された環境をWindowsコンテナに展開すべく、ChocolateyをインストールすることでWindowsコンテナでのDocker実行を現実的にしてみることにしました。
イメージサンプル
今回はPythonをインストールするイメージを作ってみます
プロジェクト構造
あとで整理しやすいように少々仰々しく組んでいます
📂 . (プロジェクトのルート)
├── 📂images
│ └── 📂python
│ ├── 📂data
│ │ ├── 📂psm
│ │ │ └── chocolatey.psm1
│ │ └── image.ps1
│ └── dockerfile
└── docker-compose
images
にイメージ毎のフォルダを作成する想定。
data
はイメージ内にコピーするファイルをまとめておくためのフォルダです。
docker-compose
1つのイメージしか作りませんのでシンプルに
services:
python:
container_name: python
build: ./images/python
tty: true
dockerfile
こちらはほとんど定型文になると思います
# Windows coreサーバー
FROM mcr.microsoft.com/windows/servercore:ltsc2022
# データをあらかじめコピーしておく
COPY data/ C:/data
# 管理者権限
USER ContainerAdministrator
# PowerShell で実行
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# イメージ作成処理を実行
RUN C:/data/image.ps1 C:/data
chocolatey.psm1
Chocolateyを使うのにあたって定型文になりそうな関数をモジュール化したもの
# Chocolateyのインストール関数
function installChocolatey {
[alias("Install-Chocolatey")] param()
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Import-Module -Global $env:ALLUSERSPROFILE\chocolatey\helpers\chocolateyInstaller.psm1
refreshenv | Out-Null
}
# フォルダを検索し、PATHへ追加する
function appendPath {
[alias("Append-FindPath")] param($folderName)
$path = (Get-ChildItem $folderName).FullName
$old = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine')
[System.Environment]::SetEnvironmentVariable('PATH', $old + ';' + $path, 'Machine')
refreshenv | Out-Null
return $path
}
image.ps1
Dockerイメージを作る本処理、モジュール化してるので結構シンプル
param($data_root)
# choco のインストール
Import-Module $data_root\psm\chocolatey.psm1
Install-Chocolatey
# python のインストール
choco install -y python
$path_python = Append-FindPath C:\python*
python -VV
サンプルの解説
使用すべき元イメージ
こちらの記事でWindowsイメージは複数用意されていると説明しました。
イメージ名称 | |
---|---|
mcr.microsoft.com/windows/nanoserver | Windowsコンテナを実行する最低限をサポートしたイメージ |
mcr.microsoft.com/windows/servercore | Windowsのコマンドをある程度サポートしたイメージ |
mcr.microsoft.com/windows/server | 完全なWindows APIをサポートしたイメージ |
この表は上の方が軽量なイメージで、下にいくほど既にインストールされているプラグインが多いです。
この「インストールされているプラグイン」というのがミソで、Chocolateyのインストールには.NET 4.8が必要なのですが、nanoserverにはそれがインストールされていません。
そのまま実行してしまうと、下記の通りのエラーが出力されてイメージの作成に失敗します。
そのため、今回が真ん中のservercoreを使用しています。
chocolatey.psm1
について
今回、Dockerそのものについては一般的なものと変わりがないので解説が必要なのはPowerShellで記述されたもの、特にchocolatey.psm1
に限られると思います。
installChocolatey
まずはChocolateyのインストール処理について。
# インストーラをダウンロードし、即時実行する処理
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Chocolateyは公式HP上にインストーラとなるスクリプトが公開されています。
このスクリプトをダウンロードし、即時実行することでWindows上にchocoがインストールされます。
※こちらのリンクを開くとスクリプトファイルの中身(ps1)が表示されます
インストールはされる……のですが、これだけでは少し足りません
というのも、Chocolateyをインストールした後、本来ならばシステム環境変数であるPATH
が更新されてchoco
コマンドが利用できるはずでこれには再起動が必要となので、再起動ができないDockerではすぐには使用できません。
ですのでその代わりにrefreshenv
を使用します。
refreshenv
はChocolateyで用意されている関数で、システム環境変数が変更されているかを検出して現在のセッション(イメージの作成処理)にも変更を反映するという関数です。
しかしrefreshenv
を使用するためにもrefreshenv
をセッション中に読み込んでもらわないと実行できないため、手動で読み込む必要があります。
それをしてくれるのがchocolateyInstaller.psm1
です。
# Chocolateyのモジュールを読み込む処理
Import-Module -Global $env:ALLUSERSPROFILE\chocolatey\helpers\chocolateyInstaller.psm1
chocolateyInstaller.psm1
を実行した後はChocolateyが提供している様々な関数が使用できるようになるので、この後にrefreshenv
を実行することでchoco
コマンドが使用できるようにします。
appendPath
もう一方の関数appendPath
は、システム環境変数PATH
に任意のパスを追加するためのものです
function appendPath {
[alias("Append-FindPath")] param($folderName)
# パスが存在するか検索、名称を取得(ワイルドカード可)
$path = (Get-ChildItem $folderName).FullName
# 現在のPATHを取得
$old = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine')
# PATHの末尾に取得したパスを追加する
[System.Environment]::SetEnvironmentVariable('PATH', $old + ';' + $path, 'Machine')
# セッションにPATHを反映
refreshenv | Out-Null
return $path
}
特殊なのはその使用方法。
Append-FindPath
で指定された引数$folderName
は、ワイルドカードを許可しています。
というのも、Chocolateyでインストールするソフトウェアのフォルダ名が基本的に完全には不明であるためです。
今回例にしているPythonは、Chocolateyでインストールすると例えばC:\Python312
に配置されます。
この312
とはPythonのバージョン番号です。
これはPythonのリポジトリがインストール先を指定しているためまだいいのですが、今回は例示していませんがインストール先を指定していないソフトウェアは「Chocolateyが用意しているTool
フォルダに展開(解凍)する」という使用もあります。
たとえばNginxが該当し、その展開先は(Toolフォルダ)\nginx28.x.x
(28.X.Xはバージョン番号)です。
このToolフォルダはrefreshenv
で実行したchocolateyProfile.psm1
によって追加されたコマンドGet-ToolsLocation
で知ることができます。
何がいいたいか。
「バージョン番号が邪魔で常に同じパスを(解説上)保証できない」、「Chocolateyの設定に依存するToolフォルダを動的に取得する必要がある」のであまりベタ書きでパス名を指定したくないのです。
なので極力指定方法は裾野を広く受け付けることにしました。
当然、パスが存在しなければエラーになり、複数存在する場合もエラーになるかと思います。
しかしまぁ、Dockerイメージ上の話なので複数存在する状況の方がおかしいのではないかなと思います(都度イメージそのものが再作成されるので、常に1つのバージョンが残るのが基本です)
複数バージョンが必要な場合は……ごめんなさい、いまのところイメージをわけるしか思いつきません。
最後のrefreshenv
にパイプでOut-Null
をつなげているのは、refreshenv
が標準出力でRefreshing environment variables from the registry for powershell.exe. Please wait... Finished
という文字列を出力してしまってるので、それをかき消すためです。
図は$path_python
をOut-Null
がない状態で標準出力してみた結果
戻り値はパスであってほしいので余計な出力はOut-Null
で消してしまいましょう
image.ps1
について
あとはchocolatey.psm1
で定義したコマンドを使用してインストールするだけです
param($data_root)
# choco のインストール
Import-Module $data_root\psm\chocolatey.psm1
Install-Chocolatey
# python のインストール
choco install -y python
$path_python = Append-FindPath C:\python*
Write-Host $path_python
# Pythonの実行(Append-FindPathを実行していないとpythonコマンドは使用できません)
python -VV
他のリポジトリ(Nginxなど)をChocolateyでインストール進場合は、Append-FindPath C:\python*
の部分に気を付ける必要があります。
リポジトリによってインストールされている先はことなるため、python
を他の名称に書き換えればいい……ということには一切なりません。
たとえば、Nginxの場合はChocolateyが定義しているツールフォルダにインストールされるため、読み込むべきフォルダは以下の通りです。
Append-FindPath (Join-Path (Get-ToolsLocation) "nginx*")
これについてはChocolateyリポジトリの各ページを参考にしてもらうほかありませんので、よく読んでから、或いは実行時に書きだされるログから推測して指定してください。
Pythonの場合
実行ログより読み解きました
Nginxの場合
リポジトリのページより読み解きました
〆
今回はWindowsコンテナでのDockerを利用方法を解説しました。
こんどこそ快適にDockerが楽しめそうです。……ホンマか???
ところで
パッケージ管理ツールをご存じの方だと「Install-PackageProvider
(OneGet)を使えばいいじゃん!」と突っ込んでいただいたかもしれません。
……すまん、やり方がわからなかったんや……。
# choco のインストール
Find-PackageProvider ChocolateyGet -Force
Install-PackageProvider ChocolateyGet -Scope CurrentUser -Force
# pythonのインストール
Find-Package -Name vscode -ProviderName ChocolateyGet
どうしてもInstall-PackageProvider
で下記のエラーが発生してしまうんです。
これについては完全に私の知識不足が原因ですので、ご存じの方はこっそり教えていただくか、記事を書いていただけると嬉しいです。
見てる感じ、nanoserver
で実行できる気配があり、より小さなイメージ、コンテナで完結できる見込みがありそうなんですけどねぇ……(※あくまで予想であり、実際にはできないかもしれません)
参考記事
Chocolateyについて
PowerShellについて
Discussion