28 KiB
服务器部署记录:nginx 欢迎页(lzwlab.cn)
说明:服务器凭据见
Authentication.md,本文档中密码一律以$PASS代替。 执行方式:本地通过 Paramiko 以 SSH 密码方式登录远程执行(远程用户 ubuntu,sudo 组免密)。
0. 环境信息
| 项目 | 值 |
|---|---|
| 服务器 IP | 1.15.225.173 |
| 登录用户 | ubuntu(sudo 组,sudo -n 可免密) |
| 系统 | Ubuntu 24.04.4 LTS(kernel 6.8.0-124-generic,腾讯云 VM) |
| 域名 | lzwlab.cn / www.lzwlab.cn(DNS 已解析到 1.15.225.173) |
| 防火墙 | ufw inactive(无需额外放行) |
| 磁盘 | 50G,已用 5.3G |
1. 连接与初始状态检查
# 本地连接(密码 $PASS,取自 Authentication.md)
ssh ubuntu@1.15.225.173
# 登录后检查系统
whoami && id && hostname && uname -a
cat /etc/os-release | head -3
# 检查 sudo 免密
sudo -n true && echo sudo-ok
# 检查 nginx(结果:未安装)
command -v nginx && nginx -v
# 检查 80/443 端口占用(结果:空闲)
ss -tlnp | grep -E ":(80|443)" || echo no-80-443
# 检查防火墙(结果:inactive)
sudo -n ufw status
# 检查域名解析(结果:均解析到 1.15.225.173)
getent hosts lzwlab.cn www.lzwlab.cn
本机验证 DNS(结果:Address: 1.15.225.173):
nslookup lzwlab.cn
nslookup www.lzwlab.cn
2. 安装 nginx
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y nginx
结果:安装 nginx 1.24.0-2ubuntu7.17(nginx + nginx-common)。 安装完成后 systemd 已自动启动 nginx 并注册开机自启(默认站点为 /var/www/html 的欢迎页)。
3. 创建站点内容
本地创建 index.html(欢迎页,中文标题“欢迎访问 lzwlab.cn”,1219 字节),
通过 SFTP 上传到服务器 /tmp,再用 root 安装到站点目录:
# 本地:上传文件
# (Paramiko SFTP: index.html -> /tmp/lzwlab-index.html)
# 远程:
sudo mkdir -p /var/www/lzwlab.cn
sudo install -m 644 -o root -g root /tmp/lzwlab-index.html /var/www/lzwlab.cn/index.html
页面内容要点(详见仓库内 index.html):
<h1>🎉 欢迎访问 lzwlab.cn</h1>
<span class="badge">Powered by Nginx</span>
后续变更:2026-08-26 按需求移除了页面中的
<p>网站搭建成功!这是部署在个人云服务器上的第一个页面。</p>提示信息(详见 CHANGELOG)。
4. 配置 nginx 站点
创建站点配置 /etc/nginx/sites-available/lzwlab.cn(内容同仓库内 lzwlab.nginx.conf):
server {
listen 80;
listen [::]:80;
server_name lzwlab.cn www.lzwlab.cn;
root /var/www/lzwlab.cn;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
启用站点并移除默认站点:
# 上传配置
# (Paramiko SFTP: lzwlab.nginx.conf -> /tmp/lzwlab.conf)
# 远程:
sudo install -m 644 -o root -g root /tmp/lzwlab.conf /etc/nginx/sites-available/lzwlab.cn
sudo ln -sf /etc/nginx/sites-available/lzwlab.cn /etc/nginx/sites-enabled/lzwlab.cn
sudo rm -f /etc/nginx/sites-enabled/default
sudo rm -f /tmp/lzwlab-index.html /tmp/lzwlab.conf
# 校验配置语法
sudo nginx -t
# 结果:syntax is ok / test is successful
5. 启动 / 加载配置
sudo systemctl enable nginx
sudo systemctl start nginx
# 注意:apt 安装时 nginx 已启动并加载了默认配置,
# 所以修改配置后必须 reload 才会生效:
sudo nginx -t
sudo systemctl reload nginx
检查监听状态(结果:0.0.0.0:80 与 [::]:80 均已监听):
sudo systemctl is-active nginx # active
ss -tlnp | grep :80
6. 验证
服务器本机验证:
curl -s -o /dev/null -w "status=%{http_code} size=%{size_download}\n" http://127.0.0.1/ -H "Host: lzwlab.cn"
# status=200 size=1219
curl -s http://127.0.0.1/ -H "Host: lzwlab.cn" | grep -o "<title>.*</title>"
# <title>欢迎访问 lzwlab.cn</title>
本地(公网域名)验证:
curl -s -o /dev/null -w "status=%{http_code} size=%{size_download}\n" http://lzwlab.cn/
# status=200 size=1219
curl -s http://lzwlab.cn/ | grep -o "<title>.*</title>"
# <title>欢迎访问 lzwlab.cn</title>
curl -s -o /dev/null -w "status=%{http_code}\n" http://www.lzwlab.cn/
# status=200
✅ 部署完成:http://lzwlab.cn 与 http://www.lzwlab.cn 均已正常展示欢迎页。
7. 部署 HTTPS 证书并配置 443 自动跳转
7.1 证书信息
本地证书目录 lzwlab.cn_nginx/:
| 文件 | 用途 |
|---|---|
lzwlab.cn.key |
RSA 私钥(1700 字节) |
lzwlab.cn_bundle.crt / .pem |
证书 + 证书链(两者内容相同) |
lzwlab.cn.csr |
证书签名请求(部署用不到) |
证书校验(本机 openssl):
openssl x509 -in lzwlab.cn_nginx/lzwlab.cn_bundle.crt -noout -subject -issuer -dates
# subject=CN=lzwlab.cn
# issuer=TrustAsia DV TLS RSA CA 2024
# 有效期:2026-08-24 ~ 2026-11-22
openssl rsa -in lzwlab.cn_nginx/lzwlab.cn.key -check -noout # RSA key ok
SAN 包含:DNS:lzwlab.cn 和 DNS:www.lzwlab.cn。
7.2 上传证书与配置到服务器
# 本地:SFTP 上传到 /tmp
# lzwlab.cn_nginx/lzwlab.cn.key -> /tmp/lzwlab.cn.key
# lzwlab.cn_nginx/lzwlab.cn_bundle.crt -> /tmp/lzwlab.cn_bundle.crt
# lzwlab.nginx.conf -> /tmp/lzwlab.conf
# 远程:安装证书/私钥并更新站点配置
sudo mkdir -p /etc/nginx/ssl
sudo install -m 600 -o root -g root /tmp/lzwlab.cn.key /etc/nginx/ssl/lzwlab.cn.key
sudo install -m 644 -o root -g root /tmp/lzwlab.cn_bundle.crt /etc/nginx/ssl/lzwlab.cn_bundle.crt
sudo cp /etc/nginx/sites-available/lzwlab.cn /etc/nginx/sites-available/lzwlab.cn.bak
sudo install -m 644 -o root -g root /tmp/lzwlab.conf /etc/nginx/sites-available/lzwlab.cn
sudo rm -f /tmp/lzwlab.cn.key /tmp/lzwlab.cn_bundle.crt /tmp/lzwlab.conf
私钥权限 600、root:root;证书 644。旧配置备份为 lzwlab.cn.bak。
7.3 更新后的站点配置(/etc/nginx/sites-available/lzwlab.cn)
server {
listen 80;
listen [::]:80;
server_name lzwlab.cn www.lzwlab.cn;
return 301 https://$host$request_uri; # HTTP 全部 301 跳转到 HTTPS
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name lzwlab.cn www.lzwlab.cn;
ssl_certificate /etc/nginx/ssl/lzwlab.cn_bundle.crt;
ssl_certificate_key /etc/nginx/ssl/lzwlab.cn.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/lzwlab.cn;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
7.4 校验并加载
sudo nginx -t # syntax is ok / test is successful
sudo systemctl reload nginx
7.5 验证结果
服务器本机:
curl -s -o /dev/null -w "http status=%{http_code} redirect=%{redirect_url}\n" http://127.0.0.1/ -H "Host: lzwlab.cn"
# status=301 redirect=https://lzwlab.cn/
curl -s https://127.0.0.1/ -k -H "Host: lzwlab.cn" | grep -o "<title>.*</title>"
# <title>欢迎访问 lzwlab.cn</title>
本地公网:
curl -s -o /dev/null -w "status=%{http_code} -> %{redirect_url}\n" http://lzwlab.cn/
# status=301 -> https://lzwlab.cn/
curl -s -o /dev/null -w "status=%{http_code} ssl_verify=%{ssl_verify_result}\n" https://lzwlab.cn/
# status=200 ssl_verify=0(证书链受信任)
curl -s -o /dev/null -w "status=%{http_code}\n" https://www.lzwlab.cn/
# status=200
echo | openssl s_client -connect lzwlab.cn:443 -servername lzwlab.cn -alpn h2,http/1.1 2>/dev/null | grep "ALPN"
# ALPN protocol: h2(HTTP/2 已启用)
✅ https://lzwlab.cn 与 https://www.lzwlab.cn 已启用 HTTPS;http 访问会自动 301 跳转到 https。
8. 部署“文件快传”工具(/transfer/)
8.1 方案
- 后端:Flask 3.0.2 + gunicorn 20.1(apt 安装),监听
127.0.0.1:8090,systemd 服务lzwlab-transfer.service。 - 前端:单页 HTML(内嵌 CSS/JS),PC 与手机自适应,登录后可用。
- 存储:上传文件保存在
/var/lib/lzwlab-transfer/files/,元数据在 SQLite/var/lib/lzwlab-transfer/files.db。 - 配置:
/etc/lzwlab-transfer/config.json(root:lzwlab 640),登录密码只保存 scrypt 哈希,明文经部署环境变量传入。 - 清理:
lzwlab-transfer-cleanup.timer每 10 分钟执行cleanup.py删除过期文件;访问列表/手动清理时也会触发检查。 - nginx:
/transfer/反向代理到 8090,proxy_request_buffering off+proxy_buffering off保证大文件上传下载流式传输,client_max_body_size 5120m。
8.2 部署命令(本地运维机)
# 依赖脚本见仓库 deploy_transfer.py;密码经环境变量传入,不会写入仓库
LZWLAB_APP_PASSWORD='$PASS' python deploy_transfer.py --username admin
脚本会依次完成:apt 安装依赖 → 创建系统用户 lzwlab 与目录 → 上传/安装应用、配置、systemd 单元 → 启动服务与清理定时器 → 校验并 reload nginx。重复执行幂等。
8.3 验证结果
systemctl is-active lzwlab-transfer.service lzwlab-transfer-cleanup.timer # active active
curl -s -o /dev/null -w "%{http_code}\n" https://lzwlab.cn/transfer/ # 200(登录页)
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://lzwlab.cn/transfer/ # 301 -> https
公网 API 实测:登录成功返回会话 Cookie;未登录访问 /api/files 返回 401;上传 25 字节测试文件后列表可查,下载返回 200 且支持 Range(206);删除后列表为空;将测试文件过期时间强制置为过去并启动清理服务后,日志输出“清理完成:过期文件 1 个”,列表随即清空。
8.4 踩坑
deploy_transfer.py在 Windows 本地生成远端路径时不能使用os.path.join(会把/变成\),SQLite 路径必须手工拼接 Linux 路径,否则服务报unable to open database file。- gunicorn 手动排障时不要在 SSH 会话里前台运行(会话断开会留下占端口的孤儿进程),应直接通过
systemctl管理;排障后可sudo pkill -u lzwlab -f gunicorn && sudo systemctl restart lzwlab-transfer.service。 - Windows 下 curl 的
-F 'file=@...;filename=中文名'对非 ASCII 文件名支持不佳,测试时用 ASCII 文件名,浏览器上传不受影响。
9. 部署 RustDesk 自建服务器(Docker)
9.1 方案
- 组件:RustDesk Server OSS 版两个容器——hbbs(ID/注册服务器)与 hbbr(中继服务器),镜像
rustdesk/rustdesk-server:latest。 - 网络:
network_mode: host(官方推荐,无需-p映射),数据卷./data挂载到容器/root(密钥、SQLite 库)。 - 端口(防火墙需放行):
- hbbs:21115/TCP(NAT 类型测试)、21116/TCP+UDP(UDP 用于 ID 注册与心跳,TCP 用于打洞与连接服务)、21118/TCP(网页客户端);
- hbbr:21117/TCP(中继服务)、21119/TCP(网页客户端);
- 21114/TCP 网页控制台仅 Pro 版需要,OSS 版不监听。
- 参考文档:https://rustdesk.com/docs/zh-cn/self-host/rustdesk-server-oss/docker/ ;客户端配置:https://rustdesk.com/docs/zh-cn/self-host/client-configuration/
9.2 安装 Docker 并配置镜像加速
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
docker --version # Docker version 29.1.3(apt docker.io 自带守护进程服务)
踩坑:腾讯云访问 Docker Hub 被拒(registry-1.docker.io connection refused),必须配置镜像加速:
echo '{"registry-mirrors":["https://docker.m.daocloud.io"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
9.3 部署 hbbs/hbbr
Compose 文件为仓库内 rustdesk/docker-compose.yml:
services:
hbbs:
container_name: hbbs
image: rustdesk/rustdesk-server:latest
command: hbbs
volumes:
- ./data:/root
network_mode: "host"
depends_on:
- hbbr
restart: unless-stopped
hbbr:
container_name: hbbr
image: rustdesk/rustdesk-server:latest
command: hbbr
volumes:
- ./data:/root
network_mode: "host"
restart: unless-stopped
部署命令:
# 本地:SFTP 上传 rustdesk/docker-compose.yml 到 /tmp
sudo mkdir -p /opt/rustdesk
sudo install -m 644 -o root -g root /tmp/rustdesk-compose.yml /opt/rustdesk/compose.yml
cd /opt/rustdesk && sudo docker compose up -d
验证结果:
sudo docker ps # hbbs、hbbr 均 Up
sudo ss -tlnup | grep -E '2111[4-9]'
# tcp 21115/21116、udp 21116、tcp 21118 归 hbbs;tcp 21117/21119 归 hbbr
sudo docker logs --tail 5 hbbs
# Key: UB1fNGcVYA9NTIEA7ra3Nm0R6o2m64lNQE24L7CIAkY=
# Listening on tcp/udp :21116 / Listening on tcp :21115 / Listening on websocket :21118
9.4 客户端配置
RustDesk 客户端(设置 → 网络,点击“解锁”后可编辑)填写:
| 项目 | 值 |
|---|---|
| ID 服务器 | lzwlab.cn(hbbs,端口 21116) |
| 中继服务器 | 留空自动推导;或填 lzwlab.cn(hbbr,端口 21117) |
| Key | UB1fNGcVYA9NTIEA7ra3Nm0R6o2m64lNQE24L7CIAkY= |
| API 服务器 | 留空(仅 Pro 版需要) |
- Key 由 hbbs 首次启动自动生成,保存在服务器
/opt/rustdesk/data/id_ed25519.pub(容器内/root/id_ed25519.pub),重建容器会复用;Key 需分发给每个客户端。 - 连通性:本地已验证 21115-21119 TCP 公网可达;hbbs 日志可见外部客户端注册记录。UDP 21116 用于 ID 注册与心跳,若腾讯云控制台防火墙未放行 UDP 21116,客户端将无法注册。
9.5 运维
# 更新镜像并重建
cd /opt/rustdesk && sudo docker compose pull && sudo docker compose up -d
# 查看日志
sudo docker logs -f hbbs
sudo docker logs -f hbbr
# 查看密钥
sudo cat /opt/rustdesk/data/id_ed25519.pub
10. 部署 Gitea Git 服务(/git/)
10.1 方案
- 组件:Gitea 1.27.2 官方镜像
docker.gitea.com/gitea:1.27.2(Docker Compose,容器名gitea),数据库 SQLite,数据卷./gitea:/data(持久化在服务器/opt/gitea/gitea/)。 - 入口:服务器没有
git.lzwlab.cn子域名解析,按 Gitea 官方反向代理文档的 sub-path 方案部署在https://lzwlab.cn/git/(ROOT_URL=https://lzwlab.cn/git/)。 - 端口:Web 仅监听
127.0.0.1:3000(不对公网直接暴露),由 nginx 将/git/反代到 3000;SSH 端口与主机 sshd 冲突,容器内 22 映射到主机 2222。 - 账号:管理员私有单实例——关闭开放注册(
DISABLE_REGISTRATION=true)、锁定安装器(INSTALL_LOCK=true),管理员admin(密码见 Authentication.md)。 - 参考文档:https://docs.gitea.com/installation/install-with-docker/ 与 https://docs.gitea.com/administration/reverse-proxies/ (“Nginx with a sub-path”)。
10.2 部署命令
Compose 文件为仓库内 gitea/docker-compose.yml:
# 本地:SFTP 上传 gitea/docker-compose.yml 到 /tmp
sudo mkdir -p /opt/gitea
sudo install -m 644 -o root -g root /tmp/gitea-compose.yml /opt/gitea/compose.yml
sudo rm -f /tmp/gitea-compose.yml
cd /opt/gitea && sudo docker compose up -d
创建管理员并设置站点名:
# 管理员(密码经变量传入,不写入仓库;首次启动完成后执行)
sudo docker exec -u git gitea gitea admin user create \
--username admin --password '$PASS' --email admin@lzwlab.cn \
--admin --must-change-password=false
# 站点名写入 app.ini 的 [DEFAULT] 段后重建容器
sudo sed -i '1s/.*/APP_NAME = lzwlab Git 服务/' /opt/gitea/gitea/gitea/conf/app.ini
cd /opt/gitea && sudo docker compose up -d
10.3 nginx 反代(/git/)
按官方 “Nginx with a sub-path” 方案(保持 %2F 原样并剥离 /git 前缀),在 443 server 块新增:
location ~ ^/(git)($|/) {
client_max_body_size 512M;
rewrite ^ $request_uri;
rewrite ^/(git($|/))?(.*) /$3 break;
proxy_pass http://127.0.0.1:3000$uri;
proxy_http_version 1.1;
proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
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_read_timeout 300s;
}
该配置已并入仓库内 lzwlab.nginx.conf,服务器 /etc/nginx/sites-available/lzwlab.cn 同步更新(改前自动备份 lzwlab.cn.bak-YYYYMMDD-HHMMSS),sudo nginx -t 通过后 reload。
10.4 验证结果
curl -s -o /dev/null -w "%{http_code}\n" https://lzwlab.cn/git/ # 200
curl -s https://lzwlab.cn/git/api/v1/version # {"version":"1.27.2"}
curl -s https://lzwlab.cn/git/ | grep -o '<title>.*</title>' # <title>lzwlab Git 服务</title>
curl -s https://lzwlab.cn/ | grep -o '进入 Git 服务' # 进入 Git 服务(首页按钮已生效)
# git 智能 HTTP 协议(创建测试仓库 hello 后):
curl -s 'https://lzwlab.cn/git/admin/hello.git/info/refs?service=git-upload-pack'
# 返回 service=git-upload-pack 与 HEAD 引用;测试仓库与临时令牌验证后已删除
关键配置项(/opt/gitea/gitea/gitea/conf/app.ini):ROOT_URL=https://lzwlab.cn/git/、DOMAIN/SSH_DOMAIN=lzwlab.cn、SSH_PORT=2222、SSH_LISTEN_PORT=22(容器内)、INSTALL_LOCK=true、DISABLE_REGISTRATION=true、APP_NAME=lzwlab Git 服务。
10.5 使用与运维
- 克隆地址:
- HTTPS(公网可用):
https://lzwlab.cn/git/<用户名>/<仓库>.git; - SSH(需先放行安全组,见 10.6):
ssh://git@lzwlab.cn:2222/<用户名>/<仓库>.git。
- HTTPS(公网可用):
- 运维命令:
sudo docker compose -f /opt/gitea/compose.yml ps # 容器状态
sudo docker logs -f gitea # 日志
cd /opt/gitea && sudo docker compose pull && sudo docker compose up -d # 升级镜像
sudo docker exec -u git gitea gitea admin user list # 用户列表
- 备份:SQLite 库、仓库与配置全部在数据卷内,直接备份
/opt/gitea/gitea/即可。
10.6 踩坑
GITEA__APP_NAME环境变量传入容器后未生效(站点名仍显示默认值),站点名需直接写入app.ini的[DEFAULT]段(APP_NAME = ...)并重建容器。- 腾讯云安全组只放行 22/443/21115-21119,主机映射的 2222 端口公网不可达(本机探测 TIMEOUT,服务器本地可达):SSH 克隆需在腾讯云控制台为 TCP 2222 添加入站规则;HTTPS 克隆与网页访问不受影响。
- Gitea 子路径部署必须同时满足两点:
ROOT_URL带/git/后缀,且 nginx 使用官方 rewrite 方案剥离前缀(直接proxy_pass http://127.0.0.1:3000/;会破坏前缀与%2F转义)。 - 镜像使用官方新仓库
docker.gitea.com/gitea:1.27.2(腾讯云可直连,无需镜像加速);若改用 Docker Hub 的gitea/gitea则会走 daocloud 加速。
11. 部署 Komari Lite 服务器监控(https://status.lzwlab.cn/)
11.1 方案与要点
- 组件:Komari Lite(GitHub nuomiiiii/komari 分支)。⚠️ 它与上游 Komari(komari-monitor/komari)是两个不同的工具:镜像、文档站点与 Agent 下载源都不同,不能混用。Lite 镜像为
ghcr.io/nuomiiiii/komari:latest,文档站 https://lite.komari.wiki/ ,配套 Agent 为 nuomiiiii/komari-agent。本次部署版本:服务端 2.2.3(构建码 0huo552)。 - 部署方式:Docker Compose(服务器已有 Docker 29.1.3 + compose-v2),容器名
komari,数据卷/opt/komari/data,Web 端口只绑定127.0.0.1:25774。 - 入口:
https://status.lzwlab.cn/(首页欢迎页“📊 服务器状态”按钮),nginx 反代到 25774 并保留 WebSocket;证书由 certbot 签发。 - 参考文档:https://lite.komari.wiki/guide/start 、https://lite.komari.wiki/install/docker 、https://lite.komari.wiki/security/reverse-proxy 。
11.2 踩坑:Komari 与 Komari Lite 不是同一个工具
- 先前准备工作误用了上游镜像
ghcr.io/komari-monitor/komari(文档站 komari.wiki)。Komari Lite 是独立分支:镜像ghcr.io/nuomiiiii/komari、文档站 lite.komari.wiki、Agent 下载源 nuomiiiii/komari-agent,数据库迁移与后台功能均以 Lite 分支为准。 - 腾讯云直连 ghcr.io 拉取镜像层会卡住(仅完成 manifest 阶段),改用 NJU 镜像站秒下:
sudo docker pull ghcr.nju.edu.cn/nuomiiiii/komari:latest
sudo docker tag ghcr.nju.edu.cn/nuomiiiii/komari:latest ghcr.io/nuomiiiii/komari:latest
(仓库内 compose 文件保持官方 ghcr.io/nuomiiiii/komari 镜像名,便于后续 pull 更新。)
11.3 部署命令
Compose 文件为仓库内 komari/docker-compose.yml:
# 本地:SFTP 上传 komari/docker-compose.yml 到 /tmp
# 远程:
sudo cp -a /opt/komari/docker-compose.yml /opt/komari/docker-compose.yml.bak-$(date +%Y%m%d-%H%M%S)
sudo install -m 644 -o root -g root /tmp/komari-compose.yml /opt/komari/docker-compose.yml
sudo rm -f /tmp/komari-compose.yml
sudo docker tag ghcr.nju.edu.cn/nuomiiiii/komari:latest ghcr.io/nuomiiiii/komari:latest
cd /opt/komari && sudo docker compose up -d
# 清理误拉的上游镜像
sudo docker rmi ghcr.nju.edu.cn/komari-monitor/komari:latest
验证结果:
sudo docker ps --filter name=komari # Up,127.0.0.1:25774->25774/tcp
sudo docker logs --tail 25 komari
# Komari Monitor 2.2.3 (0huo552)
# First-run installation guide is available on 0.0.0.0:25774
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://127.0.0.1:25774/
# 307 -> /install(首次初始化页,创建管理员账号)
11.4 nginx 反代(/etc/nginx/sites-available/status.lzwlab.cn)
配置为仓库内 lzwlab.status.nginx.conf,要点(按 Lite 官方反向代理文档):
location /:HEAD 请求直接返回 204;保留 Upgrade/Connection 头(远程终端、实时数据用 WebSocket);proxy_buffering off、client_max_body_size 50M。location ^~ /api/rpc2:Agent 状态长连接单独配置(补 Origin 头、读写超时 3600s),避免被普通页面策略提前断开。- 80 端口只保留
/.well-known/acme-challenge/(certbot webroot 验证,root=/var/www/certbot),其余 301 跳 HTTPS。 - ⚠️ 80 端口的 301 必须写在
location / { return 301 ...; }内:server 级return在 rewrite 阶段(location 匹配之前)就终结请求,会把 ACME 挑战路径也一起 301,导致证书签发/续期失败(详见“附:踩坑记录”第 6 条)。 - 443 证书路径指向
/etc/letsencrypt/live/status.lzwlab.cn/(certbot 签发后生效)。
安装顺序(证书未签发前不能启用 443 块,否则 nginx -t 报证书文件不存在):
# 1) 先只启用 HTTP 挑战块(服务器临时文件 status.lzwlab.cn-http)
sudo install -m 644 -o root -g root /tmp/status-http.conf /etc/nginx/sites-available/status.lzwlab.cn-http
sudo ln -sf /etc/nginx/sites-available/status.lzwlab.cn-http /etc/nginx/sites-enabled/status.lzwlab.cn-http
sudo install -m 644 -o root -g root /tmp/lzwlab-status.conf /etc/nginx/sites-available/status.lzwlab.cn
sudo nginx -t && sudo systemctl reload nginx
# 2) 证书签发后(见 11.5,脚本或手工执行):
sudo ln -sf /etc/nginx/sites-available/status.lzwlab.cn /etc/nginx/sites-enabled/status.lzwlab.cn
sudo rm -f /etc/nginx/sites-enabled/status.lzwlab.cn-http
sudo nginx -t && sudo systemctl reload nginx
11.5 DNS 与证书(Let's Encrypt)
status.lzwlab.cn 在 DNSPod(腾讯云云解析)上没有解析记录(权威 NXDOMAIN),公网入口与证书签发依赖该记录。处理方式:
- 在 DNSPod 控制台为
lzwlab.cn添加 A 记录:主机记录status,记录值1.15.225.173(本次部署时由仓库使用者添加)。 - 服务器上放了一个自动收尾脚本
/tmp/status-watch.sh(nohup 后台运行,日志/tmp/komari-status-watch.log),每 45 秒检测一次解析:DNS 生效后它自动尝试 certbot 签发。首次尝试因 11.4 所述的 301 坑失败(ACME 挑战被 301 到 HTTPS 后 404),修复配置后改为手工签发成功(脚本已完成使命并清理):
sudo certbot certonly --webroot -w /var/www/certbot -d status.lzwlab.cn \
--non-interactive --agree-tos -m admin@lzwlab.cn --keep-until-expiring
# Successfully received certificate(ECDSA,有效期至 2026-11-24)
- 证书签发后启用 443 站点并移除临时 HTTP 块(脚本会自动做,失败时手工执行):
sudo ln -sf /etc/nginx/sites-available/status.lzwlab.cn /etc/nginx/sites-enabled/status.lzwlab.cn
sudo rm -f /etc/nginx/sites-enabled/status.lzwlab.cn-http
sudo nginx -t && sudo systemctl reload nginx
证书续期:certbot webroot 方式已注册 systemd 定时器自动续期;80 端口 /.well-known/acme-challenge/ 位置保留在 status.lzwlab.cn 站点的 80 server 块中,续期验证直接可用(已验证该路径返回 404/200 而非 301)。
11.6 验证结果(公网)
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url} ssl=%{ssl_verify_result}\n" http://status.lzwlab.cn/
# 301 -> https://status.lzwlab.cn/(HTTP 强制跳 HTTPS)
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url} ssl=%{ssl_verify_result}\n" https://status.lzwlab.cn/
# 307 -> https://status.lzwlab.cn/install ssl=0(首次为 /install 初始化页,证书链受信任)
curl -sL https://status.lzwlab.cn/ | grep -o '<title>.*</title>' # <title>Komari Lite</title>
curl -s https://lzwlab.cn/ | grep -o '服务器状态' # 服务器状态(首页按钮已生效)
echo | openssl s_client -connect status.lzwlab.cn:443 -servername status.lzwlab.cn -alpn h2,http/1.1 2>/dev/null \
| grep -E "subject=|issuer=|ALPN" # CN=status.lzwlab.cn / Let's Encrypt / h2
11.7 首次使用
- 打开
https://status.lzwlab.cn/,按初始化页创建管理员账号(凭据自行保管,只记录在Authentication.md)。 - 登录后在“账户与安全”启用两步验证;“服务器”中添加节点并复制 Agent 安装参数(Agent 使用 nuomiiiii/komari-agent 2.2.0.2)。
- 配置数据保留天数、流量限额与重置日;测试通知渠道后启用告警;最后完整备份一次
/opt/komari/data。
11.8 运维
cd /opt/komari && sudo docker compose ps # 容器状态
sudo docker logs -f komari # 日志
sudo docker pull ghcr.io/nuomiiiii/komari:latest && cd /opt/komari && sudo docker compose up -d # 升级(必要时走 ghcr.nju.edu.cn 镜像)
# 备份:面板数据全部在数据卷内,直接备份 /opt/komari/data/ 即可
附:踩坑记录
- 本地 Git Bash 调用 Windows Python 时,MSYS 会把以
/开头的命令行参数(如/tmp/...)转换成 Windows 路径, 导致 SFTP 上传远端路径错误(ENOENT)。解决:调用前加MSYS_NO_PATHCONV=1,或把路径写进脚本内。 - apt 安装 nginx 会自动启动服务并加载默认站点配置;改完站点配置后
systemctl start不会重新加载, 必须systemctl reload nginx(或restart),否则看到的仍是默认欢迎页。 - 该服务器上的 nginx 1.24(Ubuntu 构建)不识别独立指令
http2 on;,nginx -t报 unknown directive "http2"; 启用 HTTP/2 应写成listen 443 ssl http2;。 - 腾讯云服务器访问 Docker Hub 被拒(pull 报 connection refused),通过
/etc/docker/daemon.json配置registry-mirrors(docker.m.daocloud.io)后解决。 remote.py的--timeout参数必须放在远程命令之前(python remote.py --timeout 500 "命令"); 放在命令之后会被 argparse 当作远程命令的一部分传给服务器执行。- nginx 中 server 级的
return在 rewrite 阶段(location 匹配之前)执行并直接终结请求:server { location /.well-known/acme-challenge/ {...} return 301 ...; }的挑战路径照样会被 301,导致 certbot 签发失败(LE 跟随 301 到 HTTPS 后得到 404)。必须把重定向写进location / { return 301 ...; }内,让 location 先参与匹配。