在第一篇文章《培训-纯前端项目的Docker镜像打包》中,提到后端项目的镜像打包方面比较复杂,需要独立一篇。我们的目标仍然是执行一条命令就要能完成应用的启动。纯前端项目使用的是docker run
,后端项目需要用到镜像编排(docker-comopse)
,启动命令变成:
docker-compose up -d --renew-anon-volumes
docker-compose
使用最新版本,否则--renew-anon-volumes
参数可能不支持
在谈论后端的镜像打包和镜像测试之前,首先要认识到Docker
要用得好,需要满足两大特征:
无状态是指容器不会保存任何东西,不论往容器里面写入任何东西,再次执行docker-compose up -d
,启动后的容器是找不到原来写入的东西的。有些场景如果需要保存数据,比如数据库、队列和存储,它们需要通过挂载保证写入的数据不因为重启而丢失,这类容器称为有状态容器。
要保证应用的水平扩展和快速迁移,所有的应用在启动时必须是无状态的。试想一下,你的应用原本在A机器上执行一句docker-comopse up -d
就行了,现在要改到B机器上运行,就在B机器上执行一句docker-compose up -d
。如果它是有状态的(比如A在启动应用的时候,将存储挂载到主机,以便用户上传的图片和应用产生的日志可以永久保留),那么迁移到B机器时,它的日志和图片仍然在A机器上,快速迁移受到了限制。
另一个更常见的场景是,A,B两台机器同时启动以便负载均衡,如果A和B是有状态的,就会出现跟迁移一样的问题,A和B都会遗漏用户上传的图片,这就限制了水平扩展。因此,应用必须是无状态的。
单一职责简单说就是只干一件事,反映到Docker上,就是最好只启动一个进程(取决于对职责的解释,也可能启动多个进程)。比如php-fpm
镜像只能启动php-fpm
,nginx
镜像只启动nginx
。要做到将nginx
,php-fpm
都在同一个里容器里启动当然是可以的,但是它带来的代价往往比比好处更大。越多不同职责的功能塞到同一个容器里,代价就越大。以Laravel
工程为例,有4个不同职责的进程:nginx
,php-fpm
,php队列
,cron定时任务
。混在一起以后,第一个问题是:任何一块有更新,都要重新打包这个镜像,而通常这个镜像里面的内容比较多,通常会很大;第二个问题是职责的混合导致它很难与已有的系统结合,典型的是我们只需要php-fpm
,nginx
由外部提供,如果打包到一起,nginx
会产生干扰;另一个典型的场景是暂时不需要用到php队列
和cron定时任务
,它们也必须开着;第三个则是日志信息的混乱,nginx
和php-fpm
的日志混在一起无法有效地提取。
后端应用要满足这2个特征,就带来2个直接的问题:
所有后端应用的配置都是比较敏感,比如数据库密码,是不能打包到镜像中的,于是就有了第3个问题:
后端镜像的打包,重点在于思考清楚这3个问题。下面我们一个个问题的分析。
Q:后端要访问的数据库、队列和存储放哪里?
A:这三个需要与应用分开单独考虑,在生产环境下,一般使用一台独立的机器,或者直接使用云服务商提供的现成服务。比如数据库使用mysql或mariadb,队列使用redis或rabbitmq,存储使用对象存储(aws的s3,阿里的oss,或者自己搭建的minio)。不论应用迁移到哪里,都要使用同一份数据库、队列和存储。在演示的时候,我们将在本地搭建mariadb, redis, minio。
Q:nginx、php-fpm、php队列、php定时任务怎么编排?
A:单一职责
在实践上是有争议的。如果是要求容器只启动一个进程的角度,将nginx
、laravel php-fpm
和laravel队列
和laravel定时任务
独立成4个任务,如果要检查nginx
证书是否过期,还得有certbot
,共5个任务。一个简单的后台应用要有5个任务,听起来头就很大。
另外一种对单一职责
的解释则是从就应用的范围去解释。nginx
和php-fpm
管的东西八竿子打不着,所以肯定是独立的两个镜像容器。但是上面的nginx
和certbot
要合在一起,而laravel php-fpm
和laravel 队列
和laravel 定时任务
也要合在一起,镜像编排时只启动2个容器。
这两种解释目前我们都接受,应用小的时候采用后一种方式,应用变大时就过度到第一种方式。
Q:敏感信息怎么保护?
A:通过环境变量的方式注入,。不论是docker-compose
,还是云容器,都有提供配置环境的方式。
如果说前面的问题主要由Docker
的特征导致,那么下面的这几个问题,则是由Laravel
应用本身引起(其他语言的后台应用也有类似的问题),它们也使镜像打包变得复杂:
这些问题在这里抛出由读者思考,以后有机会我们专门讲讲,这里的镜像打包有对其中的一些问题做了处理。
脚本文件比较多,要么将后端与脚本独立成2个项目,要么调整项目的总体目录结构
backend/
- Dockerfile
- scripts/
- start.sh
- crontab
- worker.conf
- build.sh
- push.sh
- ...其他Laravel文件直接忽略
scripts/
- nginx/ # nginx 镜像制作
- Dockerfile
- app.conf
- nginx.conf
- build.sh
- push.sh
- prod/ # 生产环境的镜像编排测试
- docker-compose.yml
- update.sh
- app-backend.env
- prod-local/ # 本地镜像编排测试
- docker-compose.yml
- update.sh
- app-backend.env
先直接看Dockerfile
,然后对这个文件做详细解释:
FROM pheye/php-fpm:latest
MAINTAINER LIUWENCAN <phenye@gmail.com>
# 将源码拷到镜像中
COPY . /var/www/backend
# 确保没有将.env打包进去
RUN rm .env
# 启动脚本,除了php-fpm还有一些额外的配置
COPY scripts/start.sh /start.sh
RUN chmod +x /start.sh
# 用于任务调度的任务
COPY scripts/crontab /etc/cron.d/www
# 用于支持worker的启动
ADD ./scripts/worker.conf /etc/supervisor/conf.d/worker.conf
# 修改属主,确保与php-fpm的用户一致
RUN chown -R www /var/www/backend
VOLUME /var/www/backend
CMD ["/start.sh"]
php-fpm
的基础镜像,官方有提供,不过php-fpm
的许多扩展需要自己配置,很容易出现遗漏,性能调优也需要自己配置,cron
和supervisord
等用于支持任务调度和队列的包也需要自己安装,整个配置过程是非常繁琐的,因此这里我使用自己在生产环境验证过的php-fpm
作为基础镜像,有做调整时就更新该包即可。
在Dockerfile
中出现了start.sh
,worker.conf
、crontab
这3个文件,针对这3个文件做个详细解释。
start.sh
是启动脚本,正常来讲,只需要启动php-fpm
就能工作,但是默认情况没有考虑到迁移文件的需求,该脚本可以做更多必要的工作。
#!/bin/sh
# 用于启动性能采集
# nohup tideways-daemon &
# 执行migration
cd /var/www/backend
php artisan migrate --force
if [ ! -f "public/storage" ] ; then php artisan storage:link; fi
# 下面这2个被注释的命令有助于提高性能,但是可能导致应用不可用,根据需要自己启动
# php artisan optimize
# php artisan api:cache
if [ $? -eq 0 ] ; then
# 启动php-fpm
php-fpm
else
exit 1
fi
worker.conf
是supersivord
的配置文件,用于确保php队列
的可靠启动和对队列的进程数量做精确控制
[program:worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/backend/artisan queue:work --sleep=3 --tries=3 --daemon
user=www
autostart=true
autorestart=true
numprocs=2
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
crontab
用于执行任务调度
* * * * * www php /var/www/backend/artisan schedule:run > /dev/null 2>&1
docker build -t app-backend:latest .
nginx
的制作与《培训-纯前端项目的Docker镜像打包》几乎一样, 这里就不再赘述。只简单提下不同的几个小点:
Dockerfile
不需要添加任何应用的源码;app.conf
与前端不一样,完整代码见下面;app.conf
中的fastcgi_pass backend:9000;
需要特别注意,backend
是实际启动的php-fpm
的容器名。Dockerfile
FROM nginx:alpine
MAINTAINER LIUWENCAN <phenye@gmail.com>
RUN adduser -D -H -u 5000 -s /bin/sh www
RUN rm /etc/nginx/conf.d/default.conf
ADD nginx.conf /etc/nginx/
ADD backend.conf /etc/nginx/sites-available/
VOLUME /var/www
CMD ["nginx"]
区别只在于app.conf
的内容不一样:
server {
listen 80;
listen [::]:80;
server_tokens off;
server_name demo-app.store.codefriend.top;
root /var/www/backend/public;
index index.php index.html index.htm;
location ~* .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
expires 30d;
add_header Cache-Control "public";
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
internal;
try_files $uri /index.php =404;
fastcgi_pass app-backend:9000; # 这个名字需要注意
fastcgi_index index.php;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/;
log_not_found off;
}
}
docker build -t app-nginx:latest .
本地镜像编排docker-comopse.yml
version: ‘2‘
services:
backend:
image: app-backend:latest
env_file: "./app-backend.env"
cron:
image: app-backend:latest
env_file: "./app-backend.env"
command: [‘cron‘, ‘-f‘]
worker:
image: app-backend:latest
env_file: "./app-backend.env"
command: [‘/usr/bin/supervisord‘, ‘-n‘, ‘-c‘, ‘/etc/supervisor/supervisord.conf‘]
nginx:
image: app-nginx:latest
volumes_from:
- backend
depends_on:
- backend
ports:
- "8888:80"
镜像编排中,出现了app-backend.env
这个文件,这个文件就是Laravel
的.env
,这些敏感一般放在保密的对象存储上,或者通过云容器的环境配置、或者放在vault
这样的密钥管理里面。
docker-compose up -d --renew-anon-volumes
--renew-anon-volumes
这个参数用于更新匿名挂载,非常重要。因为nginx
里面本身没源码,源码是跟着php-fpm
,如果没有这个参数,当php-fpm
那边的代码更新以后,nginx
这边得不到通知。
docker-compose ps
看下各个任务是否正常启动,如果已经正常启动。直接访问http://localhost:8888
应该能够正常进入。
如果不能正常进入,结合docker-compose log
,docker-compose top
以及通过docker-compose exec
进到容器内部排查。
原文:https://www.cnblogs.com/pheye/p/12873465.html