It is common practice to build Docker containers in CI pipelines using tools like Kaniko.
It is also common practice to version Docker images with tags like 1.0.1
, 1.0
, 1
, latest
.
At one point in time, all tags probably pointed to the same image. Is it possible to write a CI/CD job
that retags images without downloading the full image first?
TL/DR: docker buildx imagetools create --tag NEW EXISTING
Yes. Adding a new tag doesn’t require access to a Docker daemon.
It’s a matter of sending the correct API requests to the Docker registry. docker buildx
has all the features we need.
-
Assuming the original image is tagged as
$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
using GitLab CI/CD variables, the following lines retag an image with its major and minor version, e.g.1.0
.export newtag=$(echo $CI_COMMIT_TAG | cut -d. -f 1-2) docker buildx imagetools create --tag $CI_REGISTRY_IMAGE:$newtag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
-
To retag the image just with the major version, e.g.
1
, use the following lines.export newtag=$(echo $CI_COMMIT_TAG | cut -d. -f 1) docker buildx imagetools create --tag $CI_REGISTRY_IMAGE:$newtag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
-
To retag the image just with
latest
use the following lines.docker buildx imagetools create --tag $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
The full GitLab pipeline could look like this.
stages:
- build
- tag
build:
stage: build
allow_failure: false
image:
name: gcr.io/kaniko-project/executor:v1.20.0-debug
entrypoint: [""]
rules:
- if: $CI_COMMIT_TAG
script:
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
.tag-template:
stage: tag
image: docker:24.0.7
rules:
- if: $CI_COMMIT_TAG
when: manual
before_script:
- mkdir -p $HOME/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > $HOME/.docker/config.json
tag-latest:
extends: .tag-template
script:
- docker buildx imagetools create --tag $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
tag-major:
extends: .tag-template
script:
- export newtag=$(echo $CI_COMMIT_TAG | cut -d. -f 1)
- docker buildx imagetools create --tag $CI_REGISTRY_IMAGE:$newtag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
tag-minor:
extends: .tag-template
script:
- export newtag=$(echo $CI_COMMIT_TAG | cut -d. -f 1-2)
- docker buildx imagetools create --tag $CI_REGISTRY_IMAGE:$newtag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
Have a look at the end-to-end example.
This might also interest you