Since lesson 3.5.2, the worker has been running as a Container App with exactly one replica, day and night. But it has only a few minutes of work per day: after the nightly fetch and when someone uploads a contact list. The rest of the time it waits and costs compute time. The fetch itself still runs as a timer in the web app from K2, and that app scales to zero in dev and test. Lesson 2.9.4 named the consequence: Without a replica, no background services run, and then nobody imports anything.

In this lesson you reorganize the background work. The worker grows with the length of its queues and shrinks to zero, the fetch becomes a scheduled job, and both stop in a way that leaves no message half processed. At the end you follow a trace from the job through Service Bus to the row in SQL.

Apps and jobs

Container Apps knows two ways to run containers:

App Job
Lifetime runs continuously; a container that ends is restarted starts, does one piece of work and ends
Scaling by rules, between minReplicas and maxReplicas one or more replicas per execution
Success doesn't exist, only "running" the exit code: 0 means success
Example in the Kladde web app, worker nightly fetch

A job has one of three triggers:

  • Manual (Manual): A person or a program starts it, for example with az containerapp job start.
  • Scheduled (Schedule): A cron expression with five fields (minute, hour, day, month, weekday) sets the times. Container Apps reads it in UTC.
  • Event-driven (Event): A KEDA rule (more on that in a moment) counts events, such as messages in a queue, and starts one execution per event. Each execution typically picks up one message, processes it and ends.

When does which fit? A worker that continuously processes many small messages is an app: It starts once and then works through hundreds of messages. An event-driven job would start a container for every notice, and every start costs seconds. An event-driven job fits when each message is a lot of work, needs resources of its own or runs for a long time, such as a report over a whole month. Work by the clock is a scheduled job, and that is exactly what the fetch is.

You should know about one permission before you create jobs: Whoever may start a job may also replace the image, the command and the environment variables of the execution when starting it. That lets them run code with the job's identity. So you give the roles "Container Apps Jobs Operator" and "Container Apps Jobs Contributor" only to people and pipelines to whom you would also entrust the job's identity.