16 min read

    15 - Containers 101 and Artifact Registry

    gcpcloudcontainersdockerartifact-registry

    Welcome to Day 15 of Learn GCP in 30 Days! Today, we kick off Week 3: Serverless & Containerized Applications.

    🎯

    Today's Goal Don't worry if technical buzzwords like "Containers", "Docker", or "Artifact Registry" sound complex or intimidating! In this lesson, we break everything down using simple, everyday analogies. You will learn what containers actually are (just lightweight, portable boxes for your apps), why they save developers hours of frustration, and how to safely build and store your very first container image inside Google Cloud Artifact Registry!


    πŸ“¦ The Core Problem: "It Works on My Machine!"

    Every software developer has experienced this painful scenario:

    mermaid

    What Existed Previously:

    Developers wrote code on their personal laptops (running macOS, Windows, or Ubuntu) with specific versions of programming languages, tools, and background helper libraries installed. When it was time to deploy to a production Virtual Machine, they had to manually recreate that entire environment.

    Problems Faced:

    • 🀦 Version Mismatch: The developer had Python 3.11 with library version 2.4, but the production server had Python 3.8 with library version 1.9. The app immediately crashed.
    • 🐘 Heavy Virtual Machines: To fix compatibility, teams used full Virtual Machine snapshots. But VMs take gigabytes of disk space, minutes to boot up, and consume heavy memory just to run an entire duplicate guest operating system.
    • 🐌 Slow Scaling: If website traffic surged, launching 5 new VMs took 3 to 5 minutes each.

    How Present Technology Solves It:

    The cloud industry standardized on Docker Containers:

    1. Self-Contained Portability: Your code, runtime, system tools, and exact library versions are packaged together into one sealed, standardized box (a Container).
    2. Identical Everywhere: If a container runs on your laptop, it is mathematically guaranteed to run identically on GCP, AWS, or any Linux server.
    3. Blazing Fast & Lightweight: Containers share the host operating system's kernel. They start up in milliseconds instead of minutes and take megabytes of space instead of gigabytes!
    mermaid

    🚒 Real-World Analogy: Cargo Shipping Containers

    Before 1956, shipping goods internationally was chaotic:

    mermaid
    • Before Standard Shipping Containers: Workers manually loaded loose barrels of wine, wooden crates of fruit, and sacks of grain onto ships. Trucks and trains could not easily fit each other's goods.
    • After Standard Shipping Containers: The global shipping industry invented the Standard 20-Foot Steel Box. The crane doesn't care what is inside the box (shoes, electronics, or coffee). It only cares that the box has standard corner locks and fits every ship, truck, and train in the world!
    • Docker Containers = Software Shipping Containers: Docker is the standardized steel box for software. Cloud platforms (Google Cloud Run, GKE, Kubernetes) do not care whether your code is Python, Node.js, Java, or Go. They simply run your standardized container!

    πŸ”‘ Everyday Cloud Terms Explained in Plain English

    Let's demystify the 4 core concepts of container technology:


    1. Dockerfile vs. Container Image vs. Container

    Think of these 3 terms like baking a cake:

    mermaid
    • πŸ“ Dockerfile (The Recipe): A simple text file with step-by-step instructions on how to build your application (e.g., "1. Install Python, 2. Copy my code, 3. Run app.py").
    • πŸ“¦ Container Image (The Frozen Cake Mix Box): The static, immutable (read-only) package created after building your Dockerfile. It contains your code and all libraries frozen in time. You store and share this image.
    • πŸŽ‚ Container (The Live Cake): The active, running instance of an image. You can launch 1, 10, or 1,000 running containers simultaneously from a single Container Image!

    2. Virtual Machine (VM) vs. Container: Key Differences

    FeatureπŸ–₯️ Virtual Machine (Compute Engine)🐳 Container (Docker / Cloud Run)
    Boot Time1 to 3 minutes100 milliseconds to 2 seconds
    Size on Disk10 GB to 50 GB20 MB to 300 MB
    Operating SystemHas its own full Guest OSShares host machine's OS kernel
    Resource UsageHeavy (reserves fixed RAM/CPU)Extremely lightweight (uses only what it needs)
    Best ForLegacy apps, custom kernel driversModern microservices, APIs, scalable web apps

    3. What is Google Cloud Artifact Registry?

    Once you build a Container Image on your computer, where do you store it so your cloud servers can access and run it?

    Real-World Analogy Mapping:

    Think of Google Cloud Artifact Registry as a Private App Store / Secure Warehouse for your container images:

    mermaid
    • When you finish building your container image, you docker push it into Artifact Registry.
    • Artifact Registry securely stores, versions, and automatically scans your images for security vulnerabilities.
    • When Google Cloud Run or GKE needs to scale up, it instantly pulls the container image directly from Artifact Registry!
    πŸ’‘

    Artifact Registry vs. Container Registry (GCR) In older GCP tutorials, you might see gcr.io (Google Container Registry). Google has replaced it with Artifact Registry (pkg.dev), which is faster, cheaper, supports fine-grained IAM permissions, and stores not just Docker containers, but also Maven, npm, Python, and Go packages!


    πŸ§ͺ Hands-on Lab Activity: Build & Push a Container to GCP Artifact Registry

    In this hands-on lab, we will:

    1. Enable the Artifact Registry API.
    2. Create a private Docker repository in Artifact Registry.
    3. Write a simple Python web application and a Dockerfile.
    4. Build the container image locally in Cloud Shell.
    5. Authenticate Docker with GCP and push the image to Artifact Registry!

    Step 1: Enable the Artifact Registry API

    Option A: Web Console UI

    1. Log into console.cloud.google.com.
    2. Press /, type Artifact Registry API, and click on it.
    3. Click Enable.

    Option B: Cloud Shell CLI

    1. Open Cloud Shell (>_).
    2. Run:
      bash
      gcloud services enable artifactregistry.googleapis.com
      

    Step 2: Create a Docker Repository in Artifact Registry

    We need a dedicated repository to store our Docker container images.

    Option A: Web Console UI

    1. In the left navigation menu ☰, go to CI/CD (or Tools) β†’\rightarrow Artifact Registry β†’\rightarrow Repositories.
    2. Click Create Repository at the top.
    3. Configure the repository:
      • Name: my-docker-repo
      • Format: Docker
      • Mode: Standard
      • Location type: Region β†’\rightarrow asia-south1 (or your preferred region, e.g. us-central1)
      • Encryption: Google-managed key
    4. Click Create.

    Option B: Cloud Shell CLI

    bash
    gcloud artifacts repositories create my-docker-repo \
        --repository-format=docker \
        --location=asia-south1 \
        --description="My First Docker Repository on GCP"
    

    Step 3: Write a Simple Web App & Dockerfile in Cloud Shell

    Let's create a minimal web application using Python. Copy and paste each command block one by one into Cloud Shell:

    3.1: Create Project Directory

    bash
    mkdir -p ~/gcp-container-lab && cd ~/gcp-container-lab
    

    3.2: Create the Python Web Server (app.py)

    πŸ’‘

    How Code Files are Created in this Lab (2 Ways)

    • ⚑ Fast 1-Click Way (Recommended): Simply copy & paste the cat << 'EOF' ... EOF command block below directly into your Cloud Shell terminal. It creates and saves app.py automatically in 1 second!
    • πŸ“ Manual Way (If you want to edit code):
      1. Open editor: nano app.py
      2. Paste code: Ctrl+V (or right-click β†’\rightarrow Paste)
      3. Save: Ctrl+O then press Enter
      4. Exit: Ctrl+X
      5. (Or click the graphical Open Editor πŸ“ button in Cloud Shell).
    bash
    cat << 'EOF' > app.py
    from http.server import HTTPServer, BaseHTTPRequestHandler
    
    class SimpleHandler(BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            html = """
            <!DOCTYPE html>
            <html>
            <head>
                <title>GCP Container Lab</title>
                <style>
                    body { font-family: sans-serif; background: #0f172a; color: #f8fafc; text-align: center; padding: 50px; }
                    .box { background: #1e293b; padding: 30px; border-radius: 12px; display: inline-block; box-shadow: 0 10px 25px rgba(0,0,0,0.5); }
                    h1 { color: #38bdf8; }
                </style>
            </head>
            <body>
                <div class="box">
                    <h1>🐳 Hello from Docker Container on Google Cloud!</h1>
                    <p>Packaged and delivered safely via <strong>Artifact Registry</strong>.</p>
                </div>
            </body>
            </html>
            """
            self.wfile.write(html.encode('utf-8'))
    
    if __name__ == '__main__':
        server = HTTPServer(('0.0.0.0', 8080), SimpleHandler)
        print("Server running on port 8080...")
        server.serve_forever()
    EOF
    

    Anatomy Breakdown: What is happening in app.py line-by-line?

    Code LineWhat It Does In Plain English
    from http.server import HTTPServer, BaseHTTPRequestHandlerImports Python's built-in web server library (no external pip install required!).
    class SimpleHandler(BaseHTTPRequestHandler):Defines a custom handler that teaches Python how to respond to web browser requests.
    def do_GET(self):Automatically runs whenever a user opens our URL in their browser (an HTTP GET request).
    self.send_response(200)Sends standard HTTP 200 OK status code to tell the browser "Success! The page is ready."
    self.send_header('Content-type', 'text/html')Tells the browser to interpret the incoming content as an HTML webpage.
    self.end_headers()Marks the end of HTTP header instructions.
    html = """..."""The actual HTML/CSS code defining the webpage layout, dark theme colors, and headline.
    self.wfile.write(html.encode('utf-8'))Converts the HTML text into bytes and sends it across the network to the browser.
    server = HTTPServer(('0.0.0.0', 8080), SimpleHandler)Starts the server listening on Port 8080 across all available network interfaces (0.0.0.0).
    server.serve_forever()Keeps the server running indefinitely so it never shuts down while waiting for visitors.

    3.3: Create the Dockerfile (The Recipe)

    (Shortcut: nano Dockerfile β†’\rightarrow paste β†’\rightarrow Ctrl+O β†’\rightarrow Enter β†’\rightarrow Ctrl+X or use 1-click command below)

    bash
    cat << 'EOF' > Dockerfile
    # Step 1: Use lightweight Python base image
    FROM python:3.11-slim
    
    # Step 2: Set working directory inside the container
    WORKDIR /app
    
    # Step 3: Copy our application code into the container
    COPY app.py .
    
    # Step 4: Expose port 8080
    EXPOSE 8080
    
    # Step 5: Command to run when the container starts
    CMD ["python", "app.py"]
    EOF
    

    Anatomy Breakdown: What is happening in Dockerfile line-by-line?

    Dockerfile InstructionWhat It Does In Plain English
    FROM python:3.11-slimThe Foundation: Downloads a clean, lightweight, official Linux image with Python 3.11 pre-installed.
    WORKDIR /appThe Workspace: Creates an internal folder named /app inside the container and navigates into it (cd /app).
    COPY app.py .The Code Transfer: Copies app.py from our Cloud Shell into the container's /app directory (.).
    EXPOSE 8080The Door Opening: Informs Docker that our application communicates on network Port 8080.
    CMD ["python", "app.py"]The Startup Trigger: The default command executed automatically when the container powers on.

    Step 4: Build & Test the Docker Image Locally

    Now, let's compile our Dockerfile recipe into a real Container Image using Docker in Cloud Shell:

    bash
    docker build -t hello-app:v1 .
    

    Anatomy Breakdown: What does docker build do?

    • docker build: Tells Docker to read our Dockerfile and package everything into an image.
    • -t hello-app:v1: Tags (names) the image hello-app with version number v1.
    • . (the period at the end): Specifies the build contextβ€”tells Docker to look for Dockerfile and app.py in the current directory.

    Let's test running our container locally inside Cloud Shell:

    bash
    # 1. Run container in background on port 8080
    docker run -d -p 8080:8080 --name test-app hello-app:v1
    
    # 2. Test with curl
    curl http://localhost:8080
    
    # 3. Stop and remove the test container
    docker stop test-app && docker rm test-app
    

    Anatomy Breakdown: What does docker run do?

    • -d (Detached Mode): Runs the container in the background so your terminal remains free to type commands.
    • -p 8080:8080 (Port Mapping): Maps [Cloud Shell Port 8080]:[Container Internal Port 8080]. When traffic hits Cloud Shell on port 8080, Docker redirects it directly into the container!
    • --name test-app: Assigns a human-friendly name to the running container instance.
    • hello-app:v1: The specific image blueprint to launch.

    Step 5: Tag & Push Your Container to Artifact Registry

    To push our container image to Google Cloud, we must tag it with the exact Artifact Registry path format:

    Format:Β REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/IMAGE_NAME:TAG\text{Format: } \text{REGION}\text{-docker.pkg.dev/}\text{PROJECT\_ID/}\text{REPOSITORY\_NAME/}\text{IMAGE\_NAME:TAG}

    1. Authenticate Docker with Google Cloud

    Run this command so Docker has permission to upload directly to your GCP repository:

    bash
    gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://asia-south1-docker.pkg.dev
    

    Anatomy Breakdown: What does this command do?

    • gcloud auth print-access-token: Generates a temporary, secure OAuth2 security token for your Google account.
    • | (Pipe): Hands that token directly to Docker's login system.
    • docker login -u oauth2accesstoken --password-stdin ...: Authenticates Docker with Google's Artifact Registry servers safely without asking you to type your password!

    2. Get Your Current Project ID & Tag the Image

    bash
    PROJECT_ID=$(gcloud config get-value project)
    
    docker tag hello-app:v1 asia-south1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/hello-app:v1
    

    Anatomy Breakdown: What does docker tag do?

    • It attaches an official shipping label (full URL address) to our local image hello-app:v1 so Docker knows exactly which cloud region (asia-south1), project ID (gcp-lab-day07), and repository (my-docker-repo) to upload it to.

    3. Push the Image to Artifact Registry

    bash
    docker push asia-south1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/hello-app:v1
    

    Anatomy Breakdown: What does docker push do?

    • Uploads the image layer by layer over encrypted HTTPS into your private Google Cloud Artifact Registry warehouse!

    πŸš€

    Alternative 1-Step Method: Cloud Build If you ever encounter local network hiccups in Cloud Shell, you can also build and push directly in one step using Google Cloud Build:

    bash
    gcloud builds submit --tag asia-south1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/hello-app:v1
    

    Step 6: Verify Your Image in Artifact Registry

    1. Open your GCP Console β†’\rightarrow Search for Artifact Registry β†’\rightarrow Click Repositories.
    2. Click on my-docker-repo.
    3. You will see hello-app listed!
    4. Click on hello-app to see your tagged version v1, upload timestamp, and image size (around 50 MB).

    🧹 Step 7: Clean Up All Resources (Credit Safety Guarantee)

    To ensure your GCP Free Trial costs remain at $0.00, clean up the lab repository and local files:

    Option A: Web Console UI

    1. Go to Artifact Registry β†’\rightarrow Repositories.
    2. Check the box next to my-docker-repo β†’\rightarrow Click Delete β†’\rightarrow Type my-docker-repo to confirm.

    Option B: Cloud Shell CLI

    bash
    # 1. Delete the Artifact Registry repository and all images inside
    gcloud artifacts repositories delete my-docker-repo \
        --location=asia-south1 \
        --quiet
    
    # 2. Clean up local test files and local docker images
    rm -rf ~/gcp-container-lab
    docker rmi hello-app:v1 2>/dev/null || true
    

    Signal vs. Noise: Key Concepts & Noise Filter

    🧠

    Good to Know (Key Concepts)

    • Dockerfile: Text recipe containing instructions to build an image.
    • Container Image: Immutable frozen package containing your code, runtime, and libraries.
    • Container: Running live instance of an image.
    • Artifact Registry: Google's modern, secure storage repository for container images and language packages (pkg.dev).
    • Image Tagging Format: LOCATION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE:TAG.
    ℹ️

    Noise Filter (Don't Memorize)

    • Do NOT memorize complex Docker build optimization flags (like multi-stage build caching arguments or BuildKit syntax) right now.
    • Do NOT worry about deprecated gcr.io commands; always use pkg.dev with Artifact Registry for modern projects.

    Common Doubts & Interview Traps

    Q1: What is the main difference between a Virtual Machine and a Container?

    • Answer: A Virtual Machine virtualizes the physical hardware and runs a complete, heavy Guest Operating System. A Container virtualizes the Operating System, sharing the host OS kernel to run lightweight, isolated application packages in milliseconds.

    Q2: What does it mean when we say a Container Image is "immutable"?

    • Answer: "Immutable" means unchangeable. Once an image is built (e.g., hello-app:v1), its contents cannot be modified. If you change even one line of code, you build a new image with a new tag (e.g., hello-app:v2). This guarantees 100% consistency across testing and production.

    Q3: Can I run a Docker container without pushing it to Artifact Registry?

    • Answer: On your local computer, yes. But to run containers serverlessly on Google Cloud (such as Google Cloud Run or GKE), Google Cloud needs a centralized cloud storage warehouse to pull the image fromβ€”which is Artifact Registry.

    Daily Practice Drill & Self-Check

    Test your understanding of today's lesson:

    text
    // Try answering these:
    1. Which file contains the step-by-step recipe to build a container image: a Dockerfile or a Container Image?
    2. Why are containers able to boot up in milliseconds compared to Virtual Machines which take minutes?
    3. What is the modern GCP service used to store Docker images: Container Registry or Artifact Registry?
    
    πŸ’‘ Click for Solutions
    1. Dockerfile! It is the text file recipe.
    2. Because containers share the host OS kernel and do not need to boot up a full separate Guest Operating System!
    3. Artifact Registry (pkg.dev)!

    πŸŽ‰ Congratulations! You have completed Day 15 and your first step into Week 3: Serverless & Containers!

    You now understand the magic of Docker containers, how images work, and how to store them securely in Google Cloud Artifact Registry.

    Tomorrow on Day 16, we will use the container image you learned to build today to launch a serverless web app on Cloud Run - Serverless Container Apps!


    ← 14 - Hands-on Lab - High-Availability Web Cluster | Next Topic β†’ 16 - Cloud Run - Serverless Container Apps