Windows Server 2016からDockerがサポートされ、Windows Server上でWindowsのコンテナを動かせるようになりました。このWindowsコンテナの検証の一環で、Linuxとの違いの確認やWindowsコンテナに慣れるためにdocker-composeでweb(WordPress、IIS)・db(MySQL)が立ち上がるものを作成しました。この記事ではこれらを作る過程で気づいたWindowsコンテナでDockerfile・Docker Composeを作るときの注意点として気づいたことをまとめました。
ゴールと作成したDockerfile・docker-compose.yml
docker compose up -d
で以下のようなweb・dbの二つのコンテナが立ち上がりWordPressが使える状態で立ち上がることをゴールとします。
また、今回作成したDockerfile・docker-compose.ymlはGitHubで公開しています。使用方法等はGithubのReadmeを参照ください。
ステップ0 環境準備
Windows Server 2016に対し、Dockerとdocker-composeをインストールします。環境準備はそれぞれ、以下のドキュメントに従えば問題ありませんが、ホストにするWindows Serverが仮想マシンだと面倒な場合もありますのでご注意ください。
Dockerのインストール
docker-composeのインストール
ステップ1 Dockerfileの作成
web(IIS + WordPress)とdb(MySQL)用に二つのDockerfileを作成します。それぞれのDockerfileで行う処理は以下になります。
- web
- IISの設定・ポート解放
- WordPressのダウンロード・展開
- wp-config(DB設定)の設定
- PHPのダウンロード・インストール
- db
- MySQLダウンロード・インストール
- ポート解放
- MySQL rootパスワード設定
- WordPress用テーブル・ユーザー作成
これらの処理を実装したDockerfileが以下になります。
- WebのDockerfile
FROM microsoft/iis
ARG PORT_NUM
ARG DB_USER
ARG DB_PASSWORD
ARG DB_HOST
RUN mkdir c:\site
ADD https://ja.wordpress.org/wordpress-4.8.2-ja.zip /
ADD https://download.microsoft.com/download/C/A/F/CAF5E118-4803-4D68-B6B5-A1772903D119/VSU4/vcredist_x64.exe /build/vc_redist.x64.exe
ADD http://windows.php.net/downloads/releases/php-5.6.32-nts-Win32-VC11-x64.zip /
RUN powershell -command \
Expand-Archive -Path c:\php-5.6.32-nts-Win32-VC11-x64.zip -DestinationPath c:\php\ ; \
Remove-Item c:\php-5.6.32-nts-Win32-VC11-x64.zip -Force
ADD php.ini /php/php.ini
RUN powershell -command \
Expand-Archive -Path c:\wordpress-4.8.2-ja.zip -DestinationPath c:\site\ ; \
Remove-Item c:\wordpress-4.8.2-ja.zip -Force
RUN powershell -command \
dir c:\site\wordpress
RUN powershell -command \
chcp 65001 ; \
$WP_CONFIG = $(Get-Content "c:\site\wordpress\wp-config-sample.php") ; \
$WP_CONFIG = $WP_CONFIG.Replace('database_name_here','wordpress') ; \
$WP_CONFIG = $WP_CONFIG.Replace('username_here','%DB_USER%') ; \
$WP_CONFIG = $WP_CONFIG.Replace('password_here','%DB_PASSWORD%') ; \
$WP_CONFIG = $WP_CONFIG.Replace('localhost','%DB_HOST%') ; \
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False) ; \
[System.IO.File]::WriteAllLines('c:\site\wordpress\wp-config.php', "$WP_CONFIG" , $Utf8NoBomEncoding)
RUN powershell -NoProfile -Command \
Import-module IISAdministration; \
New-IISSite -Name "Site" -PhysicalPath C:\site -BindingInformation "*:%PORT_NUM%:"
EXPOSE ${PORT_NUM}
RUN setx path "%PATH%;c:\php"
RUN setx pathext "%PATHEXT%;.php"
RUN start /w C:\build\vc_redist.x64 /q /norestart
RUN powershell.exe -executionpolicy bypass "Add-WindowsFeature Web-CGI"
RUN %windir%\system32\inetsrv\appcmd set config /section:system.webServer/fastCGI /+[fullPath='c:\php\php-cgi.exe']
RUN %windir%\system32\inetsrv\appcmd set config /section:system.webServer/handlers /+[name='PHP_via_FastCGI',path='*.php',verb='*',modules='FastCgiModule',scriptProcessor='c:\php\php-cgi.exe',resourceType='Either']
RUN %windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/defaultDocument /+"files.[value='index.php']" /commit:apphost
- dbのDockerfile
FROM microsoft/windowsservercore
ADD https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.16-winx64.zip mysql.zip
ARG ROOT_PASSWORD
ARG WP_USER
ARG WP_PASSWORD
RUN echo SET PASSWORD FOR 'root'@'localhost' = PASSWORD('%ROOT_PASSWORD%') ; > c:\mysql-init.txt
RUN type c:\mysql-init.txt
RUN powershell -command \
Expand-Archive -Path c:\mysql.zip -DestinationPath c:\ ; \
ren c:\mysql-5.7.16-winx64 c:\MySQL ; \
New-Item -Path C:\MySQL\data -ItemType directory ; \
c:\MySQL\bin\mysqld.exe --initialize --console --explicit_defaults_for_timestamp --init-file c:\mysql-init.txt ; \
c:\MySQL\bin\mysqld.exe --install ; \
Start-Service mysql ; \
Remove-Item c:\mysql.zip -Force
ENV MYSQL c:\\MySQL
EXPOSE 3306
RUN setx PATH /M %PATH%;c:\MySQL\bin
RUN powershell -command \
Start-Sleep -s 3
RUN MySQL --user=root --password="%ROOT_PASSWORD%" --connect-expired-password -Be "grant all privileges on *.* to %WP_USER%@'%' identified by '%WP_PASSWORD%' with grant option;" & \
MySQL --user=root --password="%ROOT_PASSWORD%" --connect-expired-password -Be "CREATE DATABASE wordpress CHARACTER SET utf8 COLLATE utf8_bin;"
docker buildやdocker run等のコマンドは従来と同じ感覚で使用可能でした。また、Dockerfileの作成において、windowsコンテナでもLinuxと同じ用に命令(RUNやENV)が使えますが、その挙動はWindowsで実行されることを考慮した注意が必要です。以下では各命令の細かい注意事項をまとめました。
FROM ベースイメージ
FROMで指定できるwindowsコンテナで使用可能なイメージはDockerHubのmicrosoftのページで公開されています(Windowsコンテナ用ではないものも混ざってます)。
今回は
- web : microsoft/iis
- microsoft/windowsservercoreのイメージにIISが導入済みのイメージ
- db: microsoft/windowsservercore
- 特に何も入っていないプレーンなイメージ
を使用しました。
- 特に何も入っていないプレーンなイメージ
今回使用したイメージはどちらもwindowsservercoreがベースとなっていますが、このwindowsservercoreは数ギガあり意外と重いので、検証していませんが、nanoserverのイメージの方が良いのかもしれません。
RUN コマンド実行
RUNでコマンドを実行する場合基本はコマンドプロンプトで実行されるようです。powershellのコマンドを実行したい場合powershell -command
を付けてから実行します。
- 例
RUN powershell -NoProfile -Command \
Import-module IISAdministration; \
New-IISSite -Name "Site" -PhysicalPath C:\site -BindingInformation "*:%PORT_NUM%:"
RUN コマンド実行 powershell -Commandが上手くいかない場合
powershell上で直接実行すると問題ないのですが、RUN powershell -Command
で実行させようとするエラーが出る問題に度々ぶつかりました。これは、コマンドプロンプトのエスケープ仕様に関係していて、こちらに説明があるのですが分りにくいので以下に成功する例と失敗する例を作成しました。
- テスト用Dockerfile
FROM microsoft/windowsservercore
ARG VAR=ABC
RUN echo '123 ;;;;;' > c:\test.txt
# 失敗する例 おそらくスペースありの文字列を「"」で囲うとエラーになる。
#RUN powershell -command \
# $TEXT_FILE = $(Get-Content c:\test.txt) ; \
# $TEXT_FILE = $TEXT_FILE.Replace("123 ;;;;;","%VAR% 123 ;;;;;") ; \
# echo $TEXT_FILE > c:\test2.txt
#RUN type C:\test2.txt
# 成功する例 「"」を使用しないで「'」を使用する
RUN powershell -command \
$TEXT_FILE = $(Get-Content c:\test.txt) ; \
$TEXT_FILE = $TEXT_FILE.Replace('123 ;;;;;','%VAR% ;;;;;') ; \
echo $TEXT_FILE > c:\test2.txt
RUN type C:\test2.txt
# 成功する例 「"」を「\」でエスケープする
RUN powershell -command \
$TEXT_FILE = $(Get-Content c:\test.txt) ; \
$TEXT_FILE = $TEXT_FILE.Replace(\"123 ;;;;;\",\"%VAR% 123 ;;;;;\") ; \
echo $TEXT_FILE > c:\test2.txt
RUN type C:\test2.txt
ADD 必要ファイルのダウンロード
ADD命令でLinuxの場合と同様にホストマシンのファイル or ダウンロードURLを指定してコンテナ内にファイルの追加が可能です。ただし、Windowsコンテナの場合、圧縮ファイルの解凍はpowershellのコマンドで解凍することになるのでzipのダウンロードが無難です。
またpowershellでファイルの解凍は以下のように行います。この例では解凍後、圧縮ファイルは削除しています。
- 例
RUN powershell -command \
Expand-Archive -Path c:\wordpress-4.8.2-ja.zip -DestinationPath c:\site\ ; \
Remove-Item c:\wordpress-4.8.2-ja.zip -Force
ARG 引数・変数
ARGで変数化した値をコマンド実行中に使用したい場合は%VAR%
のようにします
- 例
RUN MySQL --user=root --password="%ROOT_PASSWORD%" --connect-expired-password -Be "grant all privileges on *.* to %WP_USER%@'%' identified by '%WP_PASSWORD%' with grant option;"
一方、それ以外で使用する場合は${VAR}
のようにします
- 例
EXPOSE ${PORT_NUM}
ENV 環境変数
環境変数の設定にはENVを使用できます。しかし、環境変数の新規作成は問題ありませんが追記の場合はENVが使えません。まずは環境変数MYSQL
を新規作成する場合、以下のようにENVを使用し問題なく動作します。
# 問題なく動作する
ENV MYSQL c:\\MySQL
同じように、環境変数PATH
を追記仕様とすると
# エラーは出ないが動かない例1
ENV PATH /M ${PATH};c:\MySQL\bin
# エラーは出ないが動かない例2
ENV PATH /M %PATH%;c:\MySQL\bin
このように書きたくなりますが、どちらも動きません。
ではどうするのかというと以下のようにsetxコマンドで設定します。
RUN setx PATH /M %PATH%;c:\MySQL\bin
この問題に関してはPRも上がっているようなので近いうちに修正が入るのかもしれません。
その他注意事項・参考情報
- Dockerfileのサンプルとして、DockerHubのmicrosoftのページ以外にDockerHubのnanoserverのページ(非公式?)で公開されているものが参考になります。
- コンテナ起動後の設定ファイルの変更等はコマンドラインエディタがないので不便でした。デバッグではホスト上に同じファイルを持っておいてホストの適当なテキストエディタで編集後、
docker cp <コンテナ名>:<ホスト上のファイルのパス>
で送るほうが簡単です。 - コンテナ起動後のデバッグ・設定変更では
docker exec -it <コンテナ名> powershell
でpowershellからコンテナ内に入ることができます - ファイルのエンコードには注意が必要です。(今回はwp-config.phpがBOMなしUTF-8なので変換しています)
ステップ2 docker-compose.ymlの作成
今回はwebとdb二つのコンテナをwebにdbをリンクさせ起動するようにしています。docker-compose.ymlとしては特にLinuxとの違いはないように見えました。
- docker-compose.yml
version: '3'
services:
web:
build:
context: ./iis
dockerfile: Dockerfile
args:
- PORT_NUM=8000
- DB_USER=wordpress
- DB_PASSWORD=p@ssw0rd
- DB_HOST=db
ports:
- 8000:8000
links:
- db
tty: true
db:
build:
context: ./mysql
dockerfile: Dockerfile
args:
- ROOT_PASSWORD=p@ssw0rd
- WP_USER=wordpress
- WP_PASSWORD=p@ssw0rd
tty: true
networks:
default:
external:
name: nat
ネットワークの新規作成で失敗?
docker-compose.ymlでnetworkを指定しないで作成した場合以下のようなエラーが出ました。
Creating network "wordpress_default" with the default driver
ERROR: HNS failed with error : パラメーターが間違っています。
ネットワーク作成に失敗しているようですが、これの原因調査は未完了です。ただ、同様の問題は起きているようで、おそらく私の環境の問題(今回はvSphare5.5上の仮想マシンで検証)です。一旦の回避策として以下のように作成済みのnatネットワーク(Dockerインストール時デフォルトで作られた)を指定して回避しています。
networks:
default:
external:
name: nat
まとめ
今回は、WindowsコンテナでWordPressの立ち上げを行い、Dockerfile、docker-composeの動作を確認しました。結果としては、一部問題が残っているところもありますが、基本的にはいままでLinuxでDockerを使用していた人であれば同じような使用感で利用できることが分りました。ただし、今回の例だとLinuxのDockerを使用した方が楽です。Windowsコンテナを何に使うべきかは考える必要があると感じました。