DockerコンテナでCronを使用する方法
スケジュールに従ってバックグラウンドタスクを実行することは、バックエンドサービスの標準的な要件です。セットアップは以前は簡単でした-サーバーのcrontabでタスクを定義し、それで終わりでした。Dockerを使用してデプロイする際にcronを利用する方法を見てみましょう。
サービスをコンテナ化することで、開発者の生産性が向上します。同時に、従来のシステム管理者の関心事がDockerの概念にどのように関連しているか疑問に思うかもしれません。Dockerコンテナでcronを使用する際にはいくつかの選択肢があり、その適合性の順に以下で探っていきます。続ける前に、Dockerをインストールし、アプリケーションのDockerイメージを構築していることを確認してください。
ホストのクロンタブを使用する
基本的には、Dockerエンジンを実行しているホストのcronインストールを常に利用できます。cronがインストールされていることを確認し、次にシステムのcrontabを通常通り編集してください。
使用できます
docker exec
既存のコンテナ内でコマンドを実行するには:
*/5 * * * * docker exec example_app_container /example-scheduled-task.shこれが機能するのは、事前にコンテナの名前を確信できる場合のみです。通常、タスクを実行するためだけに存在する新しいコンテナを作成する方が良いです。
*/5 * * * * docker run --rm example_app_image:latest /example-scheduled-task.sh5分ごとに、あなたのシステムのcronインストールは、あなたのアプリのイメージを使用して新しいDockerコンテナを作成します。Dockerは、そのコンテナ内で/example-scheduled-task.shスクリプトを実行します。スクリプトが終了すると、コンテナは破棄されます(--rm)。
コンテナ内でのCronの使用
ホストのcrontabを使用すると、スケジュール済みのタスクがシステム上で手動設定を必要とするため、Dockerのコンテナ化が壊れてしまいます。デプロイする各ホストにcronがインストールされていることを確認する必要があります。開発では役立つ場合がありますが、可能であればcronをDocker化されたサービスに統合することを検討すべきです。
最も人気のあるDockerベースイメージには、デフォルトでcronデーモンが含まれていません。Dockerfile内でそれをインストールし、アプリケーションのcrontabを登録することができます。
まず、コードベース内に新しいcrontabファイルを作成します:
*/5 * * * * /usr/bin/sh /example-scheduled-task.sh次に、Dockerfileを修正して、cronをインストールし、crontabを登録します。Debianベースのイメージでこれを行う方法は次の通りです:
RUN apt-get update && apt-get install -y cron
COPY example-crontab /etc/cron.d/example-crontab
RUN chmod 0644 /etc/cron.d/example-crontab &&
crontab /etc/cron.d/example-crontab
cronをインストールし、私たちのコードベースのcrontabを/etc/cron.dディレクトリにコピーします。次に、crontabの権限を修正してcronにアクセスできるようにする必要があります。最後に、crontabコマンドを使用してファイルをcronデーモンに認識させます。
このセットアップを完了するには、コンテナが起動するときに cron デーモンを開始するように、イメージのコマンドまたはエントリポイントを修正する必要があります。これは、Dockerfile の RUN ステージを使用しては達成できません。なぜなら、これらはイメージのビルドフェーズを超えて持続しない一時的なステップだからです。サービスは、レイヤーをビルドするために使用されるエフェメラルコンテナ内で開始され、完成したイメージを実行している最終コンテナではありません。
コンテナの唯一のタスクが cron を実行することである場合 - これは後で詳しく説明します - Dockerfile に ENTRYPOINT ["cron", "-f"] を追加して、フォアグラウンドプロセスとして起動することができます。もし、ウェブサーバーのように別のプロセスをフォアグラウンドで維持する必要がある場合は、専用のエントリポイントスクリプト (例: ENTRYPOINT ["bash", "init.sh"]) を作成し、そのファイル内で service cron start をコマンドとして追加する必要があります。
アプリケーションのサービスからCronを分離する
前のセクションで説明したセットアップを実装することは、ホストのcrontabに依存するよりもより堅牢なソリューションを提供します。アプリケーションを提供するコンテナにcronデーモンを追加することで、Dockerイメージを使用するすべての人が自動的にスケジュールされたタスクを設定できるようになります。
しかし、これでも関心の混合が生じます。コンテナは2つの責任を持つことになります - まず、アプリケーションの機能を提供すること、そして次に、cronを維持し、スケジュールされたタスクを実行することです。理想的には、各コンテナは特定の機能単位を提供すべきです。
できる限り、アプリケーションとは別のコンテナでcronタスクを実行するべきです。ウェブバックエンドを作成している場合、それはウェブサーバーを提供するための1つのコンテナと、フォアグラウンドでcronを実行する別のコンテナを意味します。
この分離がなければ、Docker SwarmやKubernetesのようなオーケストレーターを使ってアプリケーションの複数のレプリカを実行することができません。各コンテナは独自のcronデーモンを実行し、スケジュールされたタスクが複数回実行されることになります。これは、共有Dockerボリュームにバインドされたロックファイルを使用することで軽減できます。それにもかかわらず、根本的な問題に対処し、cronデーモン専用のコンテナを導入する方がよりメンテナンスしやすいです。
一般的に、両方のコンテナはアプリケーションのDockerイメージに基づく必要があります。それぞれのコンテナには、サービスのDockerボリュームとネットワークへの接続が必要です。これにより、cronコンテナはアプリケーションコンテナと同一の環境を持ち、唯一の違いはフォアグラウンドプロセスであることが保証されます。
これは厳密なルールではありません。いくつかのプロジェクトでは、スケジュールされたタスクがあなたのコードベースとは独立して動作する些細なスクリプトであるかもしれません。その場合、cron コンテナは最小限のベースイメージを使用し、不要な周辺リソースへの接続を排除することができます。
別の cron コンテナを設定する方法の一つは、docker-compose を使用することです。 cron コンテナを追加サービスとして定義します。アプリケーションのベースイメージを使用し、cron デーモンを起動するためにエントリーポイントのコマンドを上書きすることができます。 docker-compose を使用することで、必要な共有ボリュームやネットワークにコンテナを簡単に接続できるようになります。
version: "3"
services:
app:
image: demo-image:latest
volumes:
- data:/app-data
cron:
image: demo-image:latest
entrypoint: /bin/bash
command: ["cron", "-f"]
volumes:
- data:/app-data
volumes:
data:
上記の例を使用すると、1つのコンテナが画像のデフォルトエントリポイントを使用してアプリケーションを提供します。これがcronデーモンを開始しないことを確認してください!2番目のコンテナは、cronを実行するためにイメージのエントリポイントをオーバーライドします。イメージにまだcronがインストールされていて、crontabが設定されている限り、docker-compose upを使用してアプリケーションを起動できます。
KubernetesのCronジョブを使用する
最後に、Kubernetes内でスケジュールされたタスクを実行するための簡単な例を見てみましょう。Kubernetesには、マニフェストで使用できる独自のCronJobリソースがあります。
Kubernetesを使用している場合、イメージにcronをインストールしたり、特別なコンテナを設定したりする必要はありません。CronJobはベータリソースであり、今後のKubernetesのリリースで変更される可能性があることを理解してください。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: my-cron
namespace: my-namespace
spec:
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: my-container
image: my-image:latest
command: ["/bin/bash", "/my-cron-script.sh"]
restartPolicy: OnFailure
上記のマニフェストをクラスターに適用して、コンテナ内で5分ごとに/my-cron-script.shを実行する新しいcronジョブを作成します。頻度は、リソースのspec内のscheduleキーに対して通常のcron定義として指定されます。
Kubernetesがジョブの重複を許可するかどうかを制御するために、ConcurrencyPolicyをカスタマイズできます。デフォルトはAllowですが、Forbid(既存のジョブがある間に新しいジョブの開始を防ぐ)またはReplace(新しいジョブが開始されると同時に既存のジョブを終了させる)に変更することができます。
Kubernetesの組み込みリソースを使用することは、クラスタ内でスケジュールされたタスクを管理するための推奨される方法です。ジョブログに簡単にアクセスでき、cronと一緒に使用するためのコンテナの準備について心配する必要はありません。タスクが実行するために必要なすべてを含むDockerイメージを作成するだけで大丈夫です。Kubernetesは、指定したスケジュールに従ってコンテナインスタンスの作成と破棄を処理します。
