Skip to content
andrewbaxterPublic

About

Dinker, dinky Docker images

Resources

Stars

12 stars

Watchers

1 watching

Forks

Latest commit

 

History

45 Commits

Folders and files

Repository files navigation

Docker images are basically zip files, why should building them take any more privileges than writing files? This is a zero-privilege, zero-capability, zero-permission, zero-container, zero-chroot, tiny OCI image creator.

All it can do is take a base image and add files to it, updating standard metadata (command, environment, ports), and pushing the result somewhere.

Installation

go install github.com/andrewbaxter/dinker@latest

Or use the image ghcr.io/andrewbaxter/dinker.

Usage

Terraform (via Terrars)

See https://github.com/andrewbaxter/terrars/tree/master/helloworld which has an example of creating a statically linked binary and publishing it as a minimal Docker image, or use the Terraform provider directly.

Github Actions

In order to push images you need to set Settings / Code and automation / Actions / General / Workflow permissions to Read and write permissions.

Add this workflow (using rust, for example) in .github/workflows/build.yaml:

name: Build
on:
  push:
    tags:
      - "*"
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: x86_64-unknown-linux-musl
      - run: cargo build --target x86_64-unknown-linux-musl --release
      - env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          cat > dinker.json << ELEPHANT
          {
            "dests": [
              {
                "ref": "docker://ghcr.io/andrewbaxter/somewords:$GITHUB_REF_NAME",
                "user": "$GITHUB_ACTOR",
                "password": "$GITHUB_TOKEN"
              },
              {
                "ref": "docker://ghcr.io/andrewbaxter/somewords:latest",
                "user": "$GITHUB_ACTOR",
                "password": "$GITHUB_TOKEN"
              }
            ],
            "arch": "amd64",
            "os": "linux",
            "files": [
              {
                "source": "target/x86_64-unknown-linux-musl/release/somewords",
                "mode": "755"
              }
            ],
            "cmd": ["/somewords"]
          }
          ELEPHANT
      - uses: docker://ghcr.io/andrewbaxter/dinker:latest
        with:
          args: /dinker dinker.json

Command line

This is an example, where I have a Go binary hello in my current directory.

  1. Create a config file named dinker.json like

    {
      "from": "docker://alpine:3.20",
      "dests": [
        {
          "ref": "docker://localhost:5000/hello:{short_hash}",
          "http": true
        }
      ],
      "files": [
        {
          "source": "hello",
          "dest": "/usr/local/bin/hello",
          "mode": "755"
        }
      ],
      "cmd": ["hello"]
    }

    This says to base the image on Alpine, add hello to /usr/local/bin, and by default run it.

    Alpine is downloaded once and cached; later builds only check whether the 3.20 tag has moved.

    When built, push it to my private registry at localhost:5000, tagged with the start of the image digest.

  2. Run dinker dinker.json (or dinker - to read the config from stdin)

  3. Done! The image digest is printed on stdout.

Library

res, err := dinkerlib.Run(ctx, dinkerlib.Spec{
	From:  &dinkerlib.Source{Ref: dinkerlib.Ref{Transport: dinkerlib.TransportRegistry, Name: "alpine:3.20"}},
	Build: dinkerlib.BuildArgs{Files: []dinkerlib.File{{Source: "hello", Dest: "/hello", Mode: "755"}}},
	Dests: []dinkerlib.Dest{{Ref: "docker://ghcr.io/me/hello:{short_hash}"}},
})

Run is a convenience over the individual steps, which you can also call yourself: Session.LoadImage, Session.Build (which returns a go-containerregistry v1.Image), Session.Push, Inspect and ResolveDigest.

Image references

Sources and destinations use the containers-transports syntax. These transports are supported:

  • docker://host/repo:tag — a registry
  • docker-daemon:repo:tag — a Docker daemon, via its HTTP API (no Docker client needed)
  • oci:path[:tag] — an OCI layout directory
  • oci-archive:path[:tag] — a tar of an OCI layout
  • docker-archive:path[:repo:tag] — a docker save tarball (destinations need the repo:tag)

If no user/password is given for a registry, credentials are read from the standard Docker config ($DOCKER_CONFIG/config.json or ~/.docker/config.json), and anonymous access is used if there are none.

Json reference

The json file has these options:

Required

  • dests

    An array of places to save or push the built image.

    The options for the elements are:

    Required

    • ref

      Where to save the built image; see Image references.

      This is a pattern - you can add the following strings which will be replaced with generated information:

      • {hash} - The hex sha256 digest of the image manifest (the same digest registries show).

      • {short_hash} - The first 8 hex digits of the hash

    Optional

    • user

      Credentials for pushing

    • password

      Credentials for pushing

    • http

      True if this dest is over http (also disables tls validation)

    • host

      For the docker-daemon transport, the daemon address, like unix:///var/run/docker.sock or tcp://host:2375. Defaults to $DOCKER_HOST, then the standard socket. TLS client certificates aren't supported.

  • files

    Files to add to the image. This is an array of objects with these fields:

    • source - Required, the location of the file on the building system. Relative paths are relative to the current directory.

    • dest - Optional, the absolute path in the image. Defaults to the source's filename at the root of the image. Missing parent directories are created with mode 755.

    • mode - Octal string with file mode (ex: 644, the default)

Required if no from

  • arch

    Defaults to from image architecture. When from has images for several platforms, picks one. May include a variant, like arm/v7.

  • os

    Defaults to from image os. When from has images for several platforms, picks one.

Optional

  • from

    Add onto the layers from this image (like FROM in Docker); see Image references. If not specified, use no base image (this will produce a single layer image with just the specified files).

  • from_user

    Credentials for from if necessary

  • from_password

    Credentials for from if necessary

  • from_http

    True if from is over http (also disables tls validation)

  • from_host

    For the docker-daemon transport, the daemon address. See host above.

  • cache_dir

    Where to cache images pulled from registries. Defaults to dinker in the user cache directory (like ~/.cache/dinker); if the system has none, nothing is cached. Set to "" to disable caching. If the registry can't be reached, a cached copy of the same tag is used, with a warning.

  • manifest_format

    oci (the default) or docker. Base image layers are converted to match.

  • dirs

    Directories to create in the image, mostly to set their mode. An array of objects with fields:

    • dest - Required, the absolute path in the image

    • mode - Optional, octal string, defaults to 755

  • add_env

    Record with string key-value pairs. Add additional default environment values, replacing any inherited variable with the same name.

  • clear_env

    Boolean. If true, don't inherit environment variables from from image.

  • working_dir

    Container working directory, defaults to from image working directory.

  • user

    User id to run process in container as. Defaults to value in from image

  • entrypoint

    Array of strings. See Docker documentation for details. This is not inherited from the base image.

  • cmd

    Array of strings. See Docker documentation for details. This is not inherited from the base image.

  • ports

    Ports within the container to expose. This is an array of records with fields:

    • port - Required, the port that the program within the container listens on

    • transport - Optional, defaults to tcp. tcp, udp or sctp.

    These are not inherited from the base image.

  • labels

    String key-value record. Arbitrary metadata. These are not inherited from the base image.

  • stop_signal

    The signal to use when stopping the container. Values like SIGTERM SIGINT SIGQUIT. This is not inherited from the base image.

Other base image settings, such as volumes and healthchecks, are inherited.

Upgrading from 0.1

  • {hash} is now the image's manifest digest, so tags made with it will change once.
  • Images use OCI media types by default. Set "manifest_format": "docker" for the old behavior.
  • New layers now go on top of the base image's layers, instead of underneath.
  • from should be a reference like docker://alpine:3.20. The old form, a local archive path plus from_pull, still works but logs a deprecation warning.
  • files[].name is replaced by files[].dest, which takes a full path.
  • Directories default to mode 755 (they were 644).
  • The containers_image_openpgp build tag is no longer needed, and /etc/containers/policy.json is no longer read.
  • The library API is new; see Library.

About

Dinker, dinky Docker images

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages