Rozszerzając odpowiedź Petera Graingera Byłem w stanie korzystać z wersji wieloetapowej dostępnej od Dockera 17.05. Oficjalna strona zawiera:
W kompilacjach wieloetapowych używasz wielu FROMinstrukcji w swoim pliku Docker. Każda FROMinstrukcja może korzystać z innej bazy i każda z nich rozpoczyna nowy etap kompilacji. Możesz selektywnie kopiować artefakty z jednego etapu na drugi, pozostawiając za sobą wszystko, czego nie chcesz na ostatecznym obrazie.
Mając to na uwadze, mój przykład Dockerfileobejmuje trzy etapy budowy. Ma on na celu stworzenie produkcyjnego obrazu aplikacji WWW klienta.
# Stage 1: get sources from npm and git over ssh
FROM node:carbon AS sources
ARG SSH_KEY
ARG SSH_KEY_PASSPHRASE
RUN mkdir -p /root/.ssh && \
chmod 0700 /root/.ssh && \
ssh-keyscan bitbucket.org > /root/.ssh/known_hosts && \
echo "${SSH_KEY}" > /root/.ssh/id_rsa && \
chmod 600 /root/.ssh/id_rsa
WORKDIR /app/
COPY package*.json yarn.lock /app/
RUN eval `ssh-agent -s` && \
printf "${SSH_KEY_PASSPHRASE}\n" | ssh-add $HOME/.ssh/id_rsa && \
yarn --pure-lockfile --mutex file --network-concurrency 1 && \
rm -rf /root/.ssh/
# Stage 2: build minified production code
FROM node:carbon AS production
WORKDIR /app/
COPY --from=sources /app/ /app/
COPY . /app/
RUN yarn build:prod
# Stage 3: include only built production files and host them with Node Express server
FROM node:carbon
WORKDIR /app/
RUN yarn add express
COPY --from=production /app/dist/ /app/dist/
COPY server.js /app/
EXPOSE 33330
CMD ["node", "server.js"]
.dockerignorepowtarza zawartość .gitignorepliku (zapobiega kopiowaniu node_modulesi wynikowym distkatalogom projektu):
.idea
dist
node_modules
*.log
Przykład polecenia, aby zbudować obraz:
$ docker build -t ezze/geoport:0.6.0 \
--build-arg SSH_KEY="$(cat ~/.ssh/id_rsa)" \
--build-arg SSH_KEY_PASSPHRASE="my_super_secret" \
./
Jeśli twój prywatny klucz SSH nie ma hasła, podaj pusty SSH_KEY_PASSPHRASEargument.
Tak to działa:
1). Tylko na pierwszym etapie package.json, yarn.lockpliki i prywatny klucz SSH są kopiowane do pierwszej pośredniej obrazu nazwie sources. Aby uniknąć dalszych monitów o hasło SSH, jest ono automatycznie dodawane do ssh-agent. Wreszcie yarnpolecenie instaluje wszystkie wymagane zależności z NPM i klonuje prywatne repozytoria git z Bitbucket przez SSH.
2). Drugi etap buduje i minimalizuje kod źródłowy aplikacji internetowej i umieszcza go w distkatalogu następnego obrazu pośredniego o nazwie production. Zauważ, że zainstalowany kod źródłowy node_modulesjest kopiowany z obrazu o nazwie sourceswyprodukowanej w pierwszym etapie przez ten wiersz:
COPY --from=sources /app/ /app/
Prawdopodobnie może to być również następująca linia:
COPY --from=sources /app/node_modules/ /app/node_modules/
Mamy tylko node_moduleskatalog z pierwszej pośredniej obrazu tutaj, no SSH_KEYi SSH_KEY_PASSPHRASEjuż argumenty. Cała reszta wymagana do kompilacji jest kopiowana z naszego katalogu projektu.
3). W trzecim etapie zmniejszamy rozmiar końcowego obrazu, który zostanie oznaczony jako ezze/geoport:0.6.0, włączając tylko distkatalog z drugiego obrazu pośredniego o nazwie productioni instalując Node Express do uruchomienia serwera WWW.
Wyświetlanie listy obrazów daje takie dane wyjściowe:
REPOSITORY TAG IMAGE ID CREATED SIZE
ezze/geoport 0.6.0 8e8809c4e996 3 hours ago 717MB
<none> <none> 1f6518644324 3 hours ago 1.1GB
<none> <none> fa00f1182917 4 hours ago 1.63GB
node carbon b87c2ad8344d 4 weeks ago 676MB
gdzie nieoznaczone obrazy odpowiadają pierwszemu i drugiemu pośredniemu etapowi kompilacji.
Jeśli uciekniesz
$ docker history ezze/geoport:0.6.0 --no-trunc
nie będzie widać żadnych wzmianek SSH_KEYi SSH_KEY_PASSPHRASEw końcowym obrazie.