如何在Docker卷中安全存储网站/应用用户上传文件并支持备份?
尽管AWS Bucket等第三方服务是此类问题的常见解决方案,但本文聚焦于不依赖第三方服务的方案:仅使用Docker和自有VPS。假设目标网站/应用面向小流量场景(例如面向小众群体的本地博客)。
教程中的常见做法及我不认可的原因
通常项目会有一个用于存放需由服务器静态返回文件的目录,以Laravel框架为例,该目录为storage/app/public:
![Laravel项目storage/app/public目录结构]
部分教程会在此目录中混合存放两类文件:
- 开发者准备的文件(例如样式表)
- 用户上传的文件(主要是图片)
我不认可将这两类文件混存于public目录的做法。若VPS上的public目录因某种原因丢失怎么办?开发者准备的静态文件可重新上传,因为开发者本地有备份;但用户上传的文件若无特殊措施,将会永久丢失。
我的发布包架构
与PHP+Laravel不同,我的技术栈中源码与输出代码完全分离。下图中的所有文件均由项目构建工具自动生成,甚至包括package.json:
![自动生成的ProductionBuild目录结构]
"ProductionBuild"目录将直接部署到VPS。部署方式可采用FTP,Rsync或搭配输出代码子模块的Git则是更进阶的选择。
删除本地及VPS上的"ProductionBuild"目录必须是完全安全的,因为它可重新构建并上传。若将用户上传文件存储到public目录,这一要求将无法满足。
我的基础设施架构
再次说明,目标项目并非SaaS,只是小流量的网站或应用,因此无需AWS ALB这类重型组件。基础设施架构非常简单:仅包含服务器应用与数据库:
![基础设施架构示意图(服务器应用+数据库)]
服务器应用与数据库均拥有各自的Docker容器和Docker卷,但二者存在显著差异:
- Web应用的Docker容器与卷是可丢弃的。部署新版本应用时,会创建新的Web应用Docker容器与卷,与旧版本完全独立,且不包含关键数据。
- 数据库的Docker容器与卷是可复用的。创建新的Web应用容器时,可复用数据库中的数据。
以下是符合上述要求的docker-compose.yaml和Dockerfile:
version: "3.5" services: front_server: container_name: Example-Production-FrontServer build: context: . dockerfile: "FrontServer.Dockerfile" ports: [ "80:8080" ] depends_on: [ Database ] command: sh -c "npm run \"Database migration\" && node FrontServerEntryPoint.js --environment production" Database: image: postgres container_name: Example-Production-Database ports: [ "${DATABASE_PORT}:${DATABASE_PORT}" ] environment: - POSTGRES_PASSWORD=${DATABASE_PASSWORD} volumes: - DatabaseData:/data/Example.jp volumes: DatabaseData: {}
FROM node:18-alpine WORKDIR /var/www/example.com COPY . /var/www/example.com RUN npm install --no-package-lock
目标
现在需要新增一个卷(可能还需新增容器)来存储用户上传的图片及其他文件。这些文件是可复用的,丢失会造成严重影响,因此必须提供备份功能。
![新增文件存储卷后的架构示意图]
备选方案
我未曾使用过Nginx,但听说它适合存储静态文件。或许部署一个Nginx容器并关联相应卷是合适的方案。
该方案的潜在缺点是对VPS配置要求较高。我的VPS配置为1GB内存、2核CPU,不确定新增一个Web服务器容器是否可行。
同时,也可考虑仅新增卷而不新增容器的方案:让可丢弃的Web服务器容器像访问数据库容器与卷一样,访问可复用的文件存储卷。
内容的提问来源于stack exchange,提问作者Takeshi Tokugawa YD

