web-dev-qa-db-fra.com

Comment utiliser plusieurs tags d'image avec docker-compose

Selon this et this GitHub, il n'existe actuellement aucun moyen natif de fournir plusieurs balises pour l'image d'un service lorsque vous utilisez docker-compose pour créer une ou plusieurs images.

Mon cas d’utilisation serait de construire des images définies dans un fichier docker-compose.yml et de les taguer une fois avec une balise personnalisée (par exemple, un numéro de compilation ou une date ou similaire) et une fois sous la forme latest.

Tandis que cela peut être facilement réalisé avec plain docker en utilisant docker tag , docker-compose permet uniquement de définir une seule balise dans la clé image . L'utilisation de docker tag avec docker-compose n'est pas une option pour moi, car je souhaite conserver toutes mes définitions liées au docker dans le fichier docker-compose.yml et ne pas les copier dans mon script de construction.

Qu'est-ce qui serait une solution de rechange décente pour définir plusieurs balises avec docker-compose et sans devoir coder/copier les noms des images au préalable?

6
Dirk

Vous pouvez également adopter l'approche suivante:

# build is your actual build spec
build:
  image: myrepo/myimage
  build:
  ...
  ...
# these extend from build and just add new tags statically or from environment variables or 
version_tag:
  extends: build
  image: myrepo/myimage:v1.0
some_other_tag:
  extends: build
  image: myrepo/myimage:${SOME_OTHER_TAG}

Vous pouvez alors simplement exécuter docker-compose build et docker-compose Push et vous construirez et pousserez le bon jeu d'images marquées

7
mixja

J'ai proposé deux solutions de travail d'une complexité différente. Ils s’appuient tous sur l’hypothèse que ${IMAGE_TAG} stocke l’étiquette personnalisée qui représente, par exemple, un numéro de build et nous voulons marquer les images de tous les services avec cette balise, ainsi qu'avec latest.

grep les noms d'image du fichier docker-compose.yml

images=$(cat docker-compose.yml | grep 'image: ' | cut -d':' -f 2 | tr -d '"')
for image in $images
do
  docker tag "${image}":"${IMAGE_TAG}" "${image}":latest
done

Cependant, ceci est sujet aux erreurs si quelqu'un ajoute un commentaire dans docker-compose.yml qui le ferait par exemple. ressembler à # Purpose of this image: do something useful....

Construire deux fois

Utilisez ${IMAGE_TAG} en tant que variable d'environnement dans votre fichier docker-compose.yml comme décrit ici dans le premier exemple .

Ensuite, exécutez simplement le processus de construction deux fois, en remplaçant chaque fois ${IMAGE_TAG} par une valeur différente:

IMAGE_TAG="${IMAGE_TAG}" docker-compose build
IMAGE_TAG=latest docker-compose build

Le deuxième processus de construction devrait être beaucoup plus rapide que le premier, car toutes les couches d'image devraient toujours être mises en cache à partir de la première exécution.

L'inconvénient de cette approche est que la sortie de votre journal sera inondée de deux processus de construction ultérieurs pour chaque service, ce qui rendra plus difficile la recherche d'informations utiles. 

De plus, si vous avez dans votre Dockerfile une commande qui vide toujours le cache de construction (par exemple, une commande ADD récupérant depuis un emplacement distant avec des en-têtes last-modified à mise à jour automatique, ajoutant des fichiers constamment mis à jour par un processus externe, etc.), alors la construction supplémentaire pourrait ralentir les choses de manière significative.

Analyser les noms d’image du fichier docker-compose.yml avec du code Python en ligne

Utiliser un analyseur yaml réel en Python (ou tout autre langage tel que Ruby ou Perl ou tout ce qui est installé sur votre système) est plus robuste que l’approche grep mentionnée en premier, car il ne sera pas dérouté par des commentaires ou des manières étranges mais valables d’écrire la fichier yml.

En Python, ceci pourrait ressembler à ceci:

images=$(python3 <<-EOF # make sure below to indent with tabs, not spaces; or omit the "-" before "EOF" and use no indention at all
    import yaml
    content = yaml.load(open("docker-compose.build.yml"))
    services = content["services"].values()
    image_names = (service["image"].split(":")[0] for service in services)
    print("\n".join(image_names))
EOF
)

for image in ${images}
do
docker tag ${image}:${IMAGE_TAG} ${image}:latest
done

L'inconvénient de cette approche est que Python3 doit être installé sur la machine exécutant la construction, ainsi que la bibliothèque PyYAML . Comme déjà mentionné, ce modèle pourrait également être utilisé avec Python2 ou tout autre langage de programmation installé.

Obtenir des noms d'image avec la combinaison de certaines commandes docker

L'approche suivante utilisant certaines commandes docker et docker-compose natives (à l'aide de go-templates) est un peu plus complexe à écrire mais fonctionne également très bien.

# this should be set to something unique in order to avoid conflicts with other running docker-compose projects
compose_project_name=myproject.tagging

# create containers for all services without starting them
docker-compose --project-name "${compose_project_name}" up --no-start

# get image names without tags for all started containers
images=$(docker-compose --project-name "${compose_project_name}" images -q | xargs docker inspect --format='{{ index .RepoTags 0}}' | cut -d':' -f1)

# iterate over images and re-tag
for image in ${images}
do
    docker tag "${image}":"${IMAGE_TAG}" "${image}":latest
done

# clean-up created containers again
docker-compose --project-name "${compose_project_name}" down

Bien que cette approche ne comporte pas de dépendances externes et soit plus sûre que la méthode grep, l’exécution de grandes configurations pour la création et la suppression des conteneurs peut prendre quelques secondes de plus (en général, ce n’est pas un problème).

6
Dirk

J'ai quelques solutions simples et propres utilisant des variables d'environnement (syntaxe bash pour la valeur de variable par défaut, dans mon cas, c'est latest mais vous pouvez utiliser n'importe quoi), voici ma composition:

version: '3'
services:
  app:
    build: .
    image: myapp-name:${version:-latest}

build et Push (si vous avez besoin de Push dans le registre) avec la balise par défaut, modifiez la version à l'aide de la variable d'environnement, puis build et Push à nouveau:

docker-compose build
docker-compose Push
export version=0.0.1
docker-compose build
docker-compose Push
0
Maoz Zadok