Tag Archives: Ubuntu

我的乞丐版NAS及配置

几个月前跟风买了矿难之后的蜗牛星际来当NAS,目前已经稳定运行了几个月了,在这里记录一下。

硬件

  • 蜗牛星际A款单口,265包邮(2019.03的价格),包括以下配置。(就这个价格来说,等于不要钱了。)
    • 主板+ J1900 CPU(单网口,千兆)
    • DDR3L内存4G(注意是笔记本用的内存条)
    • 杂牌固态硬盘16G
    • 机箱、电源
    • 4个盘位
  • 西部数据紫盘6TB * 3,单块盘980,硬盘好贵!
  • AC Arctic F8 PWM 8厘米机箱风扇+减速线
  • 超频三风扇调速器

之所以要买机箱风扇和调速器,是因为这个NAS的主板的风扇是3针的,不支持调速,所以一开始就是全速转,声音有点吵。
换一个静音点的风扇,加上手动调速,可以把噪音降到一个合理的范围。

软件

我对于NAS的需求是:
1. 大容量的安全的存储(主要是照片+视频)
2. 24小时开机+下载
以上两点是必需的
3. 方便的照片、视频管理
4. 远程访问
以上两点是不是必要的需求,尤其是远程访问,还是有被黑的风险。

卖星际蜗牛的店铺都直接写这个东东可以装黑群辉,不过这个不是我的菜。我还是用Ubuntu吧。
稍微研究了一番各种RAID和类RAID技术,最后选定了如下的方案。

  1. Ubuntu 18.04,这个仅仅是个人喜好,换任何一个Linux应该都可以。
  2. SnapRAID,这是一个”软“RAID,是基于文件的RAID,比较轻量,相比RAID5来说,优点在于方便(不需要全盘RAID)安全,唯一的缺点是,这个不是实时的RAID,而是要定期sync的RAID。不过对于家庭用途来说,完全够用了——最坏情况,也只是损失了某一天新备份的内容。
  3. Aufs,这个是Ubuntu的kernel自带的,别的OS可能需要编kernel module。
  4. 之所以需要aufs,是为了把多块硬盘的目录”合并“成一个目录,这样对于NAS来说,只要操作一个目录就可以了。aufs会负责根据配置把文件写入某块硬盘。
  5. smartmontools,这个是为了监控硬盘的状态,并发送邮件通知。
  6. hdparm,这个是为了在空闲的时候把硬盘spin down,省电。(当然,可能的负作用是影响硬盘寿命。。。)
  7. cron,定时跑一些脚本,完成定期做snapraid sync,scrub等工作。
  8. samba,方便共享,也方便别的设备备份数据。
  9. FolderSync,这个是Android上的软件,把手机里的照片备份到NAS上。
  10. aria2c,这个是著名的下载软件了,支持HTTP,BT,等等,配合nginx反向代理,方便远程管理。

以上的配置可以完美地解决我的需求1,2,而需求3,4,放在以后再想吧。

具体的配置

(注:以下内容基本参考Setting up SnapRAID on Ubuntu to Create a Flexible Home Media Fileserver这篇文章,细节上有一些区别,比如说安装snapraid的方法、选用的unionfs等)

SnapRAID

上面引用的文章里是自己编译snapraid,其实没有必要,因为已经有PPA了!
所以安装很简单:

sudo apt install software-properties-common
sudo add-apt-repository ppa:tikhonov/snapraid
sudo apt update
sudo apt install snapraid

给每一块硬盘分区(注意我只有3块硬盘,sdb, sdc, sdd)

# 下面用parted工具给/dev/sdb分区
parted -a optimal /dev/sdb
GNU Parted 2.3
Using /dev/sdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mklabel gpt
(parted) mkpart primary 1 -1
(parted) align-check
alignment type(min/opt)  [optimal]/minimal? optimal
Partition number? 1
1 aligned
(parted) quit

# 下面和sgdisk工具备份/dev/sdb的分区情况,并应用到另外两块硬盘上
sgdisk --backup=table /dev/sdb
sgdisk --load-backup=table /dev/sdc
sgdisk --load-backup=table /dev/sdd

格式化硬盘

# 数据盘预留2%的空间
mkfs.ext4 -m 2 -T largefile4 /dev/sdb1
mkfs.ext4 -m 2 -T largefile4 /dev/sdc1
# 校验盘使用全部的空间
mkfs.ext4 -m 0 -T largefile4 /dev/sdd1

创建目录准备mount这些硬盘

mkdir -p /mnt/data/{disk1,disk2}
mkdir -p /mnt/parity/parity1

配置fstab,来自动mount

# 数据盘
/dev/disk/by-id/ata-WDC_WD60EJRX-89MP9Y1_WD-WX31D88R41HT-part1 /mnt/data/disk1 ext4 defaults 0 2
/dev/disk/by-id/ata-WDC_WD60EJRX-89MP9Y1_WD-WX31D88AEC9Z-part1 /mnt/data/disk2 ext4 defaults 0 2
# 校验盘
/dev/disk/by-id/ata-WDC_WD60EJRX-89MP9Y1_WD-WX31D88AERKL-part1 /mnt/parity/parity1 ext4 defaults 0 2

配置snapraid

$ cat /etc/snapraid.conf

parity /mnt/parity/parity1/snapraid.parity

content /var/snapraid/snapraid.content    # 这个目录是可选的
content /mnt/data/disk1/snapraid.content
content /mnt/data/disk2/snapraid.content

data d1 /mnt/data/disk1/
data d2 /mnt/data/disk2/

exclude *.unrecoverable
exclude /tmp/
exclude /lost+found/
exclude *.bak
exclude .AppleDouble
exclude ._AppleDouble
exclude .DS_Store
exclude .Thumbs.db
exclude .fseventsd
exclude .Spotlight-V100
exclude .TemporaryItems
exclude .Trashes
exclude .AppleDB

创建需要的目录,然后开始愉快的sync吧

mkdir -p /var/snapraid/
snapraid sync

SnapRAID配置好了,现在如果在/mnt/data/disk[1|2]/目录里写数据,在snapraid sync之后,这些数据就会有额外的parity来保护,即使坏了一块盘,数据也能找回来。
接下来,要配置AUFS,把这两个目录合并成一个目录,方便使用。

AUFS

之所以选用aufs,是因为Ubuntu默认支持它,并且性能还不错。

$ cat /etc/rc.local
mount -t aufs -o br:/mnt/data/disk1=rw:/mnt/data/disk2=rw,sum,create=mfs,udba=reval none /mnt/storage

上面的命令把/mnt/data/disk1/mnt/data/disk2这两个目录“merge”成/mnt/storage目录,以后所有的读写都在这个目录里操作就好了。

其中:

  • br: 定义了两个目录作为branch,都是rw
  • sum: 告诉df要显示所有的branch的block/inode的总和
  • create=mfs: 创建新文件的时候,选择free space最多的那个branch,这样两块硬盘的空间会比较均衡
  • udba=reval: 这个定义了aufs如何对待”绕过aufs”直接操作branch里的文件,一般来说我们尽量不直接去操作/mnt/data/disk[1|2],只操作mount point /mnt/storage,就不会有问题。
  • 参考:http://manpages.ubuntu.com/manpages/cosmic/man5/aufs.5.html

smartmontools

假设配置好了SMTP服务,下面的smartd.conf配置会在硬盘出问题的时候发邮件通知。
不过,smartd的配置有点复杂,下面的配置是抄来的,并不是特别了解它到底作了哪些测试。
如果仅仅想测试能否收到邮件,用下面那行注释掉的配置,重启smartd,应该就能收到邮件了。
参考:is-smartd-properly-configured-to-send-alerts-by-email

$ cat /etc/smartd.conf
DEVICESCAN -S on -o on -a -I 194 -m <my-email-address> -s (S/../.././02|L/../../6/03) -n standby,q
# Enable below to test if email is sent or not
#DEVICESCAN -M test -S on -o on -a -m <my-email-address>-s (S/../.././02|L/../../6/03)

enable smartd,让它开机自动启动

$ cat /etc/default/smartmontools
# uncomment to start smartd on system startup
start_smartd=yes
# uncomment to pass additional options to smartd on startup
smartd_opts="-q never -i 7200"

hdparm

这个工具可以配置硬盘空闲时spin down。

$ cat /etc/hdparm.conf
quiet
/dev/disk/by-id/ata-WDC_WD60EJRX-89MP9Y1_WD-WX31D88R41HT {
apm = 127
keep_features_over_reset = on
spindown_time = 242
}
/dev/disk/by-id/ata-WDC_WD60EJRX-89MP9Y1_WD-WX31D88AEC9Z {
apm = 127
keep_features_over_reset = on
spindown_time = 242
}
/dev/disk/by-id/ata-WDC_WD60EJRX-89MP9Y1_WD-WX31D88AERKL {
apm = 127
keep_features_over_reset = on
spindown_time = 242
}

其中spindown_time = 242表示如果一块硬盘idle了1个小时之后,会把它spindown

cron

上面介绍了,SnapRAID不是一个实时的RAID,所以可以配置crontab让它每天夜里做sync,然后每周做一次scrub,把结果通过邮件发到我的邮箱

MAILTO="<your-email-address>"
# SnapRAID sync every day at 02:00 and check temperatures
0 2 * * * /usr/bin/flock /tmp/snapraid.lock /usr/bin/snapraid sync; /home/mine/bin/cputemp.sh; /home/mine/bin/hddtemp.sh
# SnapRAID scrub every Sunday at 02:30
30 2 * * 0 /usr/bin/flock /tmp/snapraid.lock /usr/bin/snapraid scrub

其中,cputemp.shhddtemp.sh是两个check CPU/HDD温度的小脚本:

$ cat /home/mine/bin/cputemp.sh
#!/bin/sh
echo Mine NAS CPU Info.
echo ---------------------
MHZ0=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq)
TEMP0=$(cat /sys/class/thermal/thermal_zone0/temp)
MHZ1=$(cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq)
TEMP1=$(cat /sys/class/thermal/thermal_zone1/temp)
echo Hardware:
echo CPU0 Speed $(($MHZ0/1000)) Mhz "|" CPU Temp $(($TEMP0/1000)) C
echo CPU1 Speed $(($MHZ1/1000)) Mhz "|" CPU Temp $(($TEMP1/1000)) C

$ cat /home/mine/bin/hddtemp.sh
#!/bin/sh
echo Mine NAS HDD Info.
echo ---------------------
/usr/sbin/hddtemp /dev/sd[a-d]

samba

通过samba,把NAS上的文件共享出去,方便小米盒子之类的播放视频,也方便用手机来备份照片。

首先,创建两个不同的帐户,一个用来写(备份照片,文件等),一个用来读。

sudo adduser --home /mnt/storage/ --no-create-home --shell /usr/sbin/nologin --ingroup sambashare LeiYUNAS
# 这个是用来写的帐户,把它添加到sambashare这个group里
sudo smbpasswd -a LeiYUNAS  # 设置密码

sudo adduser --no-create-home --disabled-password --disabled-login ent #这个是用来读的帐户
sudo smbpasswd -a ent # 设置密码

配置samba的config,这里创建了3个共享:

  • 一个是用来备份数据的,只要在@sambashare这个group里就都有写权限;
  • 一个是专门用来共享/mnt/storage/Videos/这个目录,主要是给盒子播放视频用的;
  • 还有一个是用来共享/mnt/storage/aria2Download/目录,这个目录是给aria2下载用的,方便共享下载完,还没有整理的视频。
$ cat /etc/samba/smb.conf
[LeiYU_Home]
comment = LeiYU_Home_NAS
path = /mnt/storage/
browseable = yes
read only = no
wide links = yes
valid users = @sambashare @sadmin

[Videos]
comment = NAS_Videos
path = /mnt/storage/Videos/
browseable = yes
read only = yes
wide links = yes
valid users = ent

[Aria2Downloads]
comment = Aria2Downloads
path = /mnt/storage/aria2Download/
browseable = yes
read only = yes
wide links = yes
valid users = ent

aria2c

aria2c是一个支持多数协议的下载软件(不支持电驴),并且支持RPC方便远程管理。
它自己的配置这里就不贴了,网上到处都能找到。
为了方便远程管理下载,比如说,贴一个link给NAS,让它下载东西,可以用webui-aria2这个webui,配合nginx来使用。

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  root /var/www/html/webui-aria2-master/docs;
  index index.html index.htm;
  location / {
    try_files $uri $uri/ /index.html;
  }
}

上面这个最简单的配置在80端口上enable了aria2的webui。

如果放到公网上,建议换个端口,并且enable https证书。
比如说,我的网络环境里,网络的出口是树莓派,NAS只接在内网里,所以在树莓派的nginx配置里,要有类似这样的配置

server {
  listen <custom-port>
  server_name <your server name>
  ssl on;
  ssl_certificate <your crt>
  ssl_certificate_key <your key>
  ...
  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;

  location /jsonrpc/ {
    proxy_pass http://192.168.1.90:6800/jsonrpc; # aria2c的rpc端口
  }
  location / {
    proxy_pass http://192.168.1.90/; # aria2-webui的网页端口
  }
}

这样访问树莓派的特定的端口,就可以在外面通过HTTPS来访问到NAS上的aria2了。

其它

MergerFS

参考的文章里提到他最终用了MergerFS来作union fs,这个一开始也试了。
因为是用户态的文件系统,使用起来是很方便,/etc/fstab里加上下面的配置就行。

# Mergerfs
/mnt/data/* /mnt/storage fuse.mergerfs defaults,allow_other,use_ino,category.create=lfs,moveonenospc=true,minfreespace=20G,fsname=DiskPool 0 0

然而,因为是用户态的文件系统,性能实在是,一言难尽。用dd,fio等工具测试下来,写入性能只有10几MiB/s,实在是太慢太慢了。
所以放弃了MergerFS。

ownCloud

之前的所有配置,只是在NAS上用了Samba,然后配置一些备份工具(如FolderSync)来作备份,太过简单,所以也想找一些开源的私有云来尝试一下。
找了一圈,决定试用ownCloud,一方面是开源,另一方面,支持docker,所以用起来也很方便。
参考owncloud的github就可以很方便的使用了。

然而——配合ownCloud的手机app,用起来很不方便:

  • 没有导入已有图片的功能(或者说这个功能太难找?)所以硬盘里已有的照片没办法轻松的导入进去;
  • 手机上预览图片,竟然是要下载的本地的!?(这还cloud啥啊。。。)

相比起来,还是FolderSync的同步功能要好用多了,完美地完成了备份照片的功能,只缺少了图片管理的功能——这个功能以后再慢慢找开源软件来搞定吧。

WebDAV

FolderSync现在的设置是通过samba来同步,而samba服务是只在内网里能用的,所以得人在家里才能同步照片。
那人在外面,能否利用FolderSync来同步呢?答案是可以的,比如说,用WebDAV。

Nginx默认不支持WebDAV,需要编译才行,有点麻烦。幸好,我们有docker,并且找到了可用的dockerfile(虽然有bug)。
我fork了一份,把明显的错误改正之后,放在了https://github.com/mine260309/docker-nginx-webdav
用docker跑起来之后,测试的过程中发现了一些没解决的问题:

  1. 虽然用curl测试没问题,但是通过FolderSync的WebDAV来跑,总是有奇怪的错误,也许和FolderSync发的WebDAV请求有关。但是FolderSync不开源,所以也不知道具体的情况。
  2. 即使用FolderSync没问题,这个配置用的用户名、密码是放在HTTP请求里的,每次请求都会带;虽然跑在https里,但安全性总是觉得不够。。。

所以对于WebDAV也就浅尝辄止了。

Share

Enable HTTP2 for WordPress on nginx Ubuntu 14.04

HTTP2 is enabled on mainline version of nginx (v1.9.x), while Ubuntu 14.04 is using nginx 1.4.6.
To enable HTTP2, the simplest way is to upgrade nginx to the mainline version.
But I met several issues during the upgrade, and here’re the steps, the issues and solutions.

Upgrade to mainline nginx

Let’s use nginx’s official pre-built packages.

  • Add below lines into /etc/apt/sources.list.d/nginx.list
    sudo sh -c 'echo deb http://nginx.org/packages/mainline/ubuntu/ trusty nginx >> /etc/apt/sources.list.d/nginx.list'
    sudo sh -c 'echo deb-src http://nginx.org/packages/mainline/ubuntu/ trusty nginx >> /etc/apt/sources.list.d/nginx.list'
    
  • Add nginx apt key
    wget -q -O- http://nginx.org/keys/nginx_signing.key | sudo apt-key add -
    
  • Update ans install nginx
    sudo apt-get update
    sudo apt-get install nginx
    

However, it fails with error that it tries to overwrite nginx-common’s file:

dpkg: error processing archive /var/cache/apt/archives/nginx_1.9.11-1~trusty_amd64.deb (--unpack):
 trying to overwrite '/usr/share/nginx/html/index.html'

The solution is to remove old nginx on Ubuntu and re-install mainline nginx.

sudo apt-get remove nginx-common
sudo apt-get install nginx

Now nginx is upgraded to 1.9.11

Config file changes

  1.   Previously the site config files are stored in /etc/nginx/site-available, and the enabled sites are soft-linked in /etc/nginx/site-enabled/.
    But mainline nginx only load config files in /etc/nginx/conf.d/*.conf.
    So either copy the old config files to conf.d, or add include /etc/nginx/sites-enabled/ in nginx.conf.
  2. Mainline nginx’s user is nginx, but Ubuntu uses www-data, so change it in nginx.conf.
    #user nginx;
    user www-data;
    
  3. Change spdy to http2 in site config.

Reload nginx, and now the site’s HTTP2 is working.
However, try to open any page of WordPress, it just shows a blank page without any error. The log shows HTTP 200 OK.

Blank page issue

After googling the issue, it’s found that a missing FastCGI param is the root cause.
Add below line in /etc/nginx/fastcgi_params:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Reload nginx again, and now everything works fine.

Share

Nginx https load-balance on Raspberry Pi

我的Raspberry Pi工作了大约半年之后,文件系统开始出现莫名其妙的问题,各种文件都出现乱码,以至于某系统命令一执行就segment fault。到重新“安装”系统的时候了。

之前主要跑的服务是我的博客,nginx + wordpress搭的,在RPi上因为CPU的问题(php-fpm执行时几乎占满cpu),响应比较慢。既然重新弄了,考虑把某些请求交给家里的其它机器来处理。
家里还有一台跑Ubuntu的笔记本,希望满足如下要求:
1) 如果笔记本开机着,http请求(几乎)都交给笔记本处理;
2) 如果笔记本关机,http请求都由RPi来处理;
这些应该是完全自动实现的。

在Stackoverflow上问了这个问题,有人提示说可以通过nginx的load-balance功能来实现我的需求。于是开始google一番,找到了解决方案。
基本的思想是,把nginx配置成反向代理,笔记本和RPi都作为upstream来处理http请求;通过nfs共享同一个wordpress目录;配置mysql让wordpress访问同一个数据库。
一步一步来配置这些。
其中Raspberry Pi的地址固定为192.168.1.100,笔记本的地址固定为192.168.1.101,数据库名为minewpdb,数据库用户名为minedb

1. 配置Mysql
mysql仍然放在RPi上,让mysql接受网络的请求,这样不同的机器可以用同一个mysql数据库。

sudo vim /etc/mysql/my.cnf
==> 把 bind-address 改成RPi的ip地址
bind-address            = 192.168.1.100

mysql -u root -p #登录mysql控制台
mysql> grant all on minewpdb.* to 'mineblog'@'192.168.1.100' identified by 'xxx'; # 本地RPi的wordpress访问
mysql> grant all on minewpdb.* to 'mineblog'@'192.168.1.101' identified by 'xxx'; # 笔记本的wordpress访问
mysql> quit;
sudo service mysql restart # 重启mysql服务

# 在RPi和笔记本上分别测试一下
mysql -u mineblog -h 192.168.1.100 -p # 如果能登录成功,就ok了

2. 配置wordpress
Wordpress仍然在RPi上,只要修改wordpress的config文件,把数据库的host,用户名和密码都设对就行了。

sudo vim /path/to/wordpress/wp-config.php
==> 修改DB_HOST等参数
define('DB_NAME', 'minewpdb');
define('DB_USER', 'mineblog');
define('DB_PASSWORD', 'xxx');
define('DB_HOST', '192.168.1.100');

3. 配置nfs
在RPi上设置nfs export,让笔记本mount它,这样可以保证跑的wordpress是同一份。

sudo vim /etc/exports
==> 添加wordpress目录到exports
/path/to/wordpress 192.168.1.101(rw,no_root_squash,insecure,sync,no_subtree_check)
sudo service rpcbind start  # RPi的nfs依赖于rpcbind
sudo update-rc.d rpcbind enable  # 设置rpcbind自动启动
sudo service nfs-kernel-server start  # 重启nfs

在笔记本上,mount这个目录:

sudo mount -t nfs 192.168.1.100:/path/to/wordpress /path/to/wordpress

4. 配置nginx
在RPi上要把nginx配置成反向代理,upstream是RPi和笔记本,端口号都是8000,真正的wordpress运行在8000端口上。
添加新的配置文件 /etc/nginx/sites-available/wordpress-load-balance,注意我把http的访问全部重定向到https,以防GFW

# Upstream to abstract backend connection(s) for php
upstream php {
server unix:/var/run/php5-fpm.sock;
}

upstream mineservers {
# 设置两个upstream, 其中笔记本优先,同时设置5s的fail_timout
# 因为局域网很稳定,所以max_fails设成1就行了,
# 如果fail就说明笔记本关机中,让RPi自己来处理
server 192.168.1.101:8000   weight=999 fail_timeout=5s max_fails=1;
server 192.168.1.100:8000;
}

server {
listen 80;
server_name mine260309.me;
rewrite     ^ https://$server_name$request_uri? permanent;
}

server {
listen          443 ssl;
server_name     mine260309.me;

ssl_certificate     /path/to/cert/mine260309.me.2014.chain.crt;
ssl_certificate_key /path/to/cert/mine260309.me.2014.key;
ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers         HIGH:!aNULL:!MD5;
access_log         /path/to/wordpress/logs/proxy.log;
error_log            /path/to/wordpress/logs/proxy_error.log;

location / {
# 代理到upstream
proxy_pass  http://mineservers;

### force timeouts if one of backend is died ##
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;

### Set headers ####
proxy_set_header        Accept-Encoding   "";
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

### Most PHP, Python, Rails, Java App can use this header ###
#proxy_set_header X-Forwarded-Proto https;##
#This is better##
proxy_set_header        X-Forwarded-Proto $scheme;
add_header              Front-End-Https   on;

### By default we don't want to redirect it ####
proxy_redirect     off;
}
}

server {
root /path/to/wordpress;
# 在8000端口监听
listen          8000;
server_name     mine260309.me;
... # normal wordpress configurations
}

在笔记本上用同样的配置就可以了。

这样,任何一个请求到RPi,它会让nginx优先反向代理到笔记本的nginx的8000端口,由笔记本来处理;
如果笔记本关机,在5秒钟之后它会fail,于是再由RPi自己的8000端口处理。

Q.E.D.

Share