Alternative Architecture DOJO

オルターブースのクラウドネイティブ特化型ブログです。

Azure App Service LinuxでWebJobsが正式サポートされました

こんにちは、MLBお兄さんこと松村です。
今年のGW休暇では熊本の阿蘇山周辺をめぐりつつ、温泉を堪能してきました♨️


5月2日に App Service チームのブログにて、WebJobs が Azure App Service Linux で正式サポート (GA) されたという内容のこちらの記事が公開されました。

azure.github.io

WebJobs とは

WebJobs とは Azure App Service と同じインスタンスにコードを配置し、Web アプリとは異なる処理を実行させることができる機能です。
Web アプリを配置している Azure App Service の情報を参照しつつ、バックグラウンドタスクなどの実行基盤として活用できる便利な機能です。

learn.microsoft.com

今回 Azure App Service Linux で WebJobs が正式サポートとなりましたが、 OS や実行形式ごとに WebJobs のサポート状況が異なりますので注意してください。(内容は記事執筆時点の情報)

OS 実行形式 サポート状況
Windows コード 一般提供
Windows コンテナー プレビュー
Linux コード 一般提供
Linux コンテナー 一般提供

WebJobs on Linux に C# コードを配置する

久しぶりに WebJobs に関する内容を見ましたが、実は個人的には好きなサービスだったりします。
最近はバックグラウンドタスクの実行環境に Azure FunctionsAzure Container Instances を使用するケースが増えていると思いますが、同じインスタンスで実行したい場合は WebJobs を選択する場面となります。

そこで、 C# のコードを Linux の WebJobs に配置する流れを検証しましたので、以下にまとめていきます。
なおコードは GitHub で公開していますので、内容を飛ばしたい方はこちらからどうぞ。

github.com

WebJobs アプリケーションの準備

今回はサンプルとして、 .NET コンソールアプリケーションで汎用ホストを使い、バックグラウンドタスクとして実行できるようにします。
なお、Azure App Service の都合で .NET 9 を使用します。 (.NET 10 未サポート)

dotnet new console -n WebJobsSDKSample -f net9.0
cd WebJobsSDKSample
dotnet add package Microsoft.Azure.WebJobs.Host.Storage --version 5.0.1
dotnet add package Microsoft.Azure.WebJobs.Extensions --version 5.0.0
dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage --version 5.3.4
dotnet add package Microsoft.Extensions.Hosting --version 9.0.4
dotnet add package Microsoft.Extensions.Logging.Console --version 9.0.4

Program.cs では汎用ホストを利用する実装を行います。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

await Host.CreateDefaultBuilder(args)
    .UseEnvironment(Environments.Development)
    .ConfigureServices(services =>
    {
        services.AddHostedService<SampleHostedService>();
    })
    .ConfigureWebJobs(builder =>
    {
        builder.AddAzureStorageCoreServices();
        builder.AddAzureStorageQueues();
    })
    .ConfigureLogging((context, builder) =>
    {
        builder.SetMinimumLevel(LogLevel.Information);
        builder.AddConsole();
    })
    .Build()
    .RunAsync();

learn.microsoft.com

learn.microsoft.com

ホストのイベントを拾うクラスを実装します。

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public class SampleHostedService : IHostedService, IHostedLifecycleService
{
    private readonly ILogger<SampleHostedService> _logger;

    public SampleHostedService(
        ILogger<SampleHostedService> logger,
        IHostApplicationLifetime appLifetime)
    {
        _logger = logger;

        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);
    }

    Task IHostedLifecycleService.StartingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("1. StartingAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedService.StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("2. StartAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedLifecycleService.StartedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("3. StartedAsync has been called.");

        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("4. OnStarted has been called.");
    }

    private void OnStopping()
    {
        _logger.LogInformation("5. OnStopping has been called.");
    }

    Task IHostedLifecycleService.StoppingAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("6. StoppingAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedService.StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("7. StopAsync has been called.");

        return Task.CompletedTask;
    }

    Task IHostedLifecycleService.StoppedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("8. StoppedAsync has been called.");

        return Task.CompletedTask;
    }

    private void OnStopped()
    {
        _logger.LogInformation("9. OnStopped has been called.");
    }
}

learn.microsoft.com

WebJobs で実行する Queue トリガーの処理を用意します。

using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace WebJobsSDKSample;

public static class Functions
{
    public static void ProcessQueueMessage([QueueTrigger("queue")] string message, ILogger logger)
    {
        logger.LogInformation(message);
    }
}

WebJobs アプリの配置

ジョブとしてアプリケーションを実行するための、スクリプトファイルを用意します。
このファイルは、アプリケーションのビルド時に発行ディレクトリに自動的にコピーできるようにしておくと便利です。
※ここでは run.sh というファイル名とします

#!/bin/bash

chmod +x ./WebJobsSDKSample.dll
dotnet ./WebJobsSDKSample.dll

また、CRON式に似ている NCRONTAB式のファイルを用意し、ジョブの実行頻度を定義します。

{
  "schedule": "0 * * * * *"
}

WebJobs は Visual Studio から発行するか、Zip ファイルを Azure ポータルでアップロードします。

learn.microsoft.com

Azure ポータルで作業する場合、WebJobs の設定ページに以下のメッセージが表示されています。

"WEBSITE_SKIP_RUNNING_KUDUAGENT" must be set to false in order for webjobs to function on linux apps.

ドキュメントに記載がありませんが、Linux の App Service で WebJobs を利用するには "WEBSITE_SKIP_RUNNING_KUDUAGENT=false" と設定しておく必要があるようです。

動作確認

ジョブが登録できたら、まずは起動します。
ポータルの WebJobs 設定ページにて、登録済みのジョブの▶ボタンを押して実行します。
するとジョブのログボタンが有効になるため、ログを確認してみます。

SampleHostedService クラスに定義した、.NET アプリのホストイベントで出力しているログが登場していることが確認できます。

[05/05/2025 01:59:03 > 978fb3: INFO] info: SampleHostedService[0]
[05/05/2025 01:59:03 > 978fb3: INFO]       1. StartingAsync has been called.
[05/05/2025 01:59:03 > 978fb3: INFO] info: SampleHostedService[0]
[05/05/2025 01:59:03 > 978fb3: INFO]       2. StartAsync has been called.
...
[05/05/2025 01:59:04 > 978fb3: INFO] info: SampleHostedService[0]
[05/05/2025 01:59:04 > 978fb3: INFO]       3. StartedAsync has been called.
[05/05/2025 01:59:04 > 978fb3: INFO] info: SampleHostedService[0]
[05/05/2025 01:59:04 > 978fb3: INFO]       4. OnStarted has been called.

また、Queue トリガーの処理も動かしてみます。
参照している Queue ストレージにアイテムを登録すると、Queue トリガーが発火して処理が行われることが確認できます。

[05/05/2025 07:44:07 > 978fb3: INFO] info: Function.ProcessQueueMessage.User[0]
[05/05/2025 07:44:07 > 978fb3: INFO]       hello-world

以上で、 Azure App Service Linux で正式サポートされた WebJobs を実行する流れが整理できました。
冒頭に書いた通り WebJobs を使う機会はさほど多くはないと思いますが、たまに使いたくなる場面もありますので、今回の内容を踏まえて実装したいと思います。


サービス一覧 www.alterbooth.com cloudpointer.tech www.alterbooth.com