Docker 容器化部署实战:从零搭建 LNMP 环境

前言

对于个人站长和中小团队来说,LNMP(Linux + Nginx + MySQL + PHP)依然是最经典的 Web 服务组合。然而传统的手动编译安装或一键面板方式,往往会遇到环境不一致、迁移困难、版本管理混乱等问题。Docker 容器化技术的出现,彻底改变了这一局面。

本文将手把手教你如何使用 Docker Compose 从零搭建一套生产可用的 LNMP 环境,涵盖 Nginx、MySQL 8.0、PHP 8.2(含常见扩展)以及 Redis 的容器化部署。

Docker 与 Docker Compose 简介

Docker 是一个开源的容器化平台,它可以将应用及其依赖打包到一个轻量级、可移植的容器中。与传统的虚拟机不同,Docker 容器共享宿主机的操作系统内核,启动速度更快,资源占用更小。

Docker Compose 则是 Docker 官方推出的多容器编排工具,通过一个 YAML 文件定义和运行多个容器。对于 LNMP 这种多服务架构,Compose 可以让我们一键启动所有服务。

第一步:安装 Docker 和 Docker Compose

在开始之前,我们需要在服务器上安装 Docker 环境。以下是在 Ubuntu 22.04 上的安装步骤:

# 更新包索引
sudo apt update

# 安装依赖包
sudo apt install -y ca-certificates curl gnupg lsb-release

# 添加 Docker 官方 GPG 密钥
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 设置 Docker 仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker 和 Docker Compose
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 将当前用户加入 docker 组(避免每次 sudo)
sudo usermod -aG docker $USER
# 重新登录后生效,或者使用 newgrp docker

安装完成后,验证版本:

docker --version
docker compose version

第二步:规划项目目录结构

一个良好的目录结构能让后续维护变得轻松。我们在 /opt/lnmp 下创建以下结构:

/opt/lnmp/
├── docker-compose.yml     # 容器编排文件
├── nginx/
│   ├── Dockerfile         # Nginx 自定义镜像
│   ├── conf.d/
│   │   └── default.conf   # Nginx 站点配置
│   └── logs/              # 日志目录
├── php/
│   ├── Dockerfile         # PHP 自定义镜像
│   └── php.ini            # PHP 配置文件
├── mysql/
│   ├── conf.d/
│   │   └── my.cnf         # MySQL 配置文件
│   └── data/              # 数据持久化目录
├── redis/
│   ├── redis.conf         # Redis 配置文件
│   └── data/              # Redis 持久化目录
└── www/
    └── your-site/         # 网站代码目录

创建目录:

mkdir -p /opt/lnmp/{nginx/{conf.d,logs},php,mysql/{conf.d,data},redis/{data},www/your-site}

第三步:编写 Docker Compose 配置文件

docker-compose.yml 是整个 LNMP 环境的核心编排文件,它定义了四个服务:Nginx、PHP-FPM、MySQL 和 Redis。

version: '3.8'

services:
  nginx:
    image: nginx:1.25-alpine
    container_name: lnmp-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/logs:/var/log/nginx
      - ./www:/var/www
    depends_on:
      - php
    networks:
      - lnmp-network
    restart: always

  php:
    build: ./php
    container_name: lnmp-php
    volumes:
      - ./www:/var/www
      - ./php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - lnmp-network
    restart: always

  mysql:
    image: mysql:8.0
    container_name: lnmp-mysql
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-wordpress}
      MYSQL_USER: ${MYSQL_USER:-wpuser}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-wppass}
    networks:
      - lnmp-network
    restart: always

  redis:
    image: redis:7-alpine
    container_name: lnmp-redis
    ports:
      - "6379:6379"
    volumes:
      - ./redis/data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      - lnmp-network
    restart: always

networks:
  lnmp-network:
    driver: bridge

这里有几个设计要点值得注意。第一,所有服务都连接到同一个自定义网络 lnmp-network,这样容器之间可以通过服务名相互访问(例如 PHP 代码中连接数据库可以使用 lnmp-mysql 作为主机名)。第二,MySQL 和 Redis 的数据目录映射到宿主机,确保容器重启后数据不丢失。第三,使用 ${} 环境变量占位符配合 .env 文件,让敏感配置与编排文件分离。

第四步:配置 Nginx 和 PHP

Nginx 站点配置

创建 /opt/lnmp/nginx/conf.d/default.conf

server {
    listen 80;
    server_name your-domain.com;
    root /var/www/your-site/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|webp)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    # 日志配置
    access_log /var/log/nginx/your-site_access.log;
    error_log /var/log/nginx/your-site_error.log;
}

关键点是 fastcgi_pass php:9000,这里使用了服务名 php 而不是 IP 地址,Docker 内置 DNS 会自动解析到 PHP 容器的 IP。

PHP Dockerfile

创建 /opt/lnmp/php/Dockerfile

FROM php:8.2-fpm-alpine

# 安装系统依赖
RUN apk add --no-cache     libzip-dev     oniguruma-dev     libpng-dev     libjpeg-turbo-dev     freetype-dev     libxml2-dev     curl-dev

# 安装 PHP 扩展
RUN docker-php-ext-install     pdo_mysql     mysqli     mbstring     exif     pcntl     bcmath     gd     zip     opcache     intl

# 安装 Redis 扩展
RUN pecl install redis && docker-php-ext-enable redis

# 安装 Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# 配置 OPcache
RUN docker-php-ext-enable opcache

WORKDIR /var/www

PHP 8.2 的 Alpine 镜像非常精简,通过 docker-php-ext-install 命令可以方便地安装所需的扩展。这里我们安装了生产环境中常用的扩展,包括 pdo_mysql、gd、zip 和 redis。

PHP 性能配置

创建 /opt/lnmp/php/php.ini

upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
memory_limit = 256M
date.timezone = Asia/Shanghai

[opcache]
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 2
opcache.fast_shutdown = 1

第五步:配置 MySQL 和 Redis

MySQL 配置

创建 /opt/lnmp/mysql/conf.d/my.cnf

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
max_connections = 500
innodb_buffer_pool_size = 512M
innodb_log_file_size = 256M
skip-name-resolve
default-time-zone = +08:00

[client]
default-character-set = utf8mb4

skip-name-resolve 可以跳过 DNS 反向解析,加快连接速度,在容器环境中特别有用。

Redis 配置

创建 /opt/lnmp/redis/redis.conf

requirepass YourRedisPass123
bind 0.0.0.0
port 6379
databases 16
save 900 1
save 300 10
save 60 10000
maxmemory 256mb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec

第六步:启动 LNMP 环境

所有配置文件准备就绪后,只需一行命令即可启动:

cd /opt/lnmp

# 创建 .env 文件
cat > .env << EOF
MYSQL_ROOT_PASSWORD=YourRootPass123
MYSQL_DATABASE=myblog
MYSQL_USER=bloguser
MYSQL_PASSWORD=BlogPass456
EOF

# 启动所有服务
docker compose up -d

# 查看运行状态
docker compose ps

# 查看日志
docker compose logs -f

首次启动时会构建 PHP 镜像,可能需要几分钟时间。之后再次启动只需几秒钟。

第七步:验证 LNMP 环境

/opt/lnmp/www/your-site/public/ 下创建一个 info.php 文件:

<?php
phpinfo();

然后在浏览器中访问 http://your-server-ip/info.php,如果能看到 PHP 信息页面,说明 LNMP 环境搭建成功。

接着测试数据库连接:

<?php
try {
    $pdo = new PDO('mysql:host=lnmp-mysql;dbname=myblog;charset=utf8mb4', 'bloguser', 'BlogPass456');
    echo 'MySQL 连接成功!';
} catch (PDOException $e) {
    echo '连接失败: ' . $e->getMessage();
}

测试 Redis 连接:

<?php
$redis = new Redis();
$redis->connect('lnmp-redis', 6379);
$redis->auth('YourRedisPass123');
$redis->set('test', 'Hello Docker LNMP');
echo $redis->get('test');

日常运维命令

# 停止所有服务
docker compose down

# 重启某个服务
docker compose restart nginx

# 查看实时日志
docker compose logs -f php

# 进入容器内部
docker compose exec php bash

# 重新构建镜像(配置文件改动后)
docker compose build php
docker compose up -d

总结

通过 Docker 容器化部署 LNMP 环境,我们获得了以下几点优势:

  • 环境一致性:开发、测试、生产环境完全一致,告别「在我机器上能跑」的尴尬
  • 快速部署:新服务器上初始化 LNMP 只需几分钟
  • 易于扩展:需要增加 PHP 版本?加一个容器即可,互不干扰
  • 资源隔离:每个服务运行在独立容器中,一个服务挂了不影响其他
  • 便于迁移:打包整个项目目录到新服务器,一行命令恢复所有服务

这套方案已经在多台生产服务器上稳定运行超过一年,经受住了日均数万 PV 的考验。如果你是个人站长或小团队运维,不妨从今天开始拥抱容器化吧。

Last modification:July 14th, 2026 at 08:13 pm

Leave a Comment