Container / Technology · 2022年11月9日

Mac上跑Linux Docker的问题 – Case Sensitive

我遇到这个问题还要从一次代码编译说起。

大家知道,使用docker的一大好处就是研发之间可以非常轻松地“传递”一个环境给另一个人,从而大大减少重复配置带来的工作量和风险。所以我从一个研发哥们手中拿到了他在使用的编译环境的dockerfile,然后开开心心地mount自己的代码,在我的Macbook上跑了起来。

编着编着出错了,regex_t的定义找不到。这个定义很明显在regex.h中,只要正确include就不会有问题。而且我之前遇到过类似的错误,问题的原因往往是因为在某个目录下有另一个regex.h覆盖了系统的regex.h。查看编译日志的时候我发现gcc传入了一些-I的参数,指定了一些目录。啊哈,那就简单了,去这些目录下查看是否有regex.h就能确认问题了。

但是当我用find去找的时候,没有找到任何regex.h文件。这下困惑了。难道我之前的分析有误?程序使用了系统的regex.h但是仍然有错?

于是我尝试把/usr/include下的regex.h手工拷贝到当前目录,在把#include从<regex.h>改成”regex.h”,结果程序编译通过了……也就是说,系统下的regex.h本身并没有错,只要被正确include,就没有问题……

这个问题大约卡了我一天,直到我写了个小程序,一点一点地传入-I参数的时候,突然报引用了某个目录下的regex.h出错。

咦?我已经find那个目录了,怎么会有regex.h漏掉?

于是我赶紧ls,用tab联想,没有。

但是如果直接ls 全路径,又能显示有这个文件,相当诡异。

直到我到了那个目录下一点一点查看,才发现原来这个目录下有个自定义的Regex.h,R大写。

难道说,不区分大小写?那为什么我同事那里跑没事呢?

原因就出在硬盘的Case sensitive上。

首先Linux是Case sensitive的,也就是区别大小写。

Windows是不区分大小写的,也就是case insensitive。

MacOS呢?默认下也是不区分大小写的,case insensitive。

那么我在MacOS上跑Linux Docker container,里面的硬盘是case sensitive还是insensitive?

这个取决于你是否mount。

如果你mount了Mac的目录到container里,那么这块空间继承了Mac的属性,不区分大小写。

如果你不mount而是COPY到container里,那么那块空间是Linux的属性,区分大小写。

参考官方文档《File system sharing (osxfs)》:

Case sensitivity

With Docker for Mac, file systems operate in containers in the same way as they operate in macOS. If a file system on macOS is case-insensitive, that behavior is shared by any bind mount from macOS into a container.

On macOS Sierra and lower, the default file system is HFS+. On macOS High Sierra, the default file system is APFS. Both are case-insensitive by default but available in case-sensitive and case-insensitive variants.

To get case-sensitive behavior, format the volume used in your bind mount as HFS+ or APFS with case-sensitivity. See the APFS FAQ.

Reformatting your root partition is not recommended as some Mac software relies on case-insensitivity to function.

默认下,现在的MacOS用的都是APFS。

那解决办法有哪些呢?

首先很可惜,没有任何一个解决办法在我看来是完美的。

方法一:在Mac上使用磁盘工具分出一块磁盘,刷成case sensitive的。参考Aaron Saray的博客《Add a Case-Sensitive Disk in MacOS》,主要是在format的时候选择Case-sensitive的选项。然后将这块硬盘存放代码,mount进docker container中。

方法二:不mount,用COPY,把代码放入container中。

方法三:改代码,避免出现同名不同大小写的文件。

可能方法一是最直接的,但是既然MacOS的设计上就是为了case insensitive,那么用这样的磁盘就只能单纯地存放代码用了。

方法二,本来mount就是为了方便更改,这下好了,不但container的大小会一下大好多,每次都还得重新编译image。

方法三就是动代码了,workaround还不能提交。