Skip to main content

Docker Spaces

Spaces 支持使用自定义 Docker 容器 来运行超出 Streamlit 和 Gradio 范围的应用。Docker Spaces 让用户可以突破标准 SDK 之前的限制:从 FastAPI、Go 端点,到 Phoenix 应用和各类 ML Ops 工具,Docker Spaces 都可以很好地承载。

设置 Docker Spaces

创建新 Space 时选择 Docker 作为 SDK,会在 README.md 的 YAML 块中将 sdk 属性设置为 docker 来初始化 Space。或者,对于已有的 Space 仓库,你也可以直接在 README.md 顶部的 YAML 区块中写上 sdk: docker。你还可以通过设置 app_port: 7860 来修改默认对外暴露的端口 7860。之后,就可以像平时那样编写常规的 Dockerfile

---
title: Basic Docker SDK Space
emoji: 🐳
colorFrom: purple
colorTo: gray
sdk: docker
app_port: 7860
---

在容器内部,你可以按需开放任意数量的端口。例如,你可以在 Space 中安装 Elasticsearch,并在内部通过其默认端口 9200 调用。

如果你希望对外暴露运行在多个端口上的应用,一个常见做法是在容器中使用 Nginx 等反向代理:对外只打开一个端口,由反向代理将互联网请求分发到不同的内部端口。

密钥和变量管理

你可以在 Space 设置中管理 Space 的环境变量。了解更多信息请参阅此处

变量

构建时

在构建 Docker Space 时,变量会作为 build-arg 传入。如何在 Dockerfile 中使用这些参数,可以参考 Docker 官方文档

	# 使用 ARG 声明环境变量
ARG MODEL_REPO_NAME

FROM python:latest
# [...]
# 像环境变量一样使用它们
RUN predict.py $MODEL_REPO_NAME

运行时

在运行时,这些变量会注入到容器的环境变量中。

密钥

构建时

出于安全考虑,Docker Spaces 中 secrets 的管理方式与普通变量不同。你可以在 Settings 标签页 中创建 secret,然后在 Dockerfile 中通过挂载的方式在构建阶段使用它。

例如,如果 SECRET_EXAMPLE 是你在 Settings 中创建的 secret 名称,就可以在构建时将其挂载为文件,然后通过 $(cat /run/secrets/SECRET_EXAMPLE) 读取其值。

示例如下:

# 在构建时暴露 secret SECRET_EXAMPLE 并使用其值作为 git remote URL
RUN --mount=type=secret,id=SECRET_EXAMPLE,mode=0444,required=true \
git init && \
git remote add origin $(cat /run/secrets/SECRET_EXAMPLE)
# 在构建时暴露 secret SECRET_EXAMPLE 并使用其值作为 Bearer token 用于 curl 请求
RUN --mount=type=secret,id=SECRET_EXAMPLE,mode=0444,required=true \
curl test -H 'Authorization: Bearer $(cat /run/secrets/SECRET_EXAMPLE)'

运行时

和公共 Variables 一样,在运行时你可以以环境变量的方式访问 secrets。例如,在 Python 中可以使用 os.environ.get("SECRET_EXAMPLE")。可以参考这个使用 secrets 的 Docker Space 示例:secret-example

权限

容器以用户 ID 1000 运行。为避免权限问题,你应在进行任何 COPY 或下载操作之前创建一个用户并设置其 WORKDIR

# 创建一个名为 "user" 的新用户,并设置用户 ID 为 1000
RUN useradd -m -u 1000 user

# 切换到 "user" 用户
USER user

# 设置 home 为用户的 home 目录
ENV HOME=/home/user \
PATH=/home/user/.local/bin:$PATH

# 设置工作目录为用户的 home 目录
WORKDIR $HOME/app

# 尝试运行 pip 命令,设置用户后避免 Python 的权限问题
RUN pip install --no-cache-dir --upgrade pip

# 将当前目录内容复制到容器中的 $HOME/app 目录,并设置所有者为 user
COPY --chown=user . $HOME/app

# 下载一个 checkpoint
RUN mkdir content
ADD --chown=user https://<SOME_ASSET_URL> content/<SOME_ASSET_NAME>
warning

始终在 ADDCOPY 中指定 --chown=user,以确保新文件归你的用户所有。

如果你仍然遇到权限问题,可能需要在 Dockerfile 中使用 chmodchown 来授予正确的权限。例如,如果你想使用目录 /data,可以这样做:

RUN mkdir -p /data
RUN chmod 777 /data

你应该始终避免多余的 chown 操作。

warning

更新文件的元数据会在新层中创建新副本。因此,递归 chown 可能会由于所有受影响文件的重复而导致镜像非常大。

与其通过运行 chown 来修复权限:

COPY checkpoint .
RUN chown -R user checkpoint

你应该始终这样做:

COPY --chown=user checkpoint .

ADD 命令也是如此)

数据持久化

每当你的 Docker Space 重启时,写入磁盘的数据都会丢失,除非你选择持久化存储升级。

如果你选择持久化存储升级,可以使用 /data 目录来存储数据。该目录挂载在持久化卷上,这意味着写入此目录的数据将在重启后保留。

warning

目前,/data 卷仅在运行时可用,即你不能在 Dockerfile 的构建步骤中使用 /data

你也可以在特定情况下使用我们的 Datasets Hub,你可以在 git LFS 仓库中存储状态和数据。你可以在此处找到一个持久化示例:space_to_dataset_saver,它使用 huggingface_hub以编程方式将文件上传到数据集仓库。这个 Space 示例以及本指南将帮助你确定最适合你数据类型的解决方案。

最后,在某些情况下,你可能希望从 Space 的代码中使用外部存储解决方案,如外部托管数据库、S3 等。

带 GPU 的 Docker 容器

你可以通过使用我们的 GPU 类型 Spaces 硬件之一来运行支持 GPU 的 Docker 容器。

我们建议使用 Docker Hub 上的 nvidia/cuda 作为基础镜像,它预装了 CUDA 和 cuDNN。

warning

在 Docker 构建期间,你无法访问 GPU 硬件。因此,你不应该在 Dockerfile 的构建步骤中尝试运行任何与 GPU 相关的命令。例如,在构建镜像时不能运行 nvidia-smitorch.cuda.is_available()。了解更多信息请参阅此处

延伸阅读