背景
由于最近的lnmp一键包投毒事件,不太敢再继续用lnmp一键包来装lnmp环境了。个人不太喜欢用面板,不如乘着这个机会把环境移植到docker去。用docker最大的好处就是全部东西都是官方的,不太担心会被投毒。但是用了这么多年lnmp一键包,还是有点感情的(很多config不想自己手动再搓一遍)。于是决定同时尽量使用来自一键包的各种配置文件。
Docker这边,目标是做出一个lnmp最小环境并全部使用nignx,mysql,php-fpm官方镜像。
太长不看版
https://github.com/anshi233/lnmp-docker
这个repo里包含了理论上走完全文能得到的一个完整lnmp环境。使用方法请参考README。
Docker架构
经过一段时间研究后,于是决定参考https://lnmp.org/faq/lnmp-software-list.html把关键的配置文件全部映射到docker环境里面去。
Docker大致架构决定如下,主要使用docker compose来达成类似“一键”包的效果。
├── docker-compose.yml (docker compose 配置文件)
├── dockerfiles (自定义的dockerfile存放文件夹)
│ ├── nginx.Dockerfile (nginx 的dockerfile)
│ └── php-fpm.Dockerfile (php-fpm 的dockerfile)
├── mysql (mysql相关文件存放文件夹)
│ ├── conf.d (.cnf配置文件存放文件夹)
│ └── mysql (mysql主要数据库文件存放文件夹)
├── nginx (nginx相关文件存放文件夹)
│ ├── conf (nginx配置文件存放文件夹)
│ ├── logs (nginx logs文件夹)
│ └── wwwlogs (nginx 网站访问log文件夹)
├── php (php相关文件存放文件夹)
│ ├── etc (php配置文件夹)
│ │ ├── php-fpm.conf (php-fpm主要配置文件)
│ │ ├── php-fpm.d (php-fpm额外配置文件存放文件夹)
│ │ └── php.ini (php.ini)
│ └── var
│ └── log (php log存放文件夹)
├── ssl (https ssl证书存放文件夹)
├── www (www用户文件夹,主要存放网站)
└── wwwroot (lnmp默认的网站存放文件夹)
现在,我们可以尝试把lnmp一键包搭建的环境的配置文件复制到以上设计好的docker的目录里去了。
lnmp的环境的配置文件来源很自由,你可以从你已经搭了环境的机器复制过来。或者单开一台临时vps装上lnmp2.0然后复制到其他机器。这里只复制nginx,php,www的文件夹。mysql打算重新创建文件夹并手动导入sql来迁移。
#原目录 docker目录
#nginx
/usr/local/nginx/conf --> [你的docker文件夹根目录]/nginx/conf
/usr/local/nginx/logs --> [你的docker文件夹根目录]/nginx/logs
/usr/local/nginx/wwwlogs --> [你的docker文件夹根目录]/nginx/wwwlogs
#php
/usr/local/php/etc --> [你的docker文件夹根目录]/php/etc
#如果没有/usr/local/php/var/log就创建一个
/usr/local/php/var --> [你的docker文件夹根目录]/php/var
#旧版lnmp 有/home/www lnmp2.0没有这个
#也可以复制到其他地方然后软链接过来
/home/www --> [你的docker文件夹根目录]/www
/home/wwwroot --> [你的docker文件夹根目录]/wwwroot
mysql和dockerfile就手动创建
mkdir -p [你的docker文件夹根目录]/dockerfiles
mkdir -p [你的docker文件夹根目录]/mysql/conf.d
mkdir -p [你的docker文件夹根目录]/mysql/mysql
运行权限方面,我强烈不推荐使用root权限去运行任何公开服务。所以计划使用www用户来运行nginx和php-fpm。mysql用户运行mysql。
如果是新机器,我们需要手动在host主机里创建www和mysql用户。推荐创建的同时指定一个uid。具体的创建命令每个发行版都可能不太一样,请google。
如果是已经有www mysql用户的老机器,我们可以通过查看/etc/passwd来查询www用户的uid。稍后会用到。
配置目录的权限
chown -R www:www [你的docker文件夹根目录]/nginx
chown -R www:www [你的docker文件夹根目录]/php
chown -R www:www [你的docker文件夹根目录]/www
chown -R www:www [你的docker文件夹根目录]/wwwroot
chown -R mysql:mysql [你的docker文件夹根目录]/mysql
nginx
nginx的配置文件默认情况下是在/usr/local/nginx/conf 里面。理论上只要把这个文件映射到docker nginx的配置文件目录里就可以了。但是实际上,lnmp.org提供的配置文件模板里有些绝对路径也需要根据实际情况去修改。
首先理清下原设置应该被映射到docker容器里面的哪个路径
原设置
config: /usr/local/nginx/
ssl: /some_ssl_folder
web: /home/www or /home/wwwroot
logs: /usr/local/nginx/logs(原本是用来存放pid文件的的,可忽略) and /home/wwwlogs
而他们在官方nginx docker容器里的默认对应位置是
config: /etc/nginx
ssl: N/A (我们自己创建)
web: 我们自己创建
logs: 我们自己创建
于是只有config需要映射到/etc/nginx里面去,其他文件夹都是可以很灵活地放到其他地方。
于是我们在docker compose里可以创建以下卷映射:
- ./nginx/wwwlogs:/home/wwwlogs
- ./nginx/logs:/usr/local/nginx/logs
- ./www:/home/www
- ./wwwroot:/home/wwwroot
- ./nginx/conf:/etc/nginx
- ./ssl:/ssl
同时,我们可以把已经安装了lnmp2.0的机器上的/usr/local/nginx/conf文件夹全部复制到docker文件夹里面的nginx/conf里面去了。
同时,原本的www用户也要加上。由于容器内的用户名和主机并不互通。为确保一致我们需要用uid。
接下来就是修改nginx的配置文件了。我们只需要修改一些用绝对路径的选项。
nginx.conf:
修改以下地方
//使用/tmp作为pid文件存放位置。
pid /tmp/nginx.pid;
http {
//在docker container里,tmp文件夹是才是所有用户可写的
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp_path;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
.....
}
enable-php.conf 和 enable-php-pathinfo.conf也需要修改成使用端口来访问php-fpm服务。
这里我们留到php部分再修改。
以上为nginx.conf需要修改的地方。
为了让容器运行在www用户下。我的方法为在docker compose文件里明文指定www用户的uid和gid。比如在nginx service下面加上这一行:
user: "1001:1001" #www用户的uid和gid
同时,我还想给nginx加上lua支持,需要添加插件。这里有点坑,由于alpine apk能安装的nginx-lua插件不一定是最新版本的。在写这个文档时,nginx docker官方最新版本是1.25.2,apk上最新的版本是1.24.0,nginx运行时会因错误版本而报错。比较稳妥的方法就是让官方image手动编译我们要的模块。参考:docker-nginx/modules at master · nginxinc/docker-nginx (github.com)
创建dockerfiles/nginx.Dockerfile
FROM nginx:alpine
ARG ENABLED_MODULES="ndk lua"
这里使用alpine版容器因为感觉会省点内存?
(可选)如果要启动lua支持,还需要在nginx.conf里面加上
pcre_jit on;
到这里,nginx配置部分就大致准备完毕。接下来再把wwwroot里的默认网页复制到docker文件夹里并创建以下docker-compose.yml文件看看能不能工作。
services:
nginx:
build:
dockerfile: dockerfiles/nginx.Dockerfile
container_name: nginx
user: "1001:1001" #your www user uid:gid
volumes:
- ./nginx/wwwlogs:/home/wwwlogs
- ./nginx/logs:/usr/local/nginx/logs
- ./www:/home/www
- ./wwwroot:/home/wwwroot
- ./nginx/conf:/etc/nginx
- ./ssl:/ssl
ports:
- 80:80
- 443:443
restart: unless-stopped
保存完docker compose文件后使用以下命令启动nginx容器
docker compose up nginx -d
# 查看 nginx 的输出
docker compose logs nginx
如果所有东西都配置完毕,访问host主机,我们应该能看见熟悉的lnmp模板网站了。
但是现在还没有结束,探针一打开就报502错误。我们还需要配置php部分。
PHP
根据lnmp.org官方文档和php-fpm docker的文档,我们需要映射以下文件进到我们的php-fpm容器。
#ro是只读的意思
- ./php/etc/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
- ./php/etc/php.ini:/usr/local/etc/php/php.ini:ro
- ./php/var:/usr/local/php/var
#我们的网站目录也许要能够访问到
- ./www:/home/www
- ./wwwroot:/home/wwwroot
同时,php-fpm的配置文件也需要根据需求进行修改
Global块的修改:
[global]
;修改pid文件的位置到容器内可写的临时目录里
pid=/tmp/php-fpm.pid
;默认情况下php-fpm是后台运行的。为了在docker里运行,我们得让它跑在前台。
daemonize = no
www块的修改
原本php-fpm.conf的内容
[www]
listen = /tmp/php-cgi.sock
listen.backlog = -1
listen.allowed_clients = 127.0.0.1
listen.owner = www
listen.group = www
listen.mode = 0666
user = www
group = www
需要改成监听端口模式
[www]
;只留下这两行
;监听9000端口
listen = 0.0.0.0:9000
listen.backlog = -1
因为在docker里面,容器之间的交流推荐使用网络来解决。于是最好将php-fpm配置成端口监听模式。
到这里,php-fpm配置文件就修改完了。
nginx这边的php配置模板也需要更新。因为在容器里,我们需要拿端口来和php-fpm连接。
修改./nginx/conf/enable-php.conf 和 ./nginx/conf/enable-php-pathinfo.conf
# fastcgi_pass unix:/tmp/php-cgi.sock;
#php-fpm容器host名和容器名一样
fastcgi_pass php-fpm:9000;
但是目前还有一个问题,实际跑网站时我们需要很多额外的扩展(比如mysqli,gd,ioncube等等)。
可以通过dockefile里额外运行https://github.com/mlocati/docker-php-extension-installer这个项目来帮我们安装。
创建dockerfiles/php-fpm.Dockerfile
#这里使用php7.4 alpine版本,可以自己指定版本或者直接用最新版本
FROM php:7.4.32-fpm-alpine
#下载docker-php-extension-installer
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
#运行并安装gd xdebug mysqli。也可以加上其他你想要的模块名。
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions gd xdebug mysqli
接下来在 compose file nginx容器的后面加上php的部分
php-fpm:
build:
dockerfile: dockerfiles/php-fpm.Dockerfile
user: "1001:1001" #你的www uid和gid
container_name: php-fpm
volumes:
- ./php/etc/php-fpm.conf:/usr/local/etc/php-fpm.conf:ro
- ./php/etc/php.ini:/usr/local/etc/php/php.ini:ro
- ./php/var:/usr/local/php/var
- ./www:/home/www
- ./wwwroot:/home/wwwroot
restart: unless-stopped
然后运行php-fpm容器
docker compose php-fpm up -d
# 查看php-fpm的输出,如果正常运行的话应该什么都不输出
docker compose logs php-fpm
不出意外,我们就可以访问探针了
MYSQL
最后,我们来迁移mysql
由于一些原因,lnmp2.0创建的mysql数据库无法直接在docker版上运行。由于mysql可以很方便地通过mysqldump来导出整个数据库。所以采用了先导出整个数据库到sql文件,稍后再使用phpmyadmin之类的程序导入回去。
接下来准备mysql的docker compose部分。这里我使用了mysql容器官方的配置模板。
mysql:
container_name: mysql
image: mysql:8
# NOTE: use of "mysql_native_password" is not recommended: https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html#upgrade-caching-sha2-pas> # (this is just an example, not intended to be a production configuration)
# 有一些旧的程序还是只支持native password的方式
command: --default-authentication-plugin=mysql_native_password
# mysql用户的user id
user: "1002:1002"
environment:
#mysql新数据库的root默认密码,请改成你自己想要的密码。
#创建完数据库后请删掉这两段
MYSQL_ROOT_PASSWORD: CHANGE_TO_YOUR_PASSWORD
#让root用户可以被除了127.0.0.1以外的ip访问
MYSQL_ROOT_HOST: '%'
volumes:
#额外.cnf配置文件目录
- ./mysql/conf.d:/etc/mysql/conf.d
#mysql主要的数据库数据目录
- ./mysql/mysql:/var/lib/mysql
restart: unless-stopped
如果路径权限配置正确的话,mysql容器是能直接跑起来的。
其中,./mysql/conf.d是用来存放mysql额外配置文件的。(可选)这里可以创建一个网上复制来的低内存占用参数
low-mem.cnf
[mysqld]
#### These optimize the memory use of MySQL
#### http://www.tocker.ca/2014/03/10/configuring-mysql-to-use-minimal-memory.html
innodb_buffer_pool_size=5M
innodb_log_buffer_size=256K
#query_cache_size=0 #it has been removed in 8.0.3
max_connections=10
key_buffer_size=8
thread_cache_size=0
host_cache_size=0
innodb_ft_cache_size=1600000
innodb_ft_total_cache_size=32000000
# per thread or per operation settings
#thread_stack=131072
sort_buffer_size=32K
read_buffer_size=8200
read_rnd_buffer_size=8200
max_heap_table_size=16K
tmp_table_size=1K
bulk_insert_buffer_size=0
join_buffer_size=128
net_buffer_length=1K
innodb_sort_buffer_size=64K
#settings that relate to the binary log (if enabled)
binlog_cache_size=4K
binlog_stmt_cache_size=4K
#### from https://mariadb.com/de/node/579
performance_schema = off
为了让phpmyadmin能够连接到我们的数据库,我们还需要修改一些phpmyadmin的参数。
找到./wwwroot/default/phpmyadmin/config.inc.php
找到
$cfg[Servers][$i][host] = localhost;
改成
$cfg[Servers][$i][host] = mysql;
最后,运行mysql容器
docker compose up mysql -d
# 查看mysql的输出
docker compose logs mysql
至此,我们应该可以得到一个的使用docker搭建的最小lnmp2.0环境了。
如果要关闭服务的话可以使用
# 关闭 nginx
docker compose down nginx
# 关闭 php-fpm
docker compose down php-fpm
# 关闭 mysql
docker compose down mysql
SSL
SSL证书获取部分,由于SSL证书获取的方法多种多样,所以略过了。如果想让容器也能读取到SSL证书,本文使用的例子是把SSL文件放到主机里的”./ssl”文件夹里去。
并在nginx的配置文件里指定被映射的”/ssl”文件夹为ssl保存目录。
你也可以自由地映射自己的ssl目录到nignx容器的任何地方。只需要确定nginx配置文件里的位置是正确的就可以了。