はじめに
今回の記事では、Docker コンテナ内で cron を実行する方法について解説しています。
実行環境は PHP で、アプリケーション(FPM)だけ動作している環境で検証しています(Web サーバのコンテナは立てていない)
使用しているエディタは VS Code で、Remote Developmentを使用して Ubuntu サーバに SSH 接続してコンテナを起動しています。
開発環境
- OS: Ubuntu 20.04.6 LTS
- PHP: 8.2.8
- Docker: 24.0.2, build cb74dfc
コンテナ内で起動できるプロセス
通常、Docker コンテナ内で動作するフォアグラウンドプロセスは 1 つだけに設計されています。
また、そのコンテナ内のフォアグラウンドプロセスが終了するとコンテナ自体も停止してしまいます。
つまり、今回のようにcron
とFPM
のプロセスの 2 つをフォアグラウンドで起動することは出来ません。しかし、コンテナ起動時に以下のようなシェルスクリプトを実行することでcron
はバックグランド、FPM
はフォアグラウンドで起動する事が出来ます。
#!/bin/sh
# バックグラウンドでcronを起動
cron & # &はバックグランドで実行するという意味
# フォアグラウンドでPHP-FPMを起動
php-fpm
ただし、バックグランドで実行されているプロセスのログの出力やクラッシュした際の再起動など、ライフサイクルの扱いが難しくなり推奨されていません。
そこで今回は、プロセスの管理をすることが出来るツールであるSupervisorを使用したいと思います。
Dockerfile の内容
今回使用する Dockerfile の内容です。
基本的なツールのインストールの他にcron
とsupervisor
を記述します。
supervisor
というツールは、UNIX 系 OS 上でプロセスを監視・制御するためのツールです。複数のプロセスを管理したり、管理下のプロセスのクラッシュ時に自動再起動を実施して永続化したりすることが出きます。
FROM php:8.2.8-fpm
RUN apt-get update && \
# cron と supervisor を追加
apt-get -y install git cron supervisor libicu-dev libonig-dev libzip-dev unzip locales && \
apt-get clean && \
docker-php-ext-install intl pdo_mysql mbstring zip bcmath && \
pecl install xdebug && \
# Xdebug を有効化する
docker-php-ext-enable xdebug
COPY ./php.ini /usr/local/etc/php/php.ini
# cronの設定内容が書かれたファイルをコンテナ内にコピーする
COPY ./cron/root /etc/cron.d/cron
# supervisordの設定ファイルをコピーする
COPY ./supervisord/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# 権限の変更
RUN chmod 0644 /etc/cron.d/* && \
touch /var/log/cron.log && \
# 作業用ユーザーの追加
useradd -m -s /bin/bash user01 && \
# Xebugで使用するログファイルの作成とファイルへのアクセス権限を付与する
touch /var/log/xdebug.log && \
chown user01:user01 /var/log/xdebug.log && \
chmod 644 /var/log/xdebug.log
WORKDIR /workspaces
# supervisordを起動
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
次は各種設定ファイルを作成します。
cron の設定ファイルを作成する
スケジュールされた実行ジョブを記述した設定ファイルを作成します。
設定ファイルの内容に関しては、cron の設定ガイドを参考にして下さい。
# 設定ファイルの末尾に改行必須!
* * * * * root /usr/local/bin/php -q -f /workspace/public/index.php >> /var/log/cron.log 2>&1
# (ここに改行)
注意点として、cron の設定ファイルは行が改行で終端している必要があります。改行がない場合は指定したジョブは実行されません。
例として、以下の設定ファイルの場合index2.php
の後に改行が無いためindex2.php
のジョブは実行されません。
* * * * * root /usr/local/bin/php -q -f /index.php
* * * * * root /usr/local/bin/php -q -f /index2.php
index.php ファイルの作成
実行されるスクリプトファイルであるindex.php
も作成しておきます。
<?php
echo "Hello PHP Cron!!" . "\n";
supervisord の設定ファイルを作成する
supervisord の設定ファイルを作成します。詳細はコメントに記載されている通りです。
[supervisord]
nodaemon=true ; デーモンで起動させずにフォアグラウンドで起動させる
logfile=/tmp/supervisord.log ; (メインログファイルの場所を指定. デフォルトは $CWD/supervisord.log)
logfile_maxbytes=50MB ; (ログローテーションの起点となるサイズ. デフォルトは 50MB)
logfile_backups=10 ; (ログファイルのローテーション時に保持するバックアップの数を指定. デフォルトは 10)
loglevel=info ; (supervisordのログレベルを指定. デフォルトは info レベル; その他: debug, warn, trace)
[program:php-fpm]
command=php-fpm -F # フォアグラウンドで実行するコマンドを指定
stdout_logfile=/tmp/php-fpm.log # このプログラムでの標準出力のログの保存場所を指定
redirect_stderr=true # 標準エラー出力を行うか否か
[program:cron]
command=cron -f # フォアグラウンドで実行するコマンドを指定
stdout_logfile=/tmp/cron.log # このプログラムでの標準出力のログの保存場所を指定
redirect_stderr=true # 標準エラー出力を行うか否か
ここまで設定できたら一度コンテナを起動します。
cron が実行されているか確認する
コンテナ起動後、プロセスが正常に起動されているか確認します。
そのためにps
コマンドを含むパッケージをインストールします。このコマンドは cron やその他のプロセスが起動しているか確認したいときのみインストールして下さい。
root
ユーザーである必要があるので、devcontainer でコンテナにアタッチした際のユーザーではなくdocker exec
コマンドでコンテナにアクセスします。
# コンテナIDを表示する
docker ps
# コンテナに接続
docker exec -it [コンテナID] bash
apt-get update && apt-get install -y procps
インストール後ps aux
コマンドを使用してプロセスのリストを取得します。
どうやら正常に起動しているようです。次に、cron ファイルに設定したプログラムが正常に実行されているか 確認します。
実行結果のログは/var/log/cron.log
に出力するように設定していました。
1 分おきに php スクリプトが実行されていることが分かります。
今回作成した環境は以下のリポジトリから利用することが出来ます。