使用 Docker 替代 OneinStack/LNMP
持续更新中……
最近 OneinStack 和 LNMP 都被收购恶意挂马,此类一键安装脚本的安全已经不受控,建议还是手搓环境为好。
记录一下使用 Docker 搭建 Nginx、PHP、Mysql 集成环境。
必备的功能
- 支持多站点
- 支持 Let's Encrypt
目录结构
- mysql/data/ 主要存放 Mysql 数据
- nginx/ 下是 nginx 的配置文件,多站点在 conf.d 里面添加
- php/Dockerfile 安装 PHP 拓展,php/etc/ 下主要是 php.ini 和 fpm 的配置
- www/ 是站点目录,新增网站就在此新增子目录
├── mysql
└── data
├── nginx
├── Dockerfile
├── conf.d
├── default.conf
└── example.conf
├── nginx.conf
└── ssl
├── php82
├── Dockerfile
└── etc
├── php-fpm.conf
├── php-fpm.d
├── docker.conf
├── www.conf
└── zz-docker.conf
├── conf.d
└── php.ini
├── www
├── html
└── example.com
├── docker-compose.yml
在官方 PHP 镜像基础上安装必要的拓展
FROM php:8.2-fpm
LABEL maintainer="[email protected]"
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libicu-dev \
libz-dev \
libpq-dev \
libzip-dev \
libonig-dev \
libjpeg62-turbo-dev \
libjpeg-dev \
libpng-dev \
libwebp-dev \
libfreetype6-dev \
libssl-dev \
libmcrypt-dev \
zlib1g-dev \
libxml2-dev \
libbz2-dev \
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-configure bcmath --enable-bcmath \
&& docker-php-ext-configure pcntl --enable-pcntl \
&& docker-php-ext-configure pdo_mysql --with-pdo-mysql \
&& docker-php-ext-configure mbstring --enable-mbstring \
&& docker-php-ext-configure soap --enable-soap \
&& docker-php-ext-configure exif --enable-exif \
&& docker-php-ext-install \
bcmath \
intl \
mbstring \
mysqli \
pcntl \
pdo_mysql \
sockets \
zip \
soap \
exif \
&& docker-php-ext-install opcache \
&& docker-php-ext-enable opcache
RUN docker-php-ext-configure gd --with-jpeg --with-webp --with-freetype \
&& docker-php-ext-install gd;
RUN pecl install -o -f redis && docker-php-ext-enable redis;
RUN pecl install -o -f protobuf && docker-php-ext-enable protobuf;
RUN pecl install -o -f swoole && docker-php-ext-enable swoole;
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
WORKDIR /var/www
使用 docker-compose.yml 运行多容器
Docker 的设计理念就是一个容器一个进程,把 LNMP 打包到一个容器不推荐,推荐使用 docker-compose 配置多容器运行。
修改一下 Mysql Root 密码。
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- 80:80
- 443:443
restart: always
volumes:
# nginx配置文件
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl
- ./www:/var/www/
networks:
- backend
environment:
- TZ=Asia/Shanghai
links:
- php82
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./www/certbot/:/var/www/certbot/:rw
- ./nginx/ssl/:/etc/letsencrypt/:rw
networks:
- backend
environment:
- TZ=Asia/Shanghai
php82:
build:
context: ./php82
container_name: php82
restart: always
volumes:
# php 配置文件
- ./php82/etc/php.ini:/usr/local/etc/php/php.ini:ro
- ./php82/etc/conf.d/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini:ro
- ./php82/etc/php-fpm.d:/usr/local/etc/php-fpm.d:ro
# 项目代码
- ./www:/var/www/
networks:
- backend
environment:
- TZ=Asia/Shanghai
links:
- mysql
- redis
mysql:
image: mysql:latest
environment:
- MYSQL_ROOT_PASSWORD=root_password
- TZ=Asia/Shanghai
volumes:
# 数据文件
- ./mysql/data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
networks:
- backend
redis:
image: redis:latest
restart: always
networks:
- backend
environment:
- TZ=Asia/Shanghai
networks:
backend:
Nginx、PHP、FPM 配置优化
Nginx
nginx.conf
...
events {
worker_connections 65535;
}
...
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 2048;
types_hash_bucket_size 64;
client_max_body_size 50M;
gzip on;
...
PHP
php.ini
...
;post body 体积大小
post_max_size = 50M
...
;上传文件限制
upload_max_filesize = 50M
...
;禁用不安全的函数
disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_restore,dl,readlink,symlink,popepassthru,stream_socket_server,fsocket,poket,popen
...
conf.d/docker-php-ext-opcache.ini
zend_extension=opcache.so
; 开启 opcache jit
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=100000
opcache.max_wasted_percentage=5
opcache.use_cwd=1
opcache.validate_timestamps=1
opcache.revalidate_freq=60
;opcache.save_comments=0
opcache.fast_shutdown=1
opcache.consistency_checks=0
;opcache.optimization_level=0
opcache.jit=1255
opcache.jit_buffer_size=100M
FPM
php-fpm.d/www.conf
pm.max_children = 16
pm.start_servers = 10
pm.min_spare_servers = 8
pm.max_spare_servers = 16
pm.max_requests = 2048
pm.process_idle_timeout = 10s
request_terminate_timeout = 120
request_slowlog_timeout = 0
使用
1.更新系统 (Debian/Ubuntu)
apt update
apt upgrade
apt install screen wget
2.安装 Docker
3.下载 docker compose 文件
wget https://wordupr.com/public/wordupr-docker.zip
unzip wordupr-docker.zip
4.构建镜像
screen -S build #如果断线执行 screen -r build 可以恢复
cd wordupr-docker
docker compose build
5.修改 docker-compose.xml 中 Mysql 的 root 密码
6.启动容器
docker compose up -d
7.安装 phpMyadmin
为了管理数据方便安装 phpMyadmin,最好 Nginx 限制一下 ip 访问。
在 VPS shell 执行一下命令(非容器内)
- 打开默认目录
cd www/html
- 下载最新 phpMyadmin 源码,解压文件
wget https://files.phpmyadmin.net/phpMyAdmin/5.2.1/phpMyAdmin-5.2.1-all-languages.zip
unzip phpMyAdmin-5.2.1-all-languages.zip
mv phpMyAdmin-5.2.1-all-languages phpMyAdmin
- 修改配置
cd phpMyAdmin
cp config.sample.inc.php config.inc.php
nano config.inc.php
在 config.inc.php 修改 host 配置为 mysql(docker-compose 里面定义的服务名,docker 会自动解析为 mysql 容器的 IP)
$cfg['Servers'][$i]['host'] = 'mysql';
修改 cookie 密钥,需要填写32位字符串,推荐使用 密码生成器
$cfg['blowfish_secret'] = '32位字符串';
8.新增站点
- 新建目录
mkdir www/example.com
- 配置临时的 HTTP 用于申请 SSL 证书
nano nginx/conf.d/example.com.conf
内容如下
server {
listen 80;
listen [::]:80;
server_name example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
charset utf-8;
default_type text/html;
return 200 'Hello';
}
}
- 更新 Nginx 配置
docker compose exec nginx nginx -s reload
- 申请证书
docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ -d example.com
- 删除临时 example.com.conf,配置完整的站点配置
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_conf_command Options PrioritizeChaCha;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_buffer_size 2k;
add_header Strict-Transport-Security max-age=15768000;
ssl_stapling on;
ssl_stapling_verify on;
server_name example.com;
index index.html index.htm index.php;
root /var/www/example.com;
# PHP engine
location ~ \.php$ {
fastcgi_pass php82:9000;
# 404
try_files $fastcgi_script_name =404;
# default fastcgi_params
include fastcgi_params;
# fastcgi settings
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 7d;
access_log off;
}
location ~ /(\.user\.ini|\.ht|\.git|\.svn|\.project|LICENSE|README\.md) {
deny all;
}
}
- 再次更新 Nginx 配置
docker compose exec nginx nginx -s reload
- 配置 crontab 计划任务,每个月月初自动刷新
#更新https证书
1 1 1 * * cd /{目录} && docker compose run --rm certbot renew && docker compose exec nginx nginx -s reload
- 重置网站目录权限
docker compose exec php82 chown -R www-data:www-data /var/www
docker compose exec php82 find /var/www -type d -exec chmod 755 {} \;
docker compose exec php82 find /var/www -type f -exec chmod 644 {} \;