Docker 17.05 предоставляет долгожданные многоступенчатые сборки, это открывает возможность оптимизировать наши производственные образы, чтобы не включать наши зависимости сборки / компиляции и создавать меньшие образы только с самими артефактами.

Это также помогает еще больше автоматизировать процесс сборки, так как упрощает его запуск на сервере CI и отправку образа докера непосредственно из CI.

Представьте себе небольшое одностраничное приложение Angular.js со следующим форматом репозитория:

.
├── Dockerfile
├── bower.json
├── gulpfile.js
├── index.js
├── package.json
├── src
│   ├── components
│   ├── scss
│   ├── index.html
│   ├── js
│   └── views
└── yarn.lock

Теперь мы могли бы использовать Gulp, Grunt или Webpack в качестве инструмента для сборки, это не имеет особого значения для целей этой публикации.

У нас есть файл basicbower.json, давайте запустим небольшой проект Angular.js внутри,

{
  "name": "docker-build",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "author": "Guy Maliar",
  "license": "UNLICENSED",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": "^1.5.8",
    "angular-ui-router": "^0.3.1",
    "lodash": "4.17.4"
  }
}

И наш package.json будет выглядеть так,

{
  "name": "docker-build",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "author": "Guy Maliar",
  "license": "UNLICENSED",
  "scripts": {
    "build": "gulp build"
  },
  "dependencies": {},
  "devDependencies": {
    "babel-preset-es2015": "^6.13.2",
    "eslint": "^3.19.0",
    "eslint-config-airbnb-base": "^11.2.0",
    "eslint-plugin-import": "^2.2.0",
    "gulp": "^3.9.1",
    "gulp-babel": "^6.1.2",
    "gulp-image": "^2.4.1",
    "gulp-clean-css": "^3.4.2",
    "gulp-minify-html": "^1.0.6",
    "gulp-rev": "^7.1.0",
    "gulp-uglify": "^2.0.0",
    "gulp-usemin": "^0.3.23",
    "gulp-sass": "^3.1.0",
    "run-sequence": "^1.2.2"
  }
}

обратите внимание, что мы сохраняем все зависимости сборки в нашем devDependencies, как и предполагалось.

Настоящая магия начинается с нашего Dockerfile:

FROM node:8
ENV NODE_ENV=development
RUN mkdir -p /app
WORKDIR /app
RUN yarn global add bower gulp
ADD package.json /app
ADD bower.json /app
RUN yarn install && bower install --allow-root
COPY . /app
RUN npm run build
FROM kyma/docker-nginx
COPY --from=0 /app/dist /var/www
CMD ["nginx"]

Как видите, мы используем новую функцию многоэтапной сборки Docker.

COPY --from=0

Это позволяет нам собрать наш проект в одном образе Docker, а затем скопировать его в следующий образ Docker, не сохраняя и не удаляя оставшиеся сборки node_modules и bower_components!

Мы можем иметь все, что захотим, внутри нашего рабочего образа, поскольку мы копируем только встроенные статические файлы из образа, это может быть изображение Node.js, изображение Golang или, как я выбрал, простое изображение nginx.