# 私有镜像仓库

私有镜像仓库用于把 Harbor、Docker Hub 私有仓库、ECR、ACR、Artifact Registry 等仓库接入 SparkCR。接入后，你可以使用 SparkCR 生成的镜像地址拉取或推送私有镜像。

私有镜像仓库不支持匿名访问。拉取需要包含 `pull:private` 的 Access Token；推送需要额外包含 `push`。

## 创建私有镜像仓库

1. [登录 SparkCR 控制台](/login)。
2. 打开 [`Settings` -> `Private Registries`](/settings/private-registries)。
3. 点击添加私有镜像仓库。
4. 填写 `slug`、`host`、显示名称、原始仓库账号和密码或 Token。
5. 选择镜像仓库类型、访问模式和缓存策略。
6. 保存后复制页面生成的运行时命令。

字段含义：

| 字段 | 说明 |
| --- | --- |
| `slug` | 当前账号内的私有镜像仓库标识，只允许小写字母和数字，例如 `harbor`；完整路径会使用 `<username>-<slug>`。 |
| `host` | 私有镜像仓库 host，例如 `harbor.example.com` 或 `123456789012.dkr.ecr.ap-southeast-1.amazonaws.com`。 |
| 镜像仓库类型 | 通用 Docker Registry V2、Docker Hub、AWS ECR、阿里云 ACR、Google Artifact Registry、Azure ACR。 |
| 模式 | `拉取`、`推送` 或 `拉取 + 推送`。 |
| 缓存策略 | 隔离缓存、只代理不缓存、共享镜像仓库缓存。 |

如果不确定应选择哪种缓存方式，请保留默认的隔离缓存。Docker Hub、GHCR 等托管多个账号的镜像仓库不要选择共享缓存。

## 创建 Access Token

在 [`Access Token`](/tokens) 页面创建运行时使用的令牌：

- 只拉取公共镜像：选择 `pull:public`。
- 拉取私有镜像：选择 `pull:private`。
- 推送私有镜像：选择 `push`。

同一个令牌可以同时包含 `pull:private` 和 `push`。CI 建议为每个项目或 runner 单独创建令牌。

## 拉取私有镜像

页面会展示当前私有镜像仓库的拉取地址。假设地址为：

```text
https://sparkcr.cn/acme-harbor/
```

登录 SparkCR 镜像仓库：

```bash
docker login sparkcr.cn
```

用户名填写 SparkCR 账号用户名，密码填写包含 `pull:private` 的 Access Token。

拉取镜像：

```bash
docker pull sparkcr.cn/acme-harbor/myapp:latest
```

如果使用别名入口，登录和镜像 host 使用页面展示的私有镜像仓库入口。

## 推送私有镜像

私有镜像仓库需要允许推送，Access Token 需要包含 `push`。

```bash
docker login sparkcr.cn
docker tag myapp:latest sparkcr.cn/acme-harbor/myapp:latest
docker push sparkcr.cn/acme-harbor/myapp:latest
```

如果启用了缓存，推送后的内容在后续拉取时可能更快。

## containerd

containerd 使用 `certs.d` 配置。请把 host 设置为页面生成的 SparkCR 镜像地址，并根据是否需要推送设置 `capabilities`。

```toml
server = "https://harbor.example.com"

[host."https://sparkcr.cn/acme-harbor"]
  capabilities = ["pull", "resolve", "push"]
```

只拉取时可以省略 `"push"`：

```toml
capabilities = ["pull", "resolve"]
```

修改后重启 containerd：

```bash
sudo systemctl restart containerd
```

## CI/CD

在 CI 中使用私有镜像仓库时，推荐准备三类 Secret：

- `SPARKCR_REGISTRY`：SparkCR 镜像仓库 host，例如 `sparkcr.cn`。
- `SPARKCR_USERNAME`：SparkCR 账号用户名。
- `SPARKCR_TOKEN`：包含 `pull:private` 或 `push` 的 Access Token。

登录和推送示例：

```bash
echo "$SPARKCR_TOKEN" | docker login "$SPARKCR_REGISTRY" \
  --username "$SPARKCR_USERNAME" \
  --password-stdin

docker pull "$SPARKCR_REGISTRY/acme-harbor/myapp:base"
docker build -t "$SPARKCR_REGISTRY/acme-harbor/myapp:$CI_COMMIT_SHA" .
docker push "$SPARKCR_REGISTRY/acme-harbor/myapp:$CI_COMMIT_SHA"
```

## 故障排查

| 现象 | 优先检查 |
| --- | --- |
| `401 Unauthorized` | SparkCR Access Token 是否包含 `pull:private` 或 `push`。 |
| `403 DENIED` | 私有镜像仓库是否允许当前操作，原始仓库账号是否有对应权限。 |
| `manifest unknown` | 镜像路径是否使用页面生成的镜像地址和 slug。 |
| `blob upload unknown` | 推送会话可能已过期，重新执行 `docker push`。 |
| 推送成功但后续拉取仍慢 | 当前可能配置为不缓存，或本次内容尚未命中缓存。 |
