docker-compose 部署 Laravel 项目全记录

更新记录

说明

  • 使用的主机提供商是腾讯云
  • 使用部署 L05 电商教程项目作为例子
  • 域名自备,除了主域名A解析,再添加一条CNAME泛域名解析
  • 主机上已安装好docker和docker-compose
  • 以下操作,docker-compose配置文件的路径为/home/ubuntu/docker
  • 进入容器的方式,可以用docker-compose exec {service_name}(需在docker-compose.yml所在目录下操作),也可以在任意目录运行:docker exec -it {container_name} bash(nginx容器的话,bash换为sh)
  • 为了记录的连贯性,将这个过程中遇到的问题放到文章最后,遇到的时候再去查阅。

docker-compose 环境搭建

docker-compose 环境搭建参考我之前写的文章:博客:docker-compose 搭建 dnmp 总结(附踩坑指南)
运行以下命令下载 docker-compose 配置:

git clone https://github.com/HubQin/dnmp.git docker

几点说明:

  • 由于需要用到 npm 安装和编译前端资源,所以把它内置到php-fpm服务所在的容器里,安装前注意将dockerfiles/Dockerfile.php73文件中Node安装部分指令注释去掉
  • docker-compose.yml文件中,mysql部分,数据库管理员密码改为自己需要的密码
  • Redis配置文件(/home/ubuntu/docker/conf/redis/redis.conf)设置注释掉绑定ip的指令,比如注释掉:bind 127.0.0.1,另外,需要给redis设置密码,在配置文件中修改:requirepass=xxxxxx(你的redis密码)
  • php-fpm 容器还内置的 supervisor 进程监护工具
    以上修改完成,在/home/ubuntu/docker目录下运行以下命令启动各项服务:
    docker-compose up -d

用到的服务容器明细如下:

  • php-fpm 7.3
  • mysql 最新版本(当前是8.0.18)
  • redis 最新版本(当前是 5.0.7)
  • Nginx 最新版本(当前是 1.17.6)

Laravel项目安装和配置

服务启动后,会在docker-compose.yml的上一级创建一个名为project的文件夹(在本例子中的完整路径是/home/ubuntu/docker),将Laravel项目的代码放在这里。

代码下载之后,复制一份项目根目录下的.env.example文件,命名为.env并做如下修改:

APP_NAME=Larashop # <-- 应用名 APP_ENV=production # <-- 运行环境 APP_KEY=base64:oTbcE5B35aiLYtMvxdsaDzplwBYTa5DHX4IfeQ06bws= # <-- 后面将运行命令生成 APP_DEBUG=false # <-- 关闭调试(部署过程方便调试可先设为trueAPP_URL=larashop.ishare.cool # <-- 网站地址 LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=mysql # <-- mysql主机,注意是填MySQL在docker-compose中的服务名 DB_PORT=3306 DB_DATABASE=laravel-shop # <-- 数据库名称 DB_USERNAME=root DB_PASSWORD=xxxxxx # <-- 数据库密码,填写在docker-compse.yml文件中设置的密码 BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=redis # <-- 队列驱动 SESSION_DRIVER=file SESSION_LIFETIME=120 REDIS_HOST=redis # <-- redis主机,填写规则同mysql REDIS_PASSWORD=xxxxxx # <-- 填写在 redis.conf中设置的密码(requirepass) REDIS_PORT=6379 MAIL_DRIVER=smtp MAIL_HOST=smtp.qq.com MAIL_PORT=465 MAIL_USERNAME=xxxxxx@qq.com # <-- 你的邮箱 MAIL_PASSWORD=xxxxxx # <--从邮箱服务商获取的密码 MAIL_ENCRYPTION=ssl MAIL_FROM_ADDRESS=xxxxxx@qq.com # <-- 你的邮箱 MAIL_FROM_NAME=Larashop # <-- 应用名

运行docker exec -it docker_mysql_1 bash进入 mysql 所在容器,登录mysql创建一个数据库。

运行docker exec -it docker_php-fpm73_1 bash进入 php 所在的容器(容器名称可以通过docker ps命令查看), 切换到项目代码所在目录(本例子是在var/www/html/larashop文件夹下),依次做如下操作:

// 安装依赖 composer install –no-dev –prefer-dist –optimize-autoloader // 安装和编译前端资源 npm install –prod npm run prod // 生成key php artisan key:generate // 数据表迁移 php artisan migrate –force // 创建软连接 php artisan storage:link // 路由、配置、事件缓存 php artisan route:cache php artisan config:cache php artisan event:cache

Nginx 配置

docker/conf/nginx/conf.d新建一个xxx.conf文件,这里暂时先不配置 https,先配置如下:

注意的注释

server { listen 80; server_name larashop.ishare.cool; # 注意 这里写的是nginx容器中的目录 root /var/www/html/larashop/public; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri /index.php =404; # 注意这里需使用 服务名:端口 的形式 fastcgi_pass php-fpm73:9000; fastcgi_index index.php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #fixes timeouts fastcgi_read_timeout 600; include fastcgi_params; } location ~ /\.ht { deny all; } location ~ /\.(?!well-known).* { deny all; } }

配置完成之后,运行docker exec -it docker_nginx_1 sh进入Nginx容器,运行nginx -s reload重载配置。

至此,站点就应该可以访问了。

配置 Supervisor 进程监护

由于程序中使用了 laravel 中的任务队列、消息队列,需要一个进程监护工具来守护队列监听程序,这里使用 supervisor
关于Supervisor的使用,可以参考我之前写的这篇:博客:Supervisor 使用总结
在本例中,supervisor配置如下:

[program:queue] process_name=%(program_name)s_%(process_num)02d directory=/var/www/html/larashop command=php artisan queue:work --tries=3 --sleep=3 --daemon autostart=true autorestart=true numprocs=1 user=root stopasgroup=true killasgroup=true redirect_stderr=true stdout_logfile=/var/www/html/larashop/storage/logs/queue.log

配置文件位于:docker/conf/supervisor/supervisord.conf

配置完成后记得重启 supervisor。根据以上配置,有任务被队列消费,会将日志写到storage/logs/queue.log,效果如下所示:

docker-compose 部署 L05 教程记录

配置HTTPS

证书生成

这里使用Let's Encrypt,生成工具选用acme.sh。关于acme.sh的使用,通读它的wiki文档,基本就可以了解如何使用了。
既然我们使用了docker部署,能用docker的就都用上dockeracme.sh也不例外,所以我们将使用acme.shdocker镜像neilpang/acme.sh来生成证书。运行如下命令就够了:

docker run --rm -it \ -v "/home/ubuntu/docker/ssl":/acme.sh \ -e DP_Id="{your-key-id}" \ -e DP_Key="{your-key-token}" \ neilpang/acme.sh --issue --log --dns dns_dp -d ishare.cool -d *.ishare.cool 

以上命令说明:

  • 由于我使用的是腾讯云的域名,DP_Id 和 DP_Key 可以到DNSPod上创建并获取

  • -v "/home/ubuntu/docker/ssl":/acme.sh将主机上的docker/ssl目录挂在到容器的/acme.sh目录,这样证书生成之后,就会出现在docker/ssl目录

  • 这里想要配置了泛域名,所以使用-d ishare.cool -d *.ishare.cool
    生成的结果如下:

    docker-compose 部署 Laravel 项目全记录

    acme.sh的 wiki 文档说证书60天自动更新,是否能自动更新,只好等60天在验证了 :cry

    2020-5-30 更新:60天后证书真的失效了,docker pull neipang/acme.sh更新镜像,然后将上面的脚本--issue改为--renew,执行脚本更新证书。

2021-1-23 更新:自动更新证书的问题好久没有解决,现在解决方法如下:
云主机终端运行:sudo crontab -e打开定时任务文件进行编辑,写入任务命令并保存:

0 2 1 * * docker run --rm -v "/home/ubuntu/docker/ssl":/acme.sh -e DP_Id="{你的DP_id}" -e DP_Key="{你的DP_key}" neilpang/acme.sh --renew --force --log --dns dns_dp -d ishare.cool -d *.ishare.cool && bash -c "cd ~/docker && docker-compose restart nginx"

这里设置了每月1日凌晨2:00运行一次脚本。证书路径和DP_id、DP_key、域名注意替换为你自己的。

修改 Nginx 主配置文件

/home/ubuntu/docker/conf/nginx/nginx.conf做如下修改:

user nginx; worker_processes 2; # 根据主机CPU核心数类配置(lscpu查看) error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; # 主机上运行 ulimit -n 查看 } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 60; types_hash_max_size 2048; client_body_buffer_size 10K; client_header_buffer_size 1k; client_max_body_size 8m; large_client_header_buffers 2 1k; client_body_timeout 12; client_header_timeout 12; send_timeout 10; gzip on; gzip_comp_level 2; gzip_min_length 1000; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain application/x-javascript text/xml text/css application/xml; include /etc/nginx/conf.d/*.conf; } 

Nginx安全增强以及SSL配置

  • 运行以下命令生成DH文件:
    openssl dhparam -out /home/ubuntu/docker/ssl/dhparam.pem 2048
  • 新建ssl文件(/home/ubuntu/docker/ssl/options-ssl-nginx.conf)并添加以下内容:
ssl_session_cache shared:SSL:10m; ssl_session_timeout 60m; ssl_session_tickets on; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.4.4 8.8.8.8 valid=300s; resolver_timeout 10s; ssl_prefer_server_ciphers on; ssl_certificate /etc/nginx/ssl/ishare.cool/fullchain.cer; ssl_certificate_key /etc/nginx/ssl/ishare.cool/ishare.cool.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; add_header Strict-Transport-Security "max-age=16070400;includeSubDomains;preload"; add_header X-Frame-Options deny; add_header X-Content-Type-Options nosniff; add_header x-xss-protection "1; mode=block"; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https:; connect-src 'self' https:; img-src 'self' data: https: blob:; style-src 'unsafe-inline' https:; font-src data: https:"; ssl_dhparam /etc/nginx/ssl/dhparam.pem;

该配置将通过include指令包含到虚拟主机文件中。

修改虚拟主机文件

/home/ubuntu/docker/conf/nginx/conf.d/larashop.ishare.cool修改为以下内容:

# 重定向所有http请求 server { listen 80; server_name larashop.ishare.cool; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name larashop.ishare.cool; # 加载ssl配置文件 include /etc/nginx/ssl/options-ssl-nginx.conf; # 注意 这里写的是nginx容器中的目录 root /var/www/html/larashop/public; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php$is_args$args; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } location ~ \.php$ { try_files $uri /index.php =404; # 注意这里使用php-fpm服务名 fastcgi_pass php-fpm73:9000; fastcgi_index index.php; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #fixes timeouts fastcgi_read_timeout 600; include fastcgi_params; } location ~* .(jpg|jpeg|png|gif|ico|css|js)$ { expires 365d; } location ~ /\.ht { deny all; } location ~ /\.(?!well-known).* { deny all; } } 

修改完成后,重启nginx容器——在/home/ubuntu/docker目录运行:docker-compose restart nginx即可使前面所作的修改生效。

到这里,HTTPS就配置好了。关于证书生成和nginx的配置,可以参考以下文章:

可能会遇到的问题

  • /var/www/html/larashop/storage/logs/laravel.log” could not be opened: failed to open stream: Permission denied
    解决:
    stackoverflow.com/questions/234115...

    sudo chown -R $USER:www-data storage sudo chown -R $USER:www-data bootstrap/cache // 这两行可选 chmod -R 775 storage chmod -R 775 bootstrap/cache
  • yansongda/pay v2.9.0 requires ext-bcmath * -> the requested PHP extension bcmath is missing from your system
    解决:缺少bcmatch扩展,进入php容器,运行:docker-php-ext-install bcmath安装。

  • laravel连接redis提示:Connection Refuse
    解决:redis.conf 配置文件中去掉ip地址绑定,同时设置密码(requirepass字段)

  • Refused to load the font ‘data:application/font-woff2…’ because it violates the following Content Security Policy directive: “font-src https:”.
    解决:修改/home/ubuntu/docker/ssl/options-ssl-nginx.conf文件中的Content-Security-Policy配置,font-src后面加上data:以支持base64编码图片的显示。

项目地址

最后晒一下成果:larashop.ishare.cool/products

docker-compose 部署 Laravel 项目全记录

docker-compose 部署 Laravel 项目全记录

本作品采用《CC 协议》,转载必须注明作者和本文链接
Was mich nicht umbringt, macht mich stärker
本帖由系统于 5年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 26

哇 好详细的教程;页面响应飞快 :+1:

6年前 评论
tsin (楼主) 6年前
chuyang 3年前

@houmuxu docker-compose 是 docker 容器的管理工具,可以对一组容器进行管理、编排

6年前 评论

:+1:赞,感谢分享,学习一下

6年前 评论

感谢楼主的分享!好样的!

5年前 评论

老哥 你这个centos可以直接使用么?

5年前 评论

@Hello_Smile 可以,就安装包管理的工具不同,大概是 apt-get 替换为 yum

5年前 评论
Hello_Smile 5年前
Hello_Smile 5年前
tsin (作者) (楼主) 5年前

赞,我在项目中用上了,docker是真的爽 :+1:

5年前 评论

更新:自动更新证书的问题好久没有解决,现在解决方法如下:
云主机终端运行:sudo crontab -e打开定时任务文件进行编辑,写入任务命令并保存:

0 2 1 * * docker run --rm -v "/home/ubuntu/docker/ssl":/acme.sh -e DP_Id="{你的DP_id}" -e DP_Key="{你的DP_key}" neilpang/acme.sh --renew --force --log --dns dns_dp -d ishare.cool -d *.ishare.cool && bash -c "cd ~/docker && docker-compose restart nginx"

这里设置了每月1日凌晨2:00运行一次脚本。证书路径和DP_id、DP_key、域名注意替换为你自己的。
(注意这里的使用场景是配置了泛域名的,普通的域名参考acme.sh的文档作相应修改)

5年前 评论

你好 请问如何讲npm 内置到php-fpm所在的容器中呢?我是功过wget命令下载的安装包,然后配置的环境变量,您说的注释我也去掉了。但一直提示/bin/sh: node: not found file

file

file

4年前 评论
Hello_Smile 4年前
AKA-TanNaWen (作者) 4年前
tsin (楼主) 4年前

【2021-9-20 更新:acme.sh的默认证书签发机构变成了ZeroSSL】
最近一段时间,证书怎么也续签不了,错误提示如下:

Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: 1 . . . Create new order error. Le_OrderFinalize not found. 

尝试了很多方法均无效。尝试把证书删除了(注意备份一份),重新签发,出现了一个提示:

[Mon Sep 20 03:19:19 UTC 2021] acme.sh is using ZeroSSL as default CA now. [Mon Sep 20 03:19:19 UTC 2021] Please update your account with an email address first. [Mon Sep 20 03:19:19 UTC 2021] acme.sh --register-account -m my@example.com [Mon Sep 20 03:19:19 UTC 2021] See: https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA 

原来acme.sh使用的默认CA已经不是Let’s Encrypt, 而是ZeroSSL,Let’s Encrypt可能因为域名污染而无法再签发。根据提示,
执行:

sudo docker run --rm -it -v "/home/ubuntu/docker/ssl":/acme.sh neilpang/acme.sh --register-account -m {你的邮箱} 

然后,在执行:

sudo docker run --rm -it -v "/home/ubuntu/docker/ssl":/acme.sh -e DP_Id="{你的DP_id}" -e DP_Key="{你的DP_key}" neilpang/acme.sh --issue --log --dns dns_dp -d {你的域名} -d *.{你的域名} --debug

最后,重启nginx。
发现网站还是访问不了,对比原来的备份,少了两个文件:
docker/ssl/dhparam.pemdocker/ssl/options-ssl-nginx.conf,将这两个文件复制回去(ssl目录),重启nginx,问题解决。

4年前 评论
mysql uses an image, skipping redis uses an image, skipping nginx uses an image, skipping Building php-fpm73 [+] Building 23.4s (5/12) => [internal] load build definition from Dockerfile.php73 0.1s => => transferring dockerfile: 1.70kB 0.0s => [internal] load .dockerignore 0.1s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/php:7.3-fpm 1.3s => CACHED [1/9] FROM docker.io/library/php:7.3-fpm@sha256:2d68e401d2d3b9f8a6572791cd7a25062450c43ff52e5 0.0s => ERROR [2/9] RUN apt-get update -yq && apt-get install -yq apt-utils openssl libssl-dev && p 21.9s ------ > [2/9] RUN apt-get update -yq && apt-get install -yq apt-utils openssl libssl-dev && pecl channel-update pecl.php.net && apt-get install -y git && curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/bin/composer && composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/: #5 0.703 Get:1 http://security.debian.org/debian-security bullseye-security InRelease [44.1 kB] #5 0.942 Get:2 http://security.debian.org/debian-security bullseye-security/main amd64 Packages [126 kB] #5 21.85 Err:3 http://deb.debian.org/debian bullseye InRelease #5 21.85 Could not connect to deb.debian.org:80 (151.101.110.132). - connect (111: Connection refused) Could not connect to deb.debian.org:80 (151.101.74.132). - connect (111: Connection refused) #5 21.85 Err:4 http://deb.debian.org/debian bullseye-updates InRelease #5 21.85 Unable to connect to deb.debian.org:80: #5 21.86 Fetched 170 kB in 21s (7959 B/s) #5 21.86 Reading package lists... #5 21.87 W: Failed to fetch http://deb.debian.org/debian/dists/bullseye/InRelease Could not connect to deb.debian.org:80 (151.101.110.132). - connect (111: Connection refused) Could not connect to deb.debian.org:80 (151.101.74.132). - connect (111: Connection refused) #5 21.87 W: Failed to fetch http://deb.debian.org/debian/dists/bullseye-updates/InRelease Unable to connect to deb.debian.org:80: #5 21.87 W: Some index files failed to download. They have been ignored, or old ones used instead. #5 21.88 Reading package lists... #5 21.89 Building dependency tree... #5 21.89 Reading state information... #5 21.90 Package apt-utils is not available, but is referred to by another package. #5 21.90 This may mean that the package is missing, has been obsoleted, or #5 21.90 is only available from another source #5 21.90 However the following packages replace it: #5 21.90 apt #5 21.90 #5 21.90 E: Package 'apt-utils' has no installation candidate ------ executor failed running [/bin/sh -c apt-get update -yq && apt-get install -yq apt-utils openssl libssl-dev && pecl channel-update pecl.php.net && apt-get install -y git && curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/bin/composer && composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/]: exit code: 100 ERROR: Service 'php-fpm73' failed to build : Build failed

php安装失败

3年前 评论

docker-compose up -d 卡在了Building php-fpm73怎么办

2年前 评论
tsin (楼主) 2年前
manbofish (作者) 2年前