2.13. 使用容器#

2.13.1. 在 Docker 中运行工具#

Docker 容器可以为软件及其依赖的组件提供确知可正常使用的完整运行环境,进而简化软件的安装。不过,容器与宿主系统是刻意隔离开来的。因此,要在 Docker 容器中运行某个工具,需进行额外操作,以使输入文件在容器内可用,而输出文件可从容器中提取。CWL 运行程序可以自动完成这个任务,从而帮助您使用 Docker 简化对软件的管理,同时避免调用和管理 Docker 容器等复杂操作。

CWL 运行程序的任务之一,就是调整输入文件的路径,以反映它们在容器内的位置。

本示例在 Docker 容器中运行一个简单的 Node.js 脚本,将 "Hello World" 写入标准输出。

docker.cwl#
#!/usr/bin/env cwl-runner
cwlVersion: v1.2
class: CommandLineTool
baseCommand: node
hints:
  DockerRequirement:
    dockerPull: node:slim
inputs:
  src:
    type: File
    inputBinding:
      position: 1
outputs:
  example_out:
    type: stdout
stdout: output.txt
docker-job.yml#
src:
  class: File
  path: hello.js

运行这个例子前,我们先来看看代码各部分都是为了做什么。这里,多数内容已在此前的章节中讲授,而算得上新内容的就是 dockerRequirement 这一段代码。

baseCommand: node
hints:
  DockerRequirement:
    dockerPull: node:slim

baseCommand: node 告诉 CWL, 我们运行命令打算使用 Node.js 环境,意在执行 JavaScript 程序。然后,我们需要给出一点“提示” (hints), 以便找到我们需要的容器。这个例子里,我们只需要在 DockerRequirements 下列出我们 Docker 容器的需求即可。dockerPull: 参数应赋予的值,就是您假如使用 docker pull 命令时会给出的参数,即容器映像 (image) 的名称(您可进一步标明容器的标签即 tag, 用容器从事可复现研究工作时这是个好习惯)。这个例子里,我们使用的容器名为 node:slim.

创建一个名为 "hello.js" 的 JavaScript 文件,然后在命令行中,以工具描述和输入对象为参数,调用 cwltool:

hello.js#
console.log("Hello World");
$ cwltool docker.cwl docker-job.yml
INFO /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'docker.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/using-containers/docker.cwl'
INFO [job docker.cwl] /tmp/bg9l130g$ docker \
    run \
    -i \
    --mount=type=bind,source=/tmp/bg9l130g,target=/rNuzpC \
    --mount=type=bind,source=/tmp/60m1l92p,target=/tmp \
    --mount=type=bind,source=/home/runner/work/user_guide/user_guide/src/_includes/cwl/using-containers/hello.js,target=/var/lib/cwl/stg370b2907-2f1c-4336-b392-a7cd99b22130/hello.js,readonly \
    --workdir=/rNuzpC \
    --read-only=true \
    --net=none \
    --log-driver=none \
    --user=1001:127 \
    --rm \
    --cidfile=/tmp/60g0m37g/20240518114801-261148.cid \
    --env=TMPDIR=/tmp \
    --env=HOME=/rNuzpC \
    node:slim \
    node \
    /var/lib/cwl/stg370b2907-2f1c-4336-b392-a7cd99b22130/hello.js > /tmp/bg9l130g/output.txt
INFO [job docker.cwl] completed success
{
    "example_out": {
        "location": "file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/using-containers/output.txt",
        "basename": "output.txt",
        "class": "File",
        "checksum": "sha1$648a6a6ffffdaa0badb23b8baf90b6168dd16b3a",
        "size": 12,
        "path": "/home/runner/work/user_guide/user_guide/src/_includes/cwl/using-containers/output.txt"
    }
}INFO Final process status is success
$ cat output.txt
Hello World

请注意,CWL 运行程序构建出了一行 Docker 命令,用来运行脚本。

这个例子中,在容器外部,脚本 hello.js 的路径位于 /home/me/cwl/user_guide/hello.js, 而它在容器内的路径为 /var/lib/cwl/job369354770_examples/hello.js, 这从 node 命令的调用参数可见。