跳至正文

Docker部署Nginx反向代理Nextcloud,我差点把服务器搞炸了

事情是这样的,上周我想在自己的小服务器上搭个Nextcloud,方便同步手机照片和文档。之前一直用群晖,但总觉得群晖的套件版本更新太慢,而且Docker多香啊,资源隔离、迁移方便,出了问题直接删容器重来。结果这一搞,差点把我这个老司机翻车了。

我先说下环境,服务器是Ubuntu 20.04,Docker版本20.10.12,docker-compose是1.29.2。Nextcloud我选的是官方镜像,latest标签,当时是25.0.2。数据库用PostgreSQL 13,Redis做缓存,Nginx做反向代理。一切都规划得挺好,docker-compose.yml写了一大堆services,network也配了overlay,觉得稳了。

第一个坑来得很快。我启动容器后,浏览器访问Nextcloud,页面是出来了,但一直提示“内部服务器错误”。看日志,Nextcloud的web容器里报的是PHP Fatal error: Uncaught PDOException: could not find driver。我当时第一反应是PostgreSQL的PHP扩展没装。但官方镜像按理说应该带了的,我进容器一查,php -m | grep pdo,确实没有pdo_pgsql。这就很诡异了,Nextcloud官方镜像居然没编译pgsql驱动进去。查了下文档,原来官方镜像默认只支持MySQL/SQLite,如果要PostgreSQL,得用专门的fpm-alpine或者自己装扩展。我懒得自己build镜像,直接换成nextcloud:fpm-alpine,然后在docker-compose里加了个command: /bin/sh -c “docker-php-ext-install pdo_pgsql && php-fpm”。结果Command不管用,因为官方entrypoint会覆盖自定义command。最后解决办法是写个Dockerfile继承官方镜像,自己装扩展:

“`
FROM nextcloud:fpm-alpine
RUN apk add –no-cache postgresql-dev \
&& docker-php-ext-install pdo_pgsql
“`

重新build之后,pdo_pgsql有了,数据库连接通了。

你以为这就完了?太天真了。Nextcloud安装界面出来了,填数据库信息,点“完成安装”,结果卡在“正在创建管理员账户”那一步,转圈转了五分钟,然后超时。我看了下Nginx的error.log,报的是upstream timed out (110: Connection timed out) while reading response header from upstream。这说明Nginx和PHP-FPM容器通信有问题。我检查了docker network,确认两个容器在同一个网段,端口映射也没问题。后来用docker exec进Nginx容器,curl http://nextcloud:9000/ping,居然返回了empty reply from server。这就奇怪了,PHP-FPM的9000端口明明监听着。我查了PHP-FPM的配置文件,发现默认的listen = 127.0.0.1:9000,但容器里127.0.0.1是localhost,别的容器访问不到。需要在PHP-FPM的pool配置里把listen改为0.0.0.0:9000。但官方镜像的entrypoint每次启动都会重新生成www.conf,我直接改文件会被覆盖。最后是在环境变量里加了一个参数:FPM_LISTEN=9000,有的镜像版本支持这个env,我用的那个版本不支持。最后还是老老实实在Dockerfile里用sed替换:

“`
RUN sed -i ‘s/listen = 127.0.0.1:9000/listen = 0.0.0.0:9000/’ /usr/local/etc/php-fpm.d/www.conf
“`

改完重启容器,Nginx那边能正常代理了,Nextcloud安装成功。

然后就是性能问题。Nextcloud跑起来了,但上传一个10MB的图片要等十几秒,CPU占用还特别高。我一看,没有配置Redis。之前偷懒只配了PostgreSQL,文件缓存、锁、会话全部走数据库,PostgreSQL扛不住。Redis配置其实很简单,docker-compose里加一个redis服务,然后在Nextcloud的config.php里加几行:

“`
‘memcache.local’ => ‘\OC\Memcache\Redis’,
‘memcache.locking’ => ‘\OC\Memcache\Redis’,
‘redis’ => [
‘host’ => ‘redis’,
‘port’ => 6379,
],
“`

关键是Redis容器启动后,Nextcloud容器里要装redis扩展,不然报“Class ‘Redis’ not found”。又得在Dockerfile里加一行:

“`
RUN apk add –no-cache php81-pecl-redis
“`

注意版本号,我用的是php81,不同镜像版本可能不同。装完扩展重启,上传速度快了一倍,CPU占用也降下来了。

最后一个坑是Nginx的反向代理配置。Nextcloud官方推荐用https,但我只是内网用,暂时没配证书。结果Nextcloud后台一直报“您的Web服务器未正确设置以解析“/.well-known/caldav””。这其实是Nginx的location规则写漏了。官方文档给了nginx配置示例,但里面有一段是location / { rewrite ^ /index.php; },这个rewrite会覆盖所有路径,包括.well-known。需要手动加一个location块单独处理:

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

加完之后,报错消失,客户端同步也正常了。

回头想想,这次踩的坑其实都是些基础问题:镜像扩展不全、PHP-FPM监听地址、缓存配置缺失、Nginx路由规则。但每一个都折腾了我至少半小时。总结几条教训:第一,千万别迷信官方镜像,一定要检查它默认带了哪些扩展,尤其数据库驱动。第二,容器间的网络通信,监听地址一定要绑0.0.0.0,别偷懒写127.0.0.1。第三,Nextcloud这种重量级应用,Redis是必须的,不然数据库分分钟被拖死。第四,Nginx配置一定要逐行检查官方文档,多一行少一行都可能出莫名其妙的问题。

现在这套东西跑了快两周了,稳如老狗。下次再部署类似服务,我会先把官方镜像的Dockerfile翻一遍,看看它到底装了啥,省得再踩一遍坑。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注