Using different docker image when building+pushing to private registry

I have a bit of trouble understanding the mechanics, so I though I ask, since I was not able to find a suitable solution out there.

I have a Git repository with a few docker image definitions which I automatically build and push to our internal docker registry, which I realised using a small Python script. A problem arose: the repository now holds several dockerfiles and the build+push procedure already exceeds a few hours.

So, the current version of my script figures out the changed files since the last commit and works fine on my machine but fails to run within the GitLab CI process, because it is using docker:stable where not even Python is installed. The first version of the script (without using the “changed-file-feature” via gitpython) worked fine by adding the apk --no-cache add python to before_script but the final version (the one at the end: publish_images.py) needs gitpython) which I cannot install easily.

Long story short: is there a way to use another base image than docker:stable for the build job or do I have to figure out how to install docker on the preferred image? I understand that there is this docker:dind service, but I don’t graps what it’s actually doing (also not from the docs) and if it can be used in other containers too…

This is not the first time I need software in a container on which I want to build and push docker images, that’s why I am asking. Of course, this particular problem can be solved by a good old shell script, but as I said, it would be nice if there was a workaround.

.gitlab-ci.yml

image: docker:stable
services:
    - docker:dind

variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_DRIVER: overlay2


before_script:
    - apk --no-cache add python
    - pip install gitpython  # fails
    - easy_install gitpython  # fails too...


build:
    script:
        - python publish_images.py
    tags:
        - docker

publish_images.py

#!/usr/bin/env python
import subprocess as sp
import sys
import time

import git

REGISTRY_URL = 'our.private.registry'


def main():
    changed_dockerfiles = [f.a_path for f in git.Repo('.').index.diff("HEAD~1")
        if f.a_path.startswith("base/")]
    n_files = len(changed_dockerfiles)

    if n_files == 0:
        print("Nothing to do!")
        exit(0)

    print("Processing {} dockerfile{}..."
          .format(n_files, 's' if n_files else ''))
    for idx, fname in enumerate(changed_dockerfiles):
        progress = "({}/{})".format(idx + 1, n_files)

        steps = [("Building",
                    "docker build --pull -t {0}/{1} -f {1} . "
                    .format(REGISTRY_URL, fname)),
                 ("Pushing",
                    "docker push {0}/{1}"
                    .format(REGISTRY_URL, fname))]

        print('-' * 79)

        for step, cmd in steps:
            print("{} {} '{}'".format(progress, step, fname))
            child = sp.Popen(
                cmd, shell=True, stdout=sys.stdout, stderr=sys.stderr)
            child.communicate()


if __name__ == '__main__':
    main()