python - Docker 容器中的 os.getlogin 抛出 FileNotFoundError

标签 python docker

如果在 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/

相关文章:

python - 将 Thrift 客户端连接到同一主机上不同 docker 容器中的 Thrift 服务器

docker - 为什么我的容器化 Selenium 应用程序仅在 AWS Lambda 中失败?

python-openstackclient issubclass() arg 1 必须是一个类

amazon-ec2 - 从磁盘安装数据卷时,使用 docker 时,声纳质量配置文件页面无法正确显示。

docker - Gitlab CI中的Docker执行程序被错误的URL传递给Git存储库

mysql - 在 Docker 中配置 mysql

docker - 错误: No such container while using docker exec command

python - 当使用 -O 优化时,ctypes 返回错误值

python - 从 2D numpy 数组中删除运行

python - 向量化前瞻性函数 pandas 数据框