Il existe déjà des matériaux sur Habr pour configurer le docker- container pour la compilation du projet. Par exemple, Utilisation de Docker pour créer et exécuter un projet C ++ . Dans cet article, comme dans le précédent, la question de la construction du projet sera examinée, mais ici, je voudrais aller au-delà du didacticiel et approfondir les questions de l'utilisation de conteneurs dans de telles tùches, ainsi que de la construction de l'infrastructure de construction avec Docker .
Un peu de docker
Pour plus de clarté de la discussion, il est nécessaire de fournir une description de certains composants docker .
Image
L'image Docker est un modĂšle en lecture seule avec des instructions pour crĂ©er un conteneur. Pour crĂ©er l' image, vous devez crĂ©er un Dockerfile , qui dĂ©crit toutes les Ă©tapes de l'assemblage. Chacune de ces Ă©tapes crĂ©e un calque sĂ©parĂ© Ă l'intĂ©rieur de l' image . Chaque calque suivant est superposĂ© au-dessus de tous les prĂ©cĂ©dents et ne contient que les modifications qui doivent ĂȘtre apportĂ©es au calque prĂ©cĂ©dent.
Par exemple, pour un Dockerfile :
FROM ubuntu:18.04 ADD app.sh /app ENTRYPOINT /bin/bash /app/app.sh
L' image de docker aura la structure suivante:

Les couches Ă l'intĂ©rieur de l' image sont mises en cache et peuvent ĂȘtre rĂ©utilisĂ©es si aucune modification n'est dĂ©tectĂ©e. Si le calque est modifiĂ© (ajoutĂ© / supprimĂ©) , tous les calques suivants sont créés Ă partir de zĂ©ro. Pour apporter des modifications Ă l'image du conteneur (et, par consĂ©quent, Ă l'environnement du processus lancĂ©), il suffit de rĂ©parer le Dockerfile et de commencer Ă construire l'image.
Conteneur
Un conteneur Docker est une instance de dĂ©marrage de l' image . Il peut ĂȘtre créé, dĂ©marrĂ©, arrĂȘtĂ©, supprimĂ©, etc. Par dĂ©faut, les conteneurs sont isolĂ©s les uns des autres et du systĂšme hĂŽte. Au dĂ©marrage, le conteneur dĂ©marre une commande, qui peut ĂȘtre spĂ©cifiĂ©e dans ENTRYPOINT ou CMD , et s'arrĂȘte lorsqu'elle est terminĂ©e. Une situation acceptable est lorsque CMD et ENTRYPOINT sont prĂ©sents , car ils interagissent comme dĂ©crit dans la documentation .
Lorsque vous créez chaque conteneur, un nouveau calque est ajouté au-dessus de tous les conteneurs existants. Il est accessible en écriture dans le conteneur actuel et est détruit avec le conteneur. Toutes les opérations d'écriture, de création de nouveaux fichiers lors du fonctionnement du conteneur sont appliquées à cette couche, l' image reste toujours inchangée. Ainsi, la structure des couches du conteneur créé ressemblera à :

Lors de l'utilisation de la docker run
, un nouveau conteneur sera créé Ă chaque fois, avec sa propre couche pour l'Ă©criture. Dans les tĂąches de construction, cela signifie qu'Ă chaque dĂ©marrage, il crĂ©era un nouvel environnement propre qui n'a rien Ă voir avec les exĂ©cutions prĂ©cĂ©dentes. La liste des conteneurs créés peut ĂȘtre consultĂ©e en exĂ©cutant la commande: docker container ls -a
.
Nous collectons le projet dans le conteneur
Pour plus de clarté, nous décrivons briÚvement le processus de création d'une application dans un conteneur; ce processus est décrit plus en détail dans l' article 1 et l' article 2 .
Les Ă©tapes schĂ©matiquement possibles pour crĂ©er l'application dans Docker peuvent ĂȘtre reprĂ©sentĂ©es comme suit:

Analysons les étapes indiquées:
- Nous utilisons le Dockerfile , qui décrit l'environnement, les commandes pour assembler et copier les résultats, et sur cette base, nous créons une image du conteneur.
- Nous utilisons l'image résultante pour créer et lancer le conteneur avec la
docker run
. Nous montons le dossier source et le dossier oĂč le rĂ©sultat de l'assemblage sera copiĂ© dans le conteneur. - Une fois le conteneur terminĂ©, les artefacts d'assemblage seront placĂ©s dans le rĂ©pertoire montĂ©.
Un exemple est donné dans l' article .
Ătant donnĂ© que la docker run
est utilisĂ©e ici, pour chaque lancement, un conteneur distinct sera créé avec sa propre couche pour l'Ă©criture , de sorte que les fichiers temporaires des assemblys prĂ©cĂ©dents n'entreront pas dans celui en cours. N'oubliez pas de nettoyer les conteneurs arrĂȘtĂ©s.
Le montage du répertoire source facilite le débogage de l'assembly. Mais cela comporte des risques - vous pouvez collecter une version à partir d'un code qui n'a pas passé le contrÎle de qualité ou qui n'est pas du tout ajouté au systÚme de contrÎle de version. Pour éviter cela, vous pouvez cloner le référentiel git à l'intérieur du conteneur à chaque build, comme, par exemple, dans le fichier :
FROM ubuntu:bionic RUN apt-get update \ && apt-get install -y apt-utils RUN apt-get update \ && apt-get install -y make gcc g++ qt5-default git RUN mkdir -p /app/src WORKDIR /app/build # ENTRYPOINT git -C /app/src clone https://github.com/sqglobe/SimpleQtProject.git \ && qmake /app/src/SimpleQtProject/SimpleQtProject.pro \ && make \ && cp SimpleQtProject /app/res/SimpleQtProject-ubuntu-bionic
Ici, le clonage est effectué dans ENTRYPOINT
, pas dans l'instruction RUN
, en raison de la mise en cache. ENTRYPOINT
est toujours exécuté au démarrage du conteneur et le résultat de la commande RUN
peut ĂȘtre extrait du cache .
Construire une infrastructure
Pour construire un projet pour diffĂ©rents systĂšmes d'exploitation ou distributions Linux, une certaine configuration de serveurs (build machines, serveurs avec un systĂšme de contrĂŽle de version, etc.) peut ĂȘtre utilisĂ©e. Dans la pratique, j'ai dĂ» gĂ©rer les infrastructures suivantes:

Ici, l'utilisateur accĂšde au serveur Web via lequel le projet est construit sur des machines avec Ubuntu et Red Hat . Ensuite, sur chaque machine, le rĂ©fĂ©rentiel git est clonĂ© avec le projet dans un rĂ©pertoire temporaire et l'assembly dĂ©marre. L'utilisateur peut tĂ©lĂ©charger les fichiers rĂ©sultants Ă partir de la mĂȘme page Ă partir de laquelle il a commencĂ© l'ensemble du processus.
Un tel assemblage est reproductible car les dĂ©veloppeurs utilisent le mĂȘme environnement.
Parmi les inconvénients - il est nécessaire de maintenir une infrastructure entiÚre, d'administrer plusieurs serveurs, d'éliminer les bogues dans les scripts et les applications Web , etc.
Simplifiez avec Docker
Soutenir l'infrastructure illustrĂ©e ci-dessus nĂ©cessite certains coĂ»ts, Ă la fois monĂ©taires et humains. Si votre Ă©quipe travaille sur une petite startup, ou si vous ĂȘtes le seul dĂ©veloppeur, vous pouvez utiliser des conteneurs Docker pour implĂ©menter votre infrastructure de build.
Considérons un projet Qt trivial qui est construit en utilisant qmake - SimpleQtProject . Le dossier Docker du projet spécifié contient un certain nombre de fichiers:
Ces fichiers implémentent l'idée de cloner le code source à l'intérieur d'un conteneur.
L'ensemble complet est lancé à l'aide du Makefile . Il est trÚs court et contient suffisamment de commentaires. Sa base est la création d'une image et le lancement du conteneur:
%: %.docker docker build -t simple-qt-$(strip $(subst .docker,, $< )) --file $< . docker run --mount type=bind,source=$(RELEASE_DIR),target=/app/res simple-qt-$(strip $(subst .docker,, $< ))
à ce stade de l'assemblage, une image du conteneur est créée avec le nom composé du préfixe simple-qt- et le nom du systÚme (pour centos 7, ce sera simple-qt-centos7 ). En tant que Dockerfile , le fichier correspondant avec l'autorisation .docker est utilisé . Ensuite, le conteneur est lancé en fonction de l'image créée et un dossier est monté dessus pour copier les artefacts d'assemblage.
AprÚs avoir exécuté la make
dans le répertoire docker , le dossier docker / releases contiendra les résultats de construction pour plusieurs plates-formes.
Ainsi, notre infrastructure pour construire SimpleQtProject ressemblera Ă ceci:

Avantages de cette configuration:
- Localité . Le développeur collecte un projet pour plusieurs plates-formes sur sa machine locale, ce qui élimine la nécessité de contenir une flotte de serveurs, de configurer la copie des artefacts entre les serveurs sur le réseau, d'envoyer et de traiter les commandes réseau.
- Isolement de l'environnement . Le conteneur fournit un environnement complĂštement isolĂ© pour crĂ©er une application spĂ©cifique. Il est possible de crĂ©er des projets avec des environnements incompatibles sur la mĂȘme machine (par exemple, ceux qui nĂ©cessitent diffĂ©rentes versions de la mĂȘme bibliothĂšque).
- Versioning En plaçant le Dockerfile dans le référentiel git, vous pouvez suivre les modifications dans l'environnement de génération avec la publication de nouvelles versions, revenir aux versions précédentes de l'environnement de génération, etc.
- MobilitĂ© . Si nĂ©cessaire, cette infrastructure est dĂ©ployĂ©e sans problĂšme sur un autre ordinateur. La technologie de crĂ©ation d' une image de conteneur vous permet d'apporter des modifications Ă l'image elle-mĂȘme trĂšs facilement - il suffit de mettre Ă jour le Dockerfile et de commencer Ă crĂ©er l'image.
- Auto-documentation . Essentiellement, un Dockerfile contient les étapes de déploiement d'un environnement d'assemblage. Par conséquent, si nécessaire, déployez un tel environnement, mais déjà dans un systÚme normal, vous pouvez utiliser les commandes de celui-ci.
- LĂ©gĂšretĂ© . Le conteneur dĂ©marre au moment oĂč l'assemblage commence et s'arrĂȘte automatiquement Ă la fin. Il ne perd pas de temps CPU et de RAM.
Cependant, il y a un inconvénient important - l'assemblage du projet nécessitera l'assemblage de l'image du conteneur. Lorsque vous démarrez pour la premiÚre fois, cela peut prendre beaucoup de temps. Mais avec des répétitions, surtout si le Dockerfile n'a pas changé, l'image est assemblée en utilisant le cache plusieurs fois plus rapidement.
Il faut Ă©galement penser Ă nettoyer les conteneurs arrĂȘtĂ©s.
Conclusion
En conclusion, je voudrais noter que le docker n'est pas la seule technologie de conteneurisation. Mais certaines fonctionnalitĂ©s le distinguent favorablement pour les tĂąches d'assemblage du mĂȘme LXC :
- Vous pouvez créer un conteneur à l'aide d'un Dockerfile de texte. Il s'agit d'un fichier avec une syntaxe simple, vous pouvez l'ajouter au référentiel du projet (comme je le fais toujours) et le garder constamment à portée de main.
- Chaque fois, en lançant le conteneur docker avec la
docker run
nous obtenons un environnement propre , comme si nous faisions tout pour la premiÚre fois. Les fichiers temporaires entre les assemblys ne sont pas enregistrés. - Le conteneur ne lance pas l'ensemble du systÚme d'exploitation, mais uniquement le processus d'assemblage nécessaire.