# -*- coding: utf-8 -*- r'''第二步:装 Halo 博客(Docker)。 == 配置决策(以及为什么)== **用 Docker 而不是裸 JAR** 机器上已经有 Docker 29.8(1Panel 装的),直接用。好处: 不用在系统里装 Java、升级只要换镜像 tag、环境隔离。 **数据库用内置 H2(不接 MySQL)** 个人博客用 H2 完全够 —— 数据就是一个文件。 接 MySQL 要多挂一个容器,多占 400MB+ 内存,而机器只有 1.9G。 **少一个组件就少一处会坏的地方。** **端口绑 0.0.0.0:3001** 用户要求 "端口的话就跑在 <服务器IP>:3001" —— 所以要能从外面直接访问 3001。 同时 nginx 也要能反代(走域名)。 如果只绑 127.0.0.1,`IP:3001` 就不通了。 **限制 JVM 堆** `-Xmx400m`。机器 1.9G,JVM 默认会按 1/4 物理内存要堆(约 470M), 再加元空间/线程栈/直接内存容易冲到 700M+。 显式限到 400M 更可控 —— 博客流量小,400M 绰绰有余。 **外部地址设成 https://xxmxxs.top** Halo 用它生成文章链接和站点地图。设成域名之后, 就算你从 `IP:3001` 进来,页面里的链接也是域名 —— 这样更一致。 **控制台路径 /houtai** 用户要求 "xxmxxs.top/houtai 是管理博客的后台"。 Halo 2.x 支持自定义控制台路径(社区 PR #2453)。 默认是 /console,这里改成 /houtai。 ⚠ 如果这个版本的 env 变量名不对,容器会起不来 —— 脚本里会检查。 == 数据目录 == /opt/halo/data -> 容器内 /root/.halo2 里面有 H2 数据库、上传的附件、主题。 **这个目录要备份** —— 它是博客的全部内容。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from lssh import run # noqa: E402 REMOTE = r''' echo "==== 1. 看看有哪些可用的 Halo 版本 ====" sudo docker pull halohub/halo:2 >/dev/null 2>&1 && echo " ✓ halohub/halo:2 拉下来了" || echo " ✗ 拉取失败" sudo docker images halohub/halo --format ' {{.Repository}}:{{.Tag}} {{.Size}} ({{.CreatedSince}})' 2>/dev/null echo echo "==== 2. 看看镜像里支持哪些配置项 ====" # Halo 的配置项在 application.yaml 里,从镜像里抠出来看 sudo docker run --rm --entrypoint sh halohub/halo:2 -c \ "find / -name 'application*.yaml' 2>/dev/null | head -5" 2>/dev/null | sed 's/^/ /' echo echo "==== 3. 准备数据目录 ====" sudo mkdir -p /opt/halo/data sudo chown -R 1000:1000 /opt/halo 2>/dev/null || true ls -la /opt/halo | sed 's/^/ /' echo echo "==== 4. 起容器 ====" sudo docker rm -f halo 2>/dev/null >/dev/null || true sudo docker run -d \ --name halo \ --restart always \ --log-opt max-size=10m --log-opt max-file=3 \ -p 0.0.0.0:3001:8090 \ -v /opt/halo/data:/root/.halo2 \ -e HALO_EXTERNAL_URL=https://xxmxxs.top \ -e HALO_CONSOLE_PATH=/houtai \ -e JAVA_OPTS="-Xms128m -Xmx400m -XX:+UseSerialGC" \ halohub/halo:2 2>&1 | sed 's/^/ /' echo echo "==== 5. 等它起来(最多 90 秒)====" ok=0 for i in $(seq 1 45); do sleep 2 code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 http://127.0.0.1:3001/ 2>/dev/null) st=$(sudo docker inspect -f '{{.State.Status}}' halo 2>/dev/null) if [ "$code" = "200" ] || [ "$code" = "302" ] || [ "$code" = "301" ]; then echo " 第 $((i*2)) 秒:HTTP $code ✓ 起来了" ok=1 break fi if [ "$st" != "running" ]; then echo " 第 $((i*2)) 秒:容器已经退出(状态 $st)" break fi done if [ "$ok" != "1" ]; then echo " 容器状态: $(sudo docker inspect -f '{{.State.Status}}' halo 2>/dev/null)" echo " --- 日志(最后 30 行)---" sudo docker logs --tail 30 halo 2>&1 | sed 's/^/ /' fi echo echo "==== 6. 复核 ====" sudo docker ps --filter name=halo --format ' {{.Names}} {{.Status}} {{.Ports}}' echo " 首页: HTTP $(curl -s -o /dev/null -w '%{http_code}' --max-time 8 http://127.0.0.1:3001/)" echo " /houtai: HTTP $(curl -s -o /dev/null -w '%{http_code}' --max-time 8 http://127.0.0.1:3001/houtai)" echo " /console: HTTP $(curl -s -o /dev/null -w '%{http_code}' --max-time 8 http://127.0.0.1:3001/console)" echo " 从外面直连 3001(本机自测):HTTP $(curl -s -o /dev/null -w '%{http_code}' --max-time 8 http://127.0.0.1:3001/actuator/health 2>/dev/null)" echo echo " --- 内存占用 ---" sudo docker stats --no-stream --format ' {{.Name}} 内存 {{.MemUsage}} CPU {{.CPUPerc}}' halo 2>/dev/null free -m | sed 's/^/ /' ''' def main() -> int: print("=" * 62) print(" 第二步:装 Halo(Docker)") print("=" * 62) print() out = run(REMOTE, timeout=900, quiet=True) print(out[1] if isinstance(out, tuple) else out) return 0 if __name__ == "__main__": sys.exit(main())