如果在 Docker 容器内执行,os.getlogin()
会抛出 FileNotFoundError: [Errno 2] No such file or directory
。
我知道Python Docs建议使用不同的方法,但在某些代码中我无法更改。
我正在使用 ubuntu 22.04 和 python 3.10.6(都在 Docker 容器内)。
我在 Windows 10 上使用 Docker Desktop 和 WSL2 进行托管。
这是一个 MWE:
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y \
python3
构建并运行它:
docker build -t mwe .
docker run -it mwe
然后在 Docker 容器内执行以下命令:
python3 -c "import os; print(os.getlogin())"
它会抛出错误:
Traceback (most recent call last):
File "<string>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory
有什么好的方法可以避免这个错误吗?
最佳答案
这与 Python os.getlogin problem 部分重复。 ,但那里的答案并没有真正触及问题的根源。
这不是 Python 问题。我们可以使用最小的 C 程序重现相同的行为:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char *login = getlogin();
if (login == NULL) {
perror("getlogin");
exit(1);
}
printf("login: %s\n", login);
exit(0);
}
如果您在容器中运行它,您将得到输出:
getlogin: No such device or address
通过glibc源码追踪,错误来自the getlogin code ,但实际上是由 __getlogin_r_loginuid
中的这段代码引起的:
if (uid == (uid_t) -1)
{
__set_errno (ENXIO);
return ENXIO;
}
我们之所以点击该代码是因为 /proc/self/loginuid
的值是:
$ cat /proc/self/loginuid
4294967295
其中 4294967295
只是 -1
,在本例中意味着“此值尚未初始化”,这是因为我们尚未通过以下方式进入此 shell通常会为我们设置的任何类型的登录管理器。
为了一个简单的解决方法,我们可以将当前的 UID 写入该文件:
root@d4da9e11f42e:~# python3 -c 'import os; print(os.getlogin())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
OSError: [Errno 6] No such device or address
root@d4da9e11f42e:~# echo $UID > /proc/self/loginuid
root@d4da9e11f42e:~# python3 -c 'import os; print(os.getlogin())'
root
这将相对容易融入您的容器启动中。
另一个选择是对 os
模块进行猴子修补,以用其他内容替换 os.getlogin
(例如 getpass.getuser()
)。
听起来好像 WSL 不提供 loginuid
功能。
这个答案仍然准确地识别了问题:getlogin()
无法读取 loginuid
文件,并且您会收到相同的错误(您可以在前面的代码路径中看到该代码路径) __getlogin_r_loginuid
的实现。
如果您的内核不提供此功能,那么您唯一真正的选择是修复代码 - 通过修改对 os.getlogin()
的调用,或者通过安排猴子补丁 os
模块在遗留代码调用 os.getlogin()
之前。
一个不太正统的替代方案是使用 function interposition覆盖 getlogin
库调用。
从 fake_getlogin.c
中的最小 C
代码开始:
char *getlogin(void) {
return "root";
}
将其编译为共享库:
gcc -fPIC -c fake_getlogin.c
ld -o fake_getlogin.so -shared fake_getlogin.o
将其嵌入 Dockerfile 并安排通过 /etc/ld.so.preload
预加载它:
FROM ubuntu:22.04
RUN DEBIAN_FRONTEND=noninteractive \
apt-get update && \
apt-get install -y \
python3
COPY fake_getlogin.so /lib/fake_getlogin.so
RUN echo /lib/fake_getlogin.so > /etc/ld.so.preload
现在重新尝试您原来的重现器,您应该会发现它运行时没有错误:
root@4c86cde47db1:/# python3 -c 'import os; print(os.getlogin())'
root
这假设没有更多 WSL 特定的怪癖需要克服。
如果您决定在实践中使用函数插入解决方案,您可能应该安排构建共享库作为镜像构建过程的一部分;例如:
FROM ubuntu:22.04 AS builder
RUN DEBIAN_FRONTEND=noninteractive \
apt-get update && \
apt-get install -y \
build-essential
WORKDIR /src
COPY fake_getlogin.c ./
RUN gcc -fPIC -c fake_getlogin.c && \
ld -o fake_getlogin.so -shared fake_getlogin.o
FROM ubuntu:22.04
COPY --from=builder /src/fake_getlogin.so /lib/fake_getlogin.so
RUN DEBIAN_FRONTEND=noninteractive \
apt-get update && \
apt-get install -y \
python3
RUN echo /lib/fake_getlogin.so > /etc/ld.so.preload
关于python - Docker 容器中的 os.getlogin 抛出 FileNotFoundError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74187896/