Prerequisites

This is a foundational chapter with no prerequisites beyond the course prerequisite.

  • Docker Desktop installed and running (version 24.0 or later), with the Docker CLI accessible from your terminal
  • Python 3.11+ installed locally for testing the application before containerization
  • A Google Cloud account with an active Gemini API key, stored securely and never committed to version control
  • Familiarity with basic terminal commands: navigating directories, setting environment variables, and running Python scripts

Learning Goals

  1. Write a Python application that calls the Gemini API to generate text

    • Write a Python application that calls the Gemini API to generate text— Build a complete FastAPI service that accepts user prompts, sends them to the Gemini generative model, and returns structured JSON responses with proper error handling and timeout management.
    • Implement a /generate endpoint using FastAPI that validates incoming prompt requests with Pydantic models before forwarding them to the Gemini API
    • Configure the google-generativeai client library with API key authentication, model selection, and generation parameters such as temperature and max output tokens
    • Add structured error handling that distinguishes between network failures, authentication errors, and rate-limit responses from the Gemini API
  2. Create a Dockerfile with proper dependency management and multi-stage builds

    • Create a Dockerfile with proper dependency management and multi-stage builds— Construct a production-grade Dockerfile that separates build-time dependencies from runtime dependencies, producing a final image that contains only what the application needs to execute.
    • Use a multi-stage build where the first stage installs Python dependencies into a virtual environment and the second stage copies only the virtual environment and application code into a slim base image
    • Pin the base image to a specific Python version digest rather than a mutable tag like python:3.11 to guarantee reproducible builds across environments
    • Add a .dockerignore file that excludes pycache, .env files, .git, and test directories to prevent secrets and unnecessary files from leaking into the image layers
  3. Build, tag, and run Docker images for LLM applications

    • Build, tag, and run Docker images for LLM applications— Execute the full local development cycle of building a container image from your Dockerfile, assigning meaningful tags, and running the container with the correct runtime configuration.
    • Run docker build with build arguments for setting the Python version and application metadata, then verify image size and layer count using docker images and docker history
    • Tag images using a consistent naming convention that includes the registry hostname, project namespace, image name, and version identifier such as us-docker.pkg.dev/my-project/llm-apps/gemini-service:1.0.0
    • Start the container with docker run, mapping the container port to a host port, injecting environment variables for the API key, and verifying the health endpoint responds correctly
  4. Pass API endpoints and configuration via environment variables

    • Pass API endpoints and configuration via environment variables— Externalize every piece of configuration that changes between environments so the same container image runs in development, staging, and production without modification.
    • Define environment variables for the Gemini API endpoint URL, API key, model name, request timeout, and logging level, with sensible defaults for non-sensitive values set in the Dockerfile using ENV directives
    • Use Pydantic BaseSettings to load and validate environment variables at application startup, raising clear errors when required variables like the API key are missing rather than failing silently on the first API call
    • Demonstrate the difference between build-time variables (ARG) and runtime variables (ENV) in the Dockerfile, explaining why secrets must never appear in ARG directives since they persist in image layer metadata
  5. Use Docker Compose to run an LLM app with a local proxy service

    • Use Docker Compose to run an LLM app with a local proxy service— Define a multi-service application stack in a single compose.yaml file that runs your Gemini service alongside a local API proxy, connected through a shared Docker network.
    • Write a compose.yaml that declares two services: the gemini-app built from your Dockerfile and an api-proxy service running a lightweight reverse proxy that forwards requests to the application container
    • Configure inter-service networking so the gemini-app service reaches the proxy using the service name as a hostname, eliminating hardcoded IP addresses or localhost references
    • Use Compose environment files (.env) and the env_file directive to inject different configuration sets for local development versus integration testing, keeping secrets out of the Compose file itself
    • Add health checks and dependency ordering with depends_on conditions so the application container waits until the proxy service passes its readiness check before starting
  6. Push container images to a registry with semantic version tags

    • Push container images to a registry with semantic version tags— Publish your tested container image to a container registry with a tagging strategy that supports rollbacks, auditability, and automated deployment pipelines.
    • Authenticate to Google Artifact Registry using gcloud auth configure-docker, then push the image using both a semantic version tag like 1.2.0 and a latest tag to support both pinned deployments and development convenience
    • Implement a tagging convention that includes the Git commit SHA as an immutable identifier alongside the semantic version, enabling exact traceability from a running container back to the source code that produced it
    • Verify the pushed image by pulling it on a clean machine or in a CI environment, running the container, and confirming the /generate endpoint returns a valid response from the Gemini API

Key Terminology

Container Image
A read-only, layered filesystem snapshot that packages application code, runtime dependencies, and OS libraries into a single portable artifact executable by any OCI-compliant container runtime.
Dockerfile
A declarative text file containing ordered instructions such as **FROM**, **COPY**, and **RUN** that Docker executes sequentially to assemble a container image.
Multi-Stage Build
A Dockerfile technique that uses multiple **FROM** statements to create intermediate build stages, allowing you to compile dependencies in one stage and copy only the final artifacts into a smaller runtime image.
Base Image
The starting filesystem layer specified by the **FROM** instruction, typically a minimal Linux distribution like **python:3.11-slim** that provides the language runtime without unnecessary system packages.
Image Layer
An immutable filesystem diff created by each Dockerfile instruction, cached independently by Docker so that unchanged layers are reused across builds to reduce build time and storage.
Build Context
The set of files and directories sent to the Docker daemon when you run **docker build**, controlled by the **.dockerignore** file to exclude secrets, caches, and version control metadata.
Environment Variable
A key-value pair injected into the container at runtime using the **-e** flag or **ENV** directive, used to externalize configuration such as API endpoints, credentials, and feature flags without modifying the image.
Docker Compose
A tool that reads a **compose.yaml** file to define and run multi-container applications on a single host, managing shared networks, volumes, dependency ordering, and environment configuration for each service.
Container Registry
A remote storage service such as Google Artifact Registry or Docker Hub that hosts versioned container images, enabling teams to push, pull, and distribute images across development, CI, and production environments.
Image Tag
A mutable label like **1.2.0** or **latest** attached to a container image in a registry, used to identify specific versions for deployment, rollback, and auditability.
Image Digest
An immutable SHA-256 hash that uniquely identifies a specific image manifest, providing a tamper-proof reference that does not change even if a tag is reassigned to a different image.
OCI (Open Container Initiative)
The industry standard specification for container image formats and runtimes, ensuring images built with Docker are portable across any compliant runtime including containerd and CRI-O used by Kubernetes.
ENTRYPOINT
A Dockerfile instruction that defines the executable command a container runs at startup, typically set to a process manager like **uvicorn** for Python web applications so the container behaves as a self-contained service.
Health Check
A Dockerfile or Compose directive that runs a periodic command inside the container to verify the application is responding correctly, used by orchestrators to detect and restart unhealthy instances.
Semantic Versioning
A three-part version format (**MAJOR.MINOR.PATCH**) applied to image tags that communicates the scope of changes, where incrementing MAJOR signals breaking changes, MINOR signals new features, and PATCH signals bug fixes.
Build Argument (ARG)
A variable passed to the Docker build process using **--build-arg** that is available only during image construction, suitable for non-sensitive values like Python version selection but unsafe for secrets because ARG values persist in image layer metadata.
Service Discovery
The mechanism in Docker Compose networking where containers reference other services by their YAML service name as a DNS hostname, replacing hardcoded IP addresses with stable, human-readable identifiers.

On This Page