# Dockerfile 对 GitHub Actions 的支持

在为 Docker 容器动作创建 Dockerfile 时，你应该了解一些 Docker 指令如何与 GitHub Actions 以及动作的元数据文件进行交互。

### 用户

Docker 操作必须由默认 Docker 用户 (root) 运行。 不要在 `USER` 中使用 `Dockerfile` 指令，因为你将无法访问 `GITHUB_WORKSPACE` 目录。 有关详细信息，请参阅 Docker 文档中的“[变量参考](/zh/actions/reference/variables-reference#default-environment-variables)”和“[USER 参考](https://docs.docker.com/engine/reference/builder/#user)”。

### FROM

```
          `Dockerfile` 中的第一条指令必须为 `FROM`，该指令用于选择 Docker 基础映像。 有关详细信息，请参阅 Docker 文档中的 [FROM 参考](https://docs.docker.com/engine/reference/builder/#from)。
```

以下是设置 `FROM` 参数时的一些最佳做法：

* 建议使用正式的 Docker 映像。 例如，`python` 或 `ruby`。
* 使用版本标记（如果有），最好使用主要版本。 例如，请使用 `node:10` 而不是 `node:latest`。
* 建议使用基于 [Debian](https://www.debian.org/) 操作系统的 Docker 映像。

### WORKDIR

GitHub 在 `GITHUB_WORKSPACE` 环境变量中设置工作目录路径。 建议不要在 `WORKDIR` 中使用 `Dockerfile` 指令。 在操作执行之前，GitHub 将在 Docker 映像中位于该位置的任何项目上安装 `GITHUB_WORKSPACE` 目录，并将 `GITHUB_WORKSPACE` 设置为工作目录。 有关详细信息，请参阅 Docker 文档中的“[变量参考](/zh/actions/reference/variables-reference#default-environment-variables)”和“[WORKDIR 参考](https://docs.docker.com/engine/reference/builder/#workdir)”。

### ENTRYPOINT

如果在操作的元数据文件中定义 `entrypoint`，它将替代 `ENTRYPOINT` 中定义的 `Dockerfile`。 有关详细信息，请参阅“[元数据语法参考](/zh/actions/creating-actions/metadata-syntax-for-github-actions#runsentrypoint)”。

Docker `ENTRYPOINT` 指令具有 shell 形式和 exec 形式 。 Docker `ENTRYPOINT` 文档建议使用 `ENTRYPOINT` 指令的 *exec* 形式。 有关 exec 和 shell 形式的详细信息，请参阅 Docker 文档中的 *ENTRYPOINT 参考* 。

不应使用 `WORKDIR` 在 Dockerfile 中指定入口点。 相反，您应使用绝对路径。 有关详细信息，请参阅 [WORKDIR](#workdir)。

如果将容器配置为使用 `ENTRYPOINT` 指令的 *exec* 形式，则在操作的元数据文件中配置的 `args` 将不会在命令 shell 中运行。 如果操作的 `args` 包含环境变量，则不会替换该变量。 例如，使用以下 *exec* 格式将不会打印存储在 `$GITHUB_SHA` 中的值，而是会打印 `"$GITHUB_SHA"`。

```dockerfile
ENTRYPOINT ["echo $GITHUB_SHA"]
```

如果需要变量替换，则可以使用 *shell* 形式或直接执行 shell。 例如，如果使用以下 exec 格式，可执行 shell 以打印存储在 \_\_ 环境变量中的值。

```dockerfile
ENTRYPOINT ["sh", "-c", "echo $GITHUB_SHA"]
```

若要将操作的元数据文件中定义的 `args` 提供给使用 \_\_ 中的 exec 形式的 Docker 容器，建议创建一个名为 `ENTRYPOINT` 的 shell 脚本，该脚本可通过 `entrypoint.sh` 指令调用：

#### 示例 Dockerfile

```dockerfile
# Container image that runs your code
FROM debian:9.5-slim

# Copies your code file from your action repository to the filesystem path `/` of the container
COPY entrypoint.sh /entrypoint.sh

# Executes `entrypoint.sh` when the Docker container starts up
ENTRYPOINT ["/entrypoint.sh"]
```

#### 示例 entrypoint.sh 文件

使用上面的示例 Dockerfile，GitHub 会将在操作的元数据文件中配置的 `args` 以参数形式发送给 `entrypoint.sh`。 在 `entrypoint.sh` 文件的顶部添加 `#!/bin/sh`[shebang](https://en.wikipedia.org/wiki/Shebang_\(Unix\))，以显式地使用系统的与 [POSIX](https://en.wikipedia.org/wiki/POSIX) 兼容的 shell。

```shell
#!/bin/sh

# `$#` expands to the number of arguments and `$@` expands to the supplied `args`
printf '%d args:' "$#"
printf " '%s'" "$@"
printf '\n'
```

您的代码必须是可执行的。 在工作流中使用 `entrypoint.sh` 文件之前，请确保该文件具有 `execute` 权限。 您可以使用此命令从终端修改权限：

```shell
chmod +x entrypoint.sh
```

当 `ENTRYPOINT` shell 脚本不可执行时，你将收到如下所示的错误：

```shell
Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"/entrypoint.sh\": permission denied": unknown
```

### CMD（命令提示符）

如果在操作的元数据文件中定义 `args`，`args` 将替代 `CMD` 中指定的 `Dockerfile` 指令。 有关详细信息，请参阅“[元数据语法参考](/zh/actions/creating-actions/metadata-syntax-for-github-actions#runsargs)”。

如果在 `CMD` 中使用 `Dockerfile`，请遵循以下准则：

1. 在操作的自述文件中记录必要的参数，并在 `CMD` 指令的中忽略它们。
2. 使用默认值，允许不指定任何 `args` 即可使用操作。
3. 如果操作显示 `--help` 标记或类似项，请使用它让你的操作能够自行记录。

## 支持的 Linux 功能

GitHub Actions 支持 Docker 所支持的默认 Linux 功能。 无法添加或删除功能。 有关 Docker 支持的默认 Linux 功能的详细信息，请参阅 Docker 文档中的“[Linux 内核功能](https://docs.docker.com/engine/security/#linux-kernel-capabilities)”。 若要详细了解 Linux 功能，请参阅 Linux 手册页中的“[Linux 功能概述](http://man7.org/linux/man-pages/man7/capabilities.7.html)”。