前言
目前业界有filebeat收集日志工具、有类似seafile的开源云盘,也有FastDFS简单的分布式文件系统等,然而某企业SaaS平台的远程备份日志还是基于传统的压缩文件scp传输到备份存储。
由于filebeat收集日志的完整性有待商榷,开源云盘的功能杂乱而且不利于管理,分布式文件系统的内部存储逻辑不太适合目前某企业SaaS平台的备份日志管理情况,scp传输又存在私钥部署在服务器的安全问题。所以简单利用OpenResty的特性,用Lua实现了一个简单的文件备份API接口。
需求分析:日志压缩文件的远程备份
在企业级SaaS平台中,日志文件的远程备份是一个常见需求。特别是在海外服务场景下,我们需要:
- 高效传输:能够处理大文件压缩包的传输
- 安全可靠:避免使用SSH密钥等不安全的方式
- 易于管理:支持按主机名、文件类型等维度分类存储
- 校验机制:确保传输文件的完整性
- 分片支持:支持大文件的分片传输
现有的解决方案各有不足:
- Filebeat:日志收集的完整性有待商榷
- 开源云盘:功能杂乱,不利于管理
- 分布式文件系统:内部存储逻辑不适合备份日志管理
- SCP传输:存在私钥部署的安全问题
技术选型:OpenResty + Lua
基于以上需求,我们选择OpenResty + Lua的技术组合来实现轻量级文件备份系统,主要原因如下:
OpenResty的优势
- 高性能:基于Nginx的事件驱动架构,具有出色的并发性能
- Lua脚本支持:可以直接在Nginx中嵌入Lua代码,无需额外进程
- HTTP API:原生支持RESTful API,便于客户端集成
- 配置简单:Nginx配置文件即可完成所有配置
- 部署简单:单二进制文件,无需复杂的依赖
技术架构
flowchart TD
A[客户端] -->|HTTP POST| B[OpenResty]
B --> C[Lua脚本处理]
C --> D[文件分片处理]
C --> E[MD5校验]
C --> F[目录创建]
C --> G[文件存储]
核心实现
1. 文件上传API
我们设计了两个主要的API端点:
/frontproxy 端点
用于前端代理服务的文件备份:
1
2
3
4
5
6
| location /frontproxy {
set $fb_root_path '/mnt/data/frontproxy/'; # 该API调用存储的根目录
set $fb_chunk_size '8192'; # 设置上传文件包的大小8k,默认4k
set $fb_upload_time '30000'; # 设置上传超时时间30s,默认1s
content_by_lua_file conf/lua/fbsimple/fbFromUpload.lua;
}
|
/mxproxy 端点
用于邮件代理服务的文件备份:
1
2
3
4
5
6
| location /mxproxy {
set $fb_root_path '/mnt/data/mxproxy/';
set $fb_chunk_size '8192';
set $fb_upload_time '30000';
content_by_lua_file conf/lua/fbsimple/fbFromUpload.lua;
}
|
2. 分片上传支持
系统支持大文件的分片上传,通过设置fb_chunk_size参数来控制分片大小:
1
| set $fb_chunk_size '8192'; # 设置上传文件包的大小8k,默认4k
|
3. 校验与去重
系统支持MD5校验机制,确保文件传输的完整性:
1
2
3
4
5
6
| # 客户端需要提供MD5校验值
curl http://example.com/mxproxy -X POST \
-F 'data=@bin/bashrc/icmbash.sh' \
-H 'src-hostname:test.example.com' \
-H 'src-file-md5:cab8acb6d32e3b01be9e19efec57fc33' \
-H "src-file-type:mxproxy"
|
部署与使用
服务器端配置
完整的OpenResty配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| server {
listen 80;
server_name localhost;
set $template_location "/usr/local/openresty/nginx/conf/lua";
client_max_body_size 2048M;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# 文件备份API - 前端代理
location /frontproxy {
set $fb_root_path '/mnt/data/frontproxy/'; # 该API调用存储的根目录
set $fb_chunk_size '8192'; # 设置上传文件包的大小8k,默认4k
set $fb_upload_time '30000'; # 设置上传超时时间30s,默认1s
content_by_lua_file conf/lua/fbsimple/fbFromUpload.lua;
}
# 文件备份API - 邮件代理
location /mxproxy {
set $fb_root_path '/mnt/data/mxproxy/';
set $fb_chunk_size '8192';
set $fb_upload_time '30000';
content_by_lua_file conf/lua/fbsimple/fbFromUpload.lua;
}
}
|
客户端使用
客户端可以通过标准的HTTP POST请求上传文件。该指令会在API的目录下根据hostname/type的方式新建目录并保存在该目录下。如果没有hostname和type参数,文件会保存在API的根目录下。
基本上传示例
1
2
3
4
5
| curl http://backup-api.example.com/mxproxy -X POST \
-F 'data=@bin/bashrc/icmbash.sh' \
-H 'src-hostname:test.example.com' \
-H 'src-file-md5:cab8acb6d32e3b01be9e19efec57fc33' \
-H "src-file-type:mxproxy"
|
批量文件上传脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #!/bin/bash
# 批量上传日志文件备份
BACKUP_URL="http://backup-api.example.com"
API_PATH="/mxproxy"
FILE_PATH="/var/log/app/backup.zip"
# 计算文件MD5
MD5_SUM=$(md5sum "$FILE_PATH" | cut -d' ' -f1)
HOSTNAME=$(hostname)
FILE_TYPE="app-backup"
# 上传文件
curl "$BACKUP_URL$API_PATH" -X POST \
-F "data=@$FILE_PATH" \
-H "src-hostname:$HOSTNAME" \
-H "src-file-md5:$MD5_SUM" \
-H "src-file-type:$FILE_TYPE"
echo "文件上传完成: $FILE_PATH"
|
存储结构
系统会根据请求头中的参数自动创建存储目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| /mnt/data/frontproxy/
├── hostname1/
│ ├── type1/
│ └── type2/
└── hostname2/
├── type1/
└── type2/
/mnt/data/mxproxy/
├── hostname1/
│ ├── mxproxy/
│ └── transport/
└── hostname2/
├── mxproxy/
└── transport/
|
技术实现细节
Lua脚本处理流程
文件备份API的核心逻辑在fbFromUpload.lua脚本中实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| -- 伪代码:文件上传处理流程
function handle_upload(request)
-- 1. 解析请求参数
local hostname = request.headers['src-hostname']
local md5 = request.headers['src-file-md5']
local filetype = request.headers['src-file-type']
local file_data = request.files['data']
-- 2. 验证MD5校验
if md5 and calculate_md5(file_data) ~= md5 then
return 400, "MD5校验失败"
end
-- 3. 确定存储路径
local storage_path = determine_path(hostname, filetype)
-- 4. 创建存储目录
create_directory(storage_path)
-- 5. 保存文件
save_file(storage_path, file_data)
-- 6. 返回成功响应
return 200, "文件上传成功"
end
|
错误处理
系统包含完善的错误处理机制:
- MD5校验失败:返回400错误,提示校验失败
- 文件过大:根据
client_max_body_size配置拒绝过大文件 - 超时处理:根据
fb_upload_time配置处理上传超时 - 目录权限:检查并确保存储目录可写
- 磁盘空间:检查剩余磁盘空间是否足够
性能优化
- 分片上传:大文件分片上传,避免内存溢出
- 流式处理:文件流式处理,减少内存占用
- 并行处理:支持多客户端并发上传
- 缓存优化:Lua脚本缓存,减少编译开销
应用场景
1. 日志文件备份
系统主要用于企业级SaaS平台的日志文件备份:
- 应用日志:各服务组件的运行日志
- 访问日志:Web服务器的访问日志
- 错误日志:系统错误和异常日志
- 审计日志:安全审计和操作日志
2. 配置文件备份
- 配置版本管理:配置文件的定期备份和版本控制
- 配置同步:多服务器配置文件的同步备份
- 配置恢复:故障时的配置文件恢复
3. 数据文件备份
- 临时文件:处理过程中产生的临时数据文件
- 导出文件:系统导出的数据文件
- 备份文件:数据库备份文件等
监控与维护
日志监控
1
2
| access_log /var/log/nginx/backup_access.log main;
error_log /var/log/nginx/backup_error.log;
|
性能监控
关键监控指标:
- 上传成功率:成功上传的文件数量
- 上传失败率:失败上传的比例
- 平均上传时间:文件上传的平均耗时
- 磁盘使用率:存储目录的磁盘使用情况
定期维护
- 日志轮转:定期清理旧的访问日志
- 磁盘清理:清理过期的备份文件
- 权限检查:检查文件权限设置
- 备份验证:定期验证备份文件的完整性
总结
基于OpenResty实现的轻量级文件备份系统具有以下优势:
技术优势
- 高性能:基于Nginx的事件驱动架构,支持高并发
- 轻量级:无需额外依赖,单二进制文件部署
- 配置简单:Nginx配置文件即可完成所有配置
- 易于集成:标准的HTTP API,便于客户端集成
- 成本效益:资源占用少,运行成本低
业务价值
- 提升效率:自动化文件备份,减少人工干预
- 保障安全:避免使用不安全的传输方式
- 易于管理:结构化的存储和分类管理
- 可扩展性:易于扩展到更多服务类型
- 维护简单:技术栈熟悉,维护成本低
适用场景
该系统特别适合以下场景:
- 中小型企业的文件备份需求
- SaaS平台的日志文件收集
- 分布式系统的配置文件同步
- 临时文件的集中管理
- 需要高性能文件传输的场景
通过这种轻量级的实现方式,我们成功解决了企业级SaaS平台文件备份的需求,同时保持了系统的简单性和可维护性。这种基于OpenResty+Lua的解决方案为类似的文件传输和管理问题提供了一个很好的参考模式。