グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



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スクリプトを実行する構成で、 ファイル走査にはRecursiveIteratorIteratorRecursiveDirectoryIteratorを使い、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検索すると出てきたこちらが参考になりました。

Hi there, I finally find out that the reason to problem is actually the environment.

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を使用したいといったケースもあると思います。

その際にはこの記事が参考になったら嬉しいです。

参考資料

この記事を書いた人

美髭公
美髭公事業開発部 web application engineer
2013年にアーティスに入社。システムエンジニアとしてアーティスCMSを使用したWebサイトや受託システムの構築・保守に携わる。環境構築が好き。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします