返回博客
技术 2025年2月19日 5 分钟阅读 · 1045 字
Docker 容器化部署实战:从 Dockerfile 到多环境编排
从零构建 PHP + Nginx + MySQL 的 Docker 部署方案,涵盖多阶段构建、Compose 编排与健康检查。
#Docker
#DevOps
#部署
#容器化
#Nginx
本文由 AI 辅助生成,经人工审核发布
为什么用 Docker
传统部署的问题:
- 「在我机器上能跑」——环境不一致
- PHP 版本冲突、扩展缺失、配置文件散落各处
- 回滚困难,需要手动清理
Docker 的解决方案:
- 镜像即环境,一次构建到处运行
- 版本控制:每个镜像有 tag,回滚只需切换 tag
- 隔离性:应用之间互不影响
多阶段 Dockerfile
# === 阶段 1:安装依赖 ===
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --optimize-autoloader
COPY . .
RUN composer run-scripts post-autoload-dump
# === 阶段 2:构建前端资源 ===
FROM node:20-alpine AS frontend
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# === 阶段 3:最终镜像 ===
FROM php:8.4-fpm-alpine
# 安装系统依赖
RUN apk add --no-cache \
nginx \
libzip-dev \
libpng-dev \
postgresql-dev \
&& docker-php-ext-install pdo_mysql pdo_pgsql zip gd opcache
# 配置 OPcache
RUN echo "[opcache]\n\
opcache.enable=1\n\
opcache.memory_consumption=256\n\
opcache.max_accelerated_files=20000\n\
opcache.validate_timestamps=0" > /usr/local/etc/php/conf.d/opcache.ini
# 复制应用代码
WORKDIR /var/www/html
COPY --from=vendor /app ./
COPY --from=frontend /app/public/build ./public/build
# 复制 Nginx 配置
COPY docker/nginx.conf /etc/nginx/http.d/default.conf
# 设置权限
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
EXPOSE 80
CMD ["sh", "-c", "php-fpm -D && nginx -g 'daemon off;'"]
多阶段构建的优势
# 最终镜像只包含阶段 3 的内容
# composer 和 node 不在最终镜像中
# 镜像大小从 1.2GB 降到 280MB
Nginx 配置
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php;
# 安全:禁止访问隐藏文件
location ~ /\. {
deny all;
}
# Laravel 路由
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|webp|woff2|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 健康检查端点
location /health {
access_log off;
return 200 "ok";
add_header Content-Type text/plain;
}
}
Docker Compose 编排
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:80"
volumes:
- ./storage:/var/www/html/storage
environment:
- DB_HOST=mysql
- DB_DATABASE=${DB_NAME}
- DB_USERNAME=${DB_USER}
- DB_PASSWORD=${DB_PASS}
depends_on:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
mysql:
image: mysql:8.4
environment:
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASS}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
mysql_data:
redis_data:
环境分离
# docker-compose.yml(基础配置)
# docker-compose.prod.yml(生产覆盖)
services:
app:
environment:
- APP_ENV=production
- APP_DEBUG=false
deploy:
replicas: 3
resources:
limits:
memory: 512M
cpus: '1.0'
mysql:
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
volumes:
- ./mysql/prod.cnf:/etc/mysql/conf.d/prod.cnf:ro
# 开发环境
docker compose up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
.dockerignore
.git
.gitignore
node_modules
vendor
storage/logs/*
storage/framework/cache/*
.env
docker-compose*.yml
Dockerfile*
README.md
tests
.dockerignore 防止不必要文件进入构建上下文,加速构建。
健康检查
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost/health || exit 1
# 查看健康状态
docker inspect --format='{{.State.Health.Status}}' container_name
Docker Compose 中 depends_on + condition: service_healthy 确保 MySQL 就绪后 app 才启动。
镜像优化技巧
1. 使用 Alpine 基础镜像
FROM php:8.4-fpm-alpine # ~80MB
# vs
FROM php:8.4-fpm # ~400MB
2. 合并 RUN 指令
# ❌ 每条 RUN 创建一层
RUN apk add nginx
RUN apk add libzip-dev
RUN docker-php-ext-install zip
# ✅ 合并为一条
RUN apk add --no-cache nginx libzip-dev \
&& docker-php-ext-install zip
3. 利用构建缓存
# 先复制不常变化的文件
COPY composer.json composer.lock ./
RUN composer install
# 再复制经常变化的源码
COPY . .
Docker 逐层缓存,composer install 只在 composer.json 变化时重新执行。
CI/CD 集成
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run tests
run: docker run --rm myapp:${{ github.sha }} php artisan test
- name: Push to registry
run: |
docker tag myapp:${{ github.sha }} registry.example.com/myapp:latest
docker push registry.example.com/myapp:latest
- name: Deploy
run: |
ssh server "cd /app && docker compose pull && docker compose up -d"
总结
Docker 容器化的核心价值是环境一致性和可重复性:
- 多阶段构建减小镜像体积
- Compose 编排管理多服务依赖
- 健康检查确保服务就绪后再启动
- 环境分离用 override 文件管理差异
- CI/CD 集成实现自动化构建部署
- Alpine + 合并 RUN 优化镜像大小