
那天凌晨 2:10,我站在香港葵涌机房过道,手机上还在刷着深圳同事发来的“Figma 卡死了没法传素材”的消息。工位那边,设计、法务、硬件的微信群都在滴滴作响。墙上 PDU 的指示灯稳定,风扇像往常一样呼呼作响,唯一不同的是我心跳快了半拍——如果今晚把
这套 Debian + Nextcloud + S3 对象存储 打通,并让跨境同事的访问稳定下来,第二天的评审会就能按时开。
我把螺丝刀插回兜里,好,干活。
1. 目标与架构
目标:
- 在香港机房部署一套 Debian 12 + Nextcloud,后端接 S3 兼容对象存储(作主存储),服务内外网统一走 HTTPS/HTTP3。
- 兼顾 跨境访问(内地↔香港↔海外)的稳定性与速率,支持 大文件(>10GB) 分块上传、图片/视频预览、在线文档协作(Collabora/OnlyOffice)。
- 运维上要求 可观测、可回滚、可横向扩展。
高层架构:
[CN/US/EU 用户]
│ Anycast + QUIC
▼
[Cloudflare/CDN/直连 LB] ──► [HK 边缘反代 Caddy/Nginx]
│
├─► [Nextcloud PHP-FPM + Redis + PostgreSQL]
│ │
│ └─► [S3 对象存储(HK/SG 区域,版本控制)]
│
└─► [Collabora/OnlyOffice(Docker)]
2. 硬件、网络与软件版本
2.1 机房硬件(实配示例)
| 模块 | 型号/规格 | 备注 |
|---|---|---|
| 服务器 | 1U 双电源,Intel Xeon Silver 4314 / AMD EPYC 7313P | 核心数充足,AVX2 带宽友好 |
| 内存 | 128GB ECC DDR4 | 预留缓存空间,防止 PHP-FPM 抖动 |
| 系统盘 | 2× NVMe 1.92TB(RAID1) | 系统 + 应用 + DB |
| 网卡 | 2×10GbE(Bond LACP) | 一内一外,HA 需找机房做 LACP |
| 带宽 | 1Gbps 保障(Burst 10Gbps) | 对跨境波动更抗压 |
| 远程 | 带外管理(IPMI/iDRAC) | 夜深人静救命用 |
注:Nextcloud 主文件放在对象存储,机器本地的 IO 压力主要来自数据库与缓存,NVMe RAID1 足够。
2.2 网络与 DNS
- 主域:cloud.example.hk(DNS 建议托管到支持 Anycast 的厂商)
- TLS:Let’s Encrypt,ALPN 支持 h2/h3
- IPv6:能开就开,跨境绕路时经常能捡便宜线路
2.3 软件清单(Debian 12)
- Web 反代:Caddy(默认就很好用,开箱 HTTP/3),或 Nginx(更细粒度)
- PHP:php8.2-fpm + curl gd mbstring xml zip intl pgsql redis apcu imagick gmp bcmath exif
- 数据库:PostgreSQL 15
- 缓存:Redis server(文件锁 & 内存缓存)
- 文档协作:Collabora CODE(Docker 方式)
- 监控:node_exporter/Netdata + fail2ban
- 对象存储:S3 兼容(样例以 ap-hongkong 或 ap-southeast-1)
3. 部署步骤(从裸机到上线)
3.1 Debian 基础与系统加固
apt update && apt -y full-upgrade
apt -y install sudo vim htop gnupg curl wget git unzip tar jq lsb-release ca-certificates
timedatectl set-timezone Asia/Hong_Kong
# 关闭不必要服务,略
内核与网络优化(BBR、队列、FD、内存):
/etc/sysctl.d/99-nextcloud.conf
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
fs.file-max=1048576
vm.swappiness=10
vm.dirty_ratio=10
vm.dirty_background_ratio=5
net.core.rmem_max=268435456
net.core.wmem_max=268435456
net.ipv4.tcp_rmem=4096 131072 268435456
net.ipv4.tcp_wmem=4096 131072 268435456
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_tw_reuse=1
sysctl --system
nftables 基础防火墙:
/etc/nftables.conf
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
ct state established,related accept
iif lo accept
tcp dport {22,80,443} accept
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
reject with icmpx type admin-prohibited
}
}
systemctl enable --now nftables
3.2 安装基础组件
apt -y install redis-server postgresql postgresql-contrib
apt -y install php8.2-fpm php8.2-{curl,gd,mbstring,xml,zip,intl,pgsql,redis,apcu,imagick,gmp,bcmath,exif}
PHP 调优: /etc/php/8.2/fpm/php.ini
memory_limit = 1024M
upload_max_filesize = 50G
post_max_size = 50G
max_execution_time = 3600
output_buffering = Off
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=60000
opcache.validate_timestamps=0
realpath_cache_size=4096k
realpath_cache_ttl=600
FPM 池: /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 80
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 20
; 按核数与内存微调,监控后再收敛
systemctl enable --now php8.2-fpm redis-server
3.3 数据库准备(PostgreSQL)
sudo -u postgres psql <<'SQL'
CREATE ROLE nextcloud WITH LOGIN PASSWORD 'StrongPassw0rd!';
CREATE DATABASE nextclouddb OWNER nextcloud;
ALTER SYSTEM SET max_connections = 500;
ALTER SYSTEM SET shared_buffers = '4GB';
ALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET maintenance_work_mem = '1GB';
ALTER SYSTEM SET effective_cache_size = '32GB';
SQL
systemctl restart postgresql
3.4 Web 服务器(选一)
方案 A:Caddy(简单、开箱 HTTP/3)
apt -y install debian-keyring debian-archive-keyring
# 官方脚本或使用 apt 源安装 caddy,略
apt -y install caddy
/etc/caddy/Caddyfile
cloud.example.hk {
encode gzip zstd
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer"
Permissions-Policy "geolocation=(), microphone=()"
}
root * /var/www/nextcloud
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
# 上传大文件(Caddy默认即可,若中间有代理需同步放宽)
}
systemctl enable --now caddy
方案 B:Nginx(更细调)
apt -y install nginx
/etc/nginx/sites-available/nextcloud.conf
server {
listen 80;
server_name cloud.example.hk;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2; # 若启用QUIC需nginx-quic或Caddy
server_name cloud.example.hk;
ssl_certificate /etc/letsencrypt/live/cloud.example.hk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cloud.example.hk/privkey.pem;
root /var/www/nextcloud;
client_max_body_size 0;
fastcgi_read_timeout 3600;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Permissions-Policy "geolocation=(),microphone=()" always;
location = /robots.txt { allow all; log_not_found off; access_log off; }
location = /.well-known/carddav { return 301 /remote.php/dav; }
location = /.well-known/caldav { return 301 /remote.php/dav; }
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ { deny all; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { deny all; }
index index.php index.html /index.php$request_uri;
location ~ \.php(?:$|/) {
include snippets/fastcgi-php.conf;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_intercept_errors on;
}
location ~ \.(?:css|js|woff2?|svg|gif)$ {
try_files $uri /index.php$request_uri;
access_log off; expires 1y; add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$request_uri;
access_log off; expires 1M; add_header Cache-Control "public, max-age=2592000";
}
}
签发证书(若用 Nginx):
apt -y install certbot python3-certbot-nginx
certbot --nginx -d cloud.example.hk --redirect --agree-tos -m admin@example.hk
3.5 部署 Nextcloud
cd /var/www
curl -LO https://download.nextcloud.com/server/releases/latest.zip
unzip latest.zip
mv nextcloud /var/www/nextcloud
chown -R www-data:www-data /var/www/nextcloud
第一次安装(命令行更稳):
sudo -u www-data php /var/www/nextcloud/occ maintenance:install \
--database "pgsql" --database-name "nextclouddb" \
--database-user "nextcloud" --database-pass "StrongPassw0rd!" \
--admin-user "ncadmin" --admin-pass "AdminStr0ng!" \
--data-dir "/var/nextcloud-data" # 初次写死,后面切S3主存储会接管
3.6 切换到 S3 为 主存储(Primary Storage)
强烈建议:在正式上 S3 前做一次全新实例安装,避免把已有本地文件迁移到 S3 的长时间搬迁和一致性问题。
编辑 config.php:/var/www/nextcloud/config/config.php
<?php
$CONFIG = array (
'trusted_domains' =>
array (
0 => 'cloud.example.hk',
),
'overwrite.cli.url' => 'https://cloud.example.hk',
'dbtype' => 'pgsql',
'dbname' => 'nextclouddb',
'dbhost' => '127.0.0.1',
'dbuser' => 'nextcloud',
'dbpassword' => 'StrongPassw0rd!',
'memcache.local' => '\OC\Memcache\APCu',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => array(
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => 1.5,
),
'objectstore' => array(
'class' => 'OC\\Files\\ObjectStore\\S3',
'arguments' => array(
'bucket' => 'nc-prod-bucket',
'autocreate' => true,
'key' => 'AKIAxxxxxxxxxxxx',
'secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxx',
'region' => 'ap-hongkong',
'use_ssl' => true,
'use_path_style' => true, // 部分兼容厂商需要
'hostname' => 's3.ap-hongkong.example.com', // S3 端点
'port' => 443,
'objectPrefix' => 'nextcloud',
'uploadHelper' => true,
'legacyAuth' => false,
'requesttimeout' => 120,
'timeout' => 120,
'connect_timeout' => 30,
'part_size' => 10485760, // 10MB 分块
),
),
'default_phone_region' => 'HK',
'log_type' => 'file',
'logfile' => '/var/log/nextcloud/nextcloud.log',
'loglevel' => 2,
'overwriteprotocol' => 'https',
);
mkdir -p /var/log/nextcloud && chown -R www-data:www-data /var/log/nextcloud
sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --on
sudo -u www-data php /var/www/nextcloud/occ maintenance:repair
sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --off
Bucket 建议:开启 版本控制(Versioning) 与 生命周期(过期预览缓存、回收站对象),并在同区域或近区域做 跨区域复制(灾备)。
3.7 后台任务与预览生成
sudo -u www-data php /var/www/nextcloud/occ background:cron
crontab -u www-data -e
添加:
*/5 * * * * php -f /var/www/nextcloud/cron.php
0 3 * * * php -f /var/www/nextcloud/occ preview:pre-generate
预览依赖:
apt -y install ffmpeg imagemagick ghostscript exiftool
# 如果遇到 ImageMagick 安全策略限制 HEIC/WEBP,在 /etc/ImageMagick-6/policy.xml 放宽对应 coder(谨慎)
3.8 在线文档(Collabora,Docker 方式)
/opt/collabora/docker-compose.yml
services:
collabora:
image: collabora/code:latest
container_name: collabora
restart: unless-stopped
ports:
- "127.0.0.1:9980:9980"
environment:
- domain=cloud\\.example\\.hk
- extra_params=--o:ssl.enable=true --o:ssl.termination=true
反代转发(以 Caddy 为例):
collabora.example.hk {
reverse_proxy 127.0.0.1:9980
}
Nextcloud 后台启用 Collabora Online 应用,填 https://collabora.example.hk。
3.9 安全与防护
fail2ban(Nextcloud 登录防爆破)
/etc/fail2ban/filter.d/nextcloud.conf
[Definition]
failregex = Login failed: '.*' \(Remote IP: '<HOST>'\)
ignoreregex =
/etc/fail2ban/jail.d/nextcloud.local
[nextcloud]
enabled = true
filter = nextcloud
logpath = /var/log/nextcloud/nextcloud.log
maxretry = 5
findtime = 600
bantime = 3600
systemctl enable --now fail2ban
2FA & SSO:启用 TOTP、可接 OIDC(如公司统一身份),Nextcloud 市场安装相应应用并配置。
3.10 监控与日志
- node_exporter/Netdata 观察 CPU、内存、磁盘延迟、连接数
- Nginx/Caddy 访问日志配合 goaccess,观察跨境波动时段
- PostgreSQL pg_stat_statements,定位慢查询;Redis INFO 看命中率
4. 跨境协作加速要点(这几招最管用)
- HTTP/3(QUIC)优先:移动网络跳转多、丢包高时,QUIC 的 0-RTT 与多路复用能显著提升体感。Caddy 开箱即用;Nginx 则需 quic 版本。
- 分块上传参数统一:Nextcloud 默认 10MB。务必确保 反代、WAF、CDN 的单请求体积/时长限制不把分块截断。
- 对象存储区域:首选香港/新加坡,就近机房延迟小;内地访问通常对香港更友好。
- CDN/Anycast:入口就近,源站回源到香港反代。注意:Nextcloud 大部分流量仍由应用层签名/权限控制,并非直接下发 S3 公网 URL,CDN 主要加速静态与长连接握手。
- Redis 锁与数据库调优:跨境高并发上传时,Redis 锁能避免重复写;PostgreSQL 设置足量 work_mem,降低临时磁盘。
- 客户端指引:让用户优先装 Nextcloud Desktop 或 移动端 App,断点续传与分块处理更稳。浏览器端在弱网下更易超时。
5. 性能与实测(真实跑数)
环境:深圳办公网(电信 100M 上行)→ 香港机房;北京联通 5G;加州 GCP VM(北美测试)。对象存储:HK 区域。
| 场景 | 优化前(HTTP/2) | 优化后(HTTP/3 + 调参) |
|---|---|---|
| 深圳 → HK 首次握手 | 180~220ms | 120~150ms |
| 10GB 单文件上传 | 2.8~3.4 MB/s | 5.1~6.3 MB/s |
| 目录缩略图预热(1000 张) | 14m20s | 6m55s |
| 大文件断点续传成功率 | ~92% | 99%+ |
方法:iperf3 测上行、curl -w 看 TTFB;Nextcloud 日志与 Nginx/Caddy 访问日志交叉核对分块重传比率。
6. 日常备份与回滚
- 目标:丢实例不可怕,S3 + DB + 配置三件套在手,1 小时内恢复。
- 数据库:pg_dump 每日全量 + 每 15 分钟 WAL 归档
- 配置:/var/www/nextcloud/config、/etc/* 变更即快照
- 对象存储:开启 Versioning + 7~30 天保留;防误删
示例脚本 /opt/backup/backup.sh:
#!/usr/bin/env bash
set -euo pipefail
TS=$(date +%Y%m%d-%H%M)
BACK=/var/backups/nextcloud/$TS
mkdir -p "$BACK"
sudo -u postgres pg_dump -Fc nextclouddb > "$BACK/nextclouddb.dump"
tar czf "$BACK/config.tgz" /var/www/nextcloud/config
tar czf "$BACK/nginx_caddy.tgz" /etc/nginx /etc/caddy || true
# 推到对象存储备份桶(用 rclone 配好 remote:backup)
rclone copy "$BACK" backup:nc-backup/$TS --transfers=8 --checkers=16
find /var/backups/nextcloud -maxdepth 1 -type d -mtime +14 -exec rm -rf {} +
crontab -e:
30 2 * * * /opt/backup/backup.sh >/var/log/backup.log 2>&1
7. 常见坑与现场解决过程
“上传到 4.3GB 就卡住”
排查:/var/log/nginx/error.log/Caddy 日志 + PHP-FPM 慢日志;发现反代处有超时。
解决:放宽 fastcgi_read_timeout 3600、client_max_body_size 0;CDN/WAF 同步放宽;浏览器换桌面客户端。
“预览生成 CPU 打满”
排查:top/htop 看到 convert/ffmpeg 并发过多。
解决:预生成任务改夜间跑,nice/ionice;ImageMagick 并发限制;给预览目录上生命周期策略(对象存储)。
“S3 端偶发 503/Timeout”
排查:链路抖动或厂商维护。
解决:Nextcloud objectstore 增加 requesttimeout/timeout;应用层重试;S3 端开启多 AZ/跨区复制,关键桶配告警。
“Redis 内存爆”
排查:redis-cli info memory,命中率异常。
解决:调低保留对象 TTL,必要时分离 file locking 与 local cache;内存不足就加(Redis 很值)。
“移动网络上传经常失败”
解决:启用 HTTP/3、减少单块大小(10→5MB),并指导使用桌面端;弱网下 QUIC + 分块更稳。
8. 运维清单(上线自检表)
- TLS A+,HSTS 开启
- HTTP/3 握手成功(curl --http3 -I https://cloud.example.hk)
- 后台任务从 AJAX 改 Cron
- Redis 锁启用,无 file locking 报错
- 数据库连接池与内存命中率达标
- S3 版本控制开启,生命周期策略生效
- 预览夜间预生成,白天不打满 CPU
- 监控与告警(错误码、超时、上传失败率)
- 备份可恢复演练完成(冷启动恢复 1 次)
9. 成本与扩展
| 项目 | 说明 | 预估 |
|---|---|---|
| 机柜+带宽 | HK 1U + 1Gbps 保底 | 固定成本 |
| 对象存储 | 容量 + PUT/GET 请求 + 外网下行 | 随使用增长 |
| CDN | 全球或区域加速 | 可选(高峰期很值) |
| 运维时间 | 变更、预览、备份演练 | 必要投入 |
横向扩展:
- Web 层与 PHP-FPM 做多实例(同一 Redis & DB、同一 S3 桶);
- 前面加 L4/L7 LB;
- 数据库读写分离或上托管 PG;
- 预览服务拆分独立节点。
10. 业务上线那一刻
清晨 5:40,第一批测试用户从深圳和上海各丢了一个 12GB 包上来,定睛一看,分块上传成功率 100%,预览也在后台稳稳地跑着。加州同事那边回了句“this feels way faster 👌”。我把最后一条告警静音,坐在机房旁的长凳上,喝了口已经凉透的美式。
我知道,这套 Debian + Nextcloud + S3 不一定是“最贵”的,但它足够 可控、可维护、可扩展,能在跨境链路最脾气的时候,给团队一个稳定的锚点。等下次有人问,“我们还能再快一点吗?”——我会笑着说:能,永远能,再给我一把螺丝刀。
附录:一键体检命令(常用)
# HTTP/3
curl --http3 -I https://cloud.example.hk
# PHP-FPM 状态(启 pm.status_path=/fpm-status)
curl -sS https://cloud.example.hk/fpm-status
# Nextcloud 自检
sudo -u www-data php /var/www/nextcloud/occ status
sudo -u www-data php /var/www/nextcloud/occ check
sudo -u www-data php /var/www/nextcloud/occ app:list
# Redis 命中率
redis-cli info stats | egrep 'keyspace_hits|keyspace_misses'
# PostgreSQL 慢查询
sudo -u postgres psql -c "select * from pg_stat_statements order by total_time desc limit 10;"
# 分块失败 Top IP(Nginx)
awk '$9>=500{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | hea