尝试 Nextcloud 18

起因

这个项目是前几天在网上发现有NextCloud这么一套私有云的开源项目。然后这个项目据我所知与我之前听说过的很多项目最大的不同之处是,他已经有非常比较完备的移动端的客户端以及桌面端的客户端,所以像文件同步这类的事情是完全可以自动在后台进行的,这样的话就理论上可以代替GoogleDrive这样的服务。当然这个项目他为了代替Google drive,他自己也有自己的一套office套件叫only office。甚至还有自己的视频会议,包括通信软件,这样就组成了一个相当完备的私有云,于是就引起了我的兴趣。

另外,一直以来都有几个小需求需要满足,看起来NextCloud都是可以做到的

  • Sandra,以及我以后手机照片的存储需求
    • 以后的Google Photo恐怕没有办法保留全尺寸的原片,哪怕是Pixel手机都不可以了。
  • 远程离线下载
  • 私有文件分享,尤其是婚礼的长视频一直没有给父母看过
  • 本地Kodi stream的功能

资源

  • 之前折腾过Android x86的那个旧笔记本
  • Rogers提供的公网IP,当然,还是会根据DHCP变化的,所以需要搞一个DDNS

过程

得益于NextCloud对Docker的支持,实际过程大体来讲是非常简单的:

  • 老笔记本装Linux
    • 这部分我还是选用的mint
  • 装Docker CE and docker compose
  • 从github上面找NextCloud的Docker Compose 文件 docker-compose.yml
  • 从duckdns.org上面搞一个免费的DDNS服务
  • 用docker compose turn up all docker container
  • 路由器开port forwarding,将服务器的ip下面的80和443端口forward出去

大体流程虽然简单,但是因为对于Docker的各种不熟悉,以及对NextCloud的不熟悉,在很多细节上花费了很多时间,见之后详述。

效果 ⭐

最终的成品都放在github自己的一个小repo下面了

  • 公网访问
  • 私有云盘
    • 手机端用NextCloud App
  • 私有通信
    • 当然我不知道是不是加密了的:facepalm
    • 手机端用NextCloud Talk App
  • Kodi stream
  • 组管理
    • kodi账号访问限制
    • group/user file/folder sharing
  • 远程下载
    • BT/Magnet Link/http
    • Youtube downloader

目前来看,总体满意。(这个目前是2020-05-18)

还有基础希望未来能提高

  • 云音乐体验
    • 目前没有乐曲库,也还没有用过自带的音乐播放器,在手机端的播放体验自然也就仅仅是能播放而已。。。
    • 感觉云音乐这个问题,恐怕是没有什么好的解决方案,毕竟,乐曲库不可能很全,也不可能有什么推荐。最多也就是把自己常用的一些背景乐拿下来放在里头方便取用吧(尤其是youtube上面的)

Details

I tried to put everything in my github repository, the readme contains some steps need to be done before the service is fully usable.

💡 Note that the $(pwd) when running docker-compose up will be used to generate the prefix for all the containers spawned from this command.
If we run docker-compose up in the checkout directory of this repository, and if we don’t change the directory name, all the containers created will have a prefix: nextcloud_
Note the prefix is ‘generated’ from the directory name, it is not exactly the dir name! e.g.: a directory name: Dir-Name-a-b-c will generate a prefix: dirnameabc

Docker Installation

注意,docker在用apt装在机子上之后,还有几步后续的操作要做,否则每次运行都需要sudo

Post-installation steps for Linux

简单来说就是:

# Create a docker group and add current user to that group
$ sudo groupadd docker
$ sudo usermod -aG docker $USER

# to activate the setting without logging out
$ newgrp docker

$ sudo systemctl start docker
$ sudo systemctl enable docker

Config items in dot.env (should be renamed to .env when to be used to spin up a server)

MYSQL_ROOT_PASSWORD=

这个是买sql db这个Container所要用到的root password,这个应该跟next cloud本身没有什么关系。

MYSQL_DATABASE=nextcloud 这个我怀疑是next cloud在mysql里面建立的它的带头被子的名称,但是我并没有验证它。

MYSQL_USER=nextcloud 这个是next crowd在mysql里面client的user name。Next cloud官方的example里面用的就是这个string: next cloud,所以我就没有改。

MYSQL_PASSWORD= 这个应该是next cloud在mysql里面用到的client的password。

LOCAL_DB_DIR=/home/anthony/Data/nextcloud_local_db 这个是mysql,它的db data放的位置在host上面的mapping。这里注意,如果结合Docker-compost里面的内容的话,可以看到用的是docker的mount binding而不是volume。这是因为我想把它mount在我本地的一个可以管理的一个目录下,这样方便以后做back up。

NEXTCLOUD_TRUSTED_DOMAINS=cookiepan.duckdns.org 这个是next cloud自带的一个environment variable,它实际上设定了之后,会改变next cloud里面的config/config.php的内容。只有在这个列表里面的domains才是在访问的时候可以用的,如果没有这个设定,它默认是空的,这个时候只有localhost才可以访问。连内网内的其他主机都不可以访问,只能是server自己访问自己。

NEXTCLOUD_ADMIN_USER=dako_admin 管理员的账号。如果没有设置的话,在初次访问domain的时候是回要求设置admin的,但是我觉得在那个时候再设置太危险,所以最好是提前设置好。

不过我在这里设置之后,访问的时候还是弹出要我setup admin account,但是我感觉setup似乎是没有作用的,仅仅是因为NextCloud没有setup完,所以才显示要创建新的admin

NEXTCLOUD_ADMIN_PASSWORD= 管理员的密码,必须和上面的admin user一起设置

MY_NEXTCLOUD_VIRTUAL_HOST= 所有前面带买MY_的前缀的environment variable都是我自己定义的,用来区分Container Images所定义的Variable。

这个variable的用处是连接NextCloud和Nginx Proxy的Container

Nginx Proxy的Image是在./proxy 目录下面,是官方的Nginx Proxy再加上一个custom config,用来限制上传文件的大小。

Nginx Proxy要求它所代理的APP都定义一个VIRTUAL_HOST的Variable。我这里是用这个MY_NEXTCLOUD_来限定这里的这个值仅仅是给NextCloud用的。如果以后还有新的APP加入这个compose的话,还会再加新的variable,但是在APP里面都需要用VIRTUAL_HOST这个environment。

MY_NEXTCLOUD_LETSENCRYPT_HOST=

这个是当我们使用https来访问我们的next cloud的时候,我们需要用到let’s encrypt给我们发的certificate,我们需要在这里设定成访问next cloud所需要用到的域名,然后这个值会传给NextCloud的container。

💡 猜测 从这里看,似乎NextCloud对于Let’s Encrypt做了一些特殊的支持,所以添加Let’s encrypt的支持非常容易。

MY_NEXTCLOUD_LETSENCRYPT_EMAIL= 这个是给let’s encrypt的一个EMAIL地址,就是当let’s encrypt有一个certificate过期的时候啊,它会给这个邮箱发邮件。

💡 猜测: 我的理解是,在运行let’s encrypt这个container的时候,它应该是会自动刷新的。但是如果我们之前用过一个域名,然后我们不用了,比如我们已经把这个container bring down了,那他就不会再自动刷新了,那这个时候自动过期的时候,大概let’s encrypt就会发邮件。

LOCAL_NEXTCLOUD_DATA_DIR= 这个跟之前 LOCAL_DB_DIR类似,只不过是NextCloud的数据地址,这里面应该包括了NextCloud的Config,用户数据,Apps php代码等等所有东西。就是在NextCloud下面/var/www/html里面的所有东西。

注意这个目录也是有owner group等等的permission设定的,都会变成www-data用户以及group也是: www-data。

DUCKDNS_SUBDOMAINS= 这是给duckdns.org的,注意这块不是所有的域名,只是域名第1个点前面的那部分。

如果有多个子域名,则应当用逗号分割开。

DUCKDNS_TOKEN=

这个是我们注册duckdns.org之后,duckdns.org提供给我们的token。这个token应该会被拿来在服务器端identify account并刷新我们的ip.

下面讲讲这次了解到的一些东西

Let’s Encrypt

为了了解这个,特地去重新了解了一下HTTPS的协议原理

B站上面这个视频讲得非常好

【web安全3】【硬核】HTTPS原理全解析

所以,let’s encrypt companion 这个image里面的let’s encrypt 部分应该是包括了从let’s encrypt上面获取certificate的过程的,这之中涉及域名,因为一个证书是颁发给域名的。

之后这个证书会存在本地,在docker compose里面可以看到有很多的volume,同时在proxy以及letsencryptcompanion的container都有。这就是把得到的证书提供给proxy,好在反向代理的时候给client提供正确的证书。

不过这一部分我没有详细去看,只是我的推测。

真正的Source of truth:

nginx-proxy/docker-letsencrypt-nginx-proxy-companion

Nginx Proxy

nginx-proxy/nginx-proxy

图片来自https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion/blob/master/README.md

💡 猜测 这一句:”Update nginx vhosts configuration when a new docker instance is detected”,可能就是用到了volume里面的: /var/run/docker.sock:/tmp/docker.sock:ro

可以看到,外面进来的80 443的数据,会首先进入nginx reverse proxy,之后根据数据里面的域名,分配给不同的app。

注意LetsEncrypt在给certificate的时候,是会chanllenge 80端口的,所以在外网可见的端口中,必须要有80

💡 所以路由器port forwarding的external端口必须是80,和443。不能是8080 或者8443 什么的。 但是在internal部分是不是就可以用8080之类的,目前并不清楚,没有试过,但是我觉得应该是可以的。

后续提高

Session timeout

Updated at May 21st

# Disable keep-alive
docker exec --user www-data <nextcloud_app_container_name> php occ config:system:set \\
  session_keepalive --value=false --type=bool

# 10 Minute Lifetime
docker exec --user www-data <nextcloud_app_container_name> php occ config:system:set \\
  session_lifetime --value=600 --type=integer

Performance tuning

First thing tried, raise the php memory limit

# In nextcloud app docker container
echo "memory_limit=1G" > /usr/local/etc/php/conf.d/memory-limit.ini

But the performance is still poor.

Using top in the container, find that, when we see Error 502, it is usually the IO Waiting casing the poor performance.

💡 May 21: the issue might be the background preview generation, as I turned on a list of preview generators. The Long Wedding Video might needs a quite long time.

Second, I should add redis to the docker compose.

Reference:

nextcloud/docker

Verify Redis is used:

Verify that redis is used · Issue #140 · nextcloud/docker

docker exec -it <redis_container_name> sh
# In the redis container
redis-cli
# Then:
KEYS *

It seems like this issue has been resolved, at least at the moment of editing 2020-05-22.

After I re-up the docker-compose.yml, I see the memcache.locking already set properly with redis, and the KEYS * does return me a lot of keys.

更确切的确认方法:

How to check if Redis is used in NC

Third, need to tune the Apache process number (assume using apache webserver)

Already changed in the github repo

References

Numbers

Tuning Your Apache Server

Add a config in docker

Edit apache configuration in docker

Preview Generation

Using this app:

  • Ad-hoc Generation

Updated at May 21st

# Explicitly enable preview
docker exec --user www-data <nextcloud_app_container_name> php occ config:system:set \\
  enable_previews --value="true" --type=bool
  
# Explicitly list the providers to use
docker exec --user www-data <nextcloud_app_container_name> php occ config:system:set \\
  enabledPreviewProvider <index> --value="OC\\Preview\\<provider>" --type=string

Indices and Providers

  • 0: PNG
  • 1: JPEG
  • 2: GIF
  • 3: HEIC
  • 4: BMP
  • 5: XBitmap
  • 6: MP3
  • 7: TXT
  • 8: MarkDown
  • 9: Movie
  • 10: PDF
  • 11: MSOfficeDoc
  • 12: MSOffice2003
  • 13: MSOffice2007
  • 14: SVG
  • 15: Font

Reference

Previews configuration – Nextcloud latest Administration Manual latest documentation

  • Scheduled Generation

Reference:

Setup Cron or systemd timers for the Nextcloud Preview Generator – Nextcloud

First, we should install the preview generator app

Then, in the nextcloud container:

busybox crontab -e -u www-data

# Add following line in the opened file
0 4 * * * php -f occ preview:pre-generate

This is different from the reference above. 这是因为这里用的是Docker部署的,和直接本地部署所生成的文件系统还是有差别。

提高稳定性

在2020-05-18就出现了一次,internal server error。我也不知道是怎么回事,连docker-compose down都不能把所有东西都关上。

Appendix

Configs

MYSQL_ROOT_PASSWORD=zx9Bs6tgCCpWuSp3Lwvm
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=uk8UEGgh7zwy9YX9pVNu
LOCAL_DB_DIR=/home/anthony/Data/nextcloud_local_db

NEXTCLOUD_TRUSTED_DOMAINS=cookiepan.duckdns.org
NEXTCLOUD_ADMIN_USER=dako_admin
NEXTCLOUD_ADMIN_PASSWORD=ILikeTryNewThings900928
MY_NEXTCLOUD_VIRTUAL_HOST=cookiepan.duckdns.org
MY_NEXTCLOUD_LETSENCRYPT_HOST=cookiepan.duckdns.org
MY_NEXTCLOUD_LETSENCRYPT_EMAIL=lqn.sandra@gmail.com
LOCAL_NEXTCLOUD_DATA_DIR=/home/anthony/Data/nextcloud_local_data

DUCKDNS_SUBDOMAINS=cookiepan
DUCKDNS_TOKEN=f22de3be-29ef-4b1a-a8d2-7a844b3c5cad

Run Nextcloud command in docker

docker exec --user www-data nextcloud php occ blablabla

Bring Down and Clean

# In the original directory of docker-compose.yml
docker-compose down

# This will stop all containers that not running
# Note that there can be some containers in state: exited.
docker container prune

# This will delete all the images that is not currently used,
# includes: dangling ones and stopped ones
docker image prune --all

docker network prune

docker  volume prune

Useful commands

Docker

docker ps

docker image ls

docker volume ls

docker network ls

# print the stdout (and stderr?) of the container
docker logs <container_name_or_id>

# Get into a container and use shell
docker exec -it <container_name_or_id> bash

References

为Nextcloud添加离线下载功能 – US360

Use a wrapper script

zerpex/docker-nextcloudoc

Modify the docker file for nextcloud to add feature

nextcloud/docker

最后 😢

最后在五月25号,还是放弃了,试图解决问题花了三天,没什么进展,主要是因为性能的问题,尤其是播放视频的性能。

  1. 桌面浏览器播放大视频,似乎会被理解成很多个下载,在apache server这个服务下,就变成了有很多的进程试图读磁盘,直接导致磁盘卡死,然后每一个进程都结束不了。整个server都会因此而卡死。浏览器端直接就是502。
  2. 手机端浏览器播放大视频情况会好一点点。似乎不会启动那么多的进程,但是进程还是会持续很长时间。也就是说在用户已经关闭了视频播放器的情况下,进程还会留驻很长时间。当有频繁的视频播放操作的时候,同样会造成磁盘的阻塞,进而导致整个服务器崩溃。但是比桌面端好一点的是至少10个g的1080p高码率的视频是可以完整播放下来的。
    1. Potential related issues: Video playback is slowly raising the CPU load and also releases cpu ressources slowly after the video has stopped · Issue #487 · nextcloud/viewer trying to play unplayable videos starts long running PHP processe · Issue #330 · nextcloud/viewer
  3. 在通过kodi端从webdav stream视频时,速度非常不理想,经常卡顿。这个原因可能不是nextcloud的,可能是kodi。不过我也没有心思再去调试了

这几天也尝试了php fpm+nginx的组合。性能也不行,也有同样的问题。这个其实可能是nextcloud 上面那个viewer app的锅。不过即使不管视频播放,nc的性能也确实堪忧,大量文件上传时,在我老笔记本上出现过问题,各种出错。而mbp上实验却又没有问题。

下一个试试seafile,功能上可能差点,但是功能都比较可靠估计。

另外owncloud似乎是要做go实现的下一代的,而nc这帮人似乎什么都不打算做🙉,php的性能确实太差了。当然,最终劝退的原因还是视频播放的问题。

Golang for OwnCloud

owncloud/ocis

另外一个看起来类似seafile的选择:FileRun,不过今天(25号)就没有尝试了,因为seafile看起来不错。

关于Apache和PHP-FPM

Apache的问题

Why is Apache spawning so many processes?

判断Apache的MPM mode

cd /etc/apache2/mods-enabled
# 看看当前哪个MPM的mod有链接

Or follow: https://superuser.com/questions/284898/how-to-check-which-apache-modules-are-enabled-installed

apache2ctl -M

This will show us the MPM module at least

What is FPM:

搞不清FastCgi与PHP-fpm之间是个什么样的关系

关于 /usr/local/etc/php下的php.ini文件

注意即使使用Apache,这里的conf.d目录下的memory-limit.ini还是控制了php解释器使用的内存大小(理解不一定准确)

安装好php为什么要复制一份php.ini 到/usr/local/php/lib/php.ini

php 没有php.ini配置文件

FPM的docker compose 样板

nextcloud/docker

注意这里用了一个单独的cron的container来处理cron的指令

另外这里还有一个web的container,这里用的是nginx的web server,并不是Apache

Comparison references:

Seafile OwnCloud NextCloud 私有云软件比较