Docker
Docker builds container images from a Dockerfile: a base image plus the layers each instruction adds. The Azure blueprints ship two images, the web application's API and the ETL trigger; each is built once from its code repository, scanned, and pushed to Azure Container Registry under a tag that never changes.
What it does
A Dockerfile starts FROM a base image and adds layers: packages, application code, environment. USER sets the account the container runs as, HEALTHCHECK how the runtime probes it, and CMD or ENTRYPOINT what it runs. docker buildx builds and pushes images and can copy a manifest to a new tag inside a registry without pulling it. A tag names an image for people; the digest identifies its exact content.
How BuiltForProd uses it
| Image | Code repository | Dockerfile | What it runs |
|---|---|---|---|
acme-azure-blueprint-webapp | acme-azure-blueprint-webapp-code | Dockerfile | The Flask API under Gunicorn on port 8080, four workers with two threads each |
acme-azure-blueprint-etl-trigger | acme-azure-blueprint-etl-code | src/trigger/Dockerfile | One trigger run per execution: dequeue, start the Databricks run, exit |
Both start from python:3.12-slim, install exact pins from a requirements.txt and run as the fixed non-root user 10001, which the Helm chart pins too. Nothing secret enters either image: the data stores, Key Vault and Databricks are reached with Microsoft Entra tokens obtained at run time, and their TLS certificates chain to public CAs in ca-certificates. The API declares a health check on /health; the trigger declares none, because Container Apps judges an execution by its exit code (0 success, 1 run not started and retried, 2 configuration error). The trigger image also applies the Debian security updates published since its base was built.
FROM python:3.12-slim AS runtime
# Prevent Python from writing .pyc files and enable unbuffered output
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# The public CA trust store used by the TLS connections above (refreshed to the current bundle)
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Install Python dependencies (exact pins in src/requirements.txt)
COPY src/requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY src/ ./src/
# Create non-root user with a fixed UID/GID. The Helm chart pins
# runAsUser/runAsGroup/fsGroup to 10001, so this must not change.
RUN groupadd -g 10001 appuser && useradd -r -u 10001 -g appuser -s /usr/sbin/nologin appuser
USER 10001:10001
# Expose port
EXPOSE 8080
# Health check -- uses Python to avoid needing curl in the runtime image
HEALTHCHECK \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1
# Run with Gunicorn (production WSGI server); the clients in src/db.py are created per worker
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "4", "--threads", "2", \
"--access-logfile", "-", "--error-logfile", "-", \
"--logger-class", "src.logging_config.GunicornLogger", "src.app:app"]
Build once, promote the same bytes.
- On a pull request,
ci.ymllints with ruff, runs pytest, builds the image without pushing it and scans it with Trivy, failing on CRITICAL or HIGH findings that have a fix. - On merge to
main,cd-integration.ymlbuilds once on a GitHub-hosted runner, pushesmain-<short sha>to Azure Container Registry as a plain image manifest and locks the tag. A re-run for the same commit skips the build, because the tag already exists. - On a release,
cd-release.ymladdsvX.Y.Zto that same manifest withdocker buildx imagetools create, a copy inside the registry, and locks it. A release tag that already points at a different image fails the release.
There is no latest. The web application's single-page application is not an image: it is built per stage and uploaded to a storage static website behind Front Door. The runner image of the Container Apps runners is built by the Baseline's runner-image.yml inside the registry with az acr build, so no Docker daemon is involved.
Terms you will see
| Term | Meaning |
|---|---|
| Base image | The image a Dockerfile starts from, here python:3.12-slim. |
main-<sha> | The tag of the one build of a commit on main. |
| Release tag | vX.Y.Z, added to the already-tested manifest. |
| Trivy | The scanner that gates the image in CI. |
| Exit code | How a Container Apps job execution reports success or failure. |
Where to read more
- Azure Web App Blueprint overview and Azure Data and ETL Blueprint overview.
- GitHub Actions for the workflows that build and promote.
- Immutable artifacts for why the tag never moves.