将命名参数和文件像脚本一样传入和传出docker容器

2024-09-28 11:40:14 发布

您现在位置:Python中文网/ 问答频道 /正文

tl;dr:如何使用docker传递文件I/O+参数?还是应该放弃使用脚本之类的容器?

我正在努力学习docker,我很难掌握一些常见I/O和参数传递情况的简单示例。我已经浏览了很多StackOverflow内容,比如here以及Docker文档,但这似乎太简单了,没有人愿意回答。最接近的是here,但答案没有帮助,而且似乎大多是说“不要对Docker这样做”。但是人们似乎在谈论容器,好像他们可以在独立的应用程序中做这类事情

简而言之,在Docker中,似乎所有I/O路径都需要硬编码,但我希望这些路径能够灵活,因为我希望尽可能灵活地使用容器作为脚本。

在某些情况下,人们通过让容器处于空闲状态,然后将参数传递给它(例如herehere)来解决这个问题,但出于简单的目的,这似乎相当复杂

我不是在寻找使用venvs/conda的方法,我想看看使用Docker是否可行。

假设我有一个名为test.py的简单Python脚本:

 #!/usr/bin/env python3
 import argparse
 
 def parse_args():
     '''Parse CLI arguments
 
      Returns:
      dict: CLI arguments
     '''
     parser = argparse.ArgumentParser(description='Parse arguments for test')
     parser.add_argument('--out_file', '-o', required=True, type=str, help='output file')
     parser.add_argument('--in_file', '-i', required=True, type=str, help='input file')
     args = parser.parse_args()
     return vars(args)
 
 
 args = parse_args()
 
 with open(args["in_file"]) as input_handle:
     print(input_handle.readline())
 
 with open(args["out_file"], "w") as output_handle:
     output_handle.write("i wrote to a file")
                                                           

在Python中,我可以在一些输入文件上运行:

% cat ../input.txt
i am an input file

% python test.py -i ../input.txt -o output.txt
i am an input file

% cat output.txt 
i wrote to a file%             

让我们假设,无论出于何种原因,该脚本都需要进行docker化,同时保留参数/文件的传递方式,以便人们可以在没有docker的情况下运行它。我可以写一个非常简单的Dockerfile:

FROM continuumio/miniconda3

COPY . .

ENTRYPOINT ["python", "test.py"]

这将接受参数,但无法访问输入文件,即使完成,也无法访问输出:

% docker build .
Sending build context to Docker daemon  5.632kB
Step 1/3 : FROM continuumio/miniconda3
 ---> 52daacd3dd5d
Step 2/3 : COPY . .
 ---> 2e8f439e6766
Step 3/3 : ENTRYPOINT ["python", "test.py"]
 ---> Running in 788c40568687
Removing intermediate container 788c40568687
 ---> 15e93a7e47ed
Successfully built 15e93a7e47ed 

% docker run 15e93a7e47ed -i ../input.txt -o output.txt
Traceback (most recent call last):
  File "test.py", line 19, in <module>
    with open(args["in_file"]) as input_handle:
FileNotFoundError: [Errno 2] No such file or directory: '../input.txt'

然后,我可以尝试使用/inputs/卷挂载输入文件的目录,这让我大部分时间都在那里(尽管为一个文件传递两个参数很烦人),但这似乎不起作用:

docker run --volume /path/to/input_dir/:/inputs 15e93a7e47ed -i input.txt -o output.txt
Traceback (most recent call last):
  File "test.py", line 19, in <module>
    with open(args["in_file"]) as input_handle:
FileNotFoundError: [Errno 2] No such file or directory: 'input.txt'

我显然不了解如何在这里装入卷(可能设置WORKDIR会做很多这方面的工作),但是即使我可以装入卷,也不清楚如何将输出装载到装入的卷上,以便从容器外部访问它们。使用docker cp有一些manual solutions可以做到这一点,但整个要点是多少要自动化

似乎Dockerfile中的ENTRYPOINTCMD的字符串操作是不可能的。这样的方法似乎不可行:

ENTRYPOINT ["python", "test.py", "-i data/{i_arg}", "-o data/{o_arg}"]

在这里,我可以将一个文件写入已装入的卷/data/上的某个变量文件名中,我可以在运行时替换该文件


Tags: 文件dockerinpytesttxt脚本input
1条回答
网友
1楼 · 发布于 2024-09-28 11:40:14

如果您真的想在Docker中运行此脚本,那么通常需要的最少选项集是:

sudo                        \ # since you can bind-mount an arbitrary host directory
docker run                  \
   rm                      \ # clean up the container when done
  -it                       \ # some things depend on having a tty as stdout
  -u $(id -u):$(id -g)      \ # use host uid/gid
  -v "$PWD:$PWD"            \ # mount current directory into container
  -w "$PWD"                 \ # set working directory in container
  image-name                \
  -i input.txt -o output.txt  # .. won't work here

正如最后一条注释所指出的,这使得容器可以访问同一路径上的当前目录,但是如果要访问的文件位于父目录中,则无法访问该目录

从根本上说,Docker容器旨在与主机系统完全隔离。容器通常无法访问主机文件或主机设备,或查看主机uid到名称的映射。这种隔离导致了您注意到的许多事情:因为容器已经被隔离,所以您不需要虚拟环境来进行额外的隔离;由于容器是隔离的,/input是比/home/docker/src/my-project/data/input更容易记住的目录名

由于容器与主机隔离,因此需要访问的任何主机文件(输入或输出)都需要绑定装载到容器中。在我的示例中,我绑定挂载当前目录。在您的示例中,您有单独的/input/output容器目录,这两个目录都需要绑定装载到容器中

没有一种方法可以让这变得更容易,并且仍然使用Docker;在主机数据上运行进程不是它的设计目的。您的所有示例都是Python,Linux和MacOS系统通常都预装了Python,因此您可能会发现在虚拟环境中运行脚本要简单得多

python3 -m venv venv       # once only
./venv/bin/pip install .   # once only
./venv/bin/the_script -i ../input.txt output.txt

相关问题 更多 >

    热门问题