これは何?
ローカル環境では正常に動いていたコードが、CI環境では動かない、という現象に遭遇しました。調査の結果、ファイルシステムごとで大文字/小文字の扱いが違うことが原因であると判明しました。
この記事では、その調査で得た学びを備忘録としてまとめました。
起きた事象、原因、解決策について
発生した事象
起きた事象について簡単に説明すると、ローカル環境ではファイル名の大文字/小文字を区別しない(以降case-insensitive)が、CI環境ではファイル名の大文字/小文字を区別する(以降case-sensitive)、ということが原因でファイルの読み込みエラーが発生していました。
具体的には、期待するファイル名が hogeFuga.json の場合に以下のような挙動を確認しました。
| 環境 |
コード上でhogeFuga.jsonと指定 |
コード上でhogefuga.jsonと指定 |
| ローカル (macOS / APFS) |
✅ |
✅ |
| CI (Ubuntu 20.04 / ext4) |
✅ |
❌ |
原因
上記の違いが起こったのはdockerのバインドマウントを利用していることが原因でした。ホスト側のファイルシステムをコンテナにマウントするので、ホストであるmacOSのAPFSの挙動がそのままコンテナに反映された、ということです。
※APFSはデフォルトがcase-insensitive
解決策
ドキュメントの内容と実際の挙動に乖離があることは気になりましたが、ひとまずCI環境(ubuntu)でも動作させたいという要件を達成するためにコードをcase-sensitiveで動くように修正しました。
これで無事ローカル環境、CI環境どちらでもコードが動作するようになりました。
まとめ
- Dockerのバインドマウントではホストのファイルシステムの特性がそのまま反映される
- MacではAPFS、Linuxではext4を採用
- APFS:
case-insensitive
- ext4:
case-sensitive
- 環境差分を無くすために、ファイル名は
case-sensitiveで統一するのが無難
Docker(コンテナ型仮想化)で環境差分を吸収できると慢心していましたが、バインドマウントを利用する場合はホスト側のファイルシステムの違いに注意が必要、という学びがありました。
補足
APFSでcase-sensitiveなマウントを作成する方法
どうやらボリュームの作り方を工夫すれば大文字/小文字の区別をさせられるようです。
https://forums.docker.com/t/volume-mounts-case-insensitive/18867/3
https://github.com/docker/for-mac/issues/320#issuecomment-428656077
同じような事象に関する資料
調べてみると同じようなことが起きている数年前の資料がいくつか出てきました。
https://please-sleep.cou929.nu/afps-and-docker-for-mac-fs-case-sensitivity.html
https://qiita.com/ABE_TAKASHI/items/5bfd526862385d301f53
https://qiita.com/yutachaos/items/544a6b547303ca7bcb85
https://stackoverflow.com/questions/64645430/why-does-mac-osx-still-exhibit-case-insensitivity-when-using-docker-desktop-for
ファイルシステムの確認
Mac
$ diskutil info / | grep 'File System Personality'
もしくは
$ mount | grep '/dev/disk'
Linux
もしくは
Discussion