Skip to main content

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​

ImageCode repositoryDockerfileWhat it runs
acme-azure-blueprint-webappacme-azure-blueprint-webapp-codeDockerfileThe Flask API under Gunicorn on port 8080, four workers with two threads each
acme-azure-blueprint-etl-triggeracme-azure-blueprint-etl-codesrc/trigger/DockerfileOne 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.

Azure/acme-azure-blueprint-webapp-code/Dockerfile (lines 17-52)
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 --interval=30s --timeout=5s --start-period=10s --retries=3 \
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.

  1. On a pull request, ci.yml lints 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.
  2. On merge to main, cd-integration.yml builds once on a GitHub-hosted runner, pushes main-<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.
  3. On a release, cd-release.yml adds vX.Y.Z to that same manifest with docker 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​

TermMeaning
Base imageThe image a Dockerfile starts from, here python:3.12-slim.
main-<sha>The tag of the one build of a commit on main.
Release tagvX.Y.Z, added to the already-tested manifest.
TrivyThe scanner that gates the image in CI.
Exit codeHow a Container Apps job execution reports success or failure.

Where to read more​