We have already see how to set up a GitLab Runner and how to build a Docker image in a GitLab pipeline.

Now we are going to see how to test the image.

For this we need a little project, this is our directory layout:

.
├── app.py
├── Dockerfile
└── .gitlab-ci.yml

The application that we would like to add to the Docker image is a really small Python Flask application:

examples/gitlab-test-docker-image/app.py

from flask import Flask
from datetime import datetime
import os
import socket

version = 1

app = Flask('Demo')

@app.route("/")
def main():
    return f"""
        Version {version}<br>
        At {datetime.now()}<br>
        PID {os.getpid()}<br>
        Hostname {socket.gethostname()}<br>
    """

The Docker image is also not very complex:

examples/gitlab-test-docker-image/Dockerfile

FROM alpine:latest
RUN apk add python3 py3-pip
RUN pip install flask
WORKDIR /opt
COPY app.py .
CMD ["flask", "run", "--host", "0.0.0.0"]

GitLab CI pipeline

The interesting part is the GitLab pipeline:

examples/gitlab-test-docker-image/.gitlab-ci.yml

stages:
  - build
  - test

build:
  image: docker:19.03.12
  stage: build
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  services:
    - docker:19.03.12-dind
  script:
    - docker build -t mydocker:latest .

    - docker tag mydocker:latest $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/flasker:latest

    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/flasker:latest
  tags:
    - docker-runner-tls

test-image:
  services:
    - name: $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/flasker:latest
      alias: flasker
      #command: ["flask", "run", "--host", "0.0.0.0", "--port", "5000"]
  image: python:3.7.2
  script:
    #- curl https://code-maven.com/
    - curl http://flasker:5000/

There are two jobs.

The build job will build the image and upload it to the Docker registry of GitLab.

The test-image job will start the newly built image as a GitLab CI service. GitLab will automatically map the ports that are opened in the image and the "alias" field can used to define a hostname for the service. One could also add a command to be executed on the docker container, but in our case that's not necessary as we have it inside the Dockerfile.

Then we have the "image" field that tells GitLab what image to use for the CI job and finally we have simple "script" tag that uses curl to access the web application running in our container that was created from the image we just built.

At this point you could write any types of test that would access the application from the outside. You could also launch other services, for example a database server and thus you can have an environment that resembles your production system.

One caveat that I think I bumped into at a client, is that you might have created the GitLab username and/or the project name with mixed case letter and I using the $CI_PROJECT_NAMESPACE and variable $CI_PROJECT_NAME will retain the case, but as I recall the Docker registry did not like that. However now thinking back, that might have been a totally different issue. In any case, let me leave this warning here.