提起在VPS上建站,很多人的第一反应是:装Nginx、装MySQL、装PHP,然后对着教程改半天配置文件,环境冲突、版本不匹配、迁移服务器时又得重来一遍。Docker的出现彻底改变了这种苦日子:把网站和它依赖的一切打包进"容器",一条命令启动,一条命令销毁,换服务器就是复制粘贴的事。这篇文章从零开始,手把手带你用Docker在一台干净的VPS上部署一个完整的网站,全程不需要理解任何底层原理,照着敲就能跑通。
一、为什么建站要选Docker
先用三个真实场景说明问题:
场景一:你在服务器A上折腾好了环境,某天要迁移到服务器B。传统方式得把所有配置重做一遍,漏一个参数网站就白屏。Docker方式:打包镜像或导出compose文件,新机器上一条命令满血复活。
场景二:你想同时跑一个PHP 7.4的老站和一个PHP 8.2的新站。传统方式下多版本PHP共存能把人折腾疯,而每个容器有独立的运行环境,互不干扰。
场景三:网站被黑了,环境被污染。传统方式排查清理遥遥无期,容器方式:删掉容器重建,30秒恢复如初,数据还在数据卷里安全无恙。
一句话总结:Docker把"环境"本身变成了可管理的文件,这是它对建站用户最大的价值。
二、安装Docker环境
以Ubuntu/Debian为例,使用官方脚本一键安装:
# 更新软件源 sudo apt update # 安装依赖 sudo apt install -y ca-certificates curl gnupg # 使用官方安装脚本 curl -fsSL https://get.docker.com | sudo sh # 启动并设置开机自启 sudo systemctl enable --now docker # 验证安装 sudo docker run hello-world
看到"Hello from Docker!"的输出就说明装好了。
顺手装上Docker Compose(新版已作为插件内置,无需单独安装):
docker compose version # 能打印版本号即可,如 Docker Compose version v2.x.x
再设置当前用户免sudo使用docker(可选但强烈推荐,省得每条命令都敲sudo):
sudo usermod -aG docker $USER # 重新登录SSH后生效
三、三个核心概念,一分钟讲明白
不需要啃文档,记住这三样就够了:
- 镜像(Image):网站的"安装包",只读模板。比如wordpress镜像里装好了PHP和WordPress程序
- 容器(Container):镜像跑起来的"进程",一个镜像可以开多个容器。容器删了就没了,但镜像还在,随时重开
- 数据卷(Volume):容器之外的"硬盘"。容器随时可抛弃,但数据库文件、上传的图片这些重要数据必须放在数据卷里持久化
打个比方:镜像是菜谱,容器是按菜谱炒出来的一盘菜,数据卷是保鲜盒——菜吃完可以再炒,保鲜盒里的东西不受影响。
四、第一个容器:30秒跑起一个网站
先来个最简单的,感受一下容器的威力:
docker run -d -p 8080:80 --name mysite httpd:alpine
解释一下这条命令:
- -d:后台运行
- -p 8080:80:把服务器的8080端口映射到容器内的80端口
- --name mysite:给容器起个名字,方便管理
- httpd:alpine:Apache的精简版镜像,只有约40MB
现在浏览器访问 http://你的服务器IP:8080,能看到"It works!"页面。就这么快,没有安装Apache,没有改配置文件。
常用管理命令:
docker ps # 查看运行中的容器 docker ps -a # 查看所有容器(含已停止) docker stop mysite # 停止 docker start mysite # 再次启动 docker rm mysite # 删除容器(数据在卷里不受影响) docker logs mysite # 查看日志排错
五、实战:用Docker Compose部署一个完整博客
单个容器只是热身,真实网站是"多容器协作":Web程序一个容器、数据库一个容器。Docker Compose就是用来编排这种多容器组合的。我们在一个目录里写一个compose文件,描述整个网站,然后一条命令全部启动。
创建项目目录并编辑:
mkdir -p ~/myblog && cd ~/myblog nano docker-compose.yml
写入以下内容:
services:
app:
image: wordpress:latest
restart: always
ports:
- "80:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: 换成你的强密码
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_data:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: 换成你的强密码
MYSQL_ROOT_PASSWORD: 换成root的强密码
volumes:
- db_data:/var/lib/mysql
volumes:
wp_data:
db_data:这个文件描述了一个标准博客架构:WordPress容器对外提供80端口服务,MySQL容器只被WordPress内部访问,两个数据卷分别保存网站文件和数据库。
启动:
docker compose up -d
第一次会拉取镜像,耐心等一两分钟。完成后访问 http://你的服务器IP,就是熟悉的WordPress安装界面。整个过程没有碰过一次php.ini,没有装过一次MySQL。
六、反向代理:一个VPS跑多个网站的姿势
实际使用中你大概率不止一个网站,或者需要HTTPS。这时候需要一个反向代理统一接管80/443端口,按域名把流量转发给不同容器。推荐用Nginx Proxy Manager(NPM),带Web管理界面,鼠标点点就能配好自动续期的SSL证书。
mkdir -p ~/proxy && cd ~/proxy nano docker-compose.yml
写入:
services:
npm:
image: jc21/nginx-proxy-manager:latest
restart: always
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt启动后访问 http://服务器IP:81,默认账号 admin@example.com / changeme(首次登录强制改密码)。
使用方法:把你的域名A记录解析到服务器IP,在NPM里添加Proxy Host,域名填blog.example.com,转发目标填你博客容器的内网地址:端口,再在SSL标签页申请Let's Encrypt证书,勾选Force SSL。一个带HTTPS的网站就配好了。
注意一个细节:多个网站共用80端口时,只有NPM监听80/443,其他网站容器不要再用ports映射80端口,改用内部网络+expose,避免端口冲突。
七、数据备份:容器化了更要会备份
容器"随用随抛"的特性让人容易忽略数据安全。其实Docker的备份比传统方式更简单——数据都在卷里,备份卷就行。
备份数据库卷的通用方法:
# 方法一:mysqldump逻辑备份(推荐,通用性最好) docker exec 容器名 mysqldump -uwpuser -p密码 wordpress \ | gzip > backup_$(date +%F).sql.gz # 方法二:直接打包数据卷目录 docker run --rm -v db_data:/data -v $(pwd):/backup \ alpine tar czf /backup/db_data.tar.gz /data
把备份命令放进crontab每天自动执行,再用rclone把备份文件同步到对象存储或另一台服务器,就是一套完整的容灾方案。备份不是本文重点,展开可以写一整篇,记住核心原则:没做过恢复演练的备份等于没备份。
八、常用命令速查表
把这几条贴在备忘录里,日常管理够用了:
docker compose up -d # 启动/更新整个项目 docker compose pull # 拉取最新镜像 docker compose up -d # 再次up即用新镜像重建容器 docker compose down # 停止并删除所有容器(卷保留) docker compose logs -f app # 实时查看app容器日志 docker stats # 查看各容器CPU/内存占用 docker system df # 查看磁盘占用 docker system prune -a # 清理无用镜像释放磁盘
其中docker system prune -a要慎用,它会删掉所有未被使用的镜像,下次启动要重新拉取。磁盘紧张时再用。
九、新手避坑指南
最后是几条用血泪换来的经验:
- 镜像一定要锁定具体版本:wordpress:latest今天可能是8.2,下个月悄悄变成8.3,行为变了都不知道哪出问题。生产环境写wordpress:6.4-php8.2这样的明确版本号
- 重要数据必须放卷里:直接写进容器文件系统的东西,容器一删就没。第一次部署就规划好volumes
- 端口映射冲突:80被宿主机Nginx占了容器就起不来,报"port is already allocated",先docker ps看看谁占着
- 内存别抠太死:MySQL容器默认内存配置偏大,1G小鸡跑WordPress全家桶建议给MySQL加限制参数,或者换更轻的SQLite方案
- 别用root跑一切:容器天然隔离,但MySQL密码、环境变量里的密钥还是走.env文件管理,别硬编码进compose文件传到公开仓库
十、写在最后
Docker的学习曲线在前半小时,之后全是甜头。这篇教程覆盖了从安装、概念、部署到运维的完整链路,跟着做完你应该已经拥有一个容器化的HTTPS网站。下一步可以研究CI/CD自动化部署、容器监控(如哪吒探针、Uptime Kuma)这些进阶玩法。
建站路上最大的敌人不是技术,是"配置好就再也不敢动"的恐惧。容器给了你随意折腾的底气——反正坏了就重建,怕什么。祝部署顺利,有问题欢迎评论区交流。