在 Docker 中使用 Laravel schedule

Patrick95發表於2017-12-07

在 Docker 中部署 Laravel 應用,難免會用到 Laravel 本身的任務排程系統(Schedule),而 Schedule 需要用到 cron,在 Docker 中使用 cron,有以下三種方案:

  • 使用獨立的 cron 容器。❎ 如果有多個不同的容器應用需要依賴 cron 時,那麼這是一種很完美的解決方案,但是我們只用到 Laravel 的任務排程,因此獨立的 cron 容器顯得多餘,所以不能採用此方案。

  • 直接使用宿主機的 cron。❎ 如果直接使用宿主機的 cron,那麼「定時任務」這個功能就不屬於容器的一部分了。以後在應用遷移與快速部署時,cron 需要單獨配置,這違背了我們使用 Docker 的初衷,我們的初衷是希望用docker-compose up這一個命令就能快速構建出一個線上應用。並且,在宿主機上使用 cron,使用 Laravel schedule 任務排程時不能和 MySQL 或 Redis 進行通訊,所以這個方案需要被否決。

  • 在 PHP 容器中設定 cron。✅ 這才是最佳解決方案。Laravel 只需要在 cron 中配置一個Laravel Task Scheduler,使用此容器中的 PHP 執行檔案可以直接執行 Laravel 的任務排程。

在 PHP 容器中使用 cron

我們直接使用基於 Ubuntu 的 php:7.1.11-fpm的 Docker Hub 的官方映象,然後需要在Dockerfile檔案中安裝 cron.

RUN apt-get install cron -y

然後在Dockerfile檔案所在目錄下建立crontab檔案,內容為:

* * * * * /usr/local/bin/php /your_laravel_app_path/artisan schedule:run >> /dev/null 2>&1

其中your_laravel_app_path為 Laravel 應用在容器中的實際路徑。

其實上面這一步我是踩了坑的,之前我一直按照官方手冊的以下寫法來配置的:

* * * * * php /your_laravel_app_path/artisan schedule:run >> /dev/null 2>&1

打了日誌之後我才發現,容器內無法直接找到 PHP 的執行檔案,所以要寫全 PHP 執行檔案的完整路徑,例如/usr/local/bin/php。當然,PHP 執行檔案的路徑可能會有所不同,如果不清楚,需要到 PHP 容器中檢視一下。

然後繼續寫Dockerfile,將crontab檔案對映到容器目錄中,賦予其讀寫許可權:

COPY ./crontab /var/spool/cron/crontabs/root
RUN chmod 0644 /var/spool/cron/crontabs/root
RUN crontab /var/spool/cron/crontabs/root

至於第三行的命令,這麼做是為了使 crontab 配置生效,我參考了 這篇文章,具體原因沒有深究。

注意:如果容器檔案系統是 Debian,cron 的配置路徑是有所不同的,本文不贅述,這裡只討論 Ubuntu 的映象

執行 cron

一開始我直接在Dockerfile中寫了CMD ["cron"],發現 PHP-FPM 服務不啟動了,是因為Dockerfile中只會執行一次 CMD 命令,多條 CMD 只執行最後一條,CMD ["cron"]CMD ["php-fpm"]覆蓋了。

因此我們需要一個 bash 指令碼來啟動 cron,在Dockerfile檔案所在目錄下建立entrypoint.sh,檔案內容為:

#!/bin/bash

set -e

cron

exec "$@"

然後使用ENTRYPOINT命令新增到Dockerfile就好。
完整的Dockerfile檔案應該是這個樣子:

######
# See: https://hub.docker.com/_/php/
######

FROM php:7.1.11-fpm

RUN apt-get update && apt-get install -y cron
RUN rm -rf /var/lib/apt/lists

COPY ./crontab /var/spool/cron/crontabs/root
RUN chmod 0644 /var/spool/cron/crontabs/root
RUN crontab /var/spool/cron/crontabs/root

COPY ./entrypoint.sh /usr/local/bin/
ENTRYPOINT ["entrypoint.sh"]

CMD ["php-fpm"]

重點結論

  1. 要在容器中使用 cron,需要在Dockerfile中安裝 cron,並將 crontab 配置資訊對映到容器內。
  2. crontab 配置 Laravel-scheduler 時,要填寫 PHP 的執行檔案路徑,不然可能無法正確執行。
  3. 要寫單獨的指令碼啟動 cron,否則會覆蓋掉 PHP 容器的 PHP-FPM 服務。
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章