web-dev-qa-db-fra.com

Construire une application compilée avec Docker

Je construis un serveur, écrit en C++ et je souhaite le déployer en utilisant Docker avec docker-compose . Quelle est la "bonne façon" de le faire? Dois-je appeler make à partir de Dockerfile ou créer manuellement, télécharger sur un serveur puis COPY binaires à partir de Dockerfile?

16
Alexander Shishenko

J'ai eu des difficultés à automatiser notre build avec docker-compose, et j'ai fini par utiliser docker build pour tout:

Trois couches pour la construction

Exécuter → développer → construire

Ensuite, je copie les sorties de build dans l'image "deploy":

Exécuter → déployer

Quatre couches pour jouer avec:

  • Contient tous les packages requis pour l'exécution de l'application - par exemple libsqlite3-0
  • FROM <projname>:run
  • Contient les packages requis pour la construction
    • par exemple. g ++, cmake, libsqlite3-dev
  • Dockerfile exécute toutes les versions externes
    • par exemple. étapes pour construire boost-python3 (pas dans les référentiels du gestionnaire de paquets)
  • FROM <projname>:develop
  • Contient une source
  • Dockerfile exécute la construction interne (code qui change souvent)
  • Les binaires créés sont copiés à partir de cette image pour être utilisés dans le déploiement
  • FROM <projname>:run
  • Sortie de build copiée dans l'image et installée
  • RUN ou ENTRYPOINT utilisé pour lancer l'application

La structure des dossiers ressemble à ceci:

.
├── run
│   └── Dockerfile
├── develop
│   └── Dockerfile
├── build
│   ├── Dockerfile
│   └── removeOldImages.sh
└── deploy
    ├── Dockerfile
    └── pushImage.sh

Configurer le serveur de build signifie exécuter:

docker build -f run -t <projName>:run
docker build -f develop -t <projName>:develop

Chaque fois que nous faisons une construction, cela se produit:

# Execute the build
docker build -f build -t <projName>:build

# Install build outputs
docker build -f deploy -t <projName>:version

# If successful, Push deploy image to dockerhub
docker tag <projName>:<version> <projName>:latest
docker Push <projName>:<version>
docker Push <projName>:latest

Je renvoie les gens aux Dockerfiles en tant que documentation sur la façon de construire/exécuter/installer le projet.

Si une génération échoue et que la sortie est insuffisante pour une enquête, je peux exécuter /bin/bash dans <projname>:build et fouiller pour voir ce qui ne va pas.


J'ai mis en place n référentiel GitHub autour de cette idée. Cela fonctionne bien pour C++, mais vous pouvez probablement l'utiliser pour n'importe quoi.


Je n'ai pas exploré la fonctionnalité, mais @TaylorEdmiston a souligné que mon modèle ici est assez similaire à constructions multi-étapes , que je ne connaissais pas lorsque j'ai trouvé cela. Cela ressemble à une manière plus élégante (et mieux documentée) de réaliser la même chose.

26

Ma recommandation serait de développer, construire et tester complètement le conteneur lui-même. Cela garantit la philosophie Docker selon laquelle l'environnement du développeur est le même que l'environnement de production, voir La station de travail de développeur moderne sur MacOS avec Docker.

Surtout, dans le cas d'applications C++ où il existe généralement des dépendances avec des bibliothèques/fichiers objets partagés.

Je ne pense pas qu'il existe encore de processus de développement standardisé pour développer, tester et déployer des applications C++ sur Docker.

Pour répondre à votre question, la façon dont nous le faisons actuellement est de traiter le conteneur comme votre environnement de développement et d'appliquer un ensemble de pratiques à l'équipe comme:

  1. Notre base de code (sauf les fichiers de configuration) vit toujours sur un volume partagé (sur la machine locale) (versionné sur Git)
  2. Bibliothèques partagées/dépendantes, binaires, etc. toujours vivent dans le conteneur
  3. Générez et testez dans le conteneur et avant de valider l'image, nettoyez les fichiers d'objets indésirables, les bibliothèques, etc., et assurez-vous que docker diff les changements sont comme prévu .
  4. Les modifications/mises à jour de l'environnement, y compris les bibliothèques partagées, les dépendances, sont toujours documentées et communiquées à l'équipe.
6
blueskin

Mettre à jour

Pour toute personne visitant cette question après 2017, veuillez consulter la réponse par fuglede à propos de l'utilisation constructions Docker à plusieurs étapes , c'est vraiment une meilleure solution que ma réponse (ci-dessous) de 2015, bien avant qu'elle ne soit disponible.


Ancienne réponse

La façon dont je le ferais est d'exécuter votre build en dehors de votre conteneur et de copier uniquement la sortie de la build (votre binaire et toutes les bibliothèques nécessaires) dans votre conteneur. Vous pouvez ensuite télécharger votre conteneur dans un registre de conteneurs (par exemple, utiliser un hébergé ou exécuter le vôtre), puis extraire de ce registre sur vos machines de production. Ainsi, le flux pourrait ressembler à ceci:

  1. construire binaire
  2. tester/vérifier la validité du binaire lui-même
  3. construire une image de conteneur avec binaire
  4. tester/vérifier la validité de l'image du conteneur avec le binaire
  5. télécharger vers le registre de conteneurs
  6. déployer vers staging/test/qa, en tirant du registre
  7. déployer vers prod, extraire du registre

Comme il est important que vous testiez avant le déploiement en production, vous souhaitez tester exactement la même chose que vous déploierez en production, de sorte que vous ne voulez pas extraire ou modifier l'image Docker de quelque manière que ce soit après l'avoir créée.

Je n'exécuterais pas la construction à l'intérieur du conteneur que vous prévoyez de déployer dans prod, car alors votre conteneur aura toutes sortes d'artefacts supplémentaires (comme une construction temporaire sorties, outillage, etc.) dont vous n'avez pas besoin en production et développez inutilement votre image de conteneur avec des choses que vous n'utiliserez pas pour votre déploiement.

6
Misha Brukman

Alors que les solutions présentées dans les autres réponses - et en particulier la suggestion de Misha Brukman dans les commentaires de cette réponse sur l'utilisation d'un Dockerfile pour le développement et une pour la production - seraient considérées comme idiomatiques à l'époque la question a été écrite, il convient de noter que les problèmes qu'ils essaient de résoudre - et en particulier la question du nettoyage de l'environnement de construction pour réduire la taille de l'image tout en étant en mesure d'utiliser le même environnement de conteneur dans le développement et la production - ont été effectivement résolus par builds multi-stage, qui ont été introduits dans Docker 17.05.

L'idée ici serait de diviser le Dockerfile en deux parties, une qui est basée sur votre environnement de développement préféré, comme une image de base Debian à part entière, qui concerne la création des binaires que vous souhaitez déployer à la fin de la jour, et un autre qui exécute simplement les binaires construits dans un environnement minimal, comme Alpine.

De cette façon, vous évitez les éventuelles divergences entre les environnements de développement et de production, comme le mentionne blueskin dans l'un des commentaires, tout en vous assurant que votre image de production n'est pas polluée par les outils de développement.

La documentation fournit l'exemple suivant d'une construction en plusieurs étapes d'une application Go, que vous adopteriez ensuite dans un environnement de développement C++ (avec un problème étant qu'Alpine utilise musl vous devez donc être prudent lorsque liaison dans votre environnement de développement).

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM Alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
5
fuglede