Constructions en plusieurs étapes
Les constructions en plusieurs étapes sont utiles pour tous ceux qui ont eu du mal à optimiser les Dockerfiles tout en les gardant faciles à lire et à maintenir.
Sans constructions en plusieurs étapes
Avant Docker 17.05, il y avait généralement deux façons de construire des images Docker :
Mettre tout dans un seul Dockerfile
Une façon était d'inclure toutes les étapes de construction dans un seul Dockerfile
, y compris la compilation, les tests et l'empaquetage du projet et de ses dépendances. Cela pourrait entraîner certains problèmes :
-
Plusieurs couches d'image, taille d'image plus importante et temps de déploiement plus longs
-
Risque de fuite du code source
Par exemple, écrire un fichier app.go
qui imprime Hello World!
package main
import "fmt"
func main(){
fmt.Printf("Hello World!");
}
Écrire un fichier Dockerfile.one
FROM golang:alpine
RUN apk --no-cache add git ca-certificates
WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
WORKDIR /root/
CMD ["./app"]
Construire l'image
$ docker build -t go/helloworld:1 -f Dockerfile.one .
Diviser en plusieurs Dockerfiles
L'autre façon était de compiler, tester et empaqueter le projet et ses dépendances dans un Dockerfile
, puis de copier les artefacts dans l'environnement d'exécution dans un autre Dockerfile
. Cette approche nécessitait l'écriture de deux Dockerfiles
et de quelques scripts de construction pour automatiser l'intégration des deux étapes, ce qui rendait le processus de déploiement plus complexe, bien qu'elle évitait les risques de la première approche.
Par exemple, écrire un fichier Dockerfile.build
FROM golang:alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
Écrire un fichier Dockerfile.copy
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
Créer un script build.sh
#!/bin/sh
echo Building go/helloworld:build
docker build -t go/helloworld:build . -f Dockerfile.build
docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract
echo Building go/helloworld:2
docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app
Maintenant, exécuter le script pour construire l'image
$ chmod +x build.sh
$ ./build.sh
Comparer les tailles d'image générées par les deux approches
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
Utiliser les constructions en plusieurs étapes
Pour résoudre les problèmes ci-dessus, Docker v17.05 a introduit la prise en charge des constructions en plusieurs étapes. En utilisant les constructions en plusieurs étapes, nous pouvons facilement résoudre les problèmes mentionnés précédemment, et nous n'avons besoin que d'écrire un seul Dockerfile
:
Par exemple, écrire un fichier Dockerfile
FROM golang:alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
Construire l'image
$ docker build -t go/helloworld:3 .
Comparer les tailles des trois images
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
Il est clair que l'image construite à l'aide de constructions en plusieurs étapes est plus petite en taille, tout en résolvant parfaitement les problèmes mentionnés précédemment.
Utiliser une étape précédente comme une nouvelle étape
Lorsque vous utilisez des constructions en plusieurs étapes, vous n'êtes pas limité à la copie des étapes que vous avez créées précédemment dans votre Dockerfile. Vous pouvez utiliser l'instruction COPY --from pour copier à partir d'une image distincte, en utilisant soit le nom de l'image locale, soit une balise disponible localement ou sur un registre Docker, soit un ID de balise. Le client Docker récupère l'image si nécessaire et copie l'artéfact à partir de là. La syntaxe est :
FROM golang:alpine as builder
Par exemple, si nous voulons seulement construire l'image pour l'étape builder
, ajoutez le paramètre --target=builder
:
$ docker build --target builder -t username/imagename:tag .
Utiliser une image externe comme étape
Vous pouvez reprendre là où une étape précédente s'est arrêtée en vous y référant lors de l'utilisation de la directive FROM.
Dans l'exemple ci-dessus, nous avons utilisé COPY --from=0 /go/src/github.com/go/helloworld/app .
pour copier les fichiers de l'image de l'étape précédente. Nous pouvons également copier des fichiers depuis n'importe quelle autre image.
$ COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf