为什么你的开发环境总在“拆东墙补西墙”?
相信不少朋友都有过这样的经历:新同事入职,光配环境就花了整整两天;或者项目跑得好好的,升级了一下依赖,整个服务直接“罢工”。我们通常把这些归咎于“运气不好”,但根源在于开发环境与运行环境的不一致。你的电脑是 macOS,服务器是 CentOS,数据库版本还差了好几个小版本,不出问题才怪。
而 Docker 的出现,就像给开发环境装上了“集装箱”。它把代码、运行时、系统工具、库、设置统统打包成一个标准化的单元。无论你在哪台机器上运行,得到的都是完全一致的体验。今天,我们就来聊聊如何用 Docker 高效部署一套开发环境,让你彻底告别“环境地狱”。
第一步:理清思路——我们到底要“装”什么?
在敲击键盘之前,先别急着拉取镜像。我们需要明确一个核心问题:你的开发环境是“一体化”还是“分离式”?
- 一体化(All-in-One):适合个人开发或小型项目,把 Nginx、PHP-FPM、MySQL 装在一个容器里。优点是简单,缺点是难以扩展。
- 分离式(Multi-Container):这是目前最主流的方式,每个服务一个容器(比如一个容器跑 PHP,一个容器跑 MySQL)。这更符合微服务理念,也方便我们单独升级或重启某个组件。
笔者(站长)强烈推荐后者。虽然初期配置稍显复杂,但后续维护的幸福感会直线上升。
第二步:实战演练——用 Docker Compose 编排你的“全家桶”
既然选择了分离式,我们就要用到 Docker Compose。它就像是集装箱的吊车,能一键指挥所有容器协同工作。下面我们以最常见的 LNMP(Linux + Nginx + MySQL + PHP) 环境为例,手把手带你搭建。
1. 项目结构先行
首先,创建一个干净的目录,用来存放我们的“编排脚本”:
mkdir -p ~/docker-lnmp && cd ~/docker-lnmp
mkdir -p ./nginx/conf.d ./php ./mysql/data ./www
这个结构很清晰:nginx 存放站点配置,php 存放 Dockerfile,mysql 存放数据库数据(通过挂载卷持久化),www 则是你的代码仓库。
2. 编写核心编排文件 docker-compose.yml
这是整个部署的“总指挥”,内容如下:
version: '3.8'
services:
# Web 服务器
nginx:
image: nginx:1.25-alpine
ports:
- "8080:80"
volumes:
- ./www:/usr/share/nginx/html
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- php
networks:
- dev_net
# PHP 服务
php:
build: ./php
volumes:
- ./www:/var/www/html
networks:
- dev_net
# 数据库
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: root123456
MYSQL_DATABASE: dev_db
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
networks:
- dev_net
networks:
dev_net:
driver: bridge
注意看,这里没有写死 IP,而是通过 depends_on 和自定义网络 dev_net 让容器间通过服务名通信。这意味着在 PHP 代码里,连接数据库的主机名直接写 mysql 即可,而不是 127.0.0.1。
3. 定制你的 PHP 镜像(Dockerfile)
官方 PHP 镜像往往缺少我们需要的扩展。在 ./php 目录下创建一个没有后缀名的 Dockerfile 文件:
FROM php:8.2-fpm-alpine
# 安装必要的扩展
RUN docker-php-ext-install pdo_mysql mysqli opcache
# 如果还需要 Redis 扩展,取消下面这行注释
# RUN pecl install redis && docker-php-ext-enable redis
WORKDIR /var/www/html
这里使用了 docker-php-ext-install 这个官方脚本,它极大简化了扩展安装的复杂度。
4. 配置 Nginx 站点
在 ./nginx/conf.d 下新建一个 default.conf,让它把请求转发给 PHP 容器:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.php index.html;
location ~ \.php$ {
fastcgi_pass php:9000; # 这里的 php 是 docker-compose 中的服务名
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
这里最关键的就是 fastcgi_pass php:9000;,它实现了 Nginx 与 PHP-FPM 的跨容器通信。
5. 一键启动
所有文件就绪后,回到 ~/docker-lnmp 目录,执行:
docker-compose up -d --build
看到 Starting nginx ... done 等字样后,打开浏览器访问 http://localhost:8080,如果看到 PHP 探针页面,恭喜你,一套干净且完整的 LNMP 环境已经跑起来了!
第三步:避坑指南——那些让你“怀疑人生”的细节
虽然流程看似简单,但有几个细节笔者(站长)必须啰嗦几句,这些都是实战中踩过的坑:
权限问题
Linux 下经常遇到 Permission denied。这是因为容器内用户(通常是 root)与宿主机用户权限不一致。最简单的解决办法是在 docker-compose.yml 的 php 服务下添加 user: "${UID}:${GID}",并在启动前执行 export UID GID。
端口冲突
如果 3306 端口被本机 MySQL 占用,容器会启动失败。要么改本机服务端口,要么修改 docker-compose 中映射的端口,比如 "3307:3306"。
容器内调试
如果代码报错,别急着删容器重建。使用 docker-compose logs -f php 查看实时日志,或者用 docker exec -it php sh 进入容器内部手动执行命令排查,效率会高很多。
进阶玩法:让开发环境“快如闪电”
基础环境搭建完毕后,我们还可以做两个优化。第一,使用 Bind Mounts(也就是上面的 volumes 挂载方式),这样你修改宿主机 www 目录下的代码,容器内会立即生效,无需重新构建镜像,实现了热更新。第二,为常用命令配置别名,比如在 ~/.bashrc 中加入 alias dps='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"',从此查看容器状态只需敲三个字母。
写在最后:关于“环境”的另一种思考
当我们把开发环境容器化之后,你会发现不仅解决了“协作难”的问题,更重要的是,它倒逼我们去思考应用的可移植性。如果连本地开发环境都能像代码一样被版本控制、被分享,那部署到云端服务器(无论是阿里云还是腾讯云)自然也就是分分钟的事。这也是为什么我们一直建议开发者拥有一台属于自己的云服务器,哪怕配置不高,用来跑跑 Docker 容器、练练 Linux 命令,那种“掌控感”是本地虚拟机无法比拟的。毕竟,只有亲手在真实的 Linux 服务器上敲过 docker-compose up,你才算真正学会了 Docker。
希望这篇文章能帮你打开一扇新的大门,让你的开发之旅从此多一份从容,少一份焦虑。
📚 延伸阅读推荐
🎁 全套实操配置文件与 AI 提效资料包免费下载
本文涉及的全套 Docker Compose 配置文件、服务器运维避坑手册及 AI 提效指令库已完整打包上传至夸克网盘,可直接免费极速转存:
💡 提示:推荐使用手机【夸克网盘 App】打开保存,新用户首月免费赠送 1TB 超大空间与免流量极速下载特权。