Docker Desktop on WSL2でWin32パスを使用したファイル操作でハマったこと
皆さんファイル操作していますか。
単純な文字列や、簡便な正規表現での置換処理であれば sed や perl のワンライナーで置換することもあるかと思います。
ただ、複雑な置換操作になると何かしらのアプリケーション、スクリプトを書いて置換しないと辛い時もあります。
今回はDocker Desktop on WSL2で大量のファイルに対して、文字列の置換をしようとした際にハマったことを共有します。
- Docker desktop for Windows: 3.3.1 (63152)
- docker: 20.10.5
- docker-compose: 1.29.0, build 07737305
- PHP: 8.0.3 (Docker image: php:8.0.3-cli-alpine3.13)
- ストレージ: 外付けSSD
序章
100GB弱ある大量のファイルに対してHTMLコードの置換を行う案件があり、使い慣れているPHPで置換操作をするスクリプトを作成し、Docker Desktop on WSL2環境で実行する構成を検討しました。
全体のファイルサイズが100GB弱と大きくPCに乗り切るか微妙なラインだったことや、WSL2のVMが一度確保したディスクスペースをホストに返さない問題から、外部SSDをDドライブとしてPCに接続し、DockerのVolumeマウントでWin32パス /mnt/d/path/to/source-dir をコンテナにマウントしました。 置換用のPHPスクリプトを実行する構成で、 ファイル走査にはRecursiveIteratorIteratorとRecursiveDirectoryIteratorを使い、1ファイルずつ対象を置換していく温かみのある処理を行います。
スクリプトを作成してDockerコンテナのPHPコマンドを実行し、数ファイルを開いて置換がされていることを確認し、・・・ヨシッ!。
と思ったのですが、
思った挙動にならない
一部のファイルが置換されていない事に気づきました。
最初は置換のロジックに誤りがあったのかな?と思い確認してみましたが、同じ置換処理でもファイルによって出来ていたり、出来ていなかったりとでロジックの問題ではなさそうでした。
そこで外付けSSDに1000ファイルを配置して検証してみることにしました。
外付けSSDのフォルダ内に1000ファイルを作成
外付けSSDのVolumeマウントフォルダに1000ファイルを配置し、
for i in $(seq -w 1000); do
touch ${i}.txt
done
■ファイルの一覧
.
├── 0001.txt
├── 0002.txt
├── 0003.txt
⋮
├── 0998.txt
├── 0999.txt
└── 1000.txt
0 directories, 1000 files
1,000ファイルある事を確認。・・・ヨシッ!
検証用テスト
<?php
declare(strict_types=1);
namespace Tests;
use PHPUnit\Framework\TestCase;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
class RecursiveIteratorIteratorTest extends TestCase
{
private RecursiveIteratorIterator $recursiveIteratorIterator;
public function setUp(): void
{
parent::setUp();
$this->recursiveIteratorIterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
__DIR__ . '/../var',
RecursiveDirectoryIterator::SKIP_DOTS
),
RecursiveIteratorIterator::SELF_FIRST
);
}
public function test_ファイル数の合計は1000ファイル(): void
{
$this->assertCount(1000, iterator_to_array($this->recursiveIteratorIterator));
}
}
実行結果
PHPUnit 9.5.4 by Sebastian Bergmann and contributors.
Recursive Iterator Iterator (Tests\RecursiveIteratorIterator)
✘ ファイル数の合計は 1000ファイル
┐
├ Failed asserting that actual size 938 matches expected size 1000.
│
╵ /app/tests/RecursiveIteratorIteratorTest.php:30
┴
Time: 00:01.182, Memory: 14.00 MB
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
期待値が1,000ファイルに対して結果は938ファイルで、やはり何かしらの問題がありそうです。
原因
最初はRecursiveIteratorIterator, RecursiveDirectoryIteratorを疑ったのですが、 最終的にはv9fsが原因という事が分かりました。
WSL2とそのホストのWindows間を跨ぐような場合、lseekを使った処理で問題が出るようです。 今回だとDockerのVolumeマウントのsrcを外付けSSDのWin32パス /mnt/d/path/to/source-dir として指定したのが原因だと思います。Q. v9fsとは
WSL2は9Pファイルサーバとして、Windowsは9Pクライアントとしてふるまう、LinuxとWindows間のファイル操作を簡単に出来るようにするためのファイルシステムプロトコルの実装のようです。
回避方法
どうしたものかと悩んでいくつか検索していくなかで、 php bugs RecursiveDirectoryIterator でGoogle検索すると出てきたこちらが参考になりました。
I was using the docker with the ‘WSL 2 based engine’ under the Windows system. As soon as I changed the docker engine into ‘Legacy Hyper-V backend’, the problem disappeared.https://bugs.php.net/bug.php?id=80056
Docker for WindowsのSettingsで「Use the WSL2 based engine」のチェックを外し、legacy Hyper-V backendに変更します。
※チェックを外した後、Docker for Windowsの再起動が必要です
※Windows10 Home Editionの場合、legacy Hyper-V backendは使用できません
Docker for Windowsの再起動後、PowerShellからDocker経由でPHPコマンドを実行して回避する事ができました。
まとめ
今回は使用したPCのOSがWindows10 Pro Editionだったので上記の回避方法が利用できましたが、公式のコメントにもあるように、WSL2のファイルシステムを使用するようにしましょう。
ただ今回のように100GB弱と大容量のデータを扱うなど、外付けSSDを使用したいといったケースもあると思います。
その際にはこの記事が参考になったら嬉しいです。
参考資料
- https://bugs.php.net/bug.php?id=80227
- https://bugs.php.net/bug.php?id=80056
- https://github.com/microsoft/WSL/issues/5074
- https://github.com/microsoft/WSL/issues/6081
- https://github.com/docker/for-win/issues/8997
- https://github.com/themizzi/wsl2-php-recursive-directory-iterator-bug
- https://github.com/docker/for-win/issues/7039
- https://levelup.gitconnected.com/docker-desktop-on-wsl2-the-problem-with-mixing-file-systems-a8b5dcd79b22
この記事を書いた人
- 2013年にアーティスに入社。システムエンジニアとしてアーティスCMSを使用したWebサイトや受託システムの構築・保守に携わる。環境構築が好き。
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー