Contents
Preface
我为啥会写这篇文章?大家知道很多应用部署都需要LNMP环境,这个环境的搭建说简单也不算难,说难也不算简单,况且现在还有各种linux面板能贴心的提供一键一站式安装,我最开始的时候也是习惯使用宝塔linux面板,后来也尝试过1Panel面板,其实都不错。
后来,主要我经常折腾,各种数据倒腾来倒腾去,涉及到数据迁移的时候,docker的优势就发挥出来了,比如我忽然想利旧吃灰的第一代m1版macmini,就想把应用都容器化部署上去,这个时候我原本都是在宝塔面板里源码部署的应用要迁移就很麻烦了。当然还有一种方式是直接docker方式安装宝塔面板,,不过我个人并不喜欢这种方式,我觉得要么就裸机部署(linux面板本来就是为了裸机而生),要么就全容器化,所以才有了这篇文章。
面板的一个好处是多站点、多PHP版本、多数据库的灵活支持,如下图:
数据库切换好说,就是配置文件里改个指向就行,但是php版本的切换这种方式的确是非常的方便。而如果我要用容器化环境来替代,理想状态下应该达到以下目标:
1、复用
能够像面板这种方式一样,新建一个站点只需要添加对应的目录(以及一个站点对应的conf配置文件)。
2、灵活切换
如果要更换php版本,也要简单方便(几秒钟)。
所以总结下来说,就是只需要建立一个nginx容器和一个php容器(这里的一个指的是一个版本只需要一个,比如下面要用到的php7.4),就能多站点同时使用使用。
注:这里简单提一句我的理念,我用docker基本不用docker compose,因为我的理念就是复用,我不会每次都拉一堆容器起来,比如我有一个mariadb的容器,那我所有的应用要用数据库并且能够用mariadb的,我都会用这个,这样备份也方便。当然这只是针对我的环境和习惯而言,如果是其他环境并且有应用隔离的需求的另当别论。
部署nginx和php7.4 fpm容器
终于步入正题了,接下来进行实操,依次创建bridge及建立一个nginx容器和一个PHP7.4 fpm的容器:
Creating a bridge
Because a bridge named public-net is used below, it needs to be created in advance:
docker network create public-net
部署nginx容器:
docker run --name=nginx -d --restart=always --net=public-net \
-p 9000:80 \
-v /docker/nginx/html:/usr/share/nginx/html \
-v /docker/nginx/conf/conf.d:/etc/nginx/conf.d \
-v /docker/nginx/log:/var/log/nginx \
nginx:latest
注:nginx有默认文件,有默认文件的都不能直接-v挂载空文件夹,需要先不用-v参数创建一个容器,用命令将文件拷贝出来删除,然后重新用-v挂载卷来创建,拷贝文件类似如下:
docker cp nginx:/usr/share/nginx/html /docker/nginx/
docker cp nginx:/etc/nginx/conf.d /docker/nginx/conf/
部署php7.4 fpm的容器:
docker run --name=php_74 -d --restart=always --net=public-net \
--privileged=true \
-v /docker/nginx/html:/var/www \
php:7.4-fpm
单nginx容器、单php7.4 fpm之多站点共用
这样就基本大功告成了。但是如何实现我们上面提到的复用和灵活切换?
关键点:
1、目录结构:
其实目录结构很简单,例如:
nginx容器里站点根文件夹(/usr/share/nginx/html)在宿主机的挂载目录为/docker/nginx/html,则php容器内的根文件夹(/var/www)在宿主机的挂载目录也必须是这个,这样的好处在于nginx和php容器的文件路径统一 ,并且未来nginx创建新的站点时,只需要在这个挂载的目录(/docker/nginx/html)里新建站点文件夹即可,实现了我们前面说的复用。
2、default.conf不用动,每次新建站点只需要新建一个对应的.conf文件,这里我们用wow.conf来示例,这是我们在另一篇文章里要讲到的搭建魔兽世界注册网站的时候要用到:
在/docker/nginx/conf/conf.d文件夹里新建wow.conf文件,其配置脚本里关于fastcgi如下:
location ~/ .php {
root /var/www;
fastcgi_pass php7_4:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME document_root$fastcgi_script_name;;
include fastcgi_params;
}
灵活切换的关键点就在这部分,为什么我们php容器镜像用的是php:7.4-fpm,fpm就是:FastCGI Process Manager,就是说这个镜像的php是支持fastcgi的。因为nginx无法理解php语言,所以在需要解读的时候,就会通过fastcgi的方式,根据fastcgi_pass参数提供的网络地址和端口(php7_4:9000)去找php容器解读(为啥php7_4能够表示php容器的网络地址?只要前面创建的时候nginx和php容器都加了–net=public-net参数就行,具体原因不展开讲了,又是一大篇幅),而fastcgi_index指定了需要解读的文件名index.php。那么这个文件位置在哪里呢?就由fastcgi_param指定的变量document_root来确定,而 document_root变量的内容就是上面的root /var/www来确定(这个的默认值是root html,如果是正常裸机通过面板安装的一站式方案的话,这么写没毛病,但是现在是容器化部署,就不合时宜了)。其实如果是多站点php部署,这个conf本来就是特定站点的,这里完全可以不用$ document_root这种方式,而是直接使用根路径名字,比如/var/www/wow,前面root那行都可以不要了,如下:
location ~/ .php {
fastcgi_pass php7_4:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/wow/fastcgi_script_name;;
include fastcgi_params;
}
大功告成!
总结下关键点:nginx在指定的地址和端口找到php,然后让php在指定的路径去寻找index.php解读。为什么php能去指定的路径找到index.php?这个站点文件夹不是在nginx容器上?啊,因为php也挂载了nginx的那个目录,所以也能找到对应的站点文件夹了。
另:php对应版本的容器里推荐把各种扩展都安装好,这个我在另一篇文章里讲。