#node.js #docker #angular-cli #alpine
#node.js #docker #angular-cli #alpine-linux
Вопрос:
Я столкнулся с очень странным сценарием, и мне было интересно, смог ли кто-нибудь пролить некоторый свет на ситуацию. Короче говоря, docker build
npm install -g angular-cli@1.0.0-beta.16
сборка вместе с остальными шагами приложения занимает целую вечность. docker build
только с этим в собственном образе, а затем docker build
с остальной частью приложения FROM
, которое является первым изображением, вместе или по отдельности, они не занимают так много времени. Когда я говорю долго, я имею в виду около 2 часов. На сборку у отдельных пользователей уходит от 5 до 7 минут.
Итак, прежде чем я изложу весь код, несколько замечаний. Я пробовал это на OS X 10.10.5, OS X 10.11.X, OS X 10.12.X, Arch Linux 4.5.1-1-ARCH, Ubuntu 14.04 LTS (в vagrant box и в AWS), а также некоторые друзья помогли мне с этим на своих машинах. Все те же результаты. Я запускаю Docker 1.12.1. и я создаю FROM alpine:3.4
. Версия node
is v6.2.0
и npm
3.8.9 (из пакетов alpine). Я также пробовал это с изображением, которое я создал для nodejs v5.11.1 и npm 3 из исходного кода.
FROM alpine:3.4
MAINTAINER First Lastname <user@example.com>
ARG ENV
#Set environment vars
ENV HOME=/home
APP_DIR=/root/app
DIST_DIR=/var/www
ENV=${ENV}
AWS_REGION=us-east-1
NPM_CONFIG_LOGLEVEL=info
LANG=en_US.UTF-8
LC_ALL=C.UTF-8
LANGUAGE=en_US.UTF-8
#Install runtime packages
RUN apk --no-cache add
ca-certificates
nodejs
nginx
#Install build time packages
RUN apk --no-cache add
--virtual build-dependencies
busybox
build-base
bzip2
git
python-dev
libffi-dev
RUN mkdir -p ${APP_DIR}
WORKDIR ${APP_DIR}
COPY . ${APP_DIR}
#Bug https://github.com/npm/npm/issues/9863
RUN cd $(npm root -g)/npm amp;amp;
npm install fs-extra amp;amp;
sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js amp;amp;
rm -fr ${APP_DIR}/node_modules
#install node packages
RUN npm install -g angular-cli@1.0.0-beta.16 amp;amp;
npm install -g typescript@2.0.3 amp;amp;
npm install amp;amp; npm install -g --save process-nextick-args amp;amp; npm cache clean | tee /tmp/npm-install.log
#build project
RUN ng build --environment=${ENV}
RUN mv dist ${DIST_DIR}
#clean up
RUN rm -fr ${APP_DIR}
RUN apk del build-dependencies
#nginx config
COPY ./nginx/nginx.conf /etc/nginx/
RUN mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled/ amp;amp;
chmod -R 755 /etc/nginx/sites-*
RUN ln -sf /dev/stdout /var/log/nginx/access.log amp;amp;
ln -sf /dev/stderr /var/log/nginx/error.log
COPY ./nginx/account-curation.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/account-curation.conf /etc/nginx/sites-enabled/
#app port
EXPOSE 80 81
#start nginx
ENTRYPOINT [ "nginx" ]
И package.json выглядит так
{
"name": "app-name",
"version": "0.0.0",
"license": "MIT",
"angular-cli": {},
"scripts": {
"start": "ng serve",
"lint": "tslint "src/**/*.ts"",
"test": "ng test",
"pree2e": "webdriver-manager update",
"e2e": "protractor"
},
"private": true,
"dependencies": {
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0",
"@angular2-material/button": "2.0.0-alpha.8-2",
"@angular2-material/card": "2.0.0-alpha.8-2",
"@angular2-material/checkbox": "2.0.0-alpha.8-2",
"@angular2-material/core": "2.0.0-alpha.8-2",
"@angular2-material/grid-list": "2.0.0-alpha.8-2",
"@angular2-material/icon": "2.0.0-alpha.8-2",
"@angular2-material/input": "2.0.0-alpha.8-2",
"@angular2-material/input": "2.0.0-alpha.8-2",
"@angular2-material/list": "2.0.0-alpha.8-2",
"@angular2-material/progress-circle": "2.0.0-alpha.8-2",
"@angular2-material/tabs": "2.0.0-alpha.8-2",
"@angular2-material/toolbar": "2.0.0-alpha.8-2",
"angular2-modal": "2.0.0-beta.13",
"core-js": "2.4.1",
"es6-shim": "0.35.1",
"hammerjs": "2.0.8",
"jquery": "3.1.0",
"jstree": "3.3.1",
"localStorage": "1.0.3",
"rxjs": "5.0.0-beta.12",
"ts-helpers": "1.1.1",
"zone.js": "0.6.25"
},
"devDependencies": {
"@types/hammerjs": "^2.0.33",
"@types/jasmine": "^2.2.30",
"@types/jquery": "^2.0.32",
"@types/jstree": "^3.3.32",
"angular-cli": "1.0.0-beta.16",
"codelyzer": "0.0.26",
"jasmine-core": "2.4.1",
"jasmine-spec-reporter": "2.5.0",
"karma": "1.2.0",
"karma-chrome-launcher": "2.0.0",
"karma-cli": "1.0.1",
"karma-jasmine": "1.0.2",
"karma-remap-istanbul": "0.2.1",
"protractor": "4.0.9",
"ts-node": "1.2.1",
"tslint": "3.13.0",
"typescript": "2.0.3"
}
}
Я указываю на это, потому что в нем уже есть все пакеты, которые я пытаюсь установить глобально (та же версия), но когда это устанавливается, это происходит намного быстрее. Таким образом, сборка занимает около 2 часов. Безумие, верно? Итак, после нескольких дней дурачиться с этим, я не могу понять, почему. Я решил, что в свете времени я создам базовый образ с глобальными установками, а затем создам образ проекта FROM
. Таким образом, задание сборки CI не будет занимать несколько часов при каждом нажатии на эту ветку репозиториев. Но когда я это сделал, это волшебным образом стало быстрее. Около 7 минут для базового образа и 5 для образа приложения. Это выглядело примерно так.
Base Image:
FROM alpine:3.4
MAINTAINER First Lastname <user@example.com>
#Set environment vars
ENV NPM_CONFIG_LOGLEVEL=info
LANG=en_US.UTF-8
LC_ALL=C.UTF-8
LANGUAGE=en_US.UTF-8
#Install runtime packages
RUN apk --no-cache add
ca-certificates
nodejs
#Install build time packages
RUN apk --no-cache add
--virtual build-dependencies
busybox
build-base
bzip2
git
python-dev
libffi-dev
#Bug https://github.com/npm/npm/issues/9863
RUN cd $(npm root -g)/npm amp;amp;
npm install fs-extra amp;amp;
sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js
#install node packages
RUN npm install -g angular-cli@1.0.0-beta.16 amp;amp;
npm install -g typescript@2.0.3 amp;amp;
npm install -g --save process-nextick-args amp;amp; npm cache clean | tee /tmp/npm-global-install.log
RUN apk del build-dependencies
ENTRYPOINT [ "/bin/ash" ]
Изображение приложения
FROM company/application_base:latest
MAINTAINER First Lastname <user@example.com>
ARG ENV
#Set environment vars
ENV APP_DIR=/root/app
DIST_DIR=/var/www
ENV=${ENV}
AWS_REGION=us-east-1
NPM_CONFIG_LOGLEVEL=info
LANG=en_US.UTF-8
LC_ALL=C.UTF-8
LANGUAGE=en_US.UTF-8
#Install runtime packages
RUN apk --no-cache add
ca-certificates
nginx
#Install build time packages
RUN apk --no-cache add
--virtual build-dependencies
busybox
build-base
bzip2
git
python-dev
libffi-dev
RUN mkdir -p ${APP_DIR}
COPY . ${APP_DIR}
WORKDIR ${APP_DIR}
#install node packages
RUN npm install amp;amp; npm cache clean | tee /tmp/npm-install.log
#build project
RUN ng build amp;amp; mv dist ${DIST_DIR}
#clean up
RUN rm -fr ${APP_DIR} amp;amp; apk del build-dependencies
#nginx config
COPY ./nginx/nginx.conf /etc/nginx/
RUN mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled/ amp;amp;
chmod -R 755 /etc/nginx/sites-* amp;amp;
ln -sf /dev/stdout /var/log/nginx/access.log amp;amp;
ln -sf /dev/stderr /var/log/nginx/error.log
COPY ./nginx/account-curation.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/account-curation.conf /etc/nginx/sites-enabled/
#app port
EXPOSE 80 81
#start nginx
ENTRYPOINT [ "nginx" ]
Я не знаю nodejs
этого хорошо, поэтому я пытался избежать установки angular-cli
дважды, был npm install
один из package.json
, затем mv
он из /path/to/app/node_modules/.bin/ng
/usr/bin
, также просто добавляя /path/to/app/node_modules/.bin
к моему пути, оба привели ng not found
к.
Не уверен, что это связано, но я назову это кем угодно, я продолжаю получать эту раздражающую проблему с разрешениями npm gyp только для глобально установленных пакетов. gyp WARN EACCES user "nobody" does not have permission to access the dev dir
Они просто предупреждения, но я подумал, что погуглю и посмотрю, о чем это. Просто чтобы исключить возможности, я попробовал предложенные подходы к исправлению этого из таких сообщений, как https://docs.npmjs.com/getting-started/fixing-npm-permissions и https://github.com/nodejs/node-gyp/issues/454 Ничто из того, что я мог найти, не работало.
Что-то еще, что я пробовал, это возиться с объемом оперативной памяти и процессора, которые может использовать docker. например docker build --build-arg ENV=dev --cpuset-cpus "0-5" --no-cache -t company/app_name:0.1.0 .
.
Я действительно не хочу поддерживать два Dockerfile
, особенно когда я чувствую, что мне не хватает чего-то действительно глупого. Потому что я знаю, что они работают намного лучше, когда они разделены. Что, черт возьми, может привести к тому, что это увеличит время сборки на столько, когда все это вместе.
Ответ №1:
Сборка образа «все» занимает много времени из-за проблем с execSync
node-zopfli
их собственными модулями и очень медленной сборкой. Оба являются необязательными зависимостями, поэтому установка выдерживает, когда они не работают, но для сбоя требуется много времени.
Из-за разделенной сборки образа эти два пакета не могут быть собраны быстро. Я действительно не уверен, как это произошло, хотя, по-видимому, существует ряд изменений, которые могут привести к тому, что они не смогут быстро построить.
execAsync
Модуль execSync больше не должен существовать, он предназначен для узла 0.10. Если я удалю npm
исправление ошибки из Dockerfile
, сборка execAsync завершится немедленно, а не займет много времени для сбоя.
node-zopfli
Чтобы устранить проблемы с разрешениями, используйте npm install --unsafe-perm
, чтобы разрешить запуск сборки root
от имени пользователя в контейнере.
devDependencies
Используйте либо devDependencies
глобальные установки, либо глобальные установки для вашей ng
сборки, а не обе. An npm install --production
удалит devDependencies
из установки приложения, что означает angular-cli
, что оно больше не дублируется. Если вам нужно больше devDependencies для завершения сборки приложения, тогда вы можете пойти другим путем и не выполнять глобальные установки, а полагаться только на devDepenencies ( ./node_modules/.bin/ng
)
Другие примечания
Используйте FROM mhart/alpine-node:6
для получения последней версии node / npm и не устанавливайте nodejs
через apk
.
Установите ARG ENV
и ENV ENV
как можно позже (т. Е. Непосредственно перед ng
командой, которая его использует), чтобы изменение среды не вызвало полную перестройку образа.
При выполнении шагов очистки в более поздней команде, например, нет размера изображения, который можно было бы увеличить (или потерять на самом деле) Dockerfile
RUN apk del build-dependencies
. К этому времени файлы уже привязаны к предыдущему слою изображения.
При выполнении повторных сборок образа docker используйте что-то вроде verdacio / npm-register для npm и / или apt-cacher-ng для общих пакетов ОС. Они устранят большую часть сетевых издержек для повторных сборок docker.
Dockerfile
Итак, в итоге получается что-то вроде этого:
FROM mhart/alpine-node:6.7
MAINTAINER First Lastname <user@example.com>
# Set environment vars
ENV HOME=/root
APP_DIR=/root/app
DIST_DIR=/var/www
AWS_REGION=us-east-1
NPM_CONFIG_LOGLEVEL=info
LANG=en_US.UTF-8
LC_ALL=C.UTF-8
LANGUAGE=en_US.UTF-8
# Install packages
RUN apk --no-cache add
--virtual build-dependencies
ca-certificates
nginx
build-base
bzip2
git
python-dev
libffi-dev
RUN mkdir -p ${APP_DIR}
WORKDIR ${APP_DIR}
COPY . ${APP_DIR}
# Install node packages
RUN set -uex ;
npm install -g angular-cli@1.0.0-beta.16 typescript@2.0.3 process-nextick-args ;
npm install --production --unsafe-perm ;
npm cache clean
ARG ENV
ENV ENV=${ENV}
# Build project
RUN set -uex ;
ng build --environment=${ENV} ;
mv dist ${DIST_DIR} ;
rm -fr ${APP_DIR}
# nginx config
COPY ./nginx/nginx.conf /etc/nginx/
RUN set -uex ;
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled/ ;
chmod -R 755 /etc/nginx/sites-* ;
ln -sf /dev/stdout /var/log/nginx/access.log ;
ln -sf /dev/stderr /var/log/nginx/error.log
COPY ./nginx/account-curation.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/account-curation.conf /etc/nginx/sites-enabled/
# App port
EXPOSE 80 81
# Start nginx
ENTRYPOINT [ "nginx" ]
Также нет проблем с наличием двух файлов Dockerfile. Поскольку каждый должен делать свое дело, а не дублировать действия других. Обычно это нужно делать только тогда, когда у вас есть несколько образов для сборки из одного базового образа.
Ответ №2:
Стоит отметить, что это может быть связано с добавлением этого также перед этапами установки npm:
RUN npm config set maxsockets 10
В более старых версиях npm это бесконечность, в более новых — 50.
Я получил огромный прирост скорости, когда сам явно установил для него значение 10 (смутно помню кого-то, кто сказал, что 25 для него хорошо).