こんにちは、たびとです。
Dockerfile に .NET 6 アプリのビルドから実行までを定義し、 一定間隔でアプリを実行させたいと思ったことはありませんか?
以前、AI システムを作ったとき、Azure にビデオファイルをアップロード、 AI 解析の完了確認、AI 解析の情報を DB に格納する等、 一定間隔で実行させるにはどうすれば良いのかを悩みました。
当時は、docker 上で .NET Core アプリをビルドする方法について、 情報が少なくて、かなり試行錯誤して作った記録があります。
今回は、WSL2 Linux (Ubuntu 20.04) 環境で、.NET 6 コンソールアプリを使い、 一定間隔でバッチ処理を実行する方法を説明します。
この記事の対象者
- docker 環境で .NET 6 アプリをビルドして実行させたい。
- docker 環境で cron により、一定間隔で処理を実行したい。
事前準備
docker 環境をまだ作っていなければ、以前の記事を参考にしてください。
.NET 6 のインストールは Microsoft のサイトを参考にしてください。 Ubuntu の版数に注意してください。
docker 構築
最終的なファイル構成を先に紹介します。 Visual Studio Code (VS Code) を使うと Windows 上からファイルの編集が可能です。 さらに、拡張機能を使うと何かと便利なので、 エディタに迷ったら VS Code を選択するのがお勧めです。
最初に Remote - WSL (Microsoft) を事前にインストールしてください。
docker-compose.yml 作成
docker 資材を格納するため、適当な名前のフォルダを作成します。 ここでは、batsv とします。
$ mkdir batsv $ cd batsv
docker-compose.yml を作成し、VS Code で編集します。
$ touch docker-compose.yml
$ code .
code コマンドを実行すると、Windows 上で VS Code が起動します。 Remote - WSL 拡張機能をインストールしておくと、 WSL2 Linux への接続アイコンが左下に表示されるようになります。
docker-compose.yml にバッチ用サーバを作成するために記述します。
version: '3.8' services: batch: container_name: 'batch' build: context: ./source dockerfile: Dockerfile environment: TZ: Asia/Tokyo tty: true restart: always stdin_open: true
サンプルコード作成
次にサンプルコードを作成していきます。 docker-compose.yml に記述した source ディレクトリを作成し、移動します。
$ mkdir source $ cd source
先に crontab と Dockerfile を作成しておきます。
$ touch crontab $ touch Dockerfile
コンソールアプリを BackApp という名前で作成します。
$ dotnet new console -o BackApp Welcome to .NET 6.0! --------------------- SDK Version: 6.0.301 Telemetry --------- The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell. Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry ---------------- Installed an ASP.NET Core HTTPS development certificate. To trust the certificate run 'dotnet dev-certs https --trust' (Windows and macOS only). Learn about HTTPS: https://aka.ms/dotnet-https ---------------- Write your first app: https://aka.ms/dotnet-hello-world Find out what's new: https://aka.ms/dotnet-whats-new Explore documentation: https://aka.ms/dotnet-docs Report issues and find source on GitHub: https://github.com/dotnet/core Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli -------------------------------------------------------------------------------------- The template "Console App" was created successfully. Processing post-creation actions... Running 'dotnet restore' on /home/tabito/batsv/source/BackApp/BackApp.csproj... Determining projects to restore... Restored /home/tabito/batsv/source/BackApp/BackApp.csproj (in 58 ms). Restore succeeded.
VS Code の Program.cs を編集します。 確認に便利なので、Hello, World! はそのまま残します。
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!"); using var sw = new StreamWriter("/tmp/testfile.txt"); sw.WriteLine(DateTime.Now);
cron が正しく実行されていることを確認するために、 日時をファイルに出力するだけの簡単な内容を追加します。
crontab 編集
1 分間隔でアプリを実行するように記述します。
*/1 * * * * /app/BackApp
Dockerfile 編集
先ほど作成した BackApp をビルドし、cron 実行させる内容を記述します。 Dockerfile 作成のポイントを簡単に説明します。
- docker イメージは Ubuntu (focal) を使う。
- ビルド環境(.NET SDK)と実行環境(.NET Runtime)は別イメージを使う。
- cron 用として、busybox を追加する。
- busybox crond で起動する。
# ビルド環境 FROM mcr.microsoft.com/dotnet/sdk:6.0-focal AS build WORKDIR /source COPY ["./BackApp/BackApp.csproj", "BackApp/"] RUN dotnet restore "./BackApp/BackApp.csproj" COPY . . RUN dotnet build "./BackApp/BackApp.csproj" -c Release -o /app/build FROM build AS publish WORKDIR /source RUN dotnet publish "./BackApp/BackApp.csproj" -c Releasse -o /app/publish # 実行環境 FROM mcr.microsoft.com/dotnet/runtime:6.0.6-focal AS runtime COPY ["crontab", "/var/spool/cron/crontabs/root"] WORKDIR /app COPY --from=publish /app/publish . WORKDIR / # cron用の資材を追加する RUN apt-get update && apt-get -y install --no-install-recommends \ busybox-static \ && apt-get -y clean \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT ["/usr/bin/busybox", "crond", "-f", "-l", "2", "-L", "/dev/stderr"]
mcr.microsoft.com/dotnet/sdk:6.0-focal (runtime:6.0.6-focal) は Microsoft の公式イメージです。 最新版、別の OS を使いたい場合、dockerhub で検索してください。
ちなみに、Alpine の方が軽いので使いたいところですが、 apk コマンドがハングする(回避方法あり)ため、 この問題が解決するまでは使わない方がいいと思います。
docker 実行
早速、ビルドして実行しましょう。 docker-compose.yml がある箇所まで移動し、docker-compose コマンドを実行します。
$ cd ~/batsv $ docker-compose build [+] Building 79.0s (18/19) => [internal] load .dockerignore 0.1s => => transferring context: 2B 0.0s => [internal] load build definition from Dockerfile 0.1s => => transferring dockerfile: 902B 0.0s => [internal] load metadata for mcr.microsoft.com/dotnet/runtime:6.0.6-focal 1.9s => [internal] load metadata for mcr.microsoft.com/dotnet/sdk:6.0-focal 1.5s => [build 1/6] FROM mcr.microsoft.com/dotnet/sdk:6.0-focal@sha256:de284118af917e1f6ac2092c8c1e4a10d6c5171bd96bf 77.0s => => resolve mcr.microsoft.com/dotnet/sdk:6.0-focal@sha256:de284118af917e1f6ac2092c8c1e4a10d6c5171bd96bf4d91ed5 0.0s => => sha256:de284118af917e1f6ac2092c8c1e4a10d6c5171bd96bf4d91ed551d630285f2f 1.11kB / 1.11kB 0.0s => => sha256:0437b56a84fe8389280cdeb0ebb9fba0b6f4d4a9b1572f48604a9a31e9889a43 18.27MB / 18.27MB 11.0s => => sha256:acfd6470210c2c0dd47b82309390908214c8644069b261c333425820df6b2f32 31.62MB / 31.62MB 13.7s => => sha256:141a07091f84b3b248fc277c0cdf5cdc71517eaa819fa5062adee5ace7fb935d 2.01kB / 2.01kB 0.0s => => sha256:64c8486251a81eb825f83192fde9a175074fdce4b5a3f334a0f2b3bb1d90dcb8 7.21kB / 7.21kB 0.0s => => sha256:d7bfe07ed8476565a440c2113cc64d7c0409dba8ef761fb3ec019d7e6b5952df 28.57MB / 28.57MB 7.8s => => sha256:2b031d49096b5d56cbd8e3196a2258b455e65cf70715863ec59e46b9c869a11b 155B / 155B 8.0s => => sha256:5b91841c9ffee9ec359867ab0f7f91744f2435dbb5346eee0761833f9f7286e4 9.45MB / 9.45MB 11.6s => => extracting sha256:d7bfe07ed8476565a440c2113cc64d7c0409dba8ef761fb3ec019d7e6b5952df 68.4s => => sha256:393bb4f8e0c924ebf05e11fcd75bc182b44e35ebb05acdb73edcb1e84934286b 31.31MB / 31.31MB 25.4s => => extracting sha256:0437b56a84fe8389280cdeb0ebb9fba0b6f4d4a9b1572f48604a9a31e9889a43 65.9s => => sha256:21a1ffcb599c97db4cd71aa48a995c4efd233f007cf4db6a5d08fa23a29788e3 145.60MB / 145.60MB 47.5s => => sha256:bb210f227e9575213426ff1633c45aaebc2ff465b1aa39c980f1bc1bb8f2576e 12.89MB / 12.89MB 22.7s => => extracting sha256:acfd6470210c2c0dd47b82309390908214c8644069b261c333425820df6b2f32 63.2s => => extracting sha256:2b031d49096b5d56cbd8e3196a2258b455e65cf70715863ec59e46b9c869a11b 0.0s => => extracting sha256:5b91841c9ffee9ec359867ab0f7f91744f2435dbb5346eee0761833f9f7286e4 0.4s => => extracting sha256:393bb4f8e0c924ebf05e11fcd75bc182b44e35ebb05acdb73edcb1e84934286b 2.5s => => extracting sha256:21a1ffcb599c97db4cd71aa48a995c4efd233f007cf4db6a5d08fa23a29788e3 7.7s => => extracting sha256:bb210f227e9575213426ff1633c45aaebc2ff465b1aa39c980f1bc1bb8f2576e 0.7s => [runtime 1/6] FROM mcr.microsoft.com/dotnet/runtime:6.0.6-focal@sha256:082086b7cc716efada1744c0a608ca4f63ff2 15.6s => => resolve mcr.microsoft.com/dotnet/runtime:6.0.6-focal@sha256:082086b7cc716efada1744c0a608ca4f63ff261c794976 0.0s => => sha256:082086b7cc716efada1744c0a608ca4f63ff261c794976e17644a956ee4320b1 1.11kB / 1.11kB 0.0s => => sha256:e645d3458a1d9531a5ba089fc8b0aafdd4f627a5e761184f9f2526d9738a2bc9 1.16kB / 1.16kB 0.0s => => sha256:99ebd73a9cca6c0af2e41ddc970d249e70d4c7dc3e3abce8720f7999083aeb71 2.83kB / 2.83kB 0.0s => => sha256:d7bfe07ed8476565a440c2113cc64d7c0409dba8ef761fb3ec019d7e6b5952df 28.57MB / 28.57MB 7.8s => => sha256:0437b56a84fe8389280cdeb0ebb9fba0b6f4d4a9b1572f48604a9a31e9889a43 18.27MB / 18.27MB 11.0s => => sha256:acfd6470210c2c0dd47b82309390908214c8644069b261c333425820df6b2f32 31.62MB / 31.62MB 13.7s => => sha256:2b031d49096b5d56cbd8e3196a2258b455e65cf70715863ec59e46b9c869a11b 155B / 155B 8.0s => => extracting sha256:d7bfe07ed8476565a440c2113cc64d7c0409dba8ef761fb3ec019d7e6b5952df 2.0s => => extracting sha256:0437b56a84fe8389280cdeb0ebb9fba0b6f4d4a9b1572f48604a9a31e9889a43 1.3s => => extracting sha256:acfd6470210c2c0dd47b82309390908214c8644069b261c333425820df6b2f32 1.4s => => extracting sha256:2b031d49096b5d56cbd8e3196a2258b455e65cf70715863ec59e46b9c869a11b 61.8s => [internal] load build context 0.1s => => transferring context: 6.70kB 0.0s => [runtime 2/6] COPY [crontab, /var/spool/cron/crontabs/root] 0.1s => [runtime 3/6] WORKDIR /app 0.1s => [build 2/6] WORKDIR /source 0.1s => [build 3/6] COPY [./BackApp/BackApp.csproj, BackApp/] 0.1s => [build 4/6] RUN dotnet restore "./BackApp/BackApp.csproj" 2.4s => [build 5/6] COPY . . 0.1s => [build 6/6] RUN dotnet build "./BackApp/BackApp.csproj" -c Release -o /app/build 3.1s => [publish 1/2] WORKDIR /source 0.1s => [publish 2/2] RUN dotnet publish "./BackApp/BackApp.csproj" -c Releasse -o /app/publish 2.8s => [runtime 4/6] COPY --from=publish /app/publish . 0.1s => [runtime 5/6] RUN apt-get update && apt-get -y install --no-install-recommends busybox-static && a 11.7s => exporting to image 0.1s => => exporting layers 0.1s => => writing image sha256:1a4be80a911470403cd56936f39eceb904c1aeb745440f3287fc44b010993c94 0.0s => => naming to docker.io/library/batsv_batch 0.0s Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
初回は時間が掛かるので、しばらく待ちます。 出来上がったら、実行します。
$ docker-compose up -d [+] Running 2/2 ⠿ Network batsv_default Created 0.1s ⠿ Container batch Started 0.8s
コンテナに接続します。
$ docker-compose exec batch /bin/bash
root@9b764b3b2692:/#
cron 実行が正常に動作していることを確認します。 1分経過後に実行し、時刻が変化していることを確認します。
# cat /tmp/testfile.txt 07/10/2022 22:59:00
不要になったらコンテナを削除します。
$ docker-compose down [+] Running 2/2 ⠿ Container batch Removed 10.3s ⠿ Network batsv_default Removed 0.2s
まとめ
docker で cron を実行するには、BusyBox を使うのが常套手段なので、情報集めには困らないと思います。 .NET アプリを Dockerfile でビルドから実行までを定義する方法は、情報が少なく 当時は Microsoft のページを参考に作成した記憶があります。
dokcer の理解に苦しんでいる方は、まずは書籍で体系的に学んだ方が良いかもしれません。 私が実際に読んで参考になった書籍を紹介しておきます。
アプリ開発が得意な方は、この本が参考になると思います。 VS Code + Python で、2020年6月時点の情報に基づいています。 VS Code の使い方も学べるため、アプリ開発者には良いと思います。
インフラ構築がメインの方は、この本が参考になると思います。 CentOS ベースで、2018 年 12月時点の情報なので少し古さがあるかもしれませんが、 かなり詳細に説明されていて良かったです。
では、皆さん、よい旅を。