14 10 月 2026

用 Docker 部署网站,最大的好处是环境干净、迁移无脑:整个技术栈打成一个 compose 文件,换机器就是拷贝过去 docker compose up -d,十分钟上线。

这篇用一个完整的 LNMP 实战(Nginx + PHP-FPM + MySQL + Redis),讲 Docker 部署网站的完整方案:compose 编排、数据持久化、备份、更新、生产注意事项。

需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上,主流游戏快速部署。

一、架构设计

互联网 → Nginx 容器(80/443) → PHP-FPM 容器(9000) → MySQL 容器(3306)
                                              ↘ Redis 容器(6379)

四个容器,一个 compose 文件管全部。数据全部挂卷,容器随便删重建。

二、完整 docker-compose.yml

# /opt/website/docker-compose.yml
version: "3.8"

services:
  nginx:
    image: nginx:1.25-alpine
    container_name: web-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro        # 站点配置
      - ./nginx/certs:/etc/nginx/certs:ro          # 证书
      - ./wwwroot:/var/www/html:ro                 # 网站代码(只读)
      - ./logs/nginx:/var/log/nginx                # 日志
    depends_on:
      - php
    networks:
      - webnet

  php:
    image: php:8.1-fpm-alpine
    container_name: web-php
    restart: unless-stopped
    volumes:
      - ./wwwroot:/var/www/html
      # 按需装扩展: 在 Dockerfile 里装,这里直接改镜像也行
    environment:
      - TZ=Asia/Shanghai
    networks:
      - webnet

  mysql:
    image: mysql:8.0
    container_name: web-mysql
    restart: unless-stopped
    ports:
      - "127.0.0.1:3306:3306"      # 只映射到本地!方便用客户端连,但不暴露公网
    volumes:
      - ./data/mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=根密码
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wp
      - MYSQL_PASSWORD=应用密码
      - TZ=Asia/Shanghai
    command: --default-authentication-plugin=mysql_native_password
    networks:
      - webnet

  redis:
    image: redis:7-alpine
    container_name: web-redis
    restart: unless-stopped
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - ./data/redis:/data
    command: redis-server --requirepass Redis密码 --appendonly yes
    networks:
      - webnet

networks:
  webnet:
    driver: bridge

目录结构

/opt/website/
├── docker-compose.yml
├── nginx/
│   ├── conf.d/
│   │   └── site.conf
│   └── certs/           # SSL 证书放这
├── wwwroot/             # 网站代码
├── logs/nginx/          # 日志
└── data/
    ├── mysql/           # 数据库文件(命根子)
    └── redis/           # Redis 持久化

Nginx 站点配置

# nginx/conf.d/site.conf
server {
    listen 80;
    server_name 你的域名;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name 你的域名;
    root /var/www/html;
    index index.php index.html;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    # PHP 转发到 php 容器(容器名就是主机名)
    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

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

    location ~* \.(jpg|css|js|png|webp|woff2)$ {
        expires 30d;
        access_log off;
    }
}

关键点:fastcgi_pass php:9000 —— Docker 网络内直接用容器名通信,不用 IP。

三、启动与验证

cd /opt/website

# 启动
docker compose up -d

# 看状态
docker compose ps

# 看日志(排障全靠它)
docker compose logs -f nginx
docker compose logs -f php

# 放网站代码
# 把 WordPress/Typecho 文件放到 ./wwwroot/

# 验证
curl -I http://127.0.0.1

访问域名,按程序向导安装。数据库配置:

  • 主机:mysql(不是 localhost,容器间用服务名)
  • 库名:wordpress
  • 用户:wp
  • 密码:compose 里设的

四、PHP 扩展问题

php:8.1-fpm-alpine 官方镜像很精简,很多扩展没有。WordPress/Typecho 需要:mysqli/pdo_mysql、gd、mbstring、curl、zip、intl、opcache。

方案 A:用 Dockerfile 自己构建(推荐,可控)

# php/Dockerfile
FROM php:8.1-fpm-alpine

# 装依赖 + PHP 扩展
RUN apk add --no-cache \
    libpng libpng-dev libjpeg-turbo libjpeg-turbo-dev \
    freetype freetype-dev libwebp libwebp-dev \
    icu-dev libzip-dev oniguruma-dev curl-dev

RUN docker-php-ext-configure gd --with-jpeg --with-webp --with-freetype \
    && docker-php-ext-install -j$(nproc) gd mbstring mysqli pdo_mysql \
       curl zip intl bcmath opcache

# OPcache 生产参数
RUN { \
    echo 'opcache.enable=1'; \
    echo 'opcache.memory_consumption=128'; \
    echo 'opcache.max_accelerated_files=10000'; \
    echo 'opcache.revalidate_freq=2'; \
    } > /usr/local/etc/php/conf.d/opcache.ini

# Redis 扩展
RUN apk add --no-cache $PHPIZE_DEPS \
    && pecl install redis \
    && docker-php-ext-enable redis

compose 里改成:

  php:
    build: ./php          # 用 Dockerfile 构建
    # image: php:8.1-fpm-alpine   # 不用官方镜像了

方案 B:用第三方全功能镜像(快但不可控):wordpress:php8.1-fpm 自带全部 WordPress 依赖。

五、数据持久化与备份

备份脚本

#!/bin/bash
# /opt/scripts/web-backup.sh
DATE=$(date +%Y%m%d)
WEB_DIR="/opt/website"

# 1. 数据库(从容器里导出,不是宿主机目录!)
docker exec web-mysql mysqldump -u root -p根密码 \
  --single-transaction --all-databases | gzip > "$WEB_DIR/backups/mysql-$DATE.sql.gz"

# 2. 网站文件
tar -czf "$WEB_DIR/backups/wwwroot-$DATE.tar.gz" -C "$WEB_DIR" wwwroot

# 3. 配置(域名换了、证书换了都要能恢复)
tar -czf "$WEB_DIR/backups/config-$DATE.tar.gz" -C "$WEB_DIR" nginx docker-compose.yml

# 4. 清理 14 天前
find "$WEB_DIR/backups" -name "*.gz" -mtime +14 -delete
mkdir -p /opt/website/backups
crontab -e
0 3 * * * /opt/scripts/web-backup.sh

恢复

# 停服务
cd /opt/website && docker compose down

# 恢复文件
tar -xzf backups/wwwroot-20261014.tar.gz -C /opt/website/

# 起 MySQL,导入数据
docker compose up -d mysql
sleep 10
gunzip < backups/mysql-20261014.sql.gz | \
  docker exec -i web-mysql mysql -u root -p根密码

# 全部起来
docker compose up -d

六、更新:镜像与代码分开

# 更新容器(镜像拉新版,数据卷不动)
cd /opt/website
docker compose pull
docker compose up -d        # 只会重建有更新的服务

# 更新网站代码(WP/Typecho)
tar -xzf 新版本.tar.gz -C wwwroot/ --overwrite

# 清理旧镜像(省磁盘)
docker image prune -a

注意:docker compose up -d 重建容器时,没挂卷的目录会丢。所以代码、数据、日志、配置全部要挂卷(上面的 compose 已经这么做了)。

七、生产环境注意事项

1. 安全

# MySQL/Redis 端口只映射 127.0.0.1(上面已做)
# 验证: 外网扫不到 3306
nmap -Pn -p3306 你的IP    # 应该 filtered

# Docker 会绕过 ufw!安全组也要配
# 见 Docker 安全加固

2. 资源限制

services:
  mysql:
    # ...
    deploy:
      resources:
        limits:
          memory: 1G
  php:
    # ...
    deploy:
      resources:
        limits:
          memory: 512M

3. 日志管理

Docker 容器日志默认无限增长,能撑爆磁盘:

# 每个服务加:
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "3"

或者全局配置 /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {"max-size": "100m", "max-file": "3"}
}

4. 健康检查

  nginx:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 5s
      retries: 3

配合 depends_on: condition: service_healthy,避免 PHP 没起来 Nginx 先起报 502。

八、常见问题

Q:容器内连不上 mysql?
连接串主机名写 mysql 不是 localhost;确认两个容器在同一 network(compose 默认同网络);等 MySQL 初始化完(第一次启动要 10–30 秒)。

Q:网站代码改了没生效?
OPcache 缓存了 PHP。docker compose restart php,或者调小 opcache.revalidate_freq。

Q:磁盘越用越少?
docker system df 看占用,docker system prune -a 清理。日志、旧镜像是大头。

Q:HTTPS 证书怎么自动续期?
宿主机跑 certbot,或者用 nginx-proxy-manager(自带证书管理和可视化反代,省事)。

Q:和宿主机直接装 LNMP 比,性能差多少?
网络多一层 NAT 转发,约 1–3% 损耗;磁盘 IO 基本持平。换来的是环境隔离和迁移便利,值。

Q:NAT 型服务器用 Docker 建站?
端口链条:公网 → 网关映射 80/443 → 宿主机 → 容器。三层确认:服务商映射了 80/443、安全组放行、compose ports 映射对。域名备案后正常访问。

小结

Docker 部署网站的核心就四点:compose 编排一把梭、数据全部挂卷、端口只暴露必要的、日志设上限。搭好之后,加服务就是加一段 YAML,换机器就是拷贝目录,维护成本比裸装低一个量级。

更多 Docker 与网站内容见财富云计算知识库:Dockerfile 优化、Compose 编排、安全加固全系列;网站搭建:Nginx、WordPress、HTTPS、CDN。需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上。

延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:WordPress 搭建与性能优化完整指南:从安装到提速
  2. 下一篇:Nginx 负载均衡与 upstream 配置实战:多机分摊流量
发表评论 6

取消回复