fix(system-update): 用独立助手容器替代 CompletableFuture 实现 tenant-service 自重建
原方案:CompletableFuture 延迟调用 docker-compose up。
问题:docker-compose 发出 stop 指令后,容器内全部进程(含 CompletableFuture 线程)
被立即杀死,rm/create/start 步骤永远不会执行,tenant-service 停在停止状态。
新方案:先用 docker run -d 启动独立助手容器(xuqm-self-updater),
它不依附于 tenant-service,不会随之终止;8 秒后执行 force-recreate。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
f2e126e2d0
当前提交
a98dbca26d
@ -8,7 +8,6 @@ import org.springframework.stereotype.Service;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Service
|
||||
@ -47,20 +46,58 @@ public class SystemUpdateService {
|
||||
}
|
||||
}
|
||||
|
||||
emit.accept(">>> 即将重启 tenant-service,连接将短暂中断...");
|
||||
emit.accept("RESTART_SELF");
|
||||
// tenant-service 自身的重建:不能直接用 docker-compose,因为一旦发出 stop 指令,
|
||||
// 当前容器(含 docker-compose 进程)会立即被杀死,后续的 rm/create/start 步骤不会执行。
|
||||
// 解决方案:先用 docker run -d 启动一个独立助手容器,它不依附于 tenant-service,
|
||||
// 能在 tenant-service 停止后继续完成重建。
|
||||
emit.accept(">>> 启动自更新助手容器...");
|
||||
String registry = System.getenv().getOrDefault("REGISTRY", "");
|
||||
String imageTag = System.getenv().getOrDefault("IMAGE_TAG", "latest");
|
||||
String selfImage = registry.isEmpty() ? "alpine" : (registry + "/tenant-service:" + imageTag);
|
||||
boolean helperStarted = spawnSelfUpdater(composeFile, selfImage);
|
||||
|
||||
// 延迟 3 秒后重启自身,确保 SSE 事件已发送到客户端
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
exec(msg -> log.info("[self-restart] {}", msg),
|
||||
"docker-compose", "-f", composeFile, "-p", "xuqm",
|
||||
"up", "-d", "--no-deps", "--force-recreate", "tenant-service");
|
||||
} catch (Exception e) {
|
||||
log.error("self-restart failed", e);
|
||||
}
|
||||
});
|
||||
if (helperStarted) {
|
||||
emit.accept(">>> 助手容器已就绪,tenant-service 即将重建(连接将短暂中断)...");
|
||||
emit.accept("RESTART_SELF");
|
||||
} else {
|
||||
emit.accept(">>> [警告] 助手容器启动失败,请手动执行:");
|
||||
emit.accept(">>> docker-compose -f " + composeFile + " -p xuqm up -d --no-deps --force-recreate tenant-service");
|
||||
emit.accept("DONE");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动一个独立的 detached 容器,在 tenant-service 被停止后重建它。
|
||||
* 助手容器与 tenant-service 无父子关系,不会随 tenant-service 终止。
|
||||
*/
|
||||
private boolean spawnSelfUpdater(String composeFile, String image) {
|
||||
try {
|
||||
// 清理上次残留(若有)
|
||||
new ProcessBuilder("docker", "rm", "-f", "xuqm-self-updater")
|
||||
.redirectErrorStream(true).start().waitFor();
|
||||
|
||||
// 等待 8 秒确保 tenant-service 已完全停止,然后执行 force-recreate
|
||||
String shellCmd = "sleep 8 && docker-compose -f " + composeFile
|
||||
+ " -p xuqm up -d --no-deps --force-recreate tenant-service";
|
||||
|
||||
Process p = new ProcessBuilder(
|
||||
"docker", "run", "-d", "--rm",
|
||||
"--name", "xuqm-self-updater",
|
||||
"-v", "/var/run/docker.sock:/var/run/docker.sock",
|
||||
"-v", deployRoot + ":" + deployRoot,
|
||||
"--entrypoint", "sh",
|
||||
image,
|
||||
"-c", shellCmd
|
||||
).redirectErrorStream(true).start();
|
||||
|
||||
String out = new String(p.getInputStream().readAllBytes()).trim();
|
||||
int code = p.waitFor();
|
||||
log.info("self-updater spawn: code={} containerId={}", code, out);
|
||||
return code == 0;
|
||||
} catch (Exception e) {
|
||||
log.error("failed to spawn self-updater", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRunning(String service) {
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户