apko Documentation

repository·main·Indexed 23 days ago

https://github.com/chainguard-dev/apko

apko is a tool for building OCI-compliant container images from declarative YAML configurations. It ensures reproducibility and SBOM generation by avoiding arbitrary Unix commands, instead relying on the installation of Alpine packages. The project includes go-apk, a native Go implementation of the Alpine Package Keeper (apk) utility, and provides capabilities for managing users, filesystem mutations, and image layering strategies.

Tokens
12.3K
Snippets
25
Records
65
Agent score
82%

What's inside apko

  1. Enable package caching in go-apk

    main

    You can enable local caching of apk packages to speed up installations, especially for large packages or slow network environments. When enabled, the library checks the cache for requested apk files before attempting a download. If a cache miss occurs, the file is downloaded and then stored in the cache for subsequent use.

    To enable caching, pass the apk.WithCache() option to the apk.New() function.

  2. How Busybox symlinks are handled in apko

    main

    Instead of running busybox --install during the build process (which would require a runner environment and path resolution), apko manages Busybox symlinks directly using an internal list.

    When apko detects /bin/busybox in an image, it performs the following steps:

    1. Checks the apk installed database at /usr/lib/apk/db/installed to identify the installed Busybox version.
    2. Simplifies the version to basic semver (e.g., converting v1.36.1-r3 to 1.36.1).
    3. Matches this version against its internal symlink list.
    4. If the exact version is not found, it falls back to the latest supported version in its list.
  3. How the apko build process works

    main

    apko builds OCI-compliant images from a declarative apko.yaml configuration file. Unlike traditional Dockerfiles, apko does not allow the execution of arbitrary commands; instead, all content is generated by installing apk packages.

    The high-level build lifecycle is:

    1. Configuration: A build.Context is created using the apko.yaml file, which is parsed into an ImageConfiguration.
    2. Layer Construction: The core of the build happens via build.Context.BuildLayer(). This process lays out the desired filesystem in a temporary working directory and packages it into a .tar.gz layer file.
    3. SBoM Generation: A Software Bill of Materials (SBoM) is generated based on the installed packages.
    4. OCI Packaging: The layer .tar.gz is converted into an OCI image tarball using oci.BuildImageTarballFromLayer().

    This approach ensures that images are reproducible and derived strictly from package management rather than imperative shell scripts.

  4. Understand apko's layering implementation

    main

    apko uses a heuristic-based layering implementation designed to be simple and automatic. Unlike Dockerfile where users manually arrange layers, apko aims to minimize manual configuration by using internal heuristics to produce efficient layers.

    Key design goals include:

    • Simplicity: It is designed to be enabled without requiring authors to manually manage layer order.
    • Reproducibility: Layering decisions are based on stable, internal logic rather than external data sources (like package popularity indices) to ensure builds remain reproducible over time.
    • Efficiency: The strategy is optimized to maximize layer sharing across different images and minimize the size of incremental updates for a single image.
  5. Using apko as a library

    main

    Currently, apko is primarily distributed as a CLI tool. While there are no official plans to prioritize a formal library implementation, the maintainers welcome patches to move towards this goal.

    If you choose to wrap the apko CLI in your own application, be aware that breaking changes to the CLI interface are possible. Such changes will be announced in NEWS.md.

  6. How FullFS and Filesystem implementations work

    main

    The standard Go fs.FS interface is read-only and lacks support for write operations, symlinks, hardlinks, or permission management (chmod/chown).

    chainguard.dev/apko/pkg/apk/fs provides a FullFS interface that extends fs.FS with full read-write, chmod/chown, devices, and symlinks capabilities. It is fully compliant with fs.FS and can be used wherever a standard filesystem interface is required.

    There are two primary implementations of FullFS:

    1. memfs (fs.NewMemFS()): An in-memory implementation. It is fully functional but limited by available system memory when handling large files.
    2. rwosfs: An on-disk implementation. It provides full capabilities (including symlinks, devices, and case-sensitivity) even if the underlying host filesystem does not support them, by storing file metadata in-memory while keeping file contents on disk.
  7. How the `origin` layering strategy works

    main

    The origin strategy partitions an image into multiple layers based on package grouping to maximize deduplication. The process follows these steps:

    1. Lazy Install: apko performs a virtual installation of all packages, tracking file metadata and tar offsets in memory without writing bytes to disk. This ensures the layered rootfs is identical to a single-layer rootfs.
    2. Package Grouping: Packages are grouped into layers using three heuristics:
      • By Origin: Packages from the same build origin (e.g., the same foo.yaml in Wolfi) are grouped together because they tend to change at the same time.
      • By Replaces: If a package replaces another (e.g., libxcrypt replacing libcrypt1), both are placed in the same layer to ensure correct filesystem overlay behavior.
      • Overflow: After grouping by origin and replaces, remaining packages are sorted by size. The top $budget - 1 groups form individual layers, and all remaining smaller packages are merged into a single "overflow" layer.
    3. Top Layer: Any files not associated with a specific package (such as OS metadata like /etc/apk/world or /usr/lib/apk/db/installed) are written to a final "top" layer.
  8. Impact of path mutations on layer deduplication

    main

    Using apko's ability to mutate paths—such as modifying file ownership or permissions—will impact layer deduplication.

    Because layers are stored as tarballs, there is no efficient way to overwrite only file metadata in the top layer without rewriting the entire layer. Consequently, if you modify the permissions of a file in one image, that layer may no longer deduplicate with similar layers in other images that do not have those same modifications.

  9. Understand the Layer Build steps

    main

    The actual filesystem construction occurs during the BuildLayer() phase. The process follows these specific steps to ensure a valid and secure container filesystem:

    1. Validation: Validates the ImageConfiguration and applies default values.
    2. APK Initialization: Sets up the necessary apk directories within the working directory.
    3. Package Tagging: Adds additional tags for apk packages.
    4. Account Management: Executes MutateAccounts() to create the specified users and groups.
    5. Permissions: Sets file and directory permissions (using chmod/chown). If the underlying filesystem or user permissions prevent direct manipulation, apko tracks the intended ownership/permissions and applies them during the final tar stream creation.
    6. Busybox Symlinks: Creates symlinks for busybox commands, as busybox acts as a multi-call binary.
    7. Library Configuration: Updates ldconfig by parsing ELF headers to create necessary library symlinks.
    8. OS Metadata: Creates the /etc/os-release file.
    9. Supervision (Optional): If configured, installs the s6 supervisor and creates its configuration files.
  10. Understand how directory timestamps affect deduplication

    main

    In apko, packages often share overlapping directories. If these directories have different timestamps, cross-image deduplication will fail even if the file contents are identical.

    To solve this, apko uses a Timestamp Synthesis strategy: instead of dropping timestamps (which can break applications that stat files) or dropping directories (which relies on implementation-defined behavior), apko makes parent directories adopt the timestamps of their child files. This ensures that timestamps are synthesized from files that exist only within that specific layer, preventing inter-layer influence from invalidating deduplication.