Modèles de construction avancés en plusieurs étapes

image


La fonction de construction en plusieurs étapes dans les fichiers Dockerfile vous permet de créer de petites images de conteneur avec un niveau de mise en cache plus élevé et moins de protection. Dans cet article, je vais vous montrer quelques modèles avancés - quelque chose de plus que la copie de fichiers entre la génération et l'exécution. Ils vous permettent d'atteindre des fonctions d'efficacité maximale. Cependant, si vous êtes un débutant dans le domaine de l'assemblage en plusieurs étapes, alors tout d'abord, il ne sera probablement pas inutile de lire le manuel d' utilisation.


Compatibilité des versions


La prise en charge de la construction en plusieurs étapes a été ajoutée à Docker dans la version v17.05. Tous les modèles fonctionnent avec n'importe quelle version ultérieure, mais certains sont beaucoup plus efficaces, grâce aux éditeurs de liens utilisant le côté serveur BuildKit . Supposons que BuildKit ignore efficacement les étapes inutilisées et, si possible, crée des étapes en même temps (j'ai mis en évidence ces exemples séparément). BuildKit est actuellement ajouté à Moby en tant que backend expérimental pour la construction et devrait être disponible dans Docker CE v18.06. Il peut également être utilisé de manière autonome ou dans le cadre du projet img .




Héritage de scène


La construction en plusieurs étapes ajoute plusieurs nouveaux concepts de syntaxe. Tout d'abord, vous pouvez donner à l'étape commençant par la commande FROM le nom AS stagename et utiliser l' --from=stagename dans la COPY pour copier les fichiers de cette étape. En fait, la commande FROM et le label --from ont beaucoup plus en commun, pas pour rien qu'ils portent le même nom. Les deux prennent le même argument, le reconnaissent et démarrent une nouvelle étape à partir de ce point ou l'utilisent comme source pour copier le fichier. Autrement dit, pour utiliser l'étape précédente dans la qualité d'image d'origine de l'étape actuelle, vous pouvez prendre non seulement --from=stagename , mais également le nom d'étape FROM stagename . Il est utile si vous utilisez les mêmes parties communes dans plusieurs commandes du Dockerfile: il réduit le code commun et simplifie sa maintenance, en gardant les étapes enfants séparées. Ainsi, la reconstruction d'une étape n'affecte pas le cache d'assemblage pour les autres. En conséquence, chaque étape peut être assemblée individuellement à l'aide de l'étiquette --target lors de l'appel à la docker build .


 FROM ubuntu AS base RUN apt-get update && apt-get install git FROM base AS src1 RUN git clone … FROM base as src2 RUN git clone … 

Dans cet exemple, les deuxième et troisième phases de BuildKit sont générées en même temps.


Utilisation directe des images


Au lieu d'utiliser des noms de stade d'assemblage dans les commandes FROM qui auparavant ne prenaient en charge que les références d'image, vous pouvez directement utiliser des images à l'aide de l'étiquette --from . Il s'avère que de copier des fichiers directement à partir de ces images. Par exemple, linuxkit/ca-certificatesimage dans le code suivant copie directement les racines de l'autorité de certification TLS dans l'étape en cours.


 FROM alpine COPY --from=linuxkit/ca-certificates / / 

Alias ​​d'image commun


La phase de construction ne comprend pas nécessairement de commandes; il peut consister en une seule ligne FROM . Si vous utilisez l'image à plusieurs endroits, cela simplifiera la lecture et fera en sorte que si vous avez besoin de mettre à jour l'image globale, vous n'avez qu'à changer une ligne.


 FROM alpine:3.6 AS alpine FROM alpine RUN … FROM alpine RUN … 

Dans cet exemple, chaque endroit utilisant l'image alpine est réellement engagé dans alpine:3.6 , pas alpine:latest . Lorsque le moment sera venu de passer à la version alpine:3.7 , vous devrez modifier une seule ligne, et cela ne fait aucun doute: tous les éléments de l'assemblage utilisent désormais une version mise à jour.


Ceci est d'autant plus important lorsque l'argument build est utilisé dans l'alias. L'exemple suivant est similaire au précédent, mais permet à l'utilisateur de redéfinir toutes les instances de l'assembly dans lesquelles l'image alpine est impliquée en définissant l' --build-arg ALPINE_VERSION=value . N'oubliez pas: tous les arguments utilisés dans les commandes FROM doivent être déterminés avant la première phase de génération .


 ARG ALPINE_VERSION=3.6 FROM alpine:${ALPINE_VERSION} AS alpine FROM alpine RUN … 

Utilisation des arguments de construction dans "- de"


La valeur spécifiée dans l'étiquette --from de la COPY ne doit pas contenir d'arguments d'assembly. Par exemple, l'exemple suivant n'est pas valide.


 // THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine AS build-stage0 RUN … FROM alpine ARG src=stage0 COPY --from=build-${src} . . 

Cela est dû au fait que les dépendances entre les étapes doivent être déterminées avant le début de l'assemblage. Une évaluation constante de toutes les équipes n'est alors pas requise. Par exemple, une variable d'environnement définie dans une image alpine peut affecter l'évaluation de la valeur --from . La raison pour laquelle nous pouvons évaluer les arguments de la commande FROM est que ces arguments sont définis globalement avant le début de toute étape. Heureusement, comme nous l'avons découvert précédemment, il suffit de définir l'étape de l'alias à l'aide d'une commande FROM et de s'y référer.


 ARG src=stage0 FROM alpine AS build-stage0 RUN … FROM build-${src} AS copy-src FROM alpine COPY --from=copy-src . . 

Maintenant, si vous remplacez l'argument d'assemblage src , l'étape initiale du dernier élément COPY changera. Remarque: si certaines étapes ne sont plus utilisées, seuls les éditeurs de liens basés sur BuildKit peuvent les ignorer efficacement.


Conditions utilisant des arguments de génération


On nous a demandé d'ajouter la prise en charge des conditions de style IF/ELSE au Dockerfile. Nous ne savons pas encore si nous ajouterons quelque chose de similaire, mais à l'avenir, nous essaierons d'utiliser le support client dans BuildKit. Pendant ce temps, afin d'obtenir un comportement similaire, vous pouvez utiliser les concepts multi-étapes actuels (avec une certaine planification).


 // THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine RUN … ARG BUILD_VERSION=1 IF $BUILD_VERSION==1 RUN touch version1 ELSE IF $BUILD_VERSION==2 RUN touch version2 DONE RUN … 

L'exemple précédent montre un pseudo-code pour les conditions d'enregistrement à l'aide de IF/ELSE . Pour obtenir un comportement similaire avec les générations à plusieurs étapes actuelles, vous devrez peut-être définir diverses conditions de branche en tant qu'étapes distinctes et utiliser un argument pour sélectionner le chemin de dépendance correct.


 ARG BUILD_VERSION=1 FROM alpine AS base RUN … FROM base AS branch-version-1 RUN touch version1 FROM base AS branch-version-2 RUN touch version2 FROM branch-version-${BUILD_VERSION} AS after-condition FROM after-condition RUN … 

La dernière étape du Dockerfile est basée sur l'étape de after-condition , qui est l'alias d'image (reconnu en fonction de l' BUILD_VERSION construction BUILD_VERSION ). Selon la valeur de BUILD_VERSION , telle ou telle étape de la section centrale est sélectionnée.


Remarque: seuls les éditeurs de liens basés sur BuildKit peuvent ignorer les branches inutilisées. Dans les versions précédentes des éditeurs de liens, toutes les étapes étaient construites, mais avant de créer l'image finale, leurs résultats étaient ignorés.


Assistant développement / test pour la phase de production minimale


En conclusion, regardons un exemple de combinaison de modèles précédents pour montrer comment créer un Dockerfile qui crée une image de production minimale et peut ensuite utiliser son contenu pour tester et créer une image de développement. Commençons par l'exemple de base Dockerfile:


 FROM golang:alpine AS stage0 … FROM golang:alpine AS stage1 … FROM scratch COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin 

Lorsqu'une image de production minimale est créée, il s'agit d'une option assez courante. Mais que se passe-t-il si vous devez également obtenir une image de développeur alternative ou exécuter des tests avec ces fichiers binaires à l'étape finale? La première chose qui me vient à l'esprit est simplement de copier des fichiers binaires similaires aux stades des tests et du développement. Le problème est le suivant: rien ne garantit que vous testerez tous les binaires de production dans la même combinaison. Au stade final, quelque chose peut changer, mais vous oublierez d'apporter des modifications similaires à d'autres stades ou de faire une erreur dans la manière de copier des fichiers binaires. Au final, nous ne testons pas un fichier binaire séparé, mais l'image finale.


Une alternative est de déterminer la phase de développement et de test après la phase de production et de copier tout le contenu de la phase de production. Utilisez ensuite une commande FROM pour l'étape de production pour faire de l'étape de production par défaut la dernière étape.


 FROM golang:alpine AS stage0 … FROM scratch AS release COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin FROM golang:alpine AS dev-env COPY --from=release / / ENTRYPOINT ["ash"] FROM golang:alpine AS test COPY --from=release / / RUN go test … FROM release 

Par défaut, ce Dockerfile continuera à construire une image minimale par défaut, tandis que, par exemple, un assembly avec l'option --target=dev-env créera une image avec un shell contenant tous les binaires de la version finale.




J'espère que cela a été utile et a suggéré comment créer des Dockerfiles multi-étapes plus efficaces. Si vous participez à DockerCon2018 et souhaitez en savoir plus sur les builds multi-étapes, Dockerfiles, BuildKit ou tout autre sujet connexe, inscrivez-vous à l'éditeur de liens de piste Hallway ou suivez les réunions internes de la plateforme Docker sur les pistes Contribute et Collaborate ou Black Belt .

Source: https://habr.com/ru/post/fr433790/


All Articles