部署 Laravel 到 1Panel 面板,并集成 Git 自动部署

近期要给客户转移 Laravel 网站,打算使用 1Panel 面板部署,要求 git push 之后自动化部署。

1. 创建网站

1Panel 面板搭建在 Docker 之上,每一项服务对应单独的容器,比如 Laravel 网站需要 Web 服务、PHP FPM 服务 和 MySQL 服务,对应 3 个 Docker 容器。

在创建网站之前,先创建一个 PHP 运行环境,其实就是构建一个 Docker 镜像,构建时可选用合适的扩展,比如 pod_mysql 和 opcache。然后用该环境创建网站。

2. 设置 1000:1000 用户

服务器上建议用非 root 用户管理。如果还没有普通用户,请添加一个。系统上第一个新用户的 uid/gid 都是 1000:1000。

之前通过 1Panel 创建的 PHP 运行环境,构建时设置了 1000:1000 来执行 php-fpm,这是相关的 Dockerfile 片段:

# 1Panel 用于构建 PHP 运行环境的 Dockerfile
# /opt/1panel/resource/apps/remote/php8/8.2.10/build/php/Dockerfile

# php image's www-data user uid & gid are 82, change them to 1000 (primary user)
RUN apk --no-cache add shadow && usermod -u 1000 www-data && groupmod -g 1000 www-data

我们将使用这个普通用户来操作,这样在容器内外统一使用 1000:1000 来管理和运行网站,可以避免权限上的麻烦。注意用户名是什么不重要,只要 uid/gid 一致即可。

别忘了给这个用户添加 sudo 支持,以便执行一些需要 root 权限的操作,比如 Docker 相关的操作。

3. 初始化网站

首先,用 git clone 导入网站代码:

  • git clone 需要提前生成并添加部署密钥,就是拥有 repo 的只读权限的 ssh key
  • git clone 到当前目录,需要提前清空当前目录
$ git clone git@codeup.aliyun.com:example/laravel-1panel.git .

然后,安装 Composer 依赖,1Panel 的 PHP FPM 容器中已经包含了 Composer,我们可以直接使用。但在此之前,先设置几个变量简化后续操作:

  • 请替换 DOMAIN 为你的网站域名
  • 请替换 CONTAINER 为你的 PHP FPM 容器名,这是 1Panel 随机生成的,用 docker ps 查看
  • PHP FPM 容器内部默认 root 用户,用 -u/--user 切换到 1000:1000 用户,不然 vendor 等等都是 root 的了
DOMAIN="laravel-1panel.domain-4-testing.com"
CONTAINER="1Panel-php8-mQgd"
CONTAINER_WORKDIR="/www/sites/${DOMAIN}/index"

DOCKER="sudo docker exec -it -w $CONTAINER_WORKDIR -u 1000:1000 $CONTAINER"
COMPOSER="$DOCKER composer"
ARTISAN="$DOCKER php artisan"

此时再安装 Composer 依赖,就可以直接使用 $COMPOSER 了:

$ $COMPOSER install --no-dev --optimize-autoloader

$ARTISAN 来执行 Artisan 命令,比如:

$ $ARTISAN key:generate
$ $ARTISAN migrate

由于系统默认没有 node/npm 环境,可以使用 Docker 来构建前端资源:

$ sudo docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app -u 1000:1000 node bash -c "npm ci && npm run build"

此时网站就能访问了。别忘记调整运行目录和伪静态规则,这跟 BT 宝塔面板的设置是一样的。

4. 运行队列

1Panel 设置队列

1Panel 提供了 Supervisor 集成,这和 BT 面板是一样的。我们直接在 PHP FPM 容器来跑队列,这破坏了一个容器一个服务的原则,但这是最简单有效的方法:

  • 请注意替换容器内工作路径、容器名称
  • Supervisor 支持设置「进程数量」来控制队列的并发数,比如 3 个进程,就能同时处理 3 个任务
  • 同样用 1000:1000 用户来执行,而不是默认的 root 用户,保持权限一致
docker exec -w /www/sites/laravel-1panel.domain-4-testing.com/index -u 1000:1000 1Panel-php8-mQgd php artisan queue:work

5. 运行计划任务

1Panel 设置计划任务

1Panel 也提供了 Cron 计划任务,和队列一样,我们也复用 PHP FPM 容器:

  • 请注意替换容器内工作路径、容器名称
  • 设置每分钟执行一次
  • 同样用 1000:1000 用户来执行,而不是默认的 root 用户,保持权限一致
docker exec -w /www/sites/laravel-1panel.domain-4-testing.com/index -u 1000:1000 1Panel-php8-mQgd php artisan schedule:run

6. 设置 Git 自动部署

设置流水线自动部署

我们的项目使用 Git 管理,托管在阿里云 Codeup 上,用配套的流水线实现 git push 后自动部署,流水线部署脚本:

  • 注意替换域名和容器名称
  • 依旧用 1000:1000 用户 git pull 拉取代码,保持权限一致
# NOTE 注意替换域名和容器名称
DOMAIN="laravel-1panel.domain-4-testing.com"
CONTAINER="1Panel-php8-mQgd"

HOST_WORKDIR="/opt/1panel/apps/openresty/openresty/www/sites/${DOMAIN}/index"

set -x # 打印执行的命令
set -e # 出错后立即退出
cd $HOST_WORKDIR
su -l "$(id -nu 1000)" -c "cd $PWD && git pull"
DOMAIN=$DOMAIN CONTAINER=$CONTAINER bash ./deploy.sh

部署需要执行的步骤较多,整理到单独的脚本中,放到项目中,方便复用:

  • 部署前后开启/关闭维护模式
  • 给 php-fpm 发送 USER2 信号,重载 php-fpm,之所以要重载 php-fpm 是跟 Forge 学的,我不清楚原因,但起码没有副作用
  • 由于队列常驻内存,代码更新之后,需要用 $ARTISAN queue:restart 重启队列,不然队列还在跑旧代码
  • 计划任务不用管,因为每分钟都会从头执行一次,自然会跑新代码
#!/usr/bin/env bash

set -e # 出错后立即退出

if [ -z "$DOMAIN" ] || [ -z "$CONTAINER" ]; then
  echo "DOMAIN or CONTAINER is not set, exiting"
  exit 1
fi

HOST_WORKDIR="/opt/1panel/apps/openresty/openresty/www/sites/${DOMAIN}/index"
CONTAINER_WORKDIR="/www/sites/${DOMAIN}/index"

DOCKER="docker exec -w $CONTAINER_WORKDIR -u 1000:1000 $CONTAINER"
COMPOSER="$DOCKER composer"
ARTISAN="$DOCKER php artisan"

cd "$HOST_WORKDIR"

# 开启维护模式
$ARTISAN down

# 安装 Composer 依赖
$COMPOSER install --no-dev --optimize-autoloader

# 迁移数据库
$ARTISAN migrate --force

# 构建前端
docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app -u 1000:1000 node:latest sh -c "npm ci && npm run build"

# Reload php-fpm, 来源 https://github.com/docker-library/php/issues/399
# 注意容器内 php-fpm 父进程的 PID 为 1,是属于 root 的,所以需要 root 权限,不能用 -u 1000:1000
docker exec -w "$CONTAINER_WORKDIR" "$CONTAINER" kill -USR2 1

# 重启队列
$ARTISAN queue:restart

# 关闭维护模式
$ARTISAN up
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 11

试用了一下 1Panel 为什么没有 nginx ?

4个月前 评论
xuchunyang (楼主) 4个月前
dusty (作者) 4个月前
xuchunyang (楼主) 4个月前
xuchunyang (楼主) 4个月前

我上次测试发现:1Panel 的进程守护对 Laravel 兼容性太友好(无法重启进程、停止进程等操作),就目前而言不推荐生存环境。并且我还发现他的计划任务调度资源占用比较高(不排除我测试环境问题)

4个月前 评论
hongfs 4个月前
东子 (作者) 4个月前
xuchunyang (楼主) 4个月前
xuchunyang (楼主) 4个月前
xuchunyang (楼主) 4个月前

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