11. Porting jobs & apps to ACA

Map the Compose feature environment onto Azure Container Apps while reusing the same workload images, entrypoints, contracts, and behavioral checks.

The local Compose stack is the runnable reference implementation for this course. Porting it to Azure Container Apps (ACA) means replacing local adapters with managed identities, managed storage, ingress, schedules, and control-plane triggers. The workload source does not fork: the same images and entrypoints run in both phases.

Adapter mapping

The Terraform mapping is explicit:

Local Compose ACA definition Shared workload
train service / runner command train_job module instance train train image → train.py
eval runner command train_job module instance eval same train image → evaluate.py
batch service / runner command batch_job manual/scheduled Job batch image → score.py
serving service serving_app ACA App serving image → FastAPI app
dashboard service dashboard ACA App dashboard image → catalog, authz, and launcher API
LLM profile commands llm_job manual Jobs train image → registration or evaluator entrypoint

The local runner accepts train, eval, and batch requests, validates scalar parameters, starts independent subprocesses, and returns asynchronous execution IDs. ACA supplies the cloud execution plane. Both adapters run the same workload entrypoints, write the same statuses, and use the same registered-version identity.

Configuration is where the environments differ

Compose answers shared questions with service names and demo credentials:

MLFLOW_TRACKING_URI=http://mlflow:5000
PGHOST=postgres
PGUSER=mlplatform
PGPASSWORD=demo-password

ACA answers the same questions with managed-identity environment variables:

MLFLOW_TRACKING_URI=https://<mlflow-app>/...
PGHOST=<postgres-fqdn>
PGUSER=id-jobs-train or id-jobs-batch
PGSSLMODE=require
AZURE_CLIENT_ID=<workload-identity-client-id>

The image stays the same. DefaultAzureCredential obtains tokens in Azure, while the results store uses password mode when Compose explicitly supplies PGPASSWORD. Object storage and Key Vault access follow the same adapter boundary: local MinIO/demo values are replaced by Azure role assignments and identity-backed clients.

Schedules and execution parameters

A schedule is equivalent only when it launches the same workload with the same effective inputs. The batch module injects DATA_SOURCE and MODEL_NAME into the Job template; score.py defaults to the production alias when no exact version is supplied. Optional cron configuration starts independent executions.

Classical evaluation is a separate manual Job built from the train image. Its module instance changes the container command to python evaluate.py and supplies fallback model, version, dataset, and RMSE threshold values. A dashboard request can override those scalar arguments for one execution.

The dashboard must preserve the complete deployed template when adding execution-specific arguments, because ACA treats an override as a replacement. It reads the current template, changes the matching container’s args and TRIGGERED_BY, then calls begin_start. parallelism = 1 still means one replica inside each execution; it does not serialize separate submissions.

LLM registration and evaluation use llm_job instances. The cloud evaluator receives its dataset and model identity from the Job template and resolves its runtime model credential through Key Vault.

Apps, identity, and promotion

Serving keeps the same /healthz, /readyz, and /v1/predictions endpoints. ACA adds HTTPS ingress, id-serving, and probes.

The dashboard adds three cloud-specific controls:

  • the authConfigs/current child resource enables single-tenant Entra Easy Auth and excludes only /healthz;
  • Terraform injects the concrete train, eval, and batch Job resource names;
  • application authorization requires the configured operator-group claim on every trigger, while authenticated viewers remain read-only.

id-dashboard supplies scoped machine authorization for results reads and Job starts. The Easy Auth principal supplies the human name and group membership.

Promotion has three shared facts: the exact candidate has a passing evaluation, the MLflow production alias points to that version, and serving is pinned to MODEL_VERSION=N. demo/promote.py enforces the evaluation lookup before the alias flip; only the final repin differs between Compose and ACA.

Deploy and inspect

cd projects/ml-platform
./deploy/deploy.sh --pg-admin-upn you@example.com
./deploy/smoke-tests.sh --tf-vars infra/environments/dev.tfvars

PowerShell users can use deploy.ps1 and smoke-tests.ps1. Before supplying a dashboard image, fill the ignored infra/secret.auto.tfvars with the Easy Auth app-registration client ID/secret and the operator group object ID. The evaluation Job is enabled with the train image; optional LLM evaluation still requires LLM_EVAL_DATASET.

The deployment adapter may know Azure resource names, identities, ACR, ACA schedules, Easy Auth, and az APIs. It may not invent a second training or evaluation implementation, model loader, results schema, or readiness rule.

Next: 12 — CI/CD makes the digest and validation path repeatable.

Back to top