Building Containers – Distroless, Buildpacks & AR

Google Cloud Building Containers Best Practices

Package a single app per container

  • An “app” is considered to be a single piece of software, with a unique parent process, and potentially several child processes.
  • A container is designed to have the same lifecycle as the app it hosts, so each of the containers should contain only one app. When a container starts, so should the app, and when the app stops, so should the container. for e.g. in the case of the classic Apache/MySQL/PHP stack, each component must be hosted in a separate container.

Properly handle PID 1, signal handling, and zombie processes

  • Linux signals are the main way to control the lifecycle of processes inside a container.
  • The app within the container should handle the Linux signals, as well as the best practice of a single app per container should be implemented.
  • Process identifiers (PIDs) are unique identifiers that the Linux kernel gives to each process.
  • PIDs are namespace, i.e. the containers PIDs are different from the host and are mapped to the PIDs on the host system.
  • Docker and Kubernetes use signals to communicate with the processes inside containers, most notably to terminate them.
  • Both Docker and Kubernetes can only send signals to the process that has PID 1 inside a container.
  • For Signal handling and Zombie processes following can be followed
    • Run as PID 1 and register signal handlers
      • Launch the process with the CMD and/or ENTRYPOINT instructions in the Dockerfile, which would give the PID 1 to the process
      • Use the built-in exec command to launch the process from the shell script. The exec command replaces the script with the program and the process then inherits PID 1.
    • Enable process namespace sharing in Kubernetes
      • Process namespace sharing for a Pod can be enabled where Kubernetes uses a single process namespace for all the containers in that Pod.
      • Kubernetes Pod infrastructure container becomes PID 1 and automatically reaps orphaned processes.
    • Use a specialized init system
      • Init system such as tini created especially for containers that can be used to handle signals and reaps any zombie processes

Optimize for the Docker build cache

  • Images are built layer by layer, and in a Dockerfile, each instruction creates a layer in the resulting image.
  • Docker build cache can accelerate the building of container images.
  • During a build, when possible, Docker reuses a layer from a previous build and skips a potentially costly step.
  • Docker can use its build cache only if all previous build steps used it.

Use Multi-Stage Builds

  • Multi-stage builds allow separating the build environment from the runtime environment in a single Dockerfile using multiple FROM statements.
  • Build tools, compilers, and development dependencies are isolated in early stages and only the final artifacts are copied into the minimal runtime image.
  • This significantly reduces the final image size and attack surface.
  • Google Cloud Build supports multi-stage Dockerfiles natively and recommends separating building of the application from assembling its runtime container.
  • Example pattern: use a full SDK image to compile, then copy binaries into a distroless or minimal base image for production.

Use Distroless or Minimal Base Images

  • Distroless images (maintained by Google at gcr.io/distroless/) contain only the application runtime and its dependencies — no package managers, shells, or other programs.
  • Distroless images drastically reduce the attack surface and result in smaller image sizes with fewer CVE findings.
  • Available for multiple runtimes: Java, Python, Node.js, Go (static), .NET, and base/CC variants.
  • For cases where a shell is needed for debugging, consider using the :debug tag variants in non-production environments only.
  • Alternative minimal base images include Alpine Linux, Debian Slim, and Google’s managed base images.

Remove unnecessary tools

  • Remove unnecessary tools helps reduce the attack surface of the app by removing any unnecessary tools.
  • Avoid running as root inside the container: this method offers the first layer of security and could prevent attackers from modifying files
  • Launch the container in read-only mode using the --read-only flag from the docker run or by using the readOnlyRootFilesystem option in Kubernetes.
  • Apply seccomp profiles to containers to restrict system calls to the kernel — GKE supports the default containerd seccomp profile and custom profiles for additional hardening.
  • Use GKE Sandbox (gVisor) to run untrusted workloads with an additional kernel-level isolation layer that intercepts system calls.

Build the smallest image possible

  • Smaller image offers advantages such as faster upload and download times
  • To reduce the size of the image, install only what is strictly needed
  • Use multi-stage builds to separate build dependencies from runtime dependencies
  • Prefer distroless or scratch-based images for compiled languages (Go, Rust)
  • Combine RUN commands with && to reduce the number of layers and avoid leftover files from intermediate steps

Scan images for vulnerabilities

  • For vulnerabilities, as the containers are supposed to be immutable, the best practice is to rebuild the image, patches included, and redeploy it
  • As containers have a shorter lifecycle and a less well-defined identity than servers, a centralized inventory system would not work effectively
  • Artifact Analysis (formerly Container Analysis) can scan images for security vulnerabilities in publicly monitored packages, including OS, Java, Go, Python, and Node.js packages
  • Artifact Analysis provides both automatic scanning (triggered on every push to Artifact Registry) and on-demand scanning (manual scans of local or registry images)
  • Generate Software Bill of Materials (SBOM) for your container images to track all dependencies and license compliance
  • Integrate vulnerability scanning into CI/CD pipelines using Cloud Build and Binary Authorization to enforce that only verified, signed images are deployed

Using public image

  • Consider before using public images as you cannot control what’s inside them
  • Public image such as Debian or Alpine can be used as the base image and building everything on top of that image
  • Use Assured Open Source Software (Assured OSS) from Google for curated and tested open source packages
  • Pin images using SHA256 digest references (e.g., image@sha256:abc123...) rather than mutable tags like :latest to ensure reproducibility

Managed Base Images

  • Managed base images are base container images that are automatically patched by Google for security vulnerabilities, using the most recent patches available from the project upstream
  • Google provides Container-Optimized OS (cos) for running containers on Compute Engine and GKE nodes, which is automatically updated with security patches

Store Images in Artifact Registry

  • Container Registry was shut down on March 18, 2025 — all container images must now be stored in Artifact Registry
  • Artifact Registry supports both container images and non-container artifacts (Maven, npm, Python packages, etc.)
  • Artifact Registry provides integrated vulnerability scanning through Artifact Analysis, IAM-based access control, and regional/multi-regional storage options
  • Automatic migration tools are available to migrate existing gcr.io endpoints to Artifact Registry without downtime

Use Cloud Native Buildpacks

  • Google Cloud’s buildpacks transform application source code into production-ready OCI container images without requiring a Dockerfile
  • Buildpacks automatically detect the application language and framework, install dependencies, and produce optimized images following container best practices
  • Supported languages include Go, Java, Node.js, Python, .NET, Ruby, and PHP
  • Buildpacks produce reproducible builds and automatically apply security patches to the builder stack
  • Integrated with Cloud Build, Cloud Run, and App Engine for seamless source-to-production workflows
  • The default builder uses Ubuntu 24 as of 2025 (gcr.io/buildpacks/builder)

Supply Chain Security

  • Binary Authorization ensures that only trusted, signed container images are deployed to GKE, Cloud Run, or Distributed Cloud environments
  • Implement SLSA (Supply chain Levels for Software Artifacts) framework to establish provenance and verify the integrity of build artifacts
  • Software Delivery Shield provides an end-to-end software supply chain security solution across Google Cloud, covering source, build, artifacts, deployment, and runtime
  • Use attestations in CI/CD pipelines to cryptographically verify that images pass required checks (vulnerability scan, code review, testing) before deployment
  • Integrate with Security Command Center to view container vulnerability findings alongside other cloud security risks

GCP Certification Exam Practice Questions

  • Questions are collected from Internet and the answers are marked as per my knowledge and understanding (which might differ with yours).
  • GCP services are updated everyday and both the answers and questions might be outdated soon, so research accordingly.
  • GCP exam questions are not updated to keep up the pace with GCP updates, so even if the underlying feature has changed the question might not be updated
  • Open to further feedback, discussion and correction.
  1. When creating a secure container image, which two items should you incorporate into the build if possible?
    1. Use public container images as a base image for the app.
    2. Build the smallest image possible
    3. Use many container image layers to hide sensitive information.
    4. Package multiple applications in a container
  2. Your organization wants to ensure only signed and verified container images are deployed to your GKE clusters. Which Google Cloud service should you use?
    1. Artifact Analysis
    2. Cloud Armor
    3. Binary Authorization
    4. Container-Optimized OS
  3. Which Google Cloud service should you use to store and manage your container images now that Container Registry has been shut down?
    1. Cloud Storage
    2. Artifact Registry
    3. Container Registry (deprecated)
    4. Cloud Build
  4. You want to containerize your application without writing a Dockerfile. Which Google Cloud feature allows source-to-image transformation?
    1. Cloud Build triggers
    2. Cloud Native Buildpacks
    3. Container-Optimized OS
    4. Artifact Analysis
  5. Which type of base image is recommended by Google for production containers to minimize the attack surface?
    1. Ubuntu full image
    2. Alpine with development tools
    3. Distroless images
    4. CentOS base image
  6. Your team needs to automatically scan container images for OS and language package vulnerabilities every time an image is pushed to the registry. Which Google Cloud feature provides this?
    1. Cloud Build steps
    2. Binary Authorization attestations
    3. Artifact Analysis automatic scanning
    4. GKE Security Posture dashboard

References