cirocosta / estaleiro

building container images with bill of materials
Apache License 2.0
2 stars 0 forks source link

estaleiro [istaˈlejru]





masculine noun - shipyard

estaleiro allows you to ship container images with confidence - a declarative approach to dealing with the last mile in building container images, so you can have more control (through transparency) over what you ship.




HIGHLY EXPERIMENTAL - DO NOT USE THIS


Table of Contents

problem set

Keeping track of what has been added to a container image that one is about to ship if hard.

With the versatility of Dockerfiles, it's quite easy to shoot itself in the foot when it comes to either installing dependencies that one couldn't even consume, or that wouldn't be wise.

While it's great to talk about best practices, it's hard to enforce them.

estaleiro

estaleiro sits at the very final portion of your image building process, gating what gets into the final container image that is supposed to be shipped to your customers.


                                   .-----------.
    somehow you build stuff  --->  | estaleiro | --> final container image
                                   *-----------*              +
                                                       bill of materials

    with the Dockerfiles and                                  |
     build process that you                                   |
         already have                                         .

                                                      anything that gets into the
                                                      final container image *MUST*
                                                      declare where it comes from.

It leverages buildkit as a way of implementing a convention of how the last stage in building a container image (i.e., gathering binaries built in previous steps), putting guard-rails where needed, and enforcing a set of rules where needed.

Here's an example of how that looks like in practice:

  1. bring your Dockerfile that you've already been using to build your binary
FROM golang AS base

    ENV CGO_ENABLED=0
    RUN apt update && apt install -y git

    ADD . /src
    WORKDIR /src

    RUN go mod download

FROM base AS build

    RUN go build \
        -tags netgo -v -a \
        -o /usr/local/bin/estaleiro \
        -ldflags "-X main.version=$(cat ./VERSION) -extldflags \"-static\""
  1. bring a estaleiro file that describes how to package that binary produced
# syntax = cirocosta/estaleiro-frontend

# the final image to produce
#
image "cirocosta/estaleiro" {
  base_image = "ubuntu:bionic"

  apt {
    package "ca-certificates" {}
  }

  file "/usr/local/bin/estaleiro" {
    from_step "build" {
      path = "/bin/estaleiro"
    }
  }
}

# performs the build of `estaleiro`.
#
step "build" {
  dockerfile = "./Dockerfile"
  target     = "build"

  source_file "/bin/estaleiro" {
    vcs "git" {
      ref        = "${estaleiro-commit}"
      repository = "https://github.com/cirocosta/estaleiro"
    }
  }
}

Having those pieces in, estaleiro creates the intermediary representation to be used by buildkitd to build the final container image that starts from ubuntu:bionic, has the ca-certificates package installed, and the file that the Dockerfile built - all while keeping track of their versions and sources along the way in the form of a bill of materials:

base_image:
  name: docker.io/library/ubuntu
  digest: sha256:c303f19cfe9ee92badbbbd7567bc1ca47789f79303ddcef56f77687d4744cd7a
  packages:
    - name: fdisk
      version: 2.31.1-0.4ubuntu3.3
      source_package: util-linux
      architecture: amd64
    - name: libpam-runtime
      version: 1.1.8-3.6ubuntu2.18.04.1
      source_package: pam
      architecture: all
    # ...

changeset:
  files:
    - name: "/usr/local/bin/seataleiro"
      digest: "sha256:89f687d4744cd779303ddc7ef56f77c303f19cfe9ee92badbbbd7567bc1ca47a"
      source:
        - url: https://github.com/cirocosta/estaleiro
          type: git
          ref: 6a4d0b73673a1863a62b7ac6cbde4ae7597c56d7
      from_step:
        name: "build"
        dockerfile_digest: "sha256:9303ddc7ef56f77c303f19cfe9ee92badbbbd7567bc189f687d4744cd77ca47a"
  packages:
    - name: ca-certificates
      version: "20180409"
      source_package: ""
      architecture: all
      location:
          uri: http://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20180409_all.deb
          name: ca-certificates_20180409_all.deb
          size: "150932"
          md5sum: eae40792673dcb994af86284d0a01f36
      source:
        - uri: http://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20180409.dsc
          name: ca-certificates_20180409.dsc
          size: "1420"
          md5sum: cd1f6540d0dab28f897e0e0cb2191130cdbf897f8ce3f52c8e483b2ed1555d30
        - uri: http://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20180409.tar.xz
          name: ca-certificates_20180409.tar.xz
          size: "246908"
          md5sum: 7af6f5bfc619fd29cbf0258c1d95107c38ce840ad6274e343e1e0d971fc72b51
    # and all of its dependencies too ...

how to use

THIS IS STILL HIGHLY EXPERIMENTAL

All that you need is:

Having an estaleiro file (like the estaleiro.hcl that you find in this repo), direct docker build to it via a regular --file (-f), having DOCKER_BUILDKIT=1 set as the environent variable:

# create an `estaleiro.hcl` file.
# 
# note.: the first line (with syntax ...) is important - it's
#        what tells the docker engine to fetch our implementation
#        of `estaleiro`, responsible for creating the build
#        definition.
#
$ echo "# syntax=cirocosta/estaleiro
image "cirocosta/sample" {
  base_image = "ubuntu:bionic"
}
" > ./estaleiro.hcl

# instruct `docker` to build our image
#
$ docker build -t test -f ./estaleiro.hcl
[+] Building 9.4s (4/4) FINISHED

# retrieve the bill of materials from the filesystem
#
$ docker create --name tmp
$ docker cp tmp:/bom/merged.yml ./bom.yml

references

license

See ./LICENSE.