NAS 搭建之旅 —— 软件篇

前言

距离 NAS 搭建之旅 —— 硬件篇 已经有两个月的时间了,看到有些朋友还是比较关注后续,趁着还能记着点什么,赶紧写下来。

NAS 系统安装

成功点亮「组装 NAS」后,第一步要做的就是给 NAS 装系统。网上有很多人是刷的黑群晖,也有不少装的是 WinServer。考虑到在以后较长一段时间内都要和 Linux 打交道,我没有考虑大多数人使用的方案,而是选择踩 CentOS 这个坑。

在 CentOS 6 和 CentOS 7 之间,我当然是毫不犹豫地选择了后者。满怀信心地把刻录了 CentOS 7 的启动盘插入 NAS 的 USB 口,但是一次次地铩羽而归,要么是长时间黑屏(长到我不会认为系统在启动),要么是出现简简单单的几句报错。

刷 BIOS、换 U 盘、换刻录软件、换系统(先装 Win 10 再装 Linux)等等各种方法都尝试过,反反复复地,终于在两个星期后的一个周末的下午,找到了解决方案:

  1. 使用 Fedora LiveUSB Creator 创建启动盘
  2. 在进入安装候选菜单时,选择第二行「Test this media & install CentOS 7」,然后按 E 进入编辑页,在命令行后加 nomodset,然后使用 Ctrl + X 执行安装
  3. 如果不出意外,会出现振奋人心的安装界面,一步步执行下去即可,之后的步骤可以参考 这里

逻辑卷管理

逻辑卷管理(Logical Volume Manager)简称 LVM,它将一个或多个硬盘的分区在逻辑上集合,相当于一个大硬盘来使用,当硬盘的空间不够使用的时候,可以继续将其它的硬盘的分区加入其中,这样可以实现磁盘空间的动态管理,相对于普通的磁盘分区有很大的灵活性。

与传统的磁盘与分区相比,LVM 为计算机提供了更高层次的磁盘存储。它使系统管理员可以更方便的为应用与用户分配存储空间。在 LVM 管理下的存储卷可以按需要随时改变大小与移除(可能需对文件系统工具进行升级)。

LVM 也允许按用户组对存储卷进行管理,允许管理员用更直观的名称(如「系统盘」、「资料盘」)代替物理磁盘名(sda/sdb 等等)来标识存储卷。

更多的介绍自行谷歌,此处提供我创建逻辑卷用到的几个命令。

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
34
35
36
# 查看磁盘分区表
fdisk -l

# 创建分区
# 注意 Hex Code 选择 8e 对应 LVM
fdisk /dev/sda

# 使分区表生效 无需重启
partprobe

# 转换 PV
pvcreate /dev/sda

# 查看已经存在的 PV
pvdisplay

# 创建 VG(Volume Group)
# 可以使用已经存在的 VG 名,同一 VG 名下的 PV 组成一个 VG
vgcreate VolGroupOne /dev/sda

# 查看 VG
vgdisplay

# 创建 LV
# 从 VolGroupOne 创建 1024 M 大小的 LV
lvcreate -L 1024M -n DataVol VolGroupOne

# 显示 LV 信息
lvdisplay

# 格式化 LV
mkfs -t ext4 /dev/VolGroupOne/DataVol

# 挂载 LV
# 如果需要开机自动挂载 可以修改 /etc/fstab 文件
mount /dev/VolGroupOne/DataVol /home/hunterx

软路由

在 NAS 搭建的硬件篇里有提到我用到了一个 USB 无线网卡 EDUP-AC1620,由于 WIFI 的驱动安装和软路由配置过程比较繁琐,我另外开了一 个帖子,有需要的请看博文 EDUP-AC1620 开热点 & 软路由配置

内网穿透

没有内网穿透,NAS 就只能在家里局域网使用,可玩性大大降低。找了蛮多的内网穿透工具,最终敲定了 frp,它是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议,目前使用的效果来看「很优秀」。

如果有这方面的需求,尤其是 https 反代有疑惑的请移步另外一篇博文 frp 内网穿透和 HTTPS 踩坑记

私有云盘

Nginx + Mysql + PHP 环境我使用了 OneInStack 一键安装脚本,安装脚本针对多种安装环境做了判断和优化,非常方便。

主流的开源云盘主要有 OwnCloud、NextCloud、seafile,综合比较之后还是比较倾向于 NextCloud。NextCloud 的免费版功能强大,多种丰富的插件(比如我经常使用的「任务」插件),还提供了 Windows/Android/IOS 多种系统的客户端,拿来即用。缺点也是有的,根据使用情况来看,确实有些臃肿,效率上可能不如 seafile。

具体的安装教程这里就不多废话了,稍微 LNMP 基础的直接上 官网 一看便知。这里主要记一下其中踩的坑。

伪静态设置

进入安装页面,输入数据库和管理员信息,安装完毕后会跳转至首页,然后会提示「No input file specified」,这就是伪静态没设置好的锅了。

官网也有参考的伪静态配置的示例,这里我以 url.com 为例,给一份自己的配置,以供参考。其中 SSL 证书我是使用了 OneInStack 提供的扩展生成的 LetsEncrypt 证书。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

server {
listen 80;
listen 443 ssl http2;
ssl_certificate /usr/local/nginx/conf/ssl/url.com.crt;
ssl_certificate_key /usr/local/nginx/conf/ssl/url.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_buffer_size 1400;
add_header Strict-Transport-Security max-age=15768000;
ssl_stapling on;
ssl_stapling_verify on;
server_name url.com;
access_log off;
index index.html index.htm index.php;
root /data/wwwroot/url.com;
if ($ssl_protocol = "") { return 301 https://$host$request_uri; }

include /usr/local/nginx/conf/rewrite/none.conf;
#error_page 404 /404.html;
#error_page 502 /502.html;

add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;

location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}

location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}

# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;

# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

location / {
rewrite ^ /index.php$uri;
}

location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}

location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
#Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
include fastcgi.conf;
}

location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}

location ~ \.(?:css|js|woff|svg|gif)$ {
try_files $uri /index.php$uri$is_args$args;
add_header Cache-Control "public, max-age=15778463";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
# Optional: Don't log access to assets
access_log off;
}

location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$uri$is_args$args;
# Optional: Don't log access to other assets
access_log off;
}

location ~ .*\.(wma|wmv|asf|mp3|mmf|zip|rar|jpg|gif|png|swf|flv|mp4)$ {
valid_referers none blocked *.url.com;
if ($invalid_referer) {
return 403;
}
}

location ~ [^/]\.php(/|$) {
try_files $uri =404;
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 7d;
access_log off;
}
location ~ /\.ht {
deny all;
}
}

fileinfo 模块安装

OneInStack 默认不会安装 fileinfo 模块,缺少该模块时,NextCloud 可能不会正常获取到文件的创建时间/修改时间等等。使用 OneInStack 提供的 addon.sh 脚本安装该模块即可。

缓存设置

如果你在进入 NextCloud 之后遇到「PHP 被设置为移除内联块, 这将导致多个核心应用无法访问」的提示,这很可能是缓存/加速器导致的,例如 Zend OPcache 或 eAccelerator。

修改 /usr/local/php/etc/php.d/ext-opcache.ini 中的 opcache.save_comments=0opcache.save_comments=1,然后使用命令 service php-fpm restart 即可。

内存缓存方面,NextCloud 支持很多种类,有 APU、 Memcached 和 Redis。官方都有详细的 介绍,我自己是使用了 Redis(Redis 也可以通过 addon.sh 进行安装),给个我的配置供参考。

1
2
3
4
5
6
'memcache.local' => '\\OC\\Memcache\\Redis',
'redis' =>
array (
'host' => 'localhost',
'port' => 6379,
),

亮个相