ln 强制覆盖 symlink 失败问题研究

2023-03-08 14:34:57 浏览数 (2)

最近公司 CI 升级,将 docker 基镜像由原先的 debian 切换到了 ubuntu,导致应用一旦成功启动之后,再次执行重启将会持续失败。查看日志,发现打印 ln: failed to access '/tmp/access.log/stdout': Not a directory

看来是 ln 执行失败,导致 docker entrypoint 无法执行成功,所以一直 restarting,查看其 entrypoint.sh 检查 ln 相关逻辑:ln -sf /dev/stdout /tmp/access.log

似乎并没有问题,那为啥后面几次执行会把 access.log 识别为一个目录呢?奇怪的是,debian 镜像就没有这个问题:

代码语言:javascript复制
$ docker run -it --rm debian:10 bash
> ln -s /dev/stdout /tmp/access.log
> ln -s /dev/stdout /tmp/access.log
> exit
$ docker run -it --rm ubuntu:22.04 bash
> ln -s /dev/stdout /tmp/access.log
> ln -s /dev/stdout /tmp/access.log
ln: failed to access '/tmp/access.log/stdout': Not a directory

更为魔幻的是,同事们纷纷表示本地无法复现,只有我的开发环境有这个问题。无奈,只能寄希望于 strace 找到一些蛛丝马迹:

代码语言:javascript复制
$ docker run --privileged -it --rm ubuntu:22.04 bash
> apt-get update && apt-get install strace
> ln -sf /dev/stdout /tmp/access.log
> strace ln -sf /dev/stdout /tmp/access.log
...
symlinkat("/dev/stdout", AT_FDCWD, "/tmp/access.log") = -1 EEXIST (File exists)
openat(AT_FDCWD, "/tmp/access.log", O_RDONLY|O_PATH|O_DIRECTORY) = 3
symlinkat("/dev/stdout", 3, "stdout")   = -1 ENOTDIR (Not a directory)
newfstatat(3, "stdout", 0x7fffcaf03a90, AT_SYMLINK_NOFOLLOW) = -1 ENOTDIR (Not a directory)
write(2, "ln: ", 4ln: )
...

有意思的地方出现了,openat(AT_FDCWD, "/tmp/access.log", O_RDONLY|O_PATH|O_DIRECTORY) = 3 表示将 /tmp/access.log 按照目录打开。理论上应该返回 -1 才对,但我这里却返回了 3 表示可以成功打开,也就是当真把 /tmp/access.log 识别成了一个目录。但是在同事们的环境中,却真真实实的返回了 openat(AT_FDCWD, "/tmp/access.log", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOTDIR (Not a directory)

经询问,大家使用的内核都是 5.x,而只有我的环境用的是 3.10

0 人点赞