Building Docker Images in GitLab CI with Depot

Kyle Galbraith - Oct 24 '22 - - Dev Community

Today, there are three different options for building Docker images in GitLab CI, and each of them comes with security and complexity tradeoffs. Depot fixes a lot of these tradeoffs, and can be used to build images in GitLab CI quickly and securely.

Current options for building Docker images in GitLab CI

There are three different options for building images in GitLab CI/CD jobs:

  1. You can use the shell executor on your own GitLab Runners. This requires configuring the Docker Engine and granting the gitlab-runner user full root permissions to invoke docker commands.
  2. You can use Docker in Docker (dind) with registered runners. This uses the dind image provided by Docker, so it contains all the docker tools. To build new containers with dind, you have to run the runner container in privileged mode.
  3. Or, you can bind to the Docker socket by bind mounting /var/run/docker.sock into the container where your build is running.

All these options come with tradeoffs and downsides.

The first option requires you to manually configure your own runners and grant them root permissions. The second option requires that each build job get its own instance of the Docker engine, so jobs are slower because there is no layer caching, and privileged mode is still required. The third option requires you to bind mount the Docker socket into your container, which exposes the underlying host and any other processes on that host to privilege escalation.

Building Docker images in GitLab CI with Depot

With Depot, you can build your Docker images from GitLab CI/CD jobs without needing to configure shell executor runners, work around slow builds with dind, or bind mount the Docker socket. Instead, the depot CLI builds the container using Depot's remote builders, and can optionally push the resulting image to a registry.

The Depot CLI can be installed via a before_script:

before_script:
  - curl https://depot.dev/install-cli.sh | DEPOT_INSTALL_DIR=/usr/local/bin sh
Enter fullscreen mode Exit fullscreen mode

After the CLI is installed, you can build your image with the depot build command:

script:
  - depot build -t org/image:tag .
Enter fullscreen mode Exit fullscreen mode

Most likely, you will want to push your image to a registry, with the depot build --push flag. Depot uses the local Docker registry credentials when pushing, so you will need to configure those credentials using one of two options: using the docker CLI or creating the configuration manually.

Registry authentication with docker login

The easiest way to configure registry authentication is to use the docker login command. This will create a ~/.docker/config.json file that Depot will use to authenticate with your registry. Note that this requires running the Docker daemon with Docker-in-Docker (dind) to perform the login. However, you do not need to run the GitLab CI runner in privileged mode, the daemon is only used for registry login:

image: docker:20.10.16
services:
  - docker:20.10.16-dind
variables:
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: '/certs'

build-job:
  before_script:
    - apk add --no-cache curl # install curl as it is not available in the docker:20.10.16 image
    - curl https://depot.dev/install-cli.sh | DEPOT_INSTALL_DIR=/usr/local/bin sh
  script:
    - docker login registry.gitlab.com --username your-username --password $REGISTRY_TOKEN
    - depot build -t registry.gitlab.com/your-username/myimage:$CI_COMMIT_SHA . --push
  variables:
    DEPOT_TOKEN: $DEPOT_TOKEN
Enter fullscreen mode Exit fullscreen mode

With this workflow, you can build native multi-platform images directly from GitLab CI and push them to a private registry. The depot CLI is installed via curl and the dind service is configured with TLS enabled. Giving you the ability to run docker login to authenticate to a private registry, but leave the build part to Depot.

The Docker-in-Docker service is only used for authentication and not for the actual build. This means you get significantly faster builds because Depot manages the caching of layers for you, unlike dind which has no layer caching when you use docker build.

Registry authentication via ~/.docker/config.json

If you already have a Docker config.json file with your registry credentials, you can skip the docker login step and provide the config contents directly via a DOCKER_AUTH_CONFIG environment variable. Your workflow can save the contents of that variable to $HOME/.docker/config.json, and Depot will read from that file when pushing. This removes the need for the docker CLI and dind service at all in your image build:

build-image:
  before_script:
    - curl https://depot.dev/install-cli.sh | DEPOT_INSTALL_DIR=/usr/local/bin sh
    - mkdir -p $HOME/.docker
    - echo $DOCKER_AUTH_CONFIG > $HOME/.docker/config.json
  script:
    - depot build -t registry.gitlab.com/repo/image:tag . --push
  variables:
    DEPOT_TOKEN: $DEPOT_TOKEN
Enter fullscreen mode Exit fullscreen mode

Before the depot build runs, contents of the DOCKER_AUTH_CONFIG environment variable are saved as $HOME/.docker/config.json.

You may be able to generate a config.json file locally by running docker login and then copying the contents of the $HOME/.docker/config.json file and saving it as DOCKER_AUTH_CONFIG, if you have static authentication credentials for your registry.

Conclusion

There are multiple options for building Docker images in GitLab CI, each with security and complexity tradeoffs. They are not impossible to work around, but they are inconvenient and cause a lot of friction. This friction can manifest as slow builds, security concerns, or having to maintain complex build infrastructure. It's totally unnecessary.

Instead, you can use Depot to build Docker images in GitLab CI quickly and securely, using Depot's remote builders, without needing to configure shell executor runners, work around slow builds with dind, or bind mount the Docker socket, and all without having to grant root access to your CI runners.

If your interested in trying out Depot, we offer a 14-day free trial with no credit card required.

. . . . . . . . .