引言
作为一个跑了十几年网站的草根站长,我深知一个痛点:服务器环境搭建太费劲了。从 LAMP 到 LNMP,从 PHP 5.6 到 PHP 8.2,每次换服务器都是一场灾难。直到我接触了 Docker,才真正体会到"一次配置,到处运行"的快感。
这篇文章不扯原理,不讲架构,就用一个草根站长的视角,带你从零开始用 Docker 部署一个完整的网站环境。我保证:看完你就能上手。
为什么草根站长应该学 Docker
很多站长一听到 Docker 就觉得是高大上的东西,是大厂才用的。其实恰恰相反,Docker 对个人站长来说可能是性价比最高的技术投资。
先说几个真实场景:
场景一:换服务器。你有没有遇到过这种崩溃:旧服务器 PHP 5.6,新服务器 PHP 8.1,网站直接白屏?用 Docker 的话,你只需要把配置文件复制过去,一条命令启动,环境一模一样。
场景二:多站点管理。一个服务器上跑 5 个网站,A 站需要 PHP 7.4,B 站需要 PHP 8.2,C 站还需要特定版本的 MySQL。传统方式你得装多个 PHP 版本,各种配置冲突到怀疑人生。Docker 每个容器独立运行,互不干扰。
场景三:本地测试。在本地电脑上搭一个和线上完全一致的环境,改完代码测试没问题再上线。告别"我本地好好的啊"的尴尬。
安装 Docker
先说最基础的安装。我用的是 Debian/Ubuntu 系统,CentOS 的命令也差不多。
# 一键安装脚本(推荐)
curl -fsSL https://get.docker.com | bash
# 启动 Docker 并设置开机自启
systemctl start docker
systemctl enable docker
# 把当前用户加入 docker 组,省得每次都 sudo
usermod -aG docker $USER安装完成后验证一下:
docker --version
docker run hello-world看到 "Hello from Docker!" 就说明安装成功了。
Docker Compose:管理多个容器的神器
单独用 Docker 命令管理容器已经很方便了,但如果你的网站需要 Nginx + PHP + MySQL 三个服务一起跑,用 Docker Compose 会让你爽到飞起。一个 YAML 文件定义所有服务,一条命令全部启动。
# 安装 Docker Compose
apt install docker-compose -y实战:用 Docker 部署一个 Typecho 博客
下面我们真刀真枪来一遍,用 Docker 部署一个完整的 Typecho 博客。Typecho 是国内站长常用的轻量博客系统,对个人站长非常友好。
第一步:创建项目目录
mkdir -p /opt/typecho && cd /opt/typecho
mkdir nginx mysql typecho第二步:编写 docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
container_name: typecho_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
- ./typecho:/var/www/html
depends_on:
- php
restart: always
php:
image: php:8.1-fpm-alpine
container_name: typecho_php
volumes:
- ./typecho:/var/www/html
restart: always
mysql:
image: mysql:8.0
container_name: typecho_mysql
environment:
MYSQL_ROOT_PASSWORD: your_strong_password
MYSQL_DATABASE: typecho
MYSQL_USER: typecho
MYSQL_PASSWORD: typecho_password
volumes:
- ./mysql/data:/var/lib/mysql
restart: always第三步:配置 Nginx
创建 Nginx 配置文件 nginx/conf.d/default.conf:
server {
listen 80;
server_name your-domain.com;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}第四步:下载 Typecho 并启动
# 下载 Typecho
wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip
unzip typecho.zip -d typecho/
chown -R 33:33 typecho/
# 启动所有服务
docker-compose up -d
# 查看运行状态
docker-compose ps就这么简单!访问你的域名就能看到 Typecho 安装界面了。
Docker 常用命令速查
作为站长,下面这些命令你一定会经常用到:
# 查看所有运行中的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 启动/停止/重启容器
docker start 容器名
docker stop 容器名
docker restart 容器名
# 查看容器日志(排查问题必备)
docker logs -f 容器名
docker logs --tail 100 容器名
# 进入容器内部
docker exec -it 容器名 /bin/sh
# 删除容器
docker rm 容器名
# 删除镜像
docker rmi 镜像名
# 清理无用的镜像和容器(释放磁盘空间)
docker system prune -aDocker 网络详解:让容器之间自由通信
Docker 的网络模型对新手来说容易搞混,但理解了就很简单。Docker 默认创建三种网络:
bridge 网络:默认模式。每个容器获得一个内网 IP,通过宿主机端口映射对外提供服务。适合单机部署。
host 网络:容器直接使用宿主机的网络栈,性能最好但端口不能冲突。适合对网络性能要求极高的场景。
overlay 网络:跨主机通信,适合 Docker Swarm 集群。个人站长一般用不到。
对我们草根站长来说,99% 的场景用默认的 bridge 网络就够了。Docker Compose 会为每个项目自动创建一个独立的 bridge 网络,同一个 docker-compose.yml 里的容器可以互相通过服务名通信。
# 查看所有网络
docker network ls
# 创建自定义网络(多个 compose 文件共用)
docker network create my-network
# 查看网络详情
docker network inspect my-network数据卷深入:保证数据不丢失的关键
Docker 容器是无状态的,容器删除后里面的数据就没了。数据卷(Volume)就是解决这个问题的。
Docker 有三种挂载方式:
bind mount:把宿主机目录挂载到容器内。最灵活,也是最常用的方式。
docker run -v /host/path:/container/path nginxnamed volume:Docker 管理的卷,存储在 /var/lib/docker/volumes/ 下。
docker volume create mydata
docker run -v mydata:/data nginxtmpfs:临时文件系统,存储在内存中,容器停止就没了。仅用于缓存等临时数据。
对于生产环境,我推荐用 named volume 存储数据库数据,用 bind mount 存储网站代码。原因是 named volume 的性能更好(直接操作本地文件系统),bind mount 更方便修改代码。
services:
mysql:
volumes:
- mysql_data:/var/lib/mysql # named volume
nginx:
volumes:
- ./html:/usr/share/nginx/html # bind mount
volumes:
mysql_data:数据备份:Docker 环境下的备份策略
很多人担心 Docker 环境下数据不安全,其实 Docker 的数据备份反而更简单。因为数据都通过 Volume 映射到了宿主机目录,你只需要备份宿主机上的目录就行了。
# 备份 MySQL 数据
docker exec typecho_mysql mysqldump -u root -p typecho > /backup/typecho_$(date +%Y%m%d).sql
# 备份网站文件
tar -czf /backup/typecho_files_$(date +%Y%m%d).tar.gz /opt/typecho/typecho/
# 设置定时任务(每天凌晨 3 点自动备份)
echo "0 3 * * * /opt/scripts/backup.sh" | crontab -进阶技巧:使用 Watchtower 自动更新镜像
安全问题不能忽视。容器里的软件也需要更新补丁。Watchtower 可以自动检测并更新 Docker 镜像:
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--interval 86400这样每天自动检查一次,有更新就自动拉取新镜像并重启容器。
多站点部署实战:一机运行五个网站
草根站长经常面临一个需求:一台服务器跑多个网站。用 Docker 实现这个非常简单。
核心思路:每个网站一个独立的 docker-compose.yml,用 Nginx 反向代理统一入口。
# 目录结构
/opt/sites/
├── site-a/
│ ├── docker-compose.yml
│ └── html/
├── site-b/
│ ├── docker-compose.yml
│ └── html/
└── proxy/
├── docker-compose.yml
└── nginx/
└── conf.d/代理 Nginx 的配置:
server {
listen 80;
server_name site-a.com;
location / {
proxy_pass http://site-a-nginx:80;
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;
}
}每个网站的 nginx 监听内部端口,不对外暴露。只有代理 Nginx 对外暴露 80 和 443 端口。
常见坑和解决方案
- 端口冲突:如果 80 端口被占用,改 docker-compose.yml 里的端口映射,比如
"8080:80"。 - 权限问题:PHP 容器里默认是 www-data 用户(UID 33),宿主机文件权限要匹配。执行
chown -R 33:33 ./html解决。 - MySQL 连接失败:容器之间用服务名通信,比如 MySQL 的主机名是
mysql而不是localhost。 - 磁盘空间不足:定期执行
docker system prune清理无用的镜像和容器。Docker 的镜像层会累积占用大量磁盘空间。 - 容器启动顺序:
depends_on只保证启动顺序,不保证服务就绪。对于 MySQL 这种需要时间初始化的服务,可以在应用容器里加个等待脚本。 - 日志爆炸:Docker 默认不限制日志大小,时间长了日志文件可能把你的磁盘占满。在 docker-compose.yml 中配置日志轮转:
services:
nginx:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"总结
Docker 不是什么高深的技术,它就是帮你把环境打包起来的工具。对草根站长来说,学会 Docker 意味着:
- 换服务器不再是一场噩梦
- 多站点管理不再混乱
- 本地测试和线上环境完全一致
- 一条命令就能在新服务器上重建整个网站
花一个下午学会 Docker,将来能省下无数个折腾环境的通宵。个人觉得这笔投资非常值得。
Docker 安全加固要点
容器化环境虽然隔离性好,但也不是绝对安全的。作为站长,部署上线前有几件事必须做:
使用非 root 用户运行容器。默认情况下很多镜像以 root 运行,一旦容器被攻破,攻击者就能获得宿主机的 root 权限。
FROM php:8.1-fpm-alpine
RUN addgroup -g 1000 app && adduser -u 1000 -G app -s /bin/sh -D app
USER app限制容器资源。防止某个容器占用全部 CPU 或内存导致其他服务不可用。
services:
php:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M定期扫描镜像漏洞。使用 Docker Scout 或 Trivy 检查镜像中的已知漏洞:
docker scout quickview nginx:alpine
# 或者
trivy image nginx:alpine不要暴露不必要的端口。MySQL 容器只需要让 PHP 容器访问,不需要映射到宿主机。docker-compose.yml 中只暴露 Nginx 的端口就行。
最后的建议
Docker 的世界很大,但对于草根站长来说,掌握这篇文章里的内容已经足够应对 90% 的场景了。后续如果你感兴趣,可以继续学习 Dockerfile 编写、多阶段构建、Docker Swarm 集群等高级内容。但千万别一开始就追求完美,先用起来,在实践中学习,这才是最有效的路径。
如果你在实践过程中遇到问题,欢迎在评论区交流。