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