前言
使用Docker部署LNMP环境与WordPress有许多好处:
- 环境一致性与隔离性:无论宿主机环境均可保证一致性,容器间影响小
- 快速部署与跑路:使用Docker Compose一键部署应用,同时数据文件集中,方便一键打包跑路
- 快速更新与回滚:重新下载镜像即可实现升级/降级
- 安全性与资源控制:隔离环境
- 测试与开发效率:随时重建容器
- 轻微的性能损失
基础准备
安装Docker与Docker Compose
参考教程安装Docker与Docker Compose:
检查docker是否安装:
docker info
docker compose version
设置Docker镜像源
更多Docker镜像源可以参考Docker Hub 镜像加速器。
检查Docker镜像源:
docker info
在Registry Mirrors
中应为设置的镜像。
启用Docker的用户命名空间重映射(userns-remap)(可选)
启用用户命名空间重映射可以有效提升容器安全性。它通过 Linux 的用户命名空间(User Namespace) 技术,实现了容器内用户与宿主机用户的隔离和映射,将容器中的UID
/GID
重新映射到宿主机不同的UID
/GID
。
编辑/etc/docker/daemon.json
,添加以下内容:
{
"userns-remap": "default",
}
重启Docker:
systemctl restart docker
检查是否启用:
cat /etc/subuid
输出结果应该类似于:
dockremap:100000:65536
其中dockremap
为宿主机上的系统用户名,100000为起始子UID
/GID
,表示映射范围的起始值,65536为ID数量,表示从起始值开始连续分配多少个 UID/GID 给该用户。因此相当于容器中UID
/GID
的0~65536被映射为了宿主机中的100000~165536,容器内的 root
(UID 0
)会被映射到宿主机的100000,容器内的UID 1
映射到 100001,依此类推。
提前拉取镜像(可选)
提前拉取镜像以避免后续创建容器时拉取镜像:
docker pull nginx:1.28.0-alpine3.21
docker pull php:8.3.25-fpm-alpine3.21
docker pull mariadb:11.8.3
构建镜像
此处以nginx:1.28.0
、php:8.3.25-fpm
、mariadb:11.8.3
为例构建所需镜像。
Nginx
版本选择
同一个Nginx版本存在多个版本的镜像可选,以Nginx版本1.28.0
为例,标签中的含义为:
perl | 镜像中启用了 Perl 模块 |
otel | 镜像集成了 OpenTelemetry(OTel) 支持 |
bookworm | 基于 Debian 12(代号 bookworm )的镜像 |
slim | 基于 Debian 的精简版 |
alpine /alpine3.21 /alpine3.22 | 基于 Alpine Linux 的镜像 |
此处选择nginx:1.28.0-alpine3.21
。
构建Nginx镜像
Web服务器通常使用www-data
用户,但nginx镜像中默认没有www-data
用户,但有www-data
用户组,因此需要创建www-data
用户并加入www-data
用户组,编写Dockerfile
:
FROM nginx:1.28.0-alpine3.21
RUN adduser -u 82 -S -G www-data www-data
并在Dockerfile
同级目录下构建镜像:
docker build -t nginxm:1.28 .
PHP
版本选择
和Nginx类似,同一个PHP版本也存在多个版本的镜像可选,以PHP版本8.3.25
为例,标签中的含义为:
fpm | 使用 PHP-FPM作为运行模式,适合与 Nginx/Apache 配合 |
cli | 仅包含 PHP 的命令行接口,适合脚本或命令行工具,不包含 Web 服务器集成 |
apache | 镜像内预装了 Apache HTTP Server,PHP 作为 Apache 模块运行 |
zts | 启用 Zend Thread Safety(线程安全)的 PHP 版本 |
bookworm | 基于 Debian 12(代号 bookworm )的镜像 |
trixie | 基于 Debian 13(代号 trixie )的镜像 |
alpine /alpine3.21 /alpine3.22 | 基于 Alpine Linux 的镜像 |
此处选择php:8.3.25-fpm-alpine3.21
。
添加扩展
已经编译的扩展
官方镜像中默认编译了一部分扩展,可以通过以下指令查看容器中已经编译的扩展:
docker run --rm <PHP镜像名称> php -m
例如,查看php:8.3.25-fpm-alpine3.21
已经编译的扩展docker run --rm php:8.3.25-fpm-alpine3.21 php -m
:
core ctype curl date dom fileinfo filter hash iconv json libxml mbstring mysqlnd openssl pcre pdo pdo_sqlite phar posix random readline reflection session simplexml sodium spl sqlite3 standard tokenizer xml xmlreader xmlwriter opcache zlib
核心扩展
在官方镜像中还提供了安装核心扩展的脚本docker-php-ext-configure
、docker-php-ext-install
和docker-php-ext-enable
查看docker-php-ext-install
的用法、支持的扩展:
docker run --rm <PHP镜像名称> docker-php-ext-install -h
例如查看php:8.3.25-fpm-alpine3.21
支持的扩展docker run --rm php:8.3.25-fpm-alpine3.21 docker-php-ext-install -h
:
usage: /usr/local/bin/docker-php-ext-install [-jN] [--ini-name file.ini] ext-name [ext-name ...]
ie: /usr/local/bin/docker-php-ext-install gd mysqli
/usr/local/bin/docker-php-ext-install pdo pdo_mysql
/usr/local/bin/docker-php-ext-install -j5 gd mbstring mysqli pdo pdo_mysql shmop
if custom ./configure arguments are necessary, see docker-php-ext-configure
Possible values for ext-name:
bcmath bz2 calendar ctype curl dba dl_test dom enchant exif ffi fileinfo filter ftp gd gettext gmp hash iconv imap intl json ldap mbstring mysqli oci8 odbc opcache pcntl pdo pdo_dblib pdo_firebird pdo_mysql pdo_oci pdo_odbc pdo_pgsql pdo_sqlite pgsql phar posix pspell random readline reflection session shmop simplexml snmp soap sockets sodium spl standard sysvmsg sysvsem sysvshm tidy tokenizer xml xmlreader xmlwriter xsl zend_test zip
Some of the above modules are already compiled into PHP; please check
the output of "php -i" to see which modules are already loaded.
需要注意,安装某些扩展可能需要提前手动安装一些库:
扩展 | 可能需要额外安装的库 | Alpine 命令 |
intl | icu-dev 、icu-data-full 、icu-libs | apk add --no-cache icu-dev icu-data-full icu-libs |
zip | libzip-dev 、libzip | apk add --no-cache libzip-dev libzip |
gd (含jepg、png、webp、avif) | libjpeg-turbo-dev 、libpng-dev 、libwebp-dev 、libheif-dev 、libavif-dev 、freetype-dev 、zlib-dev | apk add --no-cache libjpeg-turbo libpng libwebp libheif-dev libavif-dev freetype-dev zlib-dev |
gmp | gmp-dev 、gmp | apk add --no-cache gmp-dev gmp |
安装扩展时可以先使用docker run --rm -it php:8.3.25-fpm-alpine3.21 sh
创建一个临时容器进行测试,根据提示安装所需要的包。某些扩展在安装时还需要运行docker-php-ext-configure
,安装完成后还需要使用docker-php-ext-enable
使能扩展。以安装gd
为例
apk add --no-cache libjpeg-turbo-dev libpng-dev libwebp-dev libheif-dev libavif-dev freetype-dev zlib-dev
docker-php-ext-configure gd --with-jpeg --with-webp --with-avif --with-freetype
docker-php-ext-install -j$(nproc) gd
docker-php-ext-enable gd
其他扩展
官方镜像可以使用pecl install
命令安装其他扩展。安装其他扩展时也可能需要提前安装一些库:
扩展 | 可能需要额外安装的库 | Alpine 命令 |
几乎所有PECL扩展 | autoconf 、g++ 、make | apk add --no-cache autoconf g++ make |
imagick (含jepg、png、webp、avif) | imagemagick-dev 、libjpeg-turbo 、libpng 、libwebp 、libheif-dev 、libavif-dev | apk add --no-cache imagemagick-dev libjpeg-turbo libpng libwebp libavif-dev libheif-dev |
memcached | libmemcached-dev 、zlib-dev 、libevent-dev | apk add --no-cache libmemcached-dev zlib-dev libevent-dev |
通常由于网络限制无法直接安装,可以先在https://pecl.php.net/package/<扩展名称>
下载,例如下载imagick
:https://pecl.php.net/package/imagick
,然后将安装包复制到容器中安装,同时也需要使用docker-php-ext-enable
使能扩展。例如安装
apk add --no-cache autoconf g++ make
apk add --no-cache imagemagick-dev libjpeg-turbo libpng libwebp libavif-dev libheif-dev
pecl install /tmp/pecl/imagick-3.8.0.tgz && docker-php-ext-enable imagick
构建适用于WordPress的PHP镜像
参考Server Environment,官方推荐安装的扩展有:
级别 | 扩展 | 镜像中不包含的核心扩展 | 镜像中不包含的其他扩展 |
必须(required) | json mysqli /mysqlnd | mysqli | |
强烈推荐(highly recommended) | curl dom exif fileinfo hash igbinary imagick /gd intl mbstring openssl pcre xml zip | exif intl zip gd | igbinary imagick |
用于缓存(cache) | apcu /memcached /redis opcache | apcu /memcached /redis | |
可选(optional) | timezonedb | timezonedb | |
备用(may) | bcmath filter iconv shmop simplexml sodium xmlreader zlib | bcmath shmop | |
文件变更 | ssh2 ftp sockets | ftp sockets | ssh2 |
用于缓存的扩展选择memcached
,不安装用于文件变更的扩展,因此额外安装以下扩展:
mysqli exif intl zip gd bcmath shmop igbinary imagick memcached timezonedb
igbinary
、imagick
、memcached
、timezonedb
的安装包手动从官网下载并复制到Dockerfile
同级目录下的pecl
目录,编写Dockerfile
:
FROM php:8.3.25-fpm-alpine3.21
RUN apk update && apk upgrade --no-cache && \
apk add --no-cache autoconf g++ make && \
apk add --no-cache icu-dev icu-data-full icu-libs libzip-dev libzip zlib-dev \
libjpeg-turbo-dev libpng-dev libwebp-dev libheif-dev libavif-dev freetype-dev \
imagemagick-dev imagemagick libmemcached-dev libevent-dev
RUN docker-php-ext-configure gd --with-jpeg --with-webp --with-avif --with-freetype && \
docker-php-ext-install -j$(nproc) mysqli exif intl zip gd bcmath shmop && \
docker-php-ext-enable mysqli exif intl zip gd bcmath shmop
COPY ./pecl/ /tmp/pecl
RUN pecl install /tmp/pecl/igbinary-3.2.16.tgz /tmp/pecl/imagick-3.8.0.tgz \
/tmp/pecl/memcached-3.3.0.tgz /tmp/pecl/timezonedb-2025.2.tgz && \
docker-php-ext-enable igbinary imagick memcached timezonedb && \
rm -rf /tmp/pecl
若apk
速度较慢建议更换镜像源。并在Dockerfile
同级目录下构建镜像:
docker build -t phpm:8.3 .
MariaDB
数据库选择MariaDB,镜像的标签主要用于区分操作系统的版本:
无标签 | 基于 Debian 的镜像 |
ubi | 基于 Red Hat Universal Base Image 的镜像 |
ubi9 | 基于 UBI 9(对应 RHEL 9 系列)的镜像 |
noble | 基于 Ubuntu 24.04 LTS(代号 noble )的镜像 |
此处选择mariadb:11.8.3
。MariaDB镜像无需特别的修改。
创建编排文件docker-compose.yaml
由于MariaDB默认不支持通过主机名进行认证,因此手动为容器分配IP地址。本应用的目录结构为:
.
├── wwwroot/ # 用于存放网站数据,此文件夹下的用户和用户组应为100082
│ ├── site1.example.com # 单个网站的数据
│ └── site2.example.com # 单个网站的数据
├── server/ # Docker容器数据,此文件夹下的用户和用户组应为100000
│ ├── mysql/ # 数据库数据
│ │ ├── data/ # 数据库原始数据
│ │ ├── config/ # 数据库配置
│ │ └── logs/ # 数据库日志,此文件夹下的用户和用户组应为100999
│ ├── php/ # PHP容器数据
│ │ ├── config/ # PHP配置
│ │ └── logs/ # PHP日志
│ └── nginx/ # Nginx数据
│ ├── ssl/ # 证书文件
│ ├── config/ # Nginx配置
│ └── logs/ # Nginx日志
└── docker/ # 应用及容器构建文件
├── docker-compose.yaml # 应用编排
├── php/ # PHP镜像构建
│ ├── Dockerfile # PHP镜像构建文件
│ └── pecl # PECL安装包
└── nginx/ # Nginx镜像构建文件
└── Dockerfile # Nginx镜像构建
因此docker-compose.yaml
为:
services:
db:
image: mariadb:11.8.3
container_name: mariadb
networks:
server-network:
ipv4_address: 172.20.0.100
volumes:
- /www/server/mysql/data:/var/lib/mysql
- /www/server/mysql/config:/etc/mysql/
- /www/server/mysql/logs:/var/log/mysql
restart: unless-stopped
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: your-password
php:
image: phpm:8.3
container_name: php
networks:
server-network:
ipv4_address: 172.20.0.102
volumes:
- /www/wwwroot:/var/www/html
- /www/server/php/config:/usr/local/etc
- /www/server/php/logs:/var/log
restart: unless-stopped
environment:
TZ: Asia/Shanghai
depends_on:
- db
nginx:
image: nginxm:1.28
container_name: nginx
networks:
server-network:
ipv4_address: 172.20.0.101
ports:
- "80:80"
- "443:443"
volumes:
- /www/wwwroot:/var/www/html
- /www/server/nginx/ssl:/etc/ssl
- /www/server/nginx/config:/etc/nginx
- /www/server/nginx/logs:/var/log/nginx
restart: unless-stopped
depends_on:
- php
networks:
server-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
注意修改数据库ROOT的密码MYSQL_ROOT_PASSWORD
。
构建文件系统
由于绑定挂载时容器中的文件/文件夹会被宿主机中的对应文件/文件夹完全替代,因此容器在运行时可能缺少必要的配置文件,因此在部署应用之前,还需要将容器中的配置文件先复制出来。以下以Nginx为例提取默认的配置文件,首先创建一个临时容器,并将目标文件/文件夹复制出来,最后再销毁容器:
docker run -d --name=temp_container nginx:1.28.0-alpine3.21
docker cp temp_container:/etc/nginx /www/server/nginx/config
docker stop temp_container
docker rm -v temp_container
其余容器的提取方法类似。需要注意修改文件夹的用户和用户组:
chown 100000 ./server/ -R
chgrp 100000 ./server/ -R
chown 100082 ./wwwroot/ -R
chgrp 100082 ./wwwroot/ -R
chown 100999 ./server/mysql/logs/ -R
chgrp 100999 ./server/mysql/logs/ -R
使用Docker Compose部署应用
启动应用
在docker-compose.yaml
同级目录下执行即可启动应用:
docker compose up -d
其他常用的指令有:
指令 | 描述 |
docker compose ps | 查看应用运行状态,正常运行时状态应为Up |
docker compose logs | 查看应用日志 |
docker compose stop | 停止应用 |
docker compose restart | 重新启动应用 |
docker compose down | 删除应用 |
管理单个容器的常用指令:
指令 | 描述 |
docker ps | 查看所有容器的运行状态,正常运行时状态应为Up |
docker logs <容器ID/容器名> | 查看单个容器日志 |
docker stop | 停止单个容器 |
docker restart | 重新单个容器 |
docker rm <容器ID/容器名> | 删除单个容器 |
docker exec -it <容器ID/容器名> sh | 进入正在运行的容器内部,并启动一个交互式 shell(sh ) |
例如,在部署acme.sh时就可以使用docker restart nginx
来重启nginx镜像。
容器配置
如何配置Nginx、PHP、MariaDB并不是本文的重点,因此不在此赘述。需要注意,PHP和Nginx的用户和用户组都是www-data
(容器内:82,容器外:100082)。
启动Wordpress
创建数据库
进入MariaDB交互式创建数据库:
docker exec -it mariadb mariadb -u root -p
然后输入编排文件中MYSQL_ROOT_PASSWORD
数据库ROOT密码即可进入SQL命令行:
CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wordpress'@'172.20.0.102' IDENTIFIED BY 'your-password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'172.20.0.102';
FLUSH PRIVILEGES;
EXIT;
注意修改数据库的密码。
安装Wordpress
最后一步便是安装WordPress了,在WordPress官网下载安装包并解压,同样需要设置用户和用户组为100082。怎样安装Wordpress请参考官方的安装教程,数据库的地址可以填容器编排时的名字db
。