低成本构建属于自己的云工具
本文介绍如何构造自己的私有云工具,其功能包括分享、全文检索在线编辑等功能
这里需要强调一点,我搭建下面的服务主要用于个人日常办公/学习使用,没有频繁下载大文件的需求,对流量需求并不大,所以我购买阿里云服务时流量是按量付费。应用场景满足所有开源工具的开源协议
Why
-
广告是互联网的灵魂,但我不想看广告
-
互联网没有隐私,那是因为我用的是别人的服务
-
通过构建服务温故知新,锻炼一下自己的实际操作能力
需求
需求 | 三方服务 | 开源工具选型 | 功能 | 内存消耗 |
---|---|---|---|---|
博客 | 免费,但有广告 | Hugo + eureka | 静态 Web | 50MB |
搜索 | 谷歌 CSE 需要FQ | yacy | 开箱即用的 web 搜索引擎 | 500MB |
笔记 Wiki | VIP 一般一年100元以上 | seafile | 多端文件自动同步 + ES 搜索 + wiki + 在线 Word 编辑 | 4GB |
文件存储 | 百度网盘不限速一年200元 | filebrowser | 浏览器访问与上传/下载文件 | 50MB |
工具与原理
基础工具
- 闲置 PC + Ubuntu + Docker,闲置的计算资源 & 简洁的软件部署与服务隔离
- 阿里云 / 腾讯云域名服务,购买自己喜欢的域名
- Let’s Encrypt,域名 HTTPs 免费证书 & 通配符域名证书
- VPS(阿里云 ECS 或者 腾讯 VPS) + Nginx + FRP,外网流量转发
我的闲置笔记本 6 核 12 线程 32GB 内存,日常 12% CPU使用率和 24GB 内存占用,1min/5min/15min 的负载一般在 1.5;VPS 只作为流量转发(只有 Nginx 和 FRP 服务),对性能要求较低,我的 VPS 单核 512MB 内存,CPU 使用率一直在 5% 左右,1min/5min/15min 的负载一般在 0.2 以内
原理
-
VPS 提供公网 IP,实现外网访问能力与流量转发
-
Nginx 实现正向代理,转发 HTTPs 请求到内部 HTTP 服务
-
FRP 实现网络隧道,打通 VPS 和闲置 PC 网络
成本
阿里云最便宜的突发型 ECS(CPU 基准为 20%,超过 20% 将消耗额外资源,流量按使用量付费,5 年费用在打折期间大概 500 元),使用阿里云 PTS 压测,系统在 HTTPs 请求 15QPS 时 VPS 的 CPU 使用率稳定在 20% 左右,搭设自己使用的服务在性能上是完全没问题的
根据我自己的使用情况,一个月流量费最高 9.8 元,平常 5 元/月,假设流量费一年 60 元。为避免恶意访问,VPS 需要设置每天流量消耗上限
我的电脑和路由器正常情况下功率低于 30W,电费按 0.7 元/度算,一年电费小于 200 块
其他费用,比如域名大概每年二十块。这样算起来我一年需要花费 400 元左右
优秀开源工具
Hugo
Hugo,将 Markdown 文件转换为静态网页,结合 Nginx 可以实现静态网站,本网站就是使用 hugo 构建的
Yacy
yacy,开箱即用的自定义搜索引擎,安装配置非常方便,当前网站的 search site 即基于 yacy
Seafile
Seafile,三个主要功能:文件同步与全文搜索 / 个人 Wiki 与全文搜索 / 在线 Word 编辑。三个用户以内,Seafile 的搜索和 Word 编辑功能是免费的
Calibre-Web
Calibre-Web,开源 web 图书管理工具(下面展示的书籍是我自己花钱买的 🤭)
filebrowser
filebrowser,web 远程文件管理与下载工具
Gogs
Gogs,搭建自己的 github 服务。国内访问 github 太慢,可以自己搭建 gogs + VPN 实现部分仓库镜像。当然也可以使用 Gitee
Monitorix / Elastic Stack
Monitorix 是轻量级 Linux 系统监控软件,指令行即可安装;Elastic Stack 则是重量级监控工具,功能更丰富,细节请参考官方示例
VPS 网络流量限制
我在 VPS 中使用 vnstat 2.6、wondershaper 命令和 Python2 脚本加上 crontab 实现了流量限制,Python2 脚本如下所示:
import datetime
import json
import commands
# vnstat 2.6 and wondershaper must installed
# */1 * * * * python /home/xxx/tmp/net_monitor.py
valid_itf_id = "eth0"
daily_max_traffic = 3.0 # GB
cmd_traffic = 'vnstat --json d 1'
vnstat_res = commands.getoutput(cmd_traffic)
net_info_jsn = json.loads(vnstat_res)
valid_itf = None
for interface in net_info_jsn["interfaces"]:
if interface["name"] == valid_itf_id:
valid_itf = interface
log = open("/tmp/net_monitor.log", "a")
time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if valid_itf != None:
day_traffic_tx_gb = valid_itf["traffic"]["day"][0]["tx"]/(1024.0*1024*1024.0)
tmp = ""
if day_traffic_tx_gb > daily_max_traffic:
limit_cmd = "/sbin/wondershaper " + valid_itf_id + " 480 480" # tx/rx 60KB/s
tmp = commands.getoutput(limit_cmd)
if len(tmp) == 0:
tmp = limit_cmd
else :
clear_cmd = "/sbin/wondershaper clear " + valid_itf_id
tmp = commands.getoutput(clear_cmd)
res = str(day_traffic_tx_gb) + "|" + tmp + "\n"
log.write(time_str + "|" + res)
else :
log.write(time_str + "|no such interface: " + valid_itf_id + "\n")
log.close()
安装示例
Seafile
seafile 专业版 可免费用于不多于三个用户的场景。访问专业版页面需要将页面语言改为英文。使用 docker-compose.yml 是最简单的安装方法
组件
- MySQL
可以从国内镜像或者官网下载,最好安装 5.7 版本,安装可参考这里。此外还需要判断 win 防火墙是否放行
mysql 的安全设置比较麻烦,默认配置可能不支持 root 登录,需要修改并允许 root 登录,细节可参考其他资料 。默认密码修改可以参考这里
- OnlyOffice
OnlyOffice 社区版 可用于免费预览与编辑 Office 文档。seafile 安装与开启 onlyoffice 支持请参考这里
# 拉取 onlyOffice 镜像并运行
docker run -i -t -d -p 8702:80 --restart=always onlyoffice/documentserver
seafile 设置 onlyoffice api 地址时需要确定地址是可以访问的,即下面的配置:
ONLYOFFICE_APIJS_URL = 'http{s}://{OnlyOffice domain or IP}/web-apps/apps/api/documents/api.js'
- 其他
pro 版 seafile 自身包含了 5.6 版本的 ES 所以不需要额外安装
- 安装 seafile
先按照官网的要求安装依赖,然后执行 setup-seafile-mysql.sh
。pro 版本不支持 sqlite
常用命令如下:
seahub.sh start-fastcgi # 查看详细错误
配置
- 如果希望网页端预览 office,请参考这里进行设置
- 开启 ES 搜索请参考这里
- 默认 seafile 的 8000 端口不对外开放,参考这里可以设置非 localhost 访问
- 开机启动请参考官网文档
开启 HTTPS
使用 FRP 将内网中的 Seafile 服务端口引导到 VPS 中,随后就可以在 VPS 中通过配置 Nginx 实现 HTTPS 的外网访问。Seafile 配置 HTTPS 访问的方式可以参考官网
修改 proxy_set_header Host $host:CUSTOMPORT
,避免 CSRF 错误
FRP 内网配置示例
[seafile_8000]
type = tcp
local_ip = 192.168.8.114
local_port = 8000
remote_port = 8000
use_encryption = true
[filebrowser_8080]
type = tcp
local_ip = 127.0.0.1
local_port = 8080
remote_port = 8080
use_encryption = true
Nginx 配置示例
利用 Nginx 端口复用功能,只要修改 server_name
就可以在 80/443 端口同时向外提供不同的服务
###################################### Web
server {
listen 443;
server_name yearn.xyz;
ssl on;
ssl_certificate /etc/xxx/live/xxx/fullchain.pem;
ssl_certificate_key /etc/xxx/live/xxx/privkey.pem;
access_log /var/log/nginx/xxx.log;
error_log /var/log/nginx/xxx.log;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
root /root/web_data/static_web;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
server_name yearn.xyz;
rewrite ^ https://$server_name$request_uri? permanent;
root /root/web_data/static_web;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
######################################### seafile
server {
listen 443;
server_name seafile.yearn.xyz;
ssl on;
ssl_certificate /etc/xxx/live/xxx/fullchain.pem;
ssl_certificate_key /etc/xxx/live/xxx/privkey.pem;
access_log /var/log/nginx/xxx.log;
error_log /var/log/nginx/xxx.log;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:xxxx;
proxy_set_header Host $host:443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
access_log /var/log/nginx/seahub.access.log;
error_log /var/log/nginx/seahub.error.log;
proxy_read_timeout 1200s;
client_max_body_size 0;
}
location /seafhttp {
rewrite ^/seafhttp(.*)$ $1 break;
proxy_pass http://127.0.0.1:xxxx;
client_max_body_size 0;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 36000s;
proxy_read_timeout 36000s;
proxy_send_timeout 36000s;
send_timeout 36000s;
}
}
server {
listen 80;
server_name seafile.yearn.xyz;
rewrite ^ https://$server_name$request_uri? permanent;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}