尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

树莓派Docker化网页服务器:从零搭建容器化部署环境

树莓派Docker化网页服务器:从零搭建容器化部署环境 1. 项目整体设计与思路拆解1.1 这套方案解决什么核心问题先说清楚一件事树莓派 Docker 化网页服务器本质上不是新技术而是把“低功耗迷你主机 容器化编排”这两个已经很成熟的思路组合到一块巴掌大的板子上。我最早用树莓派跑网页服务是裸机方案系统里直接装 Nginx、PHP-FPM、MySQL再手动配开机启动。前期折腾系统依赖、版本冲突、路径权限后期每次换机器或者换服务都要把同样的环境搭建流程再走一遍。中间经历过一次 SD 卡崩溃系统整个起不来配置文件、网站数据全得靠备份恢复那几天的感受就是这个方案能跑但不够“稳”。后来迁移到 Docker 之后整个体验完全变了。网页服务器变成一个镜像Nginx、PHP、数据库各自是独立容器数据目录单独挂载配置文件通过映射文件管理。换机器的时候把 compose 文件和挂载目录拷过去docker compose up -d一条命令就能把整套环境拉起来。这才是这套方案真正的核心价值环境可复现、故障可恢复、服务可迁移。文章写成“专栏导览”的定位也是因为这块内容可以拆成很多期。从硬件准备、系统初始化、Docker 安装到 Nginx 部署、反向代理、数据持久化、日志处理、安全加固每一块单独拿出来都值得写一写。这篇作为开篇先把整套骨架搭好后面每个方向都可以在这个基础上续起来。1.2 硬件选型与角色定位树莓派型号选择直接影响这套方案的体验上限。我自己试过的组合里树莓派 3B 也能跑 Docker只是内存只有 1GB跑一个 Nginx 加一个轻量数据库就有点吃紧编译镜像或者跑稍微重一点的容器时卡顿明显。4B 是综合体验最好的选择2GB 内存版本跑基础网页服务没问题4GB 版本就比较从容可以同时跑 Nginx、MySQL、Redis 这类组合。5 代的话性能更强SD 卡或 NVMe 的读写都更好但价格也高出不少。如果你手头已经有树莓派不用纠结“必须用最新型号”。网页服务器本身是 I/O 密集型应用瓶颈一般在网络带宽、磁盘读写和内存容量CPU 反而不是最紧张的部分。我实际测试过 4B 跑一个带数据库的 WordPress 站点日访问量在几百到一千 IP 的范围内完全扛得住。真正需要注意的是内存Docker 容器本身有开销加上 Nginx、PHP-FPM 的进程池2GB 内存是起步线4GB 更稳妥。存储方面强烈建议优先用 SSD 移动硬盘或者 NVMe HAT而不是一直依赖 SD 卡。SD 卡的问题我在后面会专门讲这里先给结论容器化方案下日志和数据的写放大明显普通 SD 卡的寿命消耗比裸机方案快得多做好数据外置是长期稳定运行的前提。1.3 为什么用容器而不是裸机很多人的第一反应是树莓派性能有限再用 Docker 套一层会不会很浪费我一开始也有这个疑问实测之后发现这个担心基本不成立。Docker 不是虚拟机它没有完整的客户机操作系统开销容器进程直接跑在宿主机内核上多出来的只是镜像分层、命名空间隔离这些轻量机制。对 Nginx 这种性能敏感的进程来说容器化和裸机运行之间的性能差距通常在几个百分点以内日常使用根本感知不到。更重要的收益在这几个方面环境隔离一个容器挂了不会把整个系统拖垮。比如某个 PHP 应用出了内存泄漏最多杀掉那个容器Nginx 和其他服务还能继续响应。依赖自治不同的网站需要不同的 PHP 版本、不同的扩展容器化之后各装各的互不干扰。裸机方案想同时跑两套 PHP 版本配置起来很痛苦。部署可复制compose 文件就是环境清单配合数据目录整个服务集群可以被完整复制到另一台机器。回滚简单镜像版本固定更新出问题直接切回旧镜像一条命令的事。成本方面容器镜像确实会占一部分磁盘空间但树莓派配合一块 120GB SSD跑十来个容器镜像绰绰有余。这个账怎么算都是划算的。2. 树莓派系统基础准备2.1 系统镜像选择与烧录要点Docker 化的第一步是先有一块干净、稳定的系统。我推荐用树莓派官方的 Raspberry Pi OS Lite 版本也就是不带桌面环境的精简版。跑服务器不需要图形界面Lite 版省掉了桌面、浏览器、办公套件这些完全用不到的东西系统占用更少更新范围也更小长期运行更省心。如果你对系统比较熟Ubuntu Server 也可以特别是后续想跑一些需要新内核特性的容器时Ubuntu 的内核版本通常会新一些。但默认我还是建议原生系统兼容性最好社区资料最多遇到问题查答案最容易。烧录工具直接用官方 Raspberry Pi Imager 就行。这里有几个细节值得注意选择镜像后Imager 支持直接配置 SSH、Wi-Fi、用户名密码不用像以前那样手动创建 ssh 空文件来开启服务。在烧录前就把这些设置好插卡通电就能直接连接。如果要用 SSD 启动先把引导器更新到支持 USB 启动的版本再把系统烧录到 SSD。树莓派 4B 之后的版本对 USB 启动支持已经很成熟。烧录完成后不要急着拔卡先把 SD 卡或 SSD 重新挂载检查一下 config.txt 里的配置。如果用的是树莓派 5建议确认供电是否足够很多莫名重启的故障都和电源有关。2.2 SSH 连接与无屏初始化树莓派当服务器用基本都是无屏操作。通电之前你要确保自己有办法连上它。最简单的做法是路由器上查树莓派的 IP 地址然后通过 SSH 连接。如果路由器管理界面不方便看也可以用 nmap 扫描局域网内开放 22 端口的设备或者用 mDNS 协议直接通过树莓派主机名.local连接。我这里说一个我自己常用的技巧给树莓派设置静态 IP 或者 DHCP 保留地址。服务器和开发板不一样开发板是临时调试IP变了就再查一次服务器是持续提供服务的IP 一旦变化所有端口映射、地址转发、定时任务全部失效。在路由器里给树莓派的 MAC 地址绑定一个固定 IP看起来是小事实际上能避免后面一大半的“连不上”问题。连接上之后第一时间改密码、更新系统。不要跳过这一步树莓派默认用户名的密码如果还是初始状态暴露在局域网里风险很大。2.3 系统更新与源优化系统装完先做一次完整的更新sudo apt update sudo apt full-upgrade -y sudo reboot这一步把内核、驱动、基础工具都同步到最新后面的 Docker 安装会更顺利。如果你觉得官方源下载速度不理想可以换用适合自己网络环境的镜像源。这一步的操作思路是备份原配置修改源列表更新索引。需要注意一点树莓派系统源和普通 Ubuntu 源不完全一样不要拿 Ubuntu 的源字符串随便替换否则会出现依赖解析失败。换源后用apt update验证是否能正常拉取索引如果报错就恢复原配置。我个人还是建议保持官方源或者至少保留官方源的一个备用入口。下载速度可以接受的话稳定性优先。3. Docker 环境搭建与关键概念3.1 安装 Docker 引擎树莓派是 ARM 架构Docker 的安装方式和其他平台稍有区别但官方提供了完整的支持。最稳妥的安装方式是用官方脚本curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh脚本会检测系统架构自动配置 Docker 的软件源并安装对应版本。装完之后把当前用户加入 docker 组避免每条命令都用 sudosudo usermod -aG docker $USER退出重新登录让用户组生效。然后用一个最基础的命令验证 Docker 是否正常工作docker run hello-world如果能看到 “Hello from Docker!” 的输出说明安装成功容器引擎可以正常拉取镜像并运行容器。这里多说一句关于 Docker Desktop。很多从 Windows、Mac 平台过来的朋友会下意识去找 Docker Desktop 的 ARM 版。但 Docker Desktop 本质上是一个桌面集成工具里面额外跑了一层虚拟机在树莓派这种资源有限的设备上完全没有必要。树莓派上直接用 Docker Engine 就足够了轻量、零图形依赖、随系统启动。3.2 Docker Compose 的配置与使用单跑一个容器不需要编排但网页服务器通常不是孤立的。一个典型的站点可能要同时跑 Nginx、PHP-FPM、MySQL甚至再加个 Redis。容器一多用docker run命令逐个启动就变得很繁琐参数多不说还容易漏。这时候就需要 Docker Compose。它用一份 YAML 文件定义所有服务、网络、数据卷然后用一条命令启动整个栈。Compose 插件在安装 Docker 时通常会一起装上可以用docker compose version验证。项目目录里创建一个docker-compose.yml把所有服务写进去之后所有操作都围绕这个文件展开。我习惯的项目目录结构是这样的~/webserver/ ├── docker-compose.yml ├── nginx/ │ ├── conf.d/ │ │ └── default.conf │ └── html/ │ └── index.html ├── mysql/ │ ├── data/ │ └── conf/ └── logs/ ├── nginx/ └── mysql/目录即文档配置文件和站点文件都挂载到宿主机上以后备份只需要打包这个目录。这个习惯是我踩过很多次坑之后养成的容器本身是临时的数据必须留在宿主机。升级镜像、重建容器都不怕丢数据。3.3 镜像、容器、数据卷的核心逻辑很多人刚接触 Docker 时容易被镜像和容器这两个概念绕晕。我用一个生活化的类比镜像像是光盘里的系统安装盘容器像是用这张盘装出来的某台电脑。你可以用同一张盘装很多台电脑每台电脑是独立运行的装错了直接把那台电脑砸掉重新装就行不会影响光盘。数据卷则像是外接硬盘电脑砸了硬盘还在数据不丢。网页服务器场景下这三者的关系特别清晰镜像Nginx、PHP、MySQL 的官方镜像或者你基于它们做的自定义修改。这些只读可以随时删除重拉。容器运行中的服务实例无状态。任何存储都应该映射到数据卷容器本身不带任何持久数据。数据卷宿主机上的目录或者 Docker 管理的卷数据库文件、网站文件、配置文件、日志都放这里。理解这个逻辑之后你会意识到容器化部署的核心工作其实是“怎么把数据和配置映射进去”而不是“怎么把服务跑起来”。服务怎么跑是镜像决定好的你只需要管数据和配置。4. 网页服务器容器化部署实操4.1 用 Nginx 跑起第一个站点基础打好之后可以直接进入实战。这里我以 Nginx 为例搭一个最简单的静态站点。先创建一个项目目录和对应的子目录然后是docker-compose.ymlservices: nginx: image: nginx:stable-alpine container_name: web-nginx restart: unless-stopped ports: - 80:80 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/html:/usr/share/nginx/html:ro - ./logs/nginx:/var/log/nginx在nginx/html里放一个index.html在nginx/conf.d里放一个站点配置server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ 404; } }然后在项目目录下执行docker compose up -d浏览器里打开树莓派的 IP能看到页面内容就说明第一个容器化网页服务已经跑起来了。这里几个细节值得展开镜像选择stable-alpine而不是latest或者默认标签原因是 Alpine 版本体积小、更新节奏稳定性能上对静态文件服务没有明显差异。配置文件和站点文件都加了:ro只读挂载。只读是一个很好的约定容器内部不应该随意修改挂载进来的宿主文件。restart: unless-stopped意味着容器异常退出时会自动拉起除非你手动停止它这对服务器场景至关重要。4.2 数据持久化网站文件与配置分离静态页面只是开胃菜实际网站总要和数据库之类的有状态服务打交道。这里我以 MySQL 为例把持久化的完整思路讲透。services: mysql: image: mysql:8.0 container_name: web-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: change_this_root_password MYSQL_DATABASE: myapp MYSQL_USER: myapp MYSQL_PASSWORD: change_this_user_password volumes: - ./mysql/data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d ports: - 127.0.0.1:3306:3306注意几个关键点数据库数据目录./mysql/data必须挂载出来。如果忘挂载容器一删数据库文件全部消失这是新手最容易犯的错误。端口绑定写成127.0.0.1:3306:3306而不是3306:3306。前者只允许本机访问 MySQL外部网络访问不到数据库端口。这是安全基线操作。应用容器通过 Docker 内部网络连接 MySQL 时并不依赖这个端口映射。初始化时的 root 密码和数据库账号要妥善保存。虽然可以之后改但初始化时的配置最干净。以 Nginx 加 PHP 为例的完整组合会在下一篇单独展开。核心原则是静态文件、动态程序、数据文件三层分离静态文件走 Nginx 直接服务动态请求转发给 PHP-FPM 容器数据库只通过内部网络与 PHP-FPM 通信所有持久化内容都映射到宿主机目录。4.3 端口映射、多站点与反向代理网页服务器运行过程中多站点是绕不开的。一台树莓派上跑一个博客、一个图床、一个私人工具页面是很常见的使用方式。端口映射的思路是宿主机 80 端口是唯一的对外入口Nginx 容器负责监听 80然后根据不同域名转发到不同服务。这样外部访问只暴露一个端口内部怎么路由由 Nginx 决定。反向代理配置示例server { listen 80; server_name blog.example.com; location / { proxy_pass http://web-php:9000; 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; } } server { listen 80; server_name gallery.example.com; location / { proxy_pass http://gallery-app:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }使用反向代理的好处很明显不需要为每个站点申请一个端口统一 80/443 入口可以在 Nginx 这一层统一处理 SSL 证书、访问日志、访问限制后续增加新服务时只需要新增一个 server 块不用修改其他站点的配置。域名解析需要提前在 DNS 管理处把blog.example.com的 A 记录指向树莓派的公网 IP 或内网 IP。如果你不想用真实域名也可以在内网搭建一个 DNS 服务不过这是另一个话题了。多站点配置变更后执行docker compose exec nginx nginx -s reload就能热加载不需要重启容器。4.4 加密访问与 HTTPS 基础配置网站一旦跑起来下一步自然是上 HTTPS。就算只有一个内网页面用 HTTPS 也能避免局域网内明文传输的尴尬。有公网域名的话直接用 Lets Encrypt 证书配合 certbot 自动续期。没有公网域名可以用自签名证书缺点是对端设备需要手动信任证书也可以用带信任根证书的服务比如某些内网 DNS 系统自带的证书签发能力。在 Nginx 容器里启用 HTTPS 的配置骨架server { listen 443 ssl; server_name blog.example.com; ssl_certificate /etc/nginx/certs/fullchain.pem; ssl_certificate_key /etc/nginx/certs/privkey.pem; location / { proxy_pass http://web-php:9000; } } server { listen 80; server_name blog.example.com; return 301 https://$host$request_uri; }证书文件的挂载建议单独一个目录比如./nginx/certs只读挂载进容器。续期脚本写入 crontab定期检测并重载 Nginx 即可。HTTPS 配置这件事技术复杂度不高容易出问题的都在证书路径、权限、续期脚本这些细节上。后面我会单独写一期专门讲容器环境下的证书管理这里先把骨架搭好。5. 性能调优与日常运维5.1 容器资源限制与系统参数调优树莓派的资源是有限的运行多个容器时如果不做限制某个容器出问题可能会拖垮整个系统。Docker 提供了资源限制机制可以在 compose 文件里指定 CPU 和内存上限services: mysql: image: mysql:8.0 deploy: resources: limits: cpus: 2.0 memory: 1g reservations: memory: 256m这里的含义是MySQL 容器最多使用 2 个 CPU 核心和 1GB 内存最低保留 256MB 内存。给关键服务设置资源上限一方面避免单个容器无限膨胀另一方面也确保 Nginx、SSH 这些基础服务始终有可用资源。还有一个容易忽略的系统级参数——内存交换。树莓派默认的 swap 配置在 SD 卡或者 SSD 上频繁换页会严重拖慢性能。我建议把 swap 调小同时给 Docker 容器加上内存限制让进程尽量在物理内存内运行。内存不够用的时候就减少容器数量而不是依赖 swap 硬撑。日志驱动也需要调整。容器默认的 json-file 日志会不断累积时间长了能占好几个 GB。在 compose 文件里限制日志大小services: nginx: image: nginx:stable-alpine logging: driver: json-file options: max-size: 10m max-file: 3这样单个服务最多保留 30MB 日志避免日志文件无限增长挤占磁盘。5.2 定期备份与数据一致性容器化之后备份的重点从“备份整个系统”变成了“备份数据目录和配置文件”。这个转变让备份变得非常简单。最低限度的备份方案用 cron 定期把项目目录打包到外部存储同时用mysqldump把数据库逻辑备份导出#!/bin/bash BACKUP_DIR/mnt/backup/webserver DATE$(date %Y%m%d) docker compose -f ~/webserver/docker-compose.yml exec -T mysql \ mysqldump -u root -p$MYSQL_ROOT_PASSWORD --all-databases $BACKUP_DIR/mysql-$DATE.sql tar -czf $BACKUP_DIR/config-$DATE.tar.gz -C ~ webserver find $BACKUP_DIR -type f -mtime 14 -delete这套脚本解决的问题是配置文件和数据目录随用随备份超过 14 天的旧备份自动删除。数据库部分是逻辑备份适合恢复时跨平台、跨版本。如果你有 NVMe SSD 或者移动硬盘备份目标放在外部存储避免和系统盘一起故障。恢复流程也要提前演练。真到机器坏了才摸索恢复很容易出问题。实际操作中先把系统重装安装 Docker 和 Compose再把备份的webserver目录解压回来最后启动容器并导入数据库备份。整条链路顺了才是真的高枕无忧。5.3 开机自启与系统监控服务器的一个重要特性是断电恢复后能自动回归服务。这一点主要靠两个层面第一Docker 服务本身设置为开机自启。systemctl enable docker通常装完就默认开启了。可以在systemctl status docker里确认一下。第二每个容器设置restart: unless-stopped。这样 Docker 启动后会自动拉起所有配置了这个策略的容器。这里有个细节不要在容器里自己写 init 脚本去启动其他进程直接用 Docker 的重启策略简单可靠。监控方面树莓派这类设备不用上特别重的监控系统一条命令就能看核心状态docker stats --no-stream每个容器的 CPU、内存、网络、磁盘占用一目了然。日常巡检时跑一下用docker logs --tail 50 容器名看异常。系统级别的温度监控可以用vcgencmd measure_temp查看长期超过 80 度就要检查散热片和风扇了。6. 常见问题与排查技巧6.1 容器反复重启怎么办容器启动后不断重启是新手遇到最多的问题。排查思路要按顺序来先看容器日志这是最有用的信息docker logs web-nginx日志里能看到的常见情况包括端口被占用宿主机上的其他进程已经占用了 80 端口。用sudo lsof -i :80或者sudo ss -tlnp | grep :80查看占用进程。配置文件语法错误Nginx 启动时校验配置失败。可以在宿主机上用docker compose exec nginx nginx -t检查配置。挂载目录权限不对容器内的进程没有权限读写挂载目录。常见于 MySQL 容器数据目录属主不是 MySQL 用户。解决办法是调整宿主机目录属主或权限。每次问题解决后执行docker compose up -d重新创建容器再用docker ps确认状态变成 Up。6.2 访问不通和端口映射问题浏览器打开 IP 无法访问先分清楚是哪个层面的问题。这个分层排查的思路很管用先测宿主机在树莓派本机执行curl -I http://localhost。通的话问题在网络层或防火墙。再测容器检查容器是否在运行端口映射是否生效docker compose ps。然后测网络从其他设备 ping 树莓派 IP能通说明二层三层正常。最后查防火墙确认 80、443 端口是否放行。树莓派默认防火墙没有开启时不受影响但如果你自己装过 ufw 或者 firewalld要检查对应规则。很多时候访问不通是因为路由器或者光猫没有做端口转发或者运营商封禁了 80/443 端口。这种情况可以换用非标准端口或者通过内网隧道、云上中转之类的方案不过那已经是另一个话题了。6.3 磁盘写满与 SD 卡寿命SD 卡是树莓派方案中最脆弱的环节。容器化之后日志、镜像层、数据卷的写放大问题更加明显。磁盘写满的常见原因有三个容器日志无限增长、镜像和悬空层堆积、数据库文件持续增长。排查命令df -h docker system dfdocker system df会告诉你镜像、容器、数据卷、构建缓存各自占了多少空间。定期执行docker system prune -af清理无用镜像和构建缓存能回收不少空间。注意-a会删除所有未被使用的中介镜像-f跳过确认执行前确认一下当前有没有需要保留的镜像。要延长 SD 卡寿命核心策略就是两个字外置。把 Docker 的数据目录整体迁移到 SSD 或者移动硬盘上系统分区保持在 SD 卡这样日志写入和镜像层操作都发生在 SSD 上。配置方式是在/etc/docker/daemon.json里指定{ data-root: /mnt/ssd/docker }改完重启 Dockersudo systemctl restart docker。迁移时注意先把旧数据复制过去再启动新 Docker避免数据丢失。7. 写在最后这个系列的后续方向写到这里整套“树莓派 Docker 化网页服务器”的骨架已经清楚了一块树莓派、一个 Linux 系统、一个 Docker 引擎、一组 compose 文件就能把原本需要一整台服务器才干得动的事搬到桌面上。我在实际使用中最大的感受是这套方案的价值不在于性能多强而在于“省心”。以前每次系统升级都提心吊胆怕服务挂掉、怕环境不一致现在升级就是拉新镜像、重建容器几分钟搞定。后续这个系列我打算按这几个方向继续往下写WordPress 或 Typecho 这类动态站点的容器化组合把 Nginx、PHP-FPM、MySQL 的协作讲透。反向代理与多应用网关用同一台树莓派承载多个独立服务。日志收集与结构化存储让日志不再只是一堆散文件而是可查询、可告警的数据。备份与容灾实战覆盖从日常备份到整机迁移的完整链路。如果你正准备用它跑点什么我的建议是先把这篇文章里的基础环境搭起来不用急着追新镜像、新工具。容器化这个东西底层逻辑通了上层都是套路。下一篇我们就拿一个真实的建站项目开刀把这套环境用起来。
返回列表