# 新的文字

上次动笔是十月廿九,不想已过去了壹月整。

# 新的手机

陪伴我三年的小米MIX 3在一个不留心的早晨滑出了我长久不穿的高中校服,重重跌在了水泥地上,屏幕着地。

如果他是一个人,那么,着地的恐怕是后脑勺,最终的诊断大概是植物人。与普通的病例不同的是,或许期限不是永远,而是到我为他移植屏幕的那一天为止。

当天晚上,去最近的Apple Store,原价5999买下了思来想去不舍得买的iPhone 13。

至于是不是九月上市时未有纠结,现下就不会有“人”受伤,此处不表。

# 新的照片

恋上拍照,大约是在暑期,MIX3的存储空间,从暑期开始前的大半可用,到跌打损伤为止已经用得七七八八。Google Photos的15GB,也已将近告急。

打量了一眼5GB的iCloud存储空间,iPhone上新拍的照片,似乎有些无处安放。

# 新的NextCloud

目光最终还是落在了NAS上,几乎无限的存储空间,似乎是留住记忆的最好选择。iPhone上的自动备份应用,包括Möbius Sync、Photo Sync以及Resilio Sync。

  1. Möbius Sync是一个第三方Syncthing客户端,Syncthing的可靠性占优,但同步后完全无法远程查看照片,尤其是不能查看Live Photos,属于致命伤。
  2. Photo Sync是订阅制付费软件,每月6元,那么iCloud服务显然是更好的选择。
  3. Resilio Sync在稳定性上受到一些诟病,并且同样不能很好的支持Live Photos

似乎NextCloud是最佳的解决方案,不仅可以存放照片,并且可以后台自动备份,网页客户端还可以让我随时随地访问我的文件,而不必打开SFTP客户端。

值得一提是,我甚至可以通过挂载外部文件夹的方式,在NextCloud中直接观看Aria下载的影片(虽然他们中的80%在我的iPhone客户端中都无法解码)。

# 部署

PHP Webserver Mechanism © Cloudways

NextCloud是使用PHP实现的,而上面一幅图就很好地描述了访问PHP应用时服务器如何做出应答。

在这一过程中,需要一个网络服务器来接收用户的请求,并按照请求找到用户需要的文件,如果文件中有PHP脚本,那么就将其传送给PHP编译器加以处理。在处理过程中,编译器可能会访问数据库进行存取,随后它会将处理好的网页数据返回给网络服务器,并传输给用户。

在这个过程中,有四个关键节点,也就是网络服务器、PHP脚本、PHP编译器以及数据库。老规矩,网络服务器我们还是使用Caddy,PHP脚本就是NextCloud本身,编译方面我们利用PHP-FPM来加快处理速度,而数据库我们则使用PostgreSQL

# 建立用户

首先,更新软件包列表及已有软件包:

sudo pkg update -f && sudo pkg upgrade

然后建立一个用户,这个用户将被用于运行Caddy与PHP-FPM,并将拥有管理NextCloud代码文件以及网盘数据的权力,我使用的用户名就是caddy本迪:

sudo pw useradd -u 1003 -g caddy -m -d /usr/local/etc/caddy -s /usr/sbin/nologin -c "Caddy web server" -n caddy

# NextCloud服务端文件

由于NextCloud本质上就是一个由网页与脚本文件组成的网站,其安装与更新的方法就仅仅是解压缩那么简单。

首先从NextCloud官方网站下载服务端文件

然后将下载的压缩文件解压:

sudo mkdir -p /usr/local/www
mv /path/to/nextcloud.tar.bz2 /usr/local/www
cd /usr/local/www
tar -xzvf /path/to/nextcloud.tar.bz2

此时所有的NextCloud代码文件应当被解压到了/usr/local/www/nextcloud中,接下来我们将解压的所有文件移交给刚刚建立的服务端用户,并赋予其正确的权限:

sudo chown -R caddy nextcloud
sudo chmod -R 750 nextcloud

# Caddy

接下来配置Caddy,首先从官方网站下载Caddy的二进制文件,注意选择正确的操作系统以及所需的模块。举例来说,由于我的网络环境中443端口不可用,我就使用了caddy-dns/dnspod模块来通过dns-challenge获得SSL证书。

然后将下载好的Caddy二进制文件移动到/usr/local/bin

sudo mv /path/to/caddy /usr/local/bin/caddy

创建一个新的Caddy服务文件:

#!/bin/sh

# PROVIDE: caddy
# REQUIRE: NETWORKING
# KEYWORD: shutdown

. /etc/rc.subr

name=caddy
rcvar=caddy_enable

load_rc_config $name

: ${caddy_enable:="no"}
: ${caddy_user:="caddy"}
: ${caddy_group:="webmaster"}
: ${caddy_conf:="/usr/local/etc/Caddyfile"}
: ${caddy_envfile:="/usr/local/etc/caddy.env"}
: ${caddy_pidfile="/var/run/caddy/caddy.pid"}

pidfile=${caddy_pidfile}
command="/usr/sbin/daemon"
command_args="-P ${caddy_pidfile} -r -f caddy run --envfile ${caddy_envfile} --config ${caddy_conf}"

run_rc_command "$1"

在使用rc.d的条件下,Caddy不能正确识别$HOME,因此需要手动创建caddy.env为Caddy指路,告诉它应当如何存放配置文件与证书:

# /usr/local/etc/caddy.env
XDG_DATA_HOME=/usr/local/etc/caddy
XDG_CONFIG_HOME=/usr/local/etc/caddy

然后创建Caddyfile:

# /usr/local/etc/Caddyfile
{
  # 如果你的80和443端口不可用,建议关闭HTTPS自动重定向
  auto_https disable_redirects
  # 将日志存储到/var/log/caddy.log
  log {
    output file /var/log/caddy.log
    format console {
      time_format wall
    }
  }
}

# 修改next.yourdomain.com为你的域名或IP地址
next.yourdomain.com {
  root * /usr/local/www/nextcloud
  file_server
  php_fastcgi unix//var/run/nextcloud.sock {
    # 美化URL,在链接中隐藏PHP文件名
    env front_controller_active true
  }
  # 启用HSTS
  header {
    Strict-Transport-Security "max-age=15768000;includeSubDomains;preload"
  }
  redir /.well-known/carddav /remote.php/dav 301
  redir /.well-known/caldav /remote.php/dav 301
  # 禁止访问以下系统文件
  @forbidden {
    path /.htaccess
    path /data/*
    path /config/*
    path /db_structure
    path /.xml
    path /README
    path /3rdparty/*
    path /lib/*
    path /templates/*
    path /occ
    path /console.php
  }

  respond @forbidden 404
}

创建日志文件,并修改Caddy相关文件的权限:

sudo touch /var/log/caddy.log
sudo chmod 755 /usr/local/etc/caddy/Caddyfile /usr/local/etc/caddy/caddy.env /var/log/caddy.log
sudo chown caddy:caddy /usr/local/etc/caddy/Caddyfile /usr/local/etc/caddy/caddy.env /var/log/caddy.log

配置完成后,启动Caddy服务。

sudo sysrc caddy_enable=YES
sudo service caddy start
# 可以用以下命令查看Caddy日志来排查故障
tail -f /var/log/caddy.log

# PHP

现在我们已经有了一个文件服务器,但还缺少处理PHP文件的编译器,也就是PHP软件包。

只需要通过pkg就可以安装PHP软件包了:

sudo pkg install php80 php80-bcmath php80-curl php80-dom php80-fileinfo php80-filter php80-gd php80-gmp php80-intl php80-mbstring php80-opcache php80-openssl php80-pcntl php80-pdo php80-pecl-imagick php80-posix php80-session php80-simplexml php80-xml php80-xmlreader php80-xmlwriter php80-zip php80-zlib

以上是NextCloud所需的PHP软件包及PHP模块,安装后,我们需要进行一些简单的配置。首先创建PHP-FPM配置文件:

# /usr/local/etc/php-fpm.d/nextcloud.conf
[nextcloud]
user = caddy 
group = caddy
listen = /var/run/nextcloud.sock
listen.owner = caddy 
listen.group = caddy
pm = dynamic
# 以下限制了服务器的线程数,可以根据自身需要调整
pm.max_children = 128 
pm.start_servers = 32
pm.min_spare_servers = 32
pm.max_spare_servers = 64 
pm.max_requests = 500

然后创建PHP配置文件:

sudo cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

建议修改php.ini的以下部分:

; 增加内存到NextCloud推荐的512MB
memory_limit = 512M
; 设置默认时区
date.timezone = Asia/Shanghai
; 设置登录Session存储位置
session.save_path = "/tmp/php"
; 使用PHP的opcache缓存,去掉分号以使其生效
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=1
opcache.save_comments=1

创建Session存储目录,否则将无法登陆:

sudo mkdir /tmp/php
sudo chown caddy:caddy /tmp/php
sudo chmod 750 /tmp/php

配置完成后即可运行PHP-FPM:

sudo sysrc php_fpm_enable=YES
sudo service caddy restart
sudo service php-fpm start

# PostgreSQL数据库

由于在成文时,FreeBSD 13.0上用于访问PostgreSQL的PHP模块(PHP module)最高只支持PostgreSQL 12.8,因此我们暂时不能安装最新的PostgreSQL 13。

我们从安装PostgreSQL 12.8以及PHP 8.0的PostgreSQL相关模块开始:

sudo pkg install postgresql12-{server,client} php80-{pdo_pgsql,pgsql}

运行PostgreSQL服务:

# 在rc.conf中开启PostgreSQL服务
sudo sysrc postgresql_enable=YES
# 初始化数据库
sudo service postgresql initdb
# 启动数据库服务
sudo service postgresql start

接下来创建数据库的用户与数据表:

# 登录到postgres账户,初始条件下只有这个账户拥有管理数据库的权限
sudo su - postgres
# 创建数据库用户“nextcloud”,赋予其创建数据库的权力(createdb),并为其设置访问密码(pwprompt)
createuser --pwprompt --createdb nextcloud 
# 使用模版(template)创建NextCloud数据库“nextclouddb”,将用户(owner)设置为刚刚创建的nextcloud
createdb --owner=nextcloud --template=template1 nextclouddb
# 退出postgres账户
exit

# 初始化NextCloud

首先创建存放用户文件的目录。这个目录应当是空白目录,并且属于运行NextCloud的用户。

sudo mkdir /path/to/data/directory
sudo chown caddy:caddy /path/to/data/directory
sudo chmod 770 /path/to/data/directory
First Time Setup

然后使用浏览器打开你在Caddy中配置的网络地址(如next.yourdomain.com),进入初始配置页面。按照提示设置NextCloud管理员账号密码、用户目录。再填入PostgreSQL账户nextcloud及其密码、数据库名nextclouddb 以及数据库地址localhost

点击安装完成开始初始化。不出意外,几分钟后你就拥有了你自己的NextCloud。

# Redis缓存

在当前条件下,NextCloud的文件访问、上传都较为缓慢,需要添加外部缓存来进行加速。

sudo pkg install redis php80-pecl-redis

修改Redis的配置参数:

# /usr/local/etc/redis.conf
port 0
unixsocket /var/run/redis/redis.sock
unixsocketperm 770

caddy用户添加到Redis用户组:

sudo pw groupmod redis -m caddy

运行Redis服务:

sudo sysrc redis_enable=YES
sudo service redis start

编辑NextCloud配置文件以利用Redis:

# /usr/local/www/nextcloud/config/config.php
<?php
$CONFIG = array (
  # ...
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'memcache.local' => '\\OC\\Memcache\\Redis',
  'redis' => 
  array (
    'host' => '/var/run/redis/redis.sock',
    'port' => 0,
  ),
);

# 调优NextCloud

如果在公网使用,建议使用HTTPS,利用Caddy获取SSL证书,并在NextCloud配置文件中打开HTTPS模式;另外,也建议在配置文件中设置默认的时区:

# /usr/local/www/nextcloud/config/config.php
<?php
$CONFIG = array (
  # ...
  'overwriteprotocol' => 'https',
  'default_language' => 'zh',
  'default_locale' => 'zh-cn',
  'default_phone_region' => 'CN',
);

# 新的不去?

RIP
NextCloud on FreeBSD
2021.11.21 - 2021.12.04

两周前,我欣喜地在iPhone 13上下载了NextCloud客户端,上传了我所有的相册照片,有备份的感觉真棒。

两周后的今天,我的NextCloud已经永远地离开了我(至少在我重新安装操作系统之前)。

原因是在测试Caddy rc配置文件的过程中,运行了sudo chown caddy .*,而错误地将/usr/local/etc中的所有文件与目录交给了caddy用户。现在打开我的NextCloud,只能看到一页空荡荡的403错误。

新的不去,旧的不来?