Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ APP_PORT=8084
# APP_IMAGE=ghcr.io/fawney19/aether:beta
# APP_IMAGE=ghcr.io/fawney19/aether:0.7.0-rc.1

# Docker Compose Web 自动更新 sidecar 镜像。
# UPDATER_IMAGE=ghcr.io/fawney19/aether-updater:latest

# API Key 前缀(默认 sk)
API_KEY_PREFIX=sk

Expand Down Expand Up @@ -63,14 +66,22 @@ ADMIN_USERNAME=admin123456

# 管理后台更新策略:
# - systemd/二进制部署使用 self:下载 GitHub Release 包,校验 SHA256 后切换 current 并重启。
# - Docker Compose 使用 docker:后台只提示版本,实际更新请在 compose 目录执行 ./update.sh。
# - Docker Compose 使用 docker:single-node 下可通过 updater sidecar 从 Web 执行 ./update.sh。
# - 源码/本地构建使用 manual:手动拉取源码或下载 release。
# Compose 默认把持久化文件放在 ./datas/{postgres,mysql,sqlite,redis},日志放在 ./logs。
# 分布式/多节点部署不要使用 ./datas 作为共享数据目录;应使用外部共享 Postgres/MySQL 和 Redis。
# 多节点不要从管理后台一键更新单个节点,应使用镜像滚动更新、systemd 分批发布或外部编排。
# AETHER_BASE_DIR=/opt/aether
# AETHER_UPDATE_STRATEGY=docker
# AETHER_DOCKER_UPDATE_COMMAND=./update.sh
# Docker Web 自动更新内部 token。生产环境请改成随机长字符串,并保持 app/updater 一致。
# 可用命令生成:openssl rand -hex 32
AETHER_UPDATER_TOKEN=change-this-updater-token
# updater 通过宿主 docker.sock 调 compose 时,部署目录需要挂载到容器内同一绝对路径。
# 普通 shell 里 compose 会使用 PWD;CI/systemd 等场景可显式设置。
# AETHER_DEPLOY_DIR=/opt/aether/compose
# app 调 updater 的内网地址,compose 默认已配置。
# AETHER_DOCKER_UPDATER_URL=http://updater:8099
# AETHER_GATEWAY_DEPLOYMENT_TOPOLOGY=single-node
# AETHER_GATEWAY_NODE_ROLE=all
# Docker Compose 默认强制把应用日志输出到 stdout/stderr,并由 Docker 轮转日志。
Expand Down
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"apps/aether-tunnel",
"apps/aether-updater",
"crates/aether-ai-formats",
"crates/aether-admin",
"crates/aether-ai-serving",
Expand Down
28 changes: 28 additions & 0 deletions Dockerfile.updater
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# syntax=docker/dockerfile:1
# Internal updater sidecar for Docker Compose deployments.

ARG RUST_VERSION=1.95.0

FROM rust:${RUST_VERSION}-alpine AS builder

WORKDIR /build
RUN apk add --no-cache musl-dev
COPY . .
RUN --mount=type=cache,id=aether-updater-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=aether-updater-cargo-git,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=aether-updater-target,target=/build/target,sharing=locked \
cargo build --release --locked -p aether-updater && \
cp target/release/aether-updater /tmp/aether-updater

FROM docker:27-cli

RUN apk add --no-cache bash

COPY --from=builder /tmp/aether-updater /usr/local/bin/aether-updater

ENV AETHER_UPDATER_BIND=0.0.0.0:8099 \
AETHER_UPDATER_SCRIPT=/aether-deploy/update.sh \
AETHER_UPDATER_WORKDIR=/aether-deploy

EXPOSE 8099
ENTRYPOINT ["/usr/local/bin/aether-updater"]
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ docker compose -f docker-compose.single-node.yml pull && docker compose -f docke

### 一键更新

Docker Compose 部署后,可在部署目录直接执行:
Docker Compose 部署后,可在管理后台右上角“版本信息”或 `/admin/system` 的“系统更新”区域执行 Web 自动更新。Compose 会启动一个内网 `aether-updater` sidecar,它只执行部署目录里的固定 `update.sh`,并通过 `AETHER_UPDATER_TOKEN` 与 app 鉴权。生产环境请在 `.env` 中把 `AETHER_UPDATER_TOKEN` 改成随机长字符串;如果不是从部署目录交互式执行 compose,请设置 `AETHER_DEPLOY_DIR` 为部署目录绝对路径。

也可以在部署目录直接执行:

```bash
./update.sh
Expand All @@ -71,9 +73,9 @@ Docker Compose 部署后,可在部署目录直接执行:

仓库自带的 Docker Compose 默认把应用日志输出到容器 `stdout/stderr`,直接用 `docker compose logs -f app` 查看,并由 Docker 轮转日志,避免正式发布镜像切换到非 root 用户后再被宿主机挂载日志目录的权限问题拖垮启动。如果你确实需要文件日志,需要在 compose 里把 `AETHER_LOG_DESTINATION` 改成 `file|both`,并额外挂载一个容器用户可写的目录到 `/opt/aether/logs`。

管理后台右上角“版本信息”会检测新版本。Docker Compose 部署只提示版本,实际更新继续执行 `./update.sh`;systemd / launchd / 二进制部署才使用后台自更新,流程是下载对应平台的 GitHub Release 包、强制校验 `SHA256SUMS`、解压到 `/opt/aether/releases/<version>`,再切换 `/opt/aether/current` 并退出进程,交给 systemd / launchd 拉起新版本。
管理后台右上角“版本信息”会检测新版本。Docker Compose single-node 部署通过 updater sidecar 拉取镜像并重建 `app` 容器;多节点部署不要从单个后台节点执行更新,应使用外部滚动发布。systemd / launchd / 二进制部署继续使用后台自更新,流程是下载对应平台的 GitHub Release 包、强制校验 `SHA256SUMS`、解压到 `/opt/aether/releases/<version>`,再切换 `/opt/aether/current` 并退出进程,交给 systemd / launchd 拉起新版本。

源码或本地构建版本不会启用后台在线更新,请继续使用源码更新流程。Docker Compose 用户如果希望“容器重建后也保持镜像层面的新版本”,仍建议定期运行 `./update.sh` 拉取并重建 app 镜像。服务器访问 GitHub 需要代理时,可设置 `AETHER_UPDATE_PROXY_URL`,也兼容 `UPDATE_PROXY_URL`、`HTTPS_PROXY`、`ALL_PROXY`、`HTTP_PROXY` 以及 `NO_PROXY`。共享出口触发 GitHub API 限流时,可设置只读 `AETHER_UPDATE_GITHUB_TOKEN`,也兼容 `GITHUB_TOKEN` / `GH_TOKEN`。下载总超时默认 600 秒,连续无响应/无数据默认 30 秒,可通过 `AETHER_UPDATE_DOWNLOAD_TIMEOUT_SECS` 和 `AETHER_UPDATE_DOWNLOAD_IDLE_TIMEOUT_SECS` 调整。
源码或本地构建版本不会启用后台在线更新,请继续使用源码更新流程。服务器访问 GitHub 需要代理时,可设置 `AETHER_UPDATE_PROXY_URL`,也兼容 `UPDATE_PROXY_URL`、`HTTPS_PROXY`、`ALL_PROXY`、`HTTP_PROXY` 以及 `NO_PROXY`。共享出口触发 GitHub API 限流时,可设置只读 `AETHER_UPDATE_GITHUB_TOKEN`,也兼容 `GITHUB_TOKEN` / `GH_TOKEN`。下载总超时默认 600 秒,连续无响应/无数据默认 30 秒,可通过 `AETHER_UPDATE_DOWNLOAD_TIMEOUT_SECS` 和 `AETHER_UPDATE_DOWNLOAD_IDLE_TIMEOUT_SECS` 调整。

标准 Docker Compose 使用 Docker named volumes 存放 Postgres/Redis/MySQL 数据;Single Node 使用部署目录下的 `./data` 存放 SQLite 数据。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ use crate::handlers::admin::system::shared::settings::{
use crate::handlers::admin::system::shared::smtp::build_admin_smtp_test_payload;
use crate::handlers::admin::system::shared::update::{
build_admin_system_update_capability_payload, current_self_update_blocker,
prepare_admin_system_update_task, read_update_history, read_update_task_status,
self_update_supported, start_admin_system_rollback_task, start_admin_system_update_task,
current_update_strategy, prepare_admin_system_update_task, read_admin_docker_update_status,
read_update_history, read_update_task_status, self_update_supported,
start_admin_docker_update_task, start_admin_system_rollback_task,
start_admin_system_update_task, UpdateStrategy,
};
use crate::important_notification::build_important_notification_test_payload;
use crate::maintenance::{ManualUsageCleanupMode, ManualUsageCleanupOptions};
Expand Down Expand Up @@ -145,8 +147,14 @@ pub(super) async fn maybe_build_local_admin_core_system_response(
.and_then(|body| serde_json::from_slice::<serde_json::Value>(body).ok())
.and_then(|v| v.get("version").and_then(|v| v.as_str().map(String::from)));

let update_result = if current_update_strategy() == UpdateStrategy::Docker {
start_admin_docker_update_task().await?
} else {
start_admin_system_update_task(version).await?
};

return Ok(Some(
match start_admin_system_update_task(version).await? {
match update_result {
Ok(payload) => attach_admin_audit_response(
Json(payload).into_response(),
"admin_system_update_started",
Expand Down Expand Up @@ -179,6 +187,9 @@ pub(super) async fn maybe_build_local_admin_core_system_response(
&& request_method == http::Method::GET
&& request_path == "/api/admin/system/update-status"
{
if current_update_strategy() == UpdateStrategy::Docker {
return Ok(Some(Json(read_admin_docker_update_status().await).into_response()));
}
let status = read_update_task_status();
return Ok(Some(
Json(json!({
Expand Down
17 changes: 8 additions & 9 deletions apps/aether-gateway/src/handlers/admin/system/shared/settings.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::handlers::admin::request::AdminAppState;
use crate::handlers::admin::shared::build_admin_usage_counter_health_payload;
use crate::handlers::admin::system::shared::update::{
current_self_update_blocker, self_update_supported,
current_web_update_blocker, web_update_supported,
};
use crate::handlers::admin::system::shared::update_client::{
build_direct_update_http_client, build_update_http_client, has_explicit_update_proxy_env,
Expand Down Expand Up @@ -55,7 +55,7 @@ pub(crate) fn build_admin_system_check_update_payload_from_release(
latest_release,
error,
);
apply_self_update_check_update_override(&mut payload, self_update_supported());
apply_self_update_check_update_override(&mut payload, web_update_supported());
payload
}

Expand All @@ -65,7 +65,7 @@ pub(crate) fn build_admin_system_releases_list_payload(
) -> serde_json::Value {
let mut payload =
build_admin_system_releases_payload(current_aether_version(), releases, error);
apply_self_update_releases_override(&mut payload, self_update_supported());
apply_self_update_releases_override(&mut payload, web_update_supported());
payload
}

Expand All @@ -77,7 +77,7 @@ fn apply_self_update_check_update_override(payload: &mut Value, supported: bool)
apply_self_update_check_update_override_with_blocker(
payload,
supported,
current_self_update_blocker(),
current_web_update_blocker(),
);
}

Expand Down Expand Up @@ -129,15 +129,14 @@ fn apply_self_update_releases_override_with_blocker(
}

fn current_self_update_release_blocker() -> &'static str {
if web_update_supported() {
return "";
}
if !current_build_is_release() {
return SOURCE_BUILD_RELEASE_BLOCKER;
}

if self_update_supported() {
""
} else {
current_self_update_blocker()
}
current_web_update_blocker()
}

#[cfg(not(test))]
Expand Down
Loading