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
本帖由系统于 3年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 26

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

4年前 评论
tsin (楼主) 4年前
chuyang 1年前

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

4年前 评论

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

4年前 评论

如何下载composer啊?

3年前 评论

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

3年前 评论

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

3年前 评论

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

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

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

3年前 评论

更新:自动更新证书的问题好久没有解决,现在解决方法如下:
云主机终端运行: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的文档作相应修改)

3年前 评论

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

file

file

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

【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,问题解决。

2年前 评论
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安装失败

1年前 评论

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

10个月前 评论
tsin (楼主) 10个月前
manbofish (作者) 10个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!