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 FROM
instrukcji w swoim pliku Docker. Każda FROM
instrukcja 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 Dockerfile
obejmuje 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"]
.dockerignore
powtarza zawartość .gitignore
pliku (zapobiega kopiowaniu node_modules
i wynikowym dist
katalogom 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_PASSPHRASE
argument.
Tak to działa:
1). Tylko na pierwszym etapie package.json
, yarn.lock
pliki 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 yarn
polecenie 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 dist
katalogu następnego obrazu pośredniego o nazwie production
. Zauważ, że zainstalowany kod źródłowy node_modules
jest kopiowany z obrazu o nazwie sources
wyprodukowanej 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_modules
katalog z pierwszej pośredniej obrazu tutaj, no SSH_KEY
i SSH_KEY_PASSPHRASE
już 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 dist
katalog z drugiego obrazu pośredniego o nazwie production
i 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_KEY
i SSH_KEY_PASSPHRASE
w końcowym obrazie.