简体中文 | English
APK Cache 是一个面向 Linux 包仓库的 HTTP 缓存代理服务,当前实现聚焦三条核心链路:
- Alpine Linux APK 缓存代理
- Debian/Ubuntu APT HTTP 代理缓存
- 通用 HTTP/HTTPS 代理转发,主要用于 HTTPS APT 源的
CONNECT隧道
项目已经按新的轻量内核重写,保留核心缓存、校验、代理、监控、Docker 部署和内置管理台能力;旧 i18n、未接入主流程的限流/磁盘配额/策略系统不再属于当前版本。
- APK 缓存:缓存
.apk包和APKINDEX.tar.gz。 - APT 缓存:支持 HTTP 代理模式和传统镜像站模式,缓存
.deb、Release、InRelease、Packages*、Sources*和by-hash请求。 - 统一缓存流水线:内存缓存 -> 磁盘缓存 -> 上游回源。
- 并发安全:同一缓存 key 使用文件级互斥,避免并发重复下载和写坏缓存。
- 完整性校验:APK 支持 APKINDEX hash 和 RSA 签名校验;APT 支持 Release/Packages 索引 SHA256、by-hash 和包文件校验。
- 多 APK 上游:支持多个 APK upstream,按健康状态和顺序 failover。
- 上游代理:APK upstream 可单独配置代理;APT/通用代理可使用统一
proxy.upstream_proxy。 - HTTPS 隧道:支持
CONNECT,用于 HTTPS APT 源透传。 - 管理台:内置
/admin/页面和/api/admin/v1/*管理 API,支持默认单管理员登录、账号修改、运行配置、APT mirror 和代理白名单管理。 - 配置持久化:首次启动从 TOML/env 导入运行配置,之后以 SQLite 中的配置为准。
- Hash 持久化:使用 Pebble 保存 APK/APT expected hash、actual hash 缓存和 APT by-hash 映射。
- 运维端点:
/_health、/metrics、/admin/。 - Docker 部署:entrypoint 可根据环境变量生成运行配置。
- CI/CD:GitHub Actions 覆盖测试、二进制构建、Docker 构建/冒烟和 tag release。
docker run -d \
--name apk-cache \
-p 3142:3142 \
-v "$PWD/cache:/app/cache" \
-v "$PWD/data:/app/data" \
ghcr.io/tursom/apk-cache:latest健康检查:
curl http://127.0.0.1:3142/_healthPrometheus 指标:
curl http://127.0.0.1:3142/metrics仓库提供 docker-compose.yml。服务镜像名默认是 GitHub Container Registry:ghcr.io/tursom/apk-cache:latest。
从当前源码构建并启动:
docker compose up -d --build只使用 GitHub 上已经发布的镜像部署:
docker compose up -d指定其他 GitHub tag:
APK_CACHE_IMAGE=ghcr.io/tursom/apk-cache:v1.2.3 \
docker compose up -d--build 会用当前源码重新构建,并把结果打成 ghcr.io/tursom/apk-cache:latest 这个本地镜像 tag;不会自动 push 到 GitHub。
默认挂载:
./cache -> /app/cache
./data -> /app/data
常用变量:
APK_CACHE_HTTP_PORT:宿主机端口,默认3142。APK_CACHE_CACHE_DIR:宿主机缓存目录,默认./cache。APK_CACHE_DATA_DIR:宿主机数据目录,默认./data。GOPROXY:构建阶段 Go module 代理。
docker-compose.yml 不再注入运行配置环境变量。首次启动会使用内置默认值导入 SQLite,之后请在 /admin/ 管理运行配置、APK upstream、APT mirror 和代理白名单。
首次进入管理台:
docker run -d \
--name apk-cache \
-p 3142:3142 \
-v "$PWD/cache:/app/cache" \
-v "$PWD/data:/app/data" \
ghcr.io/tursom/apk-cache:latest然后打开 http://127.0.0.1:3142/admin/,使用默认管理员 admin / admin123456 登录。首次登录后应立即在“账号安全”里修改用户名和密码;修改后默认凭据提示会消失。
要求:
- Go
1.25.4或兼容版本 - Node.js
22或兼容版本,用于构建 React 管理台 - 可选:Docker,用于容器构建和本地冒烟
构建:
./build.sh运行:
cp config.example.toml config.toml
./apk-cache -config config.toml测试:
go test ./...
go test ./... -coverprofile=coverage.out将 Alpine 源改为 APK Cache 服务地址。例如服务地址为 http://cache.example:3142:
sed -i 's|https://dl-cdn.alpinelinux.org|http://cache.example:3142|g' /etc/apk/repositories
apk update
apk add curlDockerfile 示例:
FROM alpine:3.23
RUN sed -i 's|https://dl-cdn.alpinelinux.org|http://cache.example:3142|g' /etc/apk/repositories \
&& apk add --no-cache curlAPK 请求示例:
/alpine/v3.23/main/x86_64/APKINDEX.tar.gz
/alpine/v3.23/main/x86_64/busybox-1.37.0-r0.apk
APT 可以通过 HTTP 代理方式使用 APK Cache。
创建 /etc/apt/apt.conf.d/01-apk-cache-proxy:
Acquire::HTTP::Proxy "http://cache.example:3142";
Acquire::HTTPS::Proxy "http://cache.example:3142";然后运行:
apt-get update
apt-get install curl注意:
http://APT 源可以被解析和缓存。https://APT 源通常通过CONNECT建立 TLS 隧道,当前服务会透传,但不会解密或缓存 TLS 内部内容。- 代理模式可在管理台配置目标网站白名单;白名单非空时,HTTP APT 和 HTTPS
CONNECT都只允许命中启用规则的 host。
APT 也支持传统镜像站模式。先在管理台 /admin/apt 的“镜像站”tab 增加映射,例如:
本地路径前缀: /debian
上游 URL: https://deb.debian.org/debian
然后客户端 sources.list 可以直接指向本服务:
deb http://cache.example:3142/debian bookworm main
镜像站模式不会解密客户端 TLS;服务只请求配置好的 upstream,并按真实 upstream host 和路径归档缓存。
默认配置文件路径是 config.toml,也可以用 -config 指定:
./apk-cache -config /path/to/config.toml启动引导示例见 config.example.toml。
# APK Cache bootstrap configuration.
#
# Most runtime settings are imported into SQLite on first boot and should be
# managed later from /admin/. Existing DB settings win over TOML/env values.
[server]
listen = ":3142"
[cache]
# Used before SQLite opens. Empty DB will import the built-in runtime defaults.
data_root = "./data"
# Optional first-import override for disk cache files.
# root = "./cache"
[database]
# Empty means "${cache.data_root}/apk-cache.db".
path = ""
[hash_store]
# Empty means "${cache.data_root}/hash.pebble".
path = ""| 配置 | 默认值 | 说明 |
|---|---|---|
server.listen |
:3142 |
HTTP 监听地址 |
database.path |
${cache.data_root}/apk-cache.db |
SQLite 数据库路径;为空时使用默认路径 |
hash_store.path |
${cache.data_root}/hash.pebble |
Pebble hash store 路径 |
hash_store.rebuild_on_corruption |
false |
hash store 损坏时是否允许删除后重建 |
hash_store.trust_file_stat |
true |
actual hash 缓存是否信任 size + mtime |
hash_store.actual_revalidate_interval |
24h |
actual hash 即使 stat 未变也重新计算的最长间隔 |
upstreams[].name |
Official Alpine CDN |
APK 上游名称,仅用于识别 |
upstreams[].url |
https://dl-cdn.alpinelinux.org |
APK 上游基础地址 |
upstreams[].kind |
apk |
当前仅 APK 上游参与 APK 回源 |
upstreams[].proxy |
空 | 当前 APK 上游使用的出站代理 |
cache.root |
./cache |
磁盘缓存目录 |
cache.data_root |
./data |
运行数据目录,默认存放 SQLite 和 Pebble |
cache.index_ttl |
24h |
索引文件缓存 TTL |
cache.package_ttl |
720h |
包文件缓存 TTL;0 表示不过期 |
cache.memory.enabled |
true |
是否启用内存缓存 |
cache.memory.max_size |
256MB |
内存缓存总大小 |
cache.memory.max_item_size |
16MB |
可进入内存缓存的单文件最大大小 |
cache.memory.ttl |
30m |
内存缓存项 TTL |
cache.memory.max_items |
2048 |
内存缓存最大条目数 |
transport.timeout |
30s |
回源 HTTP client 超时 |
transport.idle_conn_timeout |
90s |
空闲连接保留时间 |
transport.max_idle_conns |
128 |
HTTP transport 最大空闲连接数 |
apk.enabled |
true |
是否启用 APK 链路 |
apk.verify_hash |
true |
是否使用 APKINDEX 校验 .apk |
apk.verify_signature |
true |
是否校验 APK/APKINDEX RSA 签名 |
apk.keys_dir |
空 | 额外 Alpine RSA 公钥目录 |
apt.enabled |
true |
是否启用 APT 链路 |
apt.verify_hash |
true |
是否校验 APT by-hash、Release 索引引用文件和包文件 SHA256 |
apt.load_index_async |
true |
是否异步加载新缓存的 APT 索引 |
proxy.enabled |
true |
是否启用通用 HTTP/HTTPS 代理 |
proxy.allow_connect |
true |
是否允许 CONNECT 隧道 |
proxy.cache_non_package_requests |
false |
是否缓存非 APK/APT 普通 HTTP 请求 |
proxy.upstream_proxy |
空 | APT 和通用代理使用的出站代理 |
proxy.allowed_hosts |
[] |
兼容首次导入字段;后续请在管理台代理白名单中维护 |
支持的代理 URL:
socks5://127.0.0.1:1080
http://127.0.0.1:8080
https://127.0.0.1:8443
带认证的 HTTP 代理可写成:
http://user:password@proxy.example:8080
容器启动时,entrypoint.sh 会根据环境变量生成临时 TOML 配置,然后执行 /app/apk-cache -config <generated>。
| 环境变量 | 默认值 | 对应配置 |
|---|---|---|
CONFIG |
/tmp/apk-cache.toml |
生成的配置文件路径 |
LISTEN / ADDR |
:3142 |
server.listen |
CACHE_ROOT / CACHE_DIR |
/app/cache |
cache.root |
DATA_ROOT |
/app/data |
cache.data_root |
DATABASE_PATH |
空 | database.path;为空时使用 ${DATA_ROOT}/apk-cache.db |
HASH_STORE_PATH |
空 | hash_store.path;为空时使用 ${DATA_ROOT}/hash.pebble |
HASH_STORE_REBUILD_ON_CORRUPTION |
false |
hash_store.rebuild_on_corruption |
HASH_STORE_TRUST_FILE_STAT |
true |
hash_store.trust_file_stat |
HASH_STORE_ACTUAL_REVALIDATE_INTERVAL |
24h |
hash_store.actual_revalidate_interval |
APK_UPSTREAM / UPSTREAM |
https://dl-cdn.alpinelinux.org |
APK upstream URL |
APK_UPSTREAM_PROXY / PROXY |
空 | APK upstream proxy |
UPSTREAM_PROXY |
空 | proxy.upstream_proxy |
INDEX_TTL |
24h |
cache.index_ttl |
PACKAGE_TTL |
720h |
cache.package_ttl |
MEMORY_CACHE_ENABLED |
true |
cache.memory.enabled |
MEMORY_CACHE_SIZE |
256MB |
cache.memory.max_size |
MEMORY_CACHE_MAX_ITEM_SIZE |
16MB |
cache.memory.max_item_size |
MEMORY_CACHE_TTL |
30m |
cache.memory.ttl |
MEMORY_CACHE_MAX_ITEMS |
2048 |
cache.memory.max_items |
TRANSPORT_TIMEOUT |
30s |
transport.timeout |
TRANSPORT_IDLE_CONN_TIMEOUT |
90s |
transport.idle_conn_timeout |
TRANSPORT_MAX_IDLE_CONNS |
128 |
transport.max_idle_conns |
APK_ENABLED |
true |
apk.enabled |
APK_VERIFY_HASH |
true |
apk.verify_hash |
APK_VERIFY_SIGNATURE |
true |
apk.verify_signature |
APT_ENABLED |
true |
apt.enabled |
APT_VERIFY_HASH |
true |
apt.verify_hash |
APT_LOAD_INDEX_ASYNC |
true |
apt.load_index_async |
PROXY_ENABLED |
true |
proxy.enabled |
PROXY_ALLOW_CONNECT |
true |
proxy.allow_connect |
PROXY_CACHE_NON_PACKAGE_REQUESTS |
false |
proxy.cache_non_package_requests |
PROXY_ALLOWED_HOSTS |
空 | 逗号分隔的首次导入代理白名单;DB 已有配置后不再覆盖 |
Docker 示例:
docker run -d \
--name apk-cache \
-p 3142:3142 \
-v "$PWD/cache:/app/cache" \
-e APK_UPSTREAM=https://mirrors.tuna.tsinghua.edu.cn/alpine \
-e UPSTREAM_PROXY=socks5://127.0.0.1:1080 \
ghcr.io/tursom/apk-cache:latest所有请求进入同一个 HTTP handler:
/_health返回健康检查。/metrics返回 Prometheus 指标。/admin/返回内嵌管理页面。/api/admin/v1/*进入管理 API,除 login 外需要 session 和 CSRF。CONNECT进入隧道代理。- 普通请求按路径或 URL 判断为 APK、APT 或通用代理。
启动时仍会读取 TOML/env,但它们主要负责监听地址、数据目录、数据库路径和首次运行配置导入。服务打开 SQLite 后会执行迁移:
- 如果没有管理员,自动创建
admin/admin123456默认单管理员。 - 如果
settings为空,将当前 TOML/env 合并后的运行配置导入 SQLite。 - 如果 SQLite 已有配置,以 SQLite 为准;TOML/env 中的运行配置不再覆盖数据库。
- APK upstream、APT mirror、代理白名单、管理员、session、缓存对象、请求日志和管理摘要保存在 SQLite。
- APK/APT expected hash、actual hash 缓存、source mapping、APT by-hash 映射和路径字典保存在 Pebble。
这意味着管理台保存配置后,重启服务不会被旧 TOML 意外覆盖。server.listen、cache.root、cache.data_root、hash_store.path 等启动期配置会在 API 中标记为需要重启。
APK、APT 和可缓存的普通代理请求都会走统一缓存流程:
- 查询内存缓存,命中返回
X-Cache: MEMORY-HIT。 - 查询磁盘缓存,命中返回
X-Cache: HIT。 - 对同一缓存 key 加锁,避免并发重复下载。
- 再次查询缓存,防止等待锁期间已有其他请求写入。
- 回源请求,上游响应一边返回客户端,一边写入临时文件。
- 下载完成后执行协议校验。
- 校验通过后原子
rename为正式缓存文件。 - 必要时写入内存缓存。
- 首次回源返回
X-Cache: MISS。
非 200 OK 的上游响应会直接透传,不写入缓存,返回 X-Cache: BYPASS。
APK 校验由 internal/apk 实现:
APKINDEX.tar.gz会被解析为包名、版本、大小和 checksum 的映射。.apk命中缓存或下载完成后,可以按 APKINDEX 记录校验大小和 hash。- APK/APKINDEX 可以校验
.SIGN.*RSA 签名。 - 内置 Alpine 默认公钥,也可以通过
apk.keys_dir加载额外公钥。 - 新下载内容签名失败时会返回给客户端,但不会写入缓存。
APT 校验由 internal/apt 实现:
Release/InRelease会解析 SHA256 文件清单。Packages*/Sources*会解析包文件路径、大小和 SHA256。by-hash/SHA256/<hash>请求会按 URL 中声明的 hash 校验。- 如果
by-hash命中的是Release中记录的索引文件,会按原始索引路径解析Packages*/Sources*内容。 Release引用的Packages*/Sources*文件会在缓存命中和下载完成后按 SHA256 校验。.deb如果能从索引中找到 SHA256,就在缓存命中和下载完成后校验。- 未找到索引记录时不阻断请求。
实际文件 hash 会缓存在 Pebble 中。缓存命中条件是文件 size 和 mtime 与记录一致;默认最长 24 小时会重新计算一次,降低长期 stat 伪装带来的风险。
CONNECT 只建立 TCP 隧道:
- 不解析 TLS。
- 不缓存 TLS 内部内容。
- 可通过
proxy.allow_connect=false禁用。 - 可通过管理台代理白名单限制目标 host。
- 并发隧道有固定上限,防止无限占用连接。
内置管理台。首次使用流程:
- 启动服务。
- 打开
/admin/。 - 使用默认管理员
admin/admin123456登录。 - 在“账号安全”里修改用户名和密码。
- 后续通过登录态访问管理页面和管理 API。
管理台首版提供:
- 仪表盘:健康状态、上游状态、缓存对象数量、CONNECT 数、hash store 状态。
- 配置管理:按业务分组查看和修改 SQLite 中的运行配置,标识热更新/需重启字段。
- 上游管理:新增、启用、禁用、删除 APK upstream。
- 代理管理:开关通用代理、CONNECT、非包请求缓存和目标网站白名单。
- 缓存管理:搜索缓存对象、删除缓存、批量 dry-run 删除、扫描磁盘回填元数据、清空内存缓存和预热。
- APK/APT:查看索引和解析记录,管理 APT mirror,生成 sources.list,手动重载索引,触发 APT 校验。
- 日志、系统与 Hash:查看最近请求日志、错误日志、系统信息、诊断包和 Pebble hash store 统计。
前端源码位于 internal/admin/web,使用 React + TypeScript + Vite;生产构建输出到 internal/admin/static 后由 Go 二进制内嵌。./build.sh 会自动执行前端构建。
管理 API 前缀为 /api/admin/v1,统一返回:
{
"ok": true,
"data": {},
"error": null
}返回 JSON,例如:
{
"status": "healthy",
"apk_upstreams_total": 1,
"apk_upstreams": {
"healthy": 1,
"total": 1
},
"disk_cache": {
"status": "healthy"
},
"memory_cache": {
"items": 0,
"size": 0,
"max": 268435456
}
}当缓存目录不可用或 APK upstream 全部不可用时,状态会降级为 degraded,HTTP 状态码为 503。
暴露 Prometheus 指标,主要包括:
apk_cache_hits_totalapk_cache_misses_totalapk_cache_download_bytes_totalapk_cache_response_bytes_totalapk_cache_upstream_requests_totalapk_cache_upstream_failovers_totalapk_cache_validation_failures_totalapk_cache_apk_hash_failures_totalapk_cache_apk_signature_failures_totalapk_cache_apk_bypass_responses_totalapk_cache_memory_hits_totalapk_cache_memory_misses_totalapk_cache_memory_evictions_totalapk_cache_memory_size_bytesapk_cache_memory_items_total
常用命令:
npm ci --prefix internal/admin/web
npm run --prefix internal/admin/web build
go test ./...
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out
./build.sh
docker build -t apk-cache:local .真实数据集成测试在 internal/app/real_integration_test.go:
- APT 使用从
deb.debian.org获取的Release、Packages.xz和.debfixture,覆盖Release -> Packages.xz -> by-hash -> .deb的缓存与 SHA256 校验。 - APK 使用从
dl-cdn.alpinelinux.org获取的APKINDEX.tar.gz和.apkfixture,覆盖真实签名校验、APKINDEX hash 校验和缓存损坏后回源重取。 - 测试运行时由本地
httptest.Server提供这些 fixture,不依赖外网。
本地 Docker 冒烟:
docker run --rm -p 3142:3142 apk-cache:local
curl http://127.0.0.1:3142/_health.github/workflows/build.yml 当前包含:
- Go 单元测试和 coverage artifact。
- Linux amd64/arm64、macOS amd64/arm64、Windows amd64 二进制构建。
- Docker 镜像构建、
/_health冒烟和 linux/amd64、linux/arm64 镜像发布。 - 非 PR 事件推送 GHCR 镜像。
v*tag 创建 GitHub Release 并上传二进制产物。
- 管理台是单管理员模型,不支持多用户、多角色。
- 当前不做操作审计日志;请求日志只用于排障。
- 管理台轮询 API,不做 WebSocket 实时推送。
- 没有全局限流。
- 没有磁盘配额和自动清理策略。
- HTTPS APT 源通过
CONNECT透传,不解密也不缓存。
这些能力后续可以在当前简化内核之上重新设计,但不再恢复旧版已经失配的实现。