> For the complete documentation index, see [llms.txt](https://stablebuild.gitbook.io/stablebuild-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://stablebuild.gitbook.io/stablebuild-documentation/docs/cn/pinning-your-first-container.md).

# 固定你的第一个容器

在[为什么选择 StableBuild？](/stablebuild-documentation/docs/cn/why-stablebuild.md)中，我们分析了一个典型的 Dockerfile，展示了它如何依赖各种不稳定的服务和仓库——随时都可能破坏你的构建。作为回顾，这里再次列出那个 Dockerfile：

```docker
# 基于 Ubuntu 20.04 构建此 Dockerfile
FROM ubuntu:20.04
​
# 安装软件包时不显示提示
ARG DEBIAN_FRONTEND=noninteractive
​
# 安装一些 OS 级别的软件包
RUN apt update && apt install -y curl software-properties-common
​
# 我们需要较新的 Python 版本，从 Deadsnakes PPA 获取
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
    apt update && \
    apt install -y python3.9 python3.9-distutils
​
# 安装 pip（Python 包管理器）
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
    python3.9 get-pip.py
​
# 通过 pip 安装 Python 包
RUN pip3 install onnx==1.14.0
```

让我们用 StableBuild 让这个容器变得确定性且稳定！

### 注册 StableBuild

首先，前往 <https://dashboard.stablebuild.com> 注册（有免费的社区版计划可用）。注册后你将进入控制台：

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-2cde119ab1db05ddb404abee023fa29456202447%2FScreenshot%202024-03-11%20at%2011-17-45%20Dashboard%20-%20StableBuild.png?alt=media" alt=""><figcaption><p>StableBuild 控制台</p></figcaption></figure>

控制台提供对所有 StableBuild 镜像和仓库、用户和密钥管理以及计费信息的访问。

### 固定基础镜像

我们 Dockerfile 的第一行是：

```
FROM ubuntu:20.04
```

每次发布新的 Ubuntu 20.04 版本时，这个基础镜像都会在 Docker Hub 上被覆盖，甚至可能从注册表中被删除——导致不确定或失败的构建。要固定这个镜像，前往 **Dashboard > Docker mirror**。在这里你会找到你专属的 Docker 镜像服务器，可以作为前缀添加到 Dockerfile 中任何 `FROM` 语句。

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-2e805b521d54ca4e2715b05cba55219d9535e222%2Fimage%20(18).png?alt=media" alt=""><figcaption><p>你专属的 Docker 镜像服务器</p></figcaption></figure>

强烈建议同时使用更具体的标签（如果可用）。`ubuntu:20.04` 更新非常频繁，但 Ubuntu 20.04 的具体快照在 Docker Hub 上以 `focal-*` 标签发布。不固定 `ubuntu:20.04` 而是固定到例如 `focal-20231128`（Ubuntu 20.04 的 2023-11-28 版本），这样在需要时可以迁移到更新的 Ubuntu 版本，而无需在 StableBuild 中删除已固定的镜像。

因此，这里将你的 Dockerfile 修改为：

```docker
FROM your-prefix.dockermirror.stablebuild.com/ubuntu:focal-20231128
```

然后重新构建容器。第一次构建时，我们会从 Docker Hub 获取 `ubuntu:focal-20231128` 镜像（包括所有架构版本，不仅仅是你正在构建的架构），并存储到你自己的 StableBuild 缓存中。从现在起，这个镜像变为不可变。即使上游镜像被更新或删除，你始终会得到相同的基础镜像。

首次拉取后，控制台会更新：

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-2e7a1977ec7b5e7607b211ac74348ceb50e6cf33%2Fimage%20(19).png?alt=media" alt=""><figcaption><p>基础镜像现已缓存到 StableBuild</p></figcaption></figure>

如果你需要从缓存中删除镜像（例如因为你需要更新的 Ubuntu 20.04 镜像），可以在"Cached tags"中操作。

### 从 Ubuntu 软件包注册表和其他 PPA 固定软件包

接下来，我们的 Dockerfile 包含：

<pre class="language-docker"><code class="lang-docker"># 安装一些 OS 级别的软件包
<strong>RUN apt update &#x26;&#x26; apt install -y curl software-properties-common
</strong>
# 我们需要较新的 Python 版本，从 Deadsnakes PPA 获取
RUN add-apt-repository -y ppa:deadsnakes/ppa &#x26;&#x26; \
    apt update &#x26;&#x26; \
    apt install -y python3.9 python3.9-distutils
</code></pre>

这里我们从 Ubuntu 软件包注册表和 deadsnakes PPA 安装一些软件包，但这些软件包（包括版本和可用性）随时都可能变化。要固定它们，前往 **Dashboard > Ubuntu repository**。然后：

1. 下载 `sb-apt.sh`——这是一个用于设置仓库的 shell 脚本（你将从容器内运行它）；将其放在 Dockerfile 旁边。
2. 选择固定日期。我们创建完整的 Ubuntu（以及 Debian 和 Alpine）软件包注册表每日副本——包括所有软件包——以及许多 PPA，因此你需要固定到一个日期（即我们创建副本的日期）。如果你是创建新容器，直接使用默认值——即最新的镜像日期。
3. 选择要加载的软件包仓库。这里选择"Ubuntu"和"Deadsnakes (Python)"。\
   \
   *注意：如果你需要使用其他 PPA，只需给我们发消息——我们会将其添加到 StableBuild。*

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-0e944de14a7da0db7dcc87698e7c866a4dd3ca0e%2Fimage%20(24).png?alt=media" alt=""><figcaption><p>从 Ubuntu 软件包注册表（和其他 PPA）固定软件包</p></figcaption></figure>

然后，按照第 4 步的说明更新你的 Dockerfile，它现在应如下所示：

```docker
# 基于 Ubuntu 20.04（2023-11-28 版本）构建此 Dockerfile
FROM your-prefix.dockermirror.stablebuild.com/ubuntu:focal-20231128

ARG SB_API_KEY=your-prefix
ARG APT_PIN_DATE=2023-12-07T10:40:01Z

COPY ./sb-apt.sh /opt/sb-apt.sh
RUN bash /opt/sb-apt.sh load-apt-sources ubuntu deadsnakes

# 安装一些 OS 级别的软件包
RUN apt update && apt install -y curl software-properties-common

# 我们需要较新的 Python 版本，从 Deadsnakes PPA 获取（不再需要手动添加仓库）
RUN apt update && apt install -y python3.9 python3.9-distutils
```

重新构建容器，软件包现在将从 StableBuild 的镜像中拉取。

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-cd06f1541efd33c03b7ea19cbd0601b8f5950005%2Fimage%20(25).png?alt=media" alt=""><figcaption><p>软件包现在从 StableBuild 的镜像中拉取</p></figcaption></figure>

### 固定来自互联网的任意文件

接下来，我们的 Dockerfile 包含：

```docker
# 安装 pip（Python 包管理器）
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
    python3.9 get-pip.py
```

这里我们从互联网下载并安装一个文件。要固定任意 URL，前往 **Dashboard > File mirror**。

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-b68f1b27a59ad449bfe7e0310678598bd45dc50c%2Fimage%20(14).png?alt=media" alt=""><figcaption><p>StableBuild 中的文件镜像</p></figcaption></figure>

在这里你会找到一个可以添加到任何 URL 前面的前缀——这会自动缓存该文件并使其不可变。完成后，该文件会列在"Cached files"下，后续请求将始终返回完全相同的文件：

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-2fe5e9690c5ae9889f7b8fa1a8ea1f11b1842774%2Fimage%20(15).png?alt=media" alt=""><figcaption><p>在"my-first-tutorial"缓存键中查看已缓存的文件。</p></figcaption></figure>

对于大多数文件，这就足够了。然而，有些脚本在运行时会下载其他文件——对于这些情况，你还需要重写脚本中的 URL，或者设置某种 HTTP 代理。`get-pip.py` 就是这种情况——它只是一个引导脚本，会从 PyPI 注册表下载实际的 pip 包。

幸运的是，get-pip 提供了通过 `-i` 参数指定 PyPI 镜像的方式，我们可以使用 StableBuild 的 [PyPI 镜像](/stablebuild-documentation/docs/cn/jing-xiang-yu-huan-cun/pypi-mirror.md)来固定实际下载的软件包（参见下面的"固定 Python 软件包"）。你的 Dockerfile 现在应如下所示：

```docker
# 基于 Ubuntu 20.04（2023-11-28 版本）构建此 Dockerfile
FROM your-prefix.dockermirror.stablebuild.com/ubuntu:focal-20231128

ARG SB_API_KEY=your-prefix
ARG APT_PIN_DATE=2023-12-07T10:40:01Z

COPY ./sb-apt.sh /opt/sb-apt.sh
RUN bash /opt/sb-apt.sh load-apt-sources ubuntu deadsnakes

# 安装一些 OS 级别的软件包
RUN apt update && apt install -y curl software-properties-common

# 我们需要较新的 Python 版本，从 Deadsnakes PPA 获取（不再需要手动添加仓库）
RUN apt update && apt install -y python3.9 python3.9-distutils

# 安装 pip（Python 包管理器），从文件缓存下载 get-pip.py，
# 然后从 PyPI 镜像安装实际的软件包
RUN curl https://your-prefix.httpcache.stablebuild.com/my-first-tutorial/https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
    python3.9 get-pip.py -i https://your-prefix.pypimirror.stablebuild.com/2023-12-07/
```

### 固定 Python 软件包

最后，我们的 Dockerfile 包含：

```docker
# 通过 pip 安装 Python 包
RUN pip3 install onnx==1.14.0
```

看起来这将 onnx 软件包固定到了确切版本，但 onnx 本身有未固定的依赖项。要正确固定软件包及其所有子依赖项，前往 **Dashboard > Pypi mirror**。

在这里你需要选择固定日期。与 Ubuntu/Debian/PPA 软件包注册表一样，我们每日创建完整 PyPI 注册表的镜像——因此你可以从特定时间点安装完整的软件包列表。如果这是新容器，直接使用默认值——但旧版本的 PyPI 镜像对于复活旧的 Python 示例非常有用（参见 [PyPI 镜像 > 让旧的 Python 示例重新运行](/stablebuild-documentation/docs/cn/jing-xiang-yu-huan-cun/pypi-mirror.md#getting-older-python-examples-working)）。

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-edeb304cc9997563039e7f87c2c0511058067765%2Fimage%20(26).png?alt=media" alt=""><figcaption><p>使用 StableBuild 的 PyPI 镜像固定 Python 软件包</p></figcaption></figure>

要固定，只需更新 `pip3 install` 行以引用 StableBuild 镜像。你的 Dockerfile 现在应如下所示：

```docker
# 基于 Ubuntu 20.04（2023-11-28 版本）构建此 Dockerfile
FROM your-prefix.dockermirror.stablebuild.com/ubuntu:focal-20231128

ARG SB_API_KEY=your-prefix
ARG APT_PIN_DATE=2023-12-07T10:40:01Z

COPY ./sb-apt.sh /opt/sb-apt.sh
RUN bash /opt/sb-apt.sh load-apt-sources ubuntu deadsnakes

# 安装一些 OS 级别的软件包
RUN apt update && apt install -y curl software-properties-common

# 我们需要较新的 Python 版本，从 Deadsnakes PPA 获取（不再需要手动添加仓库）
RUN apt update && apt install -y python3.9 python3.9-distutils

# 安装 pip（Python 包管理器），从文件缓存下载 get-pip.py，
# 然后从 PyPI 镜像安装实际的软件包
RUN curl https://your-prefix.httpcache.stablebuild.com/my-first-tutorial/https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
    python3.9 get-pip.py -i https://your-prefix.pypimirror.stablebuild.com/2023-12-07/

# 通过 pip 安装 Python 包
RUN pip3 install \
    -i https://your-prefix.pypimirror.stablebuild.com/2023-12-07/ \
    install onnx==1.14.0
```

重新构建容器，完成。这个容器现在已完全固定，将始终安装相同的 OS 版本、软件包列表和 Python 依赖项。🎉

### 技巧与窍门

#### 流量日志

通过 StableBuild 拉取的所有内容都受流量限制约束（付费计划上超出部分会收费，请查看控制台了解你的配额）。当前计费周期的流量概览显示在控制台上，但要查看详细的流量日志，前往 **Dashboard > Billing > Raw usage logs**。这里列出了所有原始流量日志，包括服务、IP 和使用的 API 密钥：

<figure><img src="https://1490787039-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVXbSDKMfqtovGtrKeyN3%2Fuploads%2Fgit-blob-cf44c4cda936e23afa2c45f4b126b6d4dcadebf7%2Fimage%20(27).png?alt=media" alt=""><figcaption><p>StableBuild 中的原始流量日志</p></figcaption></figure>

#### 保护你的密钥

服务 URL（例如 `https://your-prefix.pypimirror.stablebuild.com/2023-12-07/` 中的部分）用于向 StableBuild 进行身份验证，并用于计费。请保持此 URL 私密，不要将其提交到公开仓库，否则任何人都可以使用你的账户从 StableBuild 拉取内容。如果你发现流量异常激增，请检查原始使用日志，看看是否有密钥泄露。你可以通过 **Dashboard > Keys** 轮换密钥。

要让公开仓库稳定且具有确定性，请改为创建一个[公开 API 密钥](/stablebuild-documentation/docs/cn/chan-pin-wen-dang/public-api-keys.md)。它是只读的，只提供你允许的依赖。
