我正在开发一个代码运行器/编译器网络服务。它由 2 项服务组成:
虽然当 HTTP/Web 在主机上运行时这可以完美工作,但在 Docker 中运行时我无法使其工作。
我用来从 Dockerized HTTP/Web 运行 CodeRunner 容器的脚本是:
#!/bin/bash
# Check if required arguments are provided
if [ $# -ne 4 ]; then
echo "Usage: $0 <RunnerImage> <Exercise> <InputDirectory> <OutputDirectory>"
exit 1
fi
# Assigning parameters to variables
RunnerImage="$1"
Exercise="$2"
InputDirectory="$3"
OutputDirectory="$4"
# Constructing JSON payload
json_payload="{\"Image\": \"$RunnerImage\", \"Cmd\": [\"$Exercise\", \"/input\", \"/output\"], \"HostConfig\": {\"Binds\": [\"$InputDirectory:/input\", \"$OutputDirectory:/output\"], \"NetworkMode\": \"none\"}}"
# Sending request to create container
response=$(curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" -d "$json_payload" -X POST http://localhost/v1.42/containers/create)
# Extracting container ID from response
container_id=$(echo "$response" | jq -r '.Id')
# Starting the container
curl --unix-socket /var/run/docker.sock -X POST "http://localhost/v1.42/containers/$container_id/start"
# Waiting for the container to finish
wait_response=$(curl --unix-socket /var/run/docker.sock -X POST "http://localhost/v1.42/containers/$container_id/wait")
# Extracting StatusCode from wait response
status_code=$(echo "$wait_response" | jq -r '.StatusCode')
echo "Container exited with status code: $status_code"
正如预期的那样,Docker 会将 $InputDirectory 和 $OutputDirectory 视为存在于主机上。
我该如何克服这个问题?
我尝试过:
{\"Binds\": [\"input:/input\"...
。
据我所知,这不起作用,因为它遇到了一些命名冲突(Docker Compose 带有一些 Compose 名称的前缀卷路径)有哪些可能的解决方案?我考虑为每个代码文件夹创建一个卷并将其传递到 CodeRunner 容器。
您无法将文件从一个容器装载到另一个容器。正如您在问题中注意到的,Docker 守护进程解析绑定安装路径,它们是主机系统上的目录,而不是调用 Docker 的上下文中的目录。 (在具有远程 Docker 守护进程或 Docker-in-Docker 容器的复杂环境中,绑定挂载位于 Docker 守护进程运行的上下文中,而不一定是本地系统中。)
它从 /input 文件夹中获取文件并将 results.json 返回到 /output 文件夹
主要读写本地文件的程序更容易作为本地进程运行。 Docker 容器具有独立的文件系统,您最终必须进行一些可能复杂的设置(包括使用户 ID 匹配)才能使用本地文件。
如果您可以将其作为子进程运行,那么这会容易得多。您需要在 Web 应用程序的 Dockerfile 中安装运行程序工具,然后您可以像调用任何其他子进程(如这个 shell 脚本)一样调用它。
# without doing anything Docker-related at all
"$Exercise" /input /output
否则,您需要在应用程序容器和运行器容器之间进行某种共享存储。主机目录或 Docker 命名卷都可以使用。我可能会在环境变量中传递此位置,因为它很容易依赖于特定的部署环境。由于您将路径作为命令行选项传递给运行程序,因此我将挂载整个共享存储,并在命令行上传递子路径。
使用命名卷的基本示例可能如下所示:
docker volume create shared
docker run -v shared:/shared ... web_app
然后,您的 Web 应用程序会将数据写入 /shared/input/$SOLUTION_ID
,并创建一个空的
/shared/output/$SOLUTION_ID
目录。当它启动运行器容器时,它看起来像
#!/bin/sh
RunnerImage="$1"
SharedVolume="$2"
Exercise="$3"
SolutionID="$4"
exec docker run \
--rm \
-v "$SharedVolume:/shared" \
--net none \
"$RunnerImage" \
"$Exercise" "/shared/input/$SolutionId" "/shared/output/$SolutionId"
(您可以将其翻译为驱动 Docker HTTP API 的手动 curl
命令,但是
docker run
可以做同样的事情,而且要短得多。)相同的解决方案适用于绑定安装的主机目录。在
docker run
语法中,相同的
-v
选项也可以使用,但使用绝对
/host/path
目录名称。我相信 API 调用略有不同。