Python C++ 的包装器。so 仅在将代码复制到 docker 图像时可用,而不是在按卷安装时可用?

Python wrapper for C++ .so is only useable when COPY'ing code into docker image and not when mounted by volume?

我已经有点成功地 docker 化了一个 software repository (KPConv) 我计划使用并扩展以下 Dockerfile

FROM tensorflow/tensorflow:1.12.0-devel-gpu-py3

# Install other required python stuff
RUN apt-get update && apt install -y --fix-missing --no-install-recommends\
    python3-setuptools python3-pip python3-tk

RUN pip install --upgrade pip
RUN pip3 install numpy scikit-learn psutil matplotlib pyqt5 laspy 

# Compile the custom operations and CPP wrappers
# For some reason this must be done within container, cannot access libcuda.so during docker build
# Ref: 
#COPY . /kpconv
#WORKDIR /kpconv/tf_custom_ops
#RUN sh compile_op.sh
#WORKDIR /kpconv/cpp_wrappers
#RUN sh compile_wrappers.sh

# Set the working directory to kpconv
WORKDIR /kpconv

# Set root user password so we can su/sudo later if need be
RUN echo "root:pass" | chpasswd

# Create a user and group akin to the host within the container
ARG USER_ID
ARG GROUP_ID
RUN addgroup --gid $GROUP_ID user
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
USER user

#Build
#sudo docker build -t kpconv-test \
#    --build-arg USER_ID=$(id -u) \
#    --build-arg GROUP_ID=$(id -g) \
#    .

在这个 Dockerfile 的末尾,我遵循了 post found here,它描述了一种正确设置生成文件权限的方法 by/within 容器,以便主机 machine/user 无需更改文件权限即可访问它们。

此外,此软件存储库使用 custom tensorflow operations in C++ (KPConv/tf_custom_ops) along with Python wrappers for custom C++ code (KPConv/cpp_wrappers)。 KPConv 的作者 Thomas Hugues 提供了一个 bash 脚本,它编译每个文件以生成各种 .so 文件。


如果我 COPY 在构建过程中将存储库添加到映像中 (COPY . /kpconv),启动容器,调用两个编译 bash 脚本和 运行 然后代码 Python 正确加载 C++ 包装器(生成的 .so grid_subsampling.cpython-35m-x86_64-linux-gnu.so)并开始 运行 将软件作为 expected/intended。

$ sudo docker run -it \
>     -v /<myhostpath>/data_sets:/data \
>     -v /<myhostpath>/_output:/output \
>     --runtime=nvidia kpconv-test /bin/bash
user@eec8553dcb5d:/kpconv$ cd tf_custom_ops 
user@eec8553dcb5d:/kpconv/tf_custom_ops$ sh compile_op.sh 
user@eec8553dcb5d:/kpconv/tf_custom_ops$ cd ..
user@eec8553dcb5d:/kpconv$ cd cpp_wrappers/
user@eec8553dcb5d:/kpconv/cpp_wrappers$ sh compile_wrappers.sh 
running build_ext
building 'grid_subsampling' extension
<Redacted for brevity>
user@eec8553dcb5d:/kpconv/cpp_wrappers$ cd ..
user@eec8553dcb5d:/kpconv$ python training_ModelNet40.py 

Dataset Preparation
*******************

Loading training points
1620.2 MB loaded in 0.6s

Loading test points
411.6 MB loaded in 0.2s
<Redacted for brevity>

这很好用,让我 运行 KPConv 软件。

还要注意稍后 .so 文件具有散列

user@eec8553dcb5d:/kpconv/cpp_wrappers/cpp_subsampling$ sha1sum grid_subsampling.cpython-35m-x86_64-linux-gnu.so 
a17eef453f6d2370a15bc2a0e6714c978390c5c3  grid_subsampling.cpython-35m-x86_64-linux-gnu.so

也有权限

user@eec8553dcb5d:/kpconv/cpp_wrappers/cpp_subsampling$ ls -al grid_subsampling.cpython-35m-x86_64-linux-gnu.so 
-rwxr-xr-x 1 user user 561056 Mar 14 02:16 grid_subsampling.cpython-35m-x86_64-linux-gnu.so

虽然它产生了一个困难的工作流程,以便快速编辑和软件用于我的目的,并快速 运行将其安装在容器中。对代码的每次更改都需要重新构建图像。因此,我宁愿 mount/volume 在 运行 时间从主机到容器的 KPConv 代码,然后编辑在容器内“实时”,因为它是 运行ning.

这样做并使用post(没有COPY . /kpconv)顶部的Dockerfile来编译镜像,执行相同的编译步骤,运行代码

$ sudo docker run -it \
>     -v /<myhostpath>/data_sets:/data \
>     -v /<myhostpath>/KPConv_Tensorflow:/kpconv \
>     -v /<myhostpath>/_output:/output \
>     --runtime=nvidia kpconv-test /bin/bash
user@a82e2c1af21a:/kpconv$ cd tf_custom_ops/
user@a82e2c1af21a:/kpconv/tf_custom_ops$ sh compile_op.sh 
user@a82e2c1af21a:/kpconv/tf_custom_ops$ cd ..
user@a82e2c1af21a:/kpconv$ cd cpp_wrappers/
user@a82e2c1af21a:/kpconv/cpp_wrappers$ sh compile_wrappers.sh 
running build_ext
building 'grid_subsampling' extension
<Redacted for brevity>
user@a82e2c1af21a:/kpconv/cpp_wrappers$ cd ..
user@a82e2c1af21a:/kpconv$ python training_ModelNet40.py 

我收到以下 Python ImportError

user@a82e2c1af21a:/kpconv$ python training_ModelNet40.py 
Traceback (most recent call last):
  File "training_ModelNet40.py", line 36, in <module>
    from datasets.ModelNet40 import ModelNet40Dataset
  File "/kpconv/datasets/ModelNet40.py", line 40, in <module>
    from datasets.common import Dataset
  File "/kpconv/datasets/common.py", line 29, in <module>
    import cpp_wrappers.cpp_subsampling.grid_subsampling as cpp_subsampling
ImportError: /kpconv/cpp_wrappers/cpp_subsampling/grid_subsampling.cpython-35m-x86_64-linux-gnu.so: failed to map segment from shared object

为什么 C++ 的 Python 包装器仅在将代码复制到 docker 图像时可用,而不是按卷安装时可用?

这个 .so 文件与第一个描述的情况具有相同的散列和权限

user@a82e2c1af21a:/kpconv/cpp_wrappers/cpp_subsampling$ sha1sum grid_subsampling.cpython-35m-x86_64-linux-gnu.so 
a17eef453f6d2370a15bc2a0e6714c978390c5c3  grid_subsampling.cpython-35m-x86_64-linux-gnu.so
user@a82e2c1af21a:/kpconv/cpp_wrappers/cpp_subsampling$ ls -al grid_subsampling.cpython-35m-x86_64-linux-gnu.so 
-rwxr-xr-x 1 user user 561056 Mar 14 02:19 grid_subsampling.cpython-35m-x86_64-linux-gnu.so

在我的主机上,该文件具有以下权限(它在主机上,因为 /kpconv 作为卷安装)(出于某种原因,容器也在未来,请检查时间戳)

$ ls -al grid_subsampling.cpython-35m-x86_64-linux-gnu.so 
-rwxr-xr-x 1 <myusername> <myusername> 561056 Mar 13 21:19 grid_subsampling.cpython-35m-x86_64-linux-gnu.so

在对错误消息进行一些研究之后,似乎每个结果都是针对特定情况的。尽管大多数人似乎都提到错误是某种 permissions issue.

的结果

我认为这个 Unix&Linux Stack answer 提供了实际问题的答案。但是我在大学实习时使用 C++ 的日子有点太远了,不一定了解如何使用它来解决这个问题。但我认为问题在于容器和主机之间以及每个用户之间的权限(即容器上的 root,容器上的 user (Dockerfile),主机上的 root,以及 <myusername> 在主机上)。


我还尝试过先使用在 Dockerfile 中创建的 root 密码提升容器内的权限,然后编译代码,然后 运行 安装软件。但这会导致同样的问题。我也曾尝试在容器中将代码编译为 user,但是 运行 以 root 身份将软件编译,同样的问题。

因此,我发现并提供的另一条线索是,.so 在“仅在”容器内编译时(没有 --volume)和在容器内编译时似乎有些不同--volume(这就是我尝试比较文件哈希值的原因)。所以也许它的权限不是那么多,而是内核如何将 .so 加载到容器中,或者它在 --volume 中的位置如何影响加载过程?


编辑: 至于 SSCCE,您应该能够将链接的存储库克隆到您的计算机并使用相同的 Dockerfile。您不需要指定 /data/output 卷或以任何方式更改代码(它会尝试在加载数据之前加载 .so (这只会出错并结束执行) )

如果您没有 GPU 或不想 install nvidia-runtime you should be able to alter the Dockerfile base image to tensorflow:1.12.0-devel-py3 和 运行 CPU 上的代码。

您的问题是由尝试动态加载库的链接器造成的。这可能有几个根本原因:

  1. 权限。用户应该有加载库的权限,因此在 docker 中挂载文件系统时,主机中的所有者 ID 和组 ID 不必与容器中的相同 ID,尽管它们可能是相同的名称.
  2. 错误的二进制格式。主机 OS 正在以错误的格式编译二进制文件。如果您 运行 在(通过示例)macOS 上编译并在 linux 容器中使用它,就会发生这种情况。
  3. 安装错误。例如,使用 noexec 安装也会阻止加载库。
  4. 两种环境的库不同。由于库编译环境的不同,可能会漏掉一些库,所以使用ldd grid_subsampling.cpython-35m-x86_64-linux-gnu.soldd -r -d -v grid_subsampling.cpython-35m-x86_64-linux-gnu.so检查所有链接的库。