本文记录一次云服务器重置后的完整重建过程。公网服务器最终只承载 Halo 个人网站和统一 Demo 展示平台;Git、真实后台、真实设备和生产数据全部与公网展示环境分离。文中的真实域名、服务器 IP、邮箱、用户名、密码等敏感信息均已替换为占位符。
目录优化说明: 本文只使用 10 个二级标题作为文章目录;具体步骤使用加粗小标题,不再进入 Halo 右侧目录,避免长文目录过度碎片化。
一、目标与整体架构
1. 最终架构
这次重建之后,公网服务器只保留两类业务:
Internet
│
├── https://blog.<your-domain>
│ │
│ Nginx
│ │
│ 127.0.0.1:8090
│ │
│ Halo
│ │
│ PostgreSQL
│
└── https://demo.<your-domain>
│
Nginx
│
/opt/stacks/demos
│
├── Demo Center
├── Demo Viewer
└── 多个静态 Demo
公网只需要开放:
22 SSH
80 HTTP
443 HTTPS
Halo 的 8090 端口只监听:
127.0.0.1:8090
PostgreSQL 不映射公网端口。
2. 为什么不再给每个 Demo 单独绑定域名
最初的方案是:
demo.<your-domain> -> 一个固定 Demo
当 Demo 增多后,这种方式会遇到几个问题:
- 一个域名只能方便地对应一个项目
- 每增加一个 Demo 都要处理 DNS
- 每增加一个 Demo 都可能要重新申请 HTTPS
- Nginx 配置越来越多
- 手工上传 ZIP、删除旧目录、解压、修改权限非常麻烦
- 更新失败时不好快速回滚
最终改成:
https://demo.<your-domain>/
https://demo.<your-domain>/view/w19/
https://demo.<your-domain>/view/oil/
https://demo.<your-domain>/view/iec61850/
真正的静态资源则位于:
https://demo.<your-domain>/apps/w19/
https://demo.<your-domain>/apps/oil/
https://demo.<your-domain>/apps/iec61850/
这样只需要维护一个:
demo.<your-domain>
二、服务器基础环境与 Docker
3. 系统基础环境
服务器环境:
Ubuntu 24.04 LTS
Docker
Docker Compose V2
Nginx
Certbot
更新系统:
sudo apt update
sudo apt upgrade -y
安装基础软件:
sudo apt install -y \
ca-certificates \
curl \
nginx \
unzip \
openssl
4. Docker 安装
最初尝试从 Docker 官方源下载安装 GPG Key:
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
服务器网络出现:
curl: (35) Recv failure: Connection reset by peer
因此没有继续依赖 Docker 官方源,而是直接使用 Ubuntu 软件源中的 Docker:
sudo apt update
sudo apt install -y \
docker.io \
docker-compose-v2
启动:
sudo systemctl enable --now docker
验证:
docker --version
docker compose version
sudo docker info
确认 Docker 服务:
sudo systemctl status docker --no-pager
正常状态:
Active: active (running)
5. 配置 Docker 国内镜像加速
服务器访问 Docker Hub 时曾出现:
dial tcp ...:443: i/o timeout
因此配置云厂商提供的 Docker 镜像加速。
创建:
sudo mkdir -p /etc/docker
编辑:
sudo nano /etc/docker/daemon.json
示例:
{
"registry-mirrors": [
"https://<docker-mirror>"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
这里同时限制 Docker 日志:
单日志文件最大 50 MB
最多保留 3 个
重新启动:
sudo systemctl restart docker
验证:
sudo docker info | grep -A 3 "Registry Mirrors"
6. 创建服务器目录
本次统一使用:
/opt/stacks/
Halo:
/opt/stacks/halo
Demo:
/opt/stacks/demos
建立目录:
sudo mkdir -p /opt/stacks/halo
sudo mkdir -p /opt/stacks/demos
7. 拉取 Halo 与 PostgreSQL
Halo:
sudo docker pull registry.fit2cloud.com/halo/halo:2.25
PostgreSQL:
sudo docker pull postgres:15
检查:
sudo docker images
三、Halo 与 PostgreSQL 部署
8. Halo 目录结构
最终:
/opt/stacks/halo/
├── .env
├── docker-compose.yml
├── halo2/
└── db/
其中:
halo2/ Halo 数据
db/ PostgreSQL 数据
.env 数据库密码、外部访问地址
9. 创建 Halo 数据库密码
进入:
cd /opt/stacks/halo
生成随机密码:
openssl rand -hex 24
创建:
nano .env
内容:
HALO_DB_PASSWORD=<strong-random-password>
HALO_EXTERNAL_URL=https://blog.<your-domain>/
收紧权限:
chmod 600 .env
注意:
不要把真实数据库密码写进博客文章
不要提交 .env 到 Git
10. Halo Docker Compose
创建:
nano /opt/stacks/halo/docker-compose.yml
示例:
services:
halo:
image: registry.fit2cloud.com/halo/halo:2.25
container_name: halo
restart: unless-stopped
depends_on:
halodb:
condition: service_healthy
networks:
- halo_network
volumes:
- ./halo2:/root/.halo2
ports:
- "127.0.0.1:8090:8090"
healthcheck:
test:
[
"CMD",
"curl",
"-f",
"http://localhost:8090/actuator/health/readiness"
]
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
environment:
JVM_OPTS: "-Xmx256m -Xms256m"
command:
- --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
- --spring.r2dbc.username=halo
- --spring.r2dbc.password=${HALO_DB_PASSWORD}
- --spring.sql.init.platform=postgresql
- --halo.external-url=${HALO_EXTERNAL_URL}
halodb:
image: postgres:15
container_name: halo-postgres
restart: unless-stopped
networks:
- halo_network
volumes:
- ./db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "halo", "-d", "halo"]
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_PASSWORD: ${HALO_DB_PASSWORD}
POSTGRES_USER: halo
POSTGRES_DB: halo
PGUSER: halo
networks:
halo_network:
11. 启动 Halo
检查 Compose:
cd /opt/stacks/halo
sudo docker compose config
启动:
sudo docker compose up -d
检查:
sudo docker compose ps
正常:
halo Up ... (healthy)
halo-postgres Up ... (healthy)
健康检查:
curl http://127.0.0.1:8090/actuator/health/readiness
预期:
{"status":"UP"}
12. Halo 502 Bad Gateway 的排查
部署过程中浏览器曾出现:
502 Bad Gateway
502 的含义是:
浏览器 -> Nginx 正常
Nginx -> Halo:8090 失败
排查:
sudo docker compose -f /opt/stacks/halo/docker-compose.yml ps
curl -v http://127.0.0.1:8090/
sudo ss -lntp | grep 8090
sudo docker compose \
-f /opt/stacks/halo/docker-compose.yml \
logs --tail=100 halo
实际问题是 Halo 的外部 URL 被写成了错误值,例如:
HALO_EXTERNAL_URL=https://blog.<your-domain>/
被错误拼接成:
HALO_EXTERNAL_URL=HALO_EXTERNAL_URL=https://blog.<your-domain>/
Halo 日志出现:
Failed to bind properties under 'halo.external-url'
修复 .env:
HALO_EXTERNAL_URL=https://blog.<your-domain>/
然后:
sudo docker compose up -d --force-recreate halo
因此遇到 Halo 502 时,应优先检查:
sudo docker compose ps
curl http://127.0.0.1:8090/
sudo docker compose logs --tail=100 halo
而不是先怀疑 HTTPS。
四、Nginx、HTTPS 与 SSH
13. Nginx 反向代理 Halo
创建:
sudo nano /etc/nginx/sites-available/blog
示例:
server {
listen 80;
listen [::]:80;
server_name blog.<your-domain>;
client_max_body_size 512m;
location / {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
启用:
sudo ln -sf \
/etc/nginx/sites-available/blog \
/etc/nginx/sites-enabled/blog
删除默认站点:
sudo rm -f /etc/nginx/sites-enabled/default
检查:
sudo nginx -t
重新加载:
sudo systemctl reload nginx
14. HTTPS
安装:
sudo apt install -y \
certbot \
python3-certbot-nginx
申请证书:
sudo certbot --nginx \
-d blog.<your-domain> \
-d demo.<your-domain>
Certbot 会询问:
联系邮箱
是否接受服务条款
是否订阅额外邮件
这里应使用真实有效邮箱,但不要把邮箱公开在博客部署记录里。
完成后:
sudo nginx -t
sudo systemctl reload nginx
检查续期:
sudo certbot renew --dry-run
15. SSH 重装后的主机指纹问题
服务器重装后,从 Windows 执行 SCP 时可能出现:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
这是因为:
服务器重装
↓
SSH Host Key 重新生成
↓
Windows known_hosts 仍保存旧指纹
先在服务器核对新的 ED25519 指纹:
sudo ssh-keygen \
-lf /etc/ssh/ssh_host_ed25519_key.pub
确认指纹确实属于新服务器后,在 Windows 删除旧记录:
ssh-keygen -R <server-ip>
重新连接:
ssh <server-user>@<server-ip>
确认新指纹后输入:
yes
之后 SCP 即可恢复。
五、多 Demo 平台设计
16. 多 Demo 平台的目录结构
旧方案:
/opt/stacks/demos/
├── backup
└── om_a300_view
只适合一个 Demo。
重构为:
/opt/stacks/demos/
├── incoming/
│
├── projects/
│ ├── w19/
│ │ ├── meta.json
│ │ ├── current -> releases/<release>
│ │ └── releases/
│ │ ├── <release-1>/
│ │ └── <release-2>/
│ │
│ └── ...
│
├── public/
│ ├── index.html
│ ├── demos.json
│ ├── _viewer/
│ │ └── index.html
│ └── apps/
│ ├── w19 -> ../../projects/w19/current
│ └── ...
│
└── scripts/
└── democtl
职责:
incoming/
上传的临时发布包
projects/
每个 Demo 的正式发布版本
public/
Nginx 唯一对外提供的目录
_viewer/
统一 Demo 顶部导航
apps/
指向各项目 current 的软链接
scripts/
Demo 管理脚本
建立:
sudo mkdir -p /opt/stacks/demos/public/apps
sudo mkdir -p /opt/stacks/demos/public/_viewer
sudo mkdir -p /opt/stacks/demos/projects
sudo mkdir -p /opt/stacks/demos/incoming
sudo mkdir -p /opt/stacks/demos/scripts
17. Demo Center
访问:
https://demo.<your-domain>/
作为统一 Demo 首页。
它不再代表某一个项目,而是展示所有可用 Demo:
Demo Center
├── W19
├── Oil
├── IEC61850
└── ...
Demo 元信息统一放:
/opt/stacks/demos/public/demos.json
初始化:
echo '[]' > /opt/stacks/demos/public/demos.json
18. Demo Viewer
博客里链接的不是:
/apps/w19/
而是:
/view/w19/
Viewer 的作用:
┌──────────────────────────────────────────┐
│ ← 返回博客 Demo 中心 W19 全屏 │
├──────────────────────────────────────────┤
│ │
│ iframe Demo │
│ │
└──────────────────────────────────────────┘
真正的 Demo:
/apps/w19/
通过 iframe 加载。
这样每个项目本身不需要重复加入:
返回博客
Demo 中心
全屏
等公共 UI。
19. 从 Demo 返回进入前的 Blog 页面
只使用:
history.back()
并不可靠。
因为用户可能:
- 刷新 Demo
- 在 Demo 内操作很多次
- 直接打开 Demo URL
- 浏览器没有可用历史记录
因此设计成:
/view/w19/?from=<blog-url>
Viewer 只接受:
https://blog.<your-domain>
来源的 URL。
伪代码:
const BLOG_ORIGIN = "https://blog.<your-domain>"
function getSafeFrom() {
const params =
new URLSearchParams(location.search)
const value =
params.get("from")
if (!value) {
return null
}
try {
const url = new URL(value)
if (url.origin === BLOG_ORIGIN) {
return url.href
}
} catch (_) {}
return null
}
返回优先级:
1. 安全合法的 from
2. Blog referrer + history.back()
3. Blog 首页
这样可以避免任意 URL 跳转漏洞。
六、Demo 的 Nginx 配置与故障排查
20. Demo Nginx 配置
Demo 统一使用:
demo.<your-domain>
示例:
server {
listen 80;
listen [::]:80;
server_name demo.<your-domain>;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name demo.<your-domain>;
ssl_certificate
/etc/letsencrypt/live/<certificate-name>/fullchain.pem;
ssl_certificate_key
/etc/letsencrypt/live/<certificate-name>/privkey.pem;
include
/etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam
/etc/letsencrypt/ssl-dhparams.pem;
root /opt/stacks/demos/public;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~ ^/view/[a-zA-Z0-9_-]+/?$ {
try_files /_viewer/index.html =404;
}
location ~ ^/(ajax|rabbitmq|login|file|upload|websocket)(/|$) {
return 503;
}
}
21. Demo 403 Forbidden 排查
Demo Center 初次访问时如果出现:
403 Forbidden
常见原因:
1. index.html 不存在
2. Nginx 无法进入目录
3. 文件权限不足
4. Nginx root 配置错误
检查:
ls -lah /opt/stacks/demos/public/
ls -l \
/opt/stacks/demos/public/index.html
namei -l \
/opt/stacks/demos/public/index.html
测试 www-data 是否可读:
sudo -u www-data \
cat /opt/stacks/demos/public/index.html \
>/dev/null \
&& echo "NGINX_READ_OK"
修复目录权限:
sudo find /opt/stacks/demos \
-type d \
-exec chmod 755 {} \;
sudo find /opt/stacks/demos/public \
-type f \
-exec chmod 644 {} \;
错误日志:
sudo tail -n 30 \
/var/log/nginx/error.log
如果看到:
permission denied
就是权限问题。
如果看到:
directory index ... is forbidden
通常表示没有找到 index.html。
七、Demo 发布、版本与回滚
22. Demo 发布工具 democtl
目标是让服务器管理方式统一成:
democtl create
democtl deploy
democtl rollback
democtl list
创建项目:
democtl create \
w19 \
"W19 Demo" \
"综合在线监测与可视化演示"
查看:
democtl list
发布:
democtl deploy \
w19 \
/opt/stacks/demos/incoming/w19-demo.tar.gz
回滚:
democtl rollback w19
最终:
projects/w19/
├── meta.json
├── current -> releases/202608xx-xxxxxx
└── releases/
├── 202608xx-xxxxxx/
└── 202608xx-xxxxxx/
Nginx 始终只读取:
current
因此更新时不直接删除正在运行的目录。
23. 为什么发布包改用 tar.gz
Windows PowerShell 的:
Compress-Archive
生成 ZIP 后,在 Linux 上曾出现:
appears to use backslashes as path separators
为了减少 Windows/Linux 路径差异,改用:
tar -czf w19-demo.tar.gz -C dist-demo .
这样压缩包根目录就是:
index.html
css/
js/
...
不会额外套一层:
dist-demo/
服务器发布:
democtl deploy \
w19 \
/opt/stacks/demos/incoming/w19-demo.tar.gz
八、Vue 子路径与 Demo Mock 兼容
24. Vue 项目部署到子路径
这是多 Demo 方案最容易踩坑的地方。
原项目默认:
/
现在 W19 的真正运行目录是:
/apps/w19/
因此 Vue CLI 项目必须支持:
publicPath
示例:
const publicPath =
process.env.OM_A300_PUBLIC_PATH || '/'
const outputDir =
process.env.OM_A300_OUTPUT_DIR || 'dist'
module.exports = {
publicPath,
outputDir
}
Demo 构建:
CMD
set "VUE_APP_DEMO_MODE=true"
set "OM_A300_OUTPUT_DIR=dist-demo"
set "OM_A300_PUBLIC_PATH=/apps/w19/"
npm run build
检查:
dir dist-demo
应该直接看到:
index.html
css
js
...
清理变量:
set VUE_APP_DEMO_MODE=
set OM_A300_OUTPUT_DIR=
set OM_A300_PUBLIC_PATH=
25. CMD 与 PowerShell 环境变量语法不同
PowerShell:
$env:VUE_APP_DEMO_MODE="true"
CMD:
set "VUE_APP_DEMO_MODE=true"
如果在 CMD 中错误输入:
$env:VUE_APP_DEMO_MODE="true"
会出现:
文件名、目录名或卷标语法不正确
因此首先确认自己使用的是:
PowerShell
还是:
cmd.exe
26. Demo Mock 模式
公开 Demo 不应该连接:
真实后台
真实设备
真实数据库
真实 WebSocket
真实上传接口
建议 Demo 模式:
VUE_APP_DEMO_MODE=true
进入 Demo 模式后:
本地登录
本地初始化数据
禁用 WebSocket
阻断真实 HTTP 请求
使用 Mock 数据
普通生产构建:
VUE_APP_DEMO_MODE 未启用
则仍保持原项目行为。
27. /logo.png 为什么在子路径部署后消失
项目中曾有:
<img src="/logo.png">
开头的:
/
代表域名根目录。
因此浏览器请求:
https://demo.<your-domain>/logo.png
但文件实际位于:
https://demo.<your-domain>/apps/w19/logo.png
因此图片 404。
如果图片在 Vue public/:
<img :src="`${process.env.BASE_URL}logo.png`">
Demo 构建时:
process.env.BASE_URL
=
/apps/w19/
普通构建仍然是:
/
如果图片位于:
src/assets/
更推荐:
<img :src="require('@/assets/logo.png')">
28. 搜索项目中的绝对静态资源路径
Windows CMD:
findstr /S /N /I "src=\"/" \
src\*.vue \
src\*.html
CSS:
findstr /S /N /I "url('/" \
src\*.vue \
src\*.css \
src\*.scss \
src\*.less
注意:
静态资源路径可以改
但不要误改:
/ajax
/login
/websocket
/file
/upload
这些是业务/API 路径,不属于静态资源。
九、W19 Demo 完整发布流程
29. W19 Demo 完整发布流程
本地进入项目:
cd <project-directory>
选择合适 Node 版本:
nvm use 16
设置 Demo 环境:
set "VUE_APP_DEMO_MODE=true"
set "OM_A300_OUTPUT_DIR=dist-demo"
set "OM_A300_PUBLIC_PATH=/apps/w19/"
构建:
npm run build
打包:
tar -czf w19-demo.tar.gz -C dist-demo .
上传:
scp w19-demo.tar.gz \
<server-user>@<server-ip>:/opt/stacks/demos/incoming/
服务器:
democtl deploy \
w19 \
/opt/stacks/demos/incoming/w19-demo.tar.gz
访问:
https://demo.<your-domain>/view/w19/
真正静态入口:
https://demo.<your-domain>/apps/w19/
对外发布时应链接:
/view/w19/
不要直接公开:
/apps/w19/
这样用户才能看到统一顶部导航。
30. 发布流程应该进一步原子化
基础版 democtl 在解压过程中如果被:
Ctrl+C
中断,可能会留下半成品 release。
更可靠的流程应该是:
上传压缩包
↓
解压到临时目录
↓
检查 index.html
↓
检查文件权限
↓
完整性检查通过
↓
移动到 releases/<timestamp>
↓
原子切换 current
↓
保留最近 N 个历史版本
例如:
.tmp-202608xx
↓ 验证成功
202608xx-xxxxxx
↓
current -> 202608xx-xxxxxx
这样即使:
解压失败
压缩包损坏
用户 Ctrl+C
磁盘空间不足
当前线上版本也不会受到影响。
十、安全、运维、备份与最终形态
31. 推荐的 Demo 安全边界
公网 Demo 最重要的原则:
Demo 是展示层
不是生产环境
建议:
✓ Mock 数据
✓ 本地 Demo 登录
✓ 禁用真实 WebSocket
✓ 禁用真实设备控制
✓ 禁用真实上传
✓ 禁止连接内网数据库
✓ 不存真实 Token
✓ 不存真实 API Key
✓ 不把生产 .env 放入构建产物
Nginx 可以额外阻断:
location ~ ^/(ajax|rabbitmq|login|file|upload|websocket)(/|$) {
return 503;
}
不过最终仍应以:
前端 Demo Mock 本身不会发出真实请求
作为第一层安全保障。
Nginx 只是第二层兜底。
32. Halo 与 Demo 的职责分离
最终推荐:
Halo
├── 项目介绍
├── 技术文章
├── 开发记录
└── Demo 入口
Demo Center
├── W19
├── Oil
├── IEC61850
└── 其他项目
Git
└── 独立放在自己的代码管理环境
也就是说:
博客负责“讲”
Demo 负责“看”
Git 负责“代码”
三者不混在一起。
33. 最终服务器目录
最终公网服务器可以保持非常简洁:
/opt/stacks/
├── halo/
│ ├── .env
│ ├── docker-compose.yml
│ ├── halo2/
│ └── db/
│
└── demos/
├── incoming/
├── projects/
│ ├── w19/
│ ├── oil/
│ └── ...
│
├── public/
│ ├── index.html
│ ├── demos.json
│ ├── _viewer/
│ └── apps/
│
└── scripts/
└── democtl
Nginx:
/etc/nginx/sites-available/
├── blog
└── demo
证书:
/etc/letsencrypt/
34. 常用维护命令
Halo
状态:
cd /opt/stacks/halo
sudo docker compose ps
日志:
sudo docker compose logs --tail=100 halo
重启:
sudo docker compose restart halo
健康检查:
curl \
http://127.0.0.1:8090/actuator/health/readiness
Nginx
检查:
sudo nginx -t
重新加载:
sudo systemctl reload nginx
日志:
sudo tail -n 50 \
/var/log/nginx/error.log
HTTPS
查看证书:
sudo certbot certificates
模拟续期:
sudo certbot renew --dry-run
Demo
查看:
democtl list
发布:
democtl deploy \
<demo-id> \
/opt/stacks/demos/incoming/<package>.tar.gz
回滚:
democtl rollback <demo-id>
35. 建议的备份范围
公网服务器现在已经很简单。
真正需要备份的主要是:
Halo PostgreSQL
Halo /root/.halo2 数据
Nginx 配置
Demo 元信息
democtl 脚本
Demo 构建产物本身不是最高优先级,因为它应当可以从 Git 源码重新构建。
数据库备份示例:
sudo docker exec halo-postgres \
pg_dump -U halo halo \
> /path/to/backup/halo.sql
Halo 数据:
sudo tar -czf \
/path/to/backup/halo-data.tar.gz \
-C /opt/stacks/halo \
halo2
注意备份目录权限,避免数据库或敏感配置被 Web 服务访问。
36. 不应该公开到文章中的信息
在公开部署记录前,应检查并替换:
服务器公网 IP
SSH 用户名
真实域名(如不希望公开)
邮箱
数据库密码
Halo 管理员密码
Demo 固定密码
API Key
Token
真实内网 IP
真实设备地址
Git 仓库私有地址
证书私钥路径中的私人命名信息
建议统一写:
<server-ip>
<server-user>
<your-domain>
<strong-password>
<demo-id>
<internal-ip>
37. 这次重建后的经验
这次最大的变化不是“重新装了一次 Halo”,而是把公网服务器重新做了职责收敛。
旧思路:
一个公网服务器承载很多基础设施
新思路:
公网服务器只承载真正需要公开访问的东西
最终:
个人网站
↓
Halo
在线展示
↓
Demo Center + Viewer
代码
↓
独立 Git 环境
真实业务
↓
不进入公网 Demo
同时,多 Demo 发布从:
build
→ ZIP
→ scp
→ rm
→ unzip
→ chmod
→ reload nginx
收敛成:
build
→ tar.gz
→ scp
→ democtl deploy
并预留:
版本保留
快速回滚
原子发布
统一导航
返回 Blog 原页面
这一套结构已经更接近一个轻量级的个人项目展示平台,而不只是“在服务器上放几个静态页面”。
38. 最终访问关系
https://blog.<your-domain>
│
├── 项目文章
│
└── 在线体验
│
▼
https://demo.<your-domain>/view/<demo-id>/
│
├── ← 返回博客
├── Demo 中心
├── 全屏
│
└── iframe
│
▼
https://demo.<your-domain>/apps/<demo-id>/
这就是本次服务器重建后的最终形态。
从零重建个人网站与多 Demo 发布平台:Halo + Nginx + HTTPS + Docker 全流程
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法