Je souhaite héberger une application de nœud dans un conteneur Docker, ce qui devrait être simple, comme le montre cet article:
https://nodejs.org/en/docs/guides/nodejs-docker-webapp/
Dans mon projet, toutefois, les sources ne peuvent pas être exécutées directement, elles doivent être compilées à partir de ES6 et/ou de TypeScript. J'utilise gulp pour construire avec babel, browserify et tsify, avec différentes configurations de navigateur et de serveur.
Quel serait le meilleur flux de travail pour créer et automatiser des images docker dans ce cas? Existe-t-il des ressources sur le Web décrivant un tel flux de travail? Dockerimage doit-il créer le bâtiment après le npm install
ou dois-je créer un script Shell pour faire tout cela et demander simplement à Dockerfile de tout compiler?
Si le fichier Dockerfile doit faire la construction - l’image devra contenir toutes les dépendances dev, qui ne sont pas idéales?
Remarque: j'ai été en mesure de configurer un conteneur Docker et de l'exécuter - mais cela nécessitait que tous les fichiers soient installés et créés au préalable.
Une solution possible consiste à intégrer votre procédure de génération dans une image de menu fixe spécifique. Il est souvent appelé Builder image. Il doit contenir toutes vos dépendances de construction: nodejs, npm, gulp, babel, tsc, etc. Il encapsule tout votre processus de construction, éliminant ainsi la nécessité d’installer ces outils sur l’hôte.
D'abord, vous exécutez l'image du générateur, en montant le répertoire du code source en tant que volume. Le même volume ou un volume séparé peut être utilisé comme répertoire de sortie . La première image prend votre code et exécute toutes les commandes de construction.
Dans un premier temps, vous prenez votre code construit et vous l'insérez dans l'image de docker de production comme vous le faites maintenant.
Voici un exemple d'image de constructeur de docker pour TypeScript: https://hub.docker.com/r/sandrokeil/TypeScript/
Il est correct d’avoir le même générateur de docker pour plusieurs projets, car il est généralement conçu pour servir d’emballage général autour de certains outils courants ..__ Mais il est correct de créer le vôtre qui décrit une procédure plus compliquée.
La bonne chose à propos de l’image constructeur est que votre environnement hôte n’est pas pollué et que vous êtes libre d’essayer les nouvelles versions du compilateur/différents outils/changer d’ordre/effectuer des tâches en parallèle en modifiant simplement le fichier Docker de votre image constructeur. Et à tout moment, vous pouvez annuler votre expérience avec la procédure de construction.
Personnellement, je préfère simplement supprimer les dépendances de dev après avoir exécuté babel pendant la construction:
FROM node:7
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install
# Copy app source
COPY src /usr/src/app/src
# Compile app sources
RUN npm run compile
# Remove dev dependencies
RUN npm Prune --production
# Expose port and CMD
EXPOSE 8080
CMD [ "npm", "start" ]
Suivez ces étapes:
Étape 1: assurez-vous d’avoir vos dépendances babel dans dependencies not dev dependencies on package.json. Ajoutez également un script de déploiement faisant référence à babel à partir du dossier node_modules. vous allez appeler ce script depuis docker Voici à quoi ressemble mon fichier package.json
{
"name": "tmeasy_api",
"version": "1.0.0",
"description": "Trade made easy Application",
"main": "build/index.js",
"scripts": {
"build": "babel -w src/ -d build/ -s inline",
"deploy" : "node_modules/babel-cli/bin/babel.js src/ -d build/",
},
"devDependencies": {
"nodemon": "^1.9.2"
},
"dependencies": {
"babel-cli": "^6.10.1",
"babel-polyfill": "^6.9.1",
"babel-preset-es2015": "^6.9.0",
"babel-preset-stage-0": "^6.5.0",
"babel-preset-stage-3": "^6.22.0"
}
}
build est à des fins de développement sur votre machine locale et deploy doit être appelé à partir de votre fichier docker.
Étape 2: puisque nous voulons effectuer nous-mêmes la transformation babael, assurez-vous d’ajouter .dockerignore au dossier de construction que vous utilisez pendant le développement . Voici à quoi ressemble mon fichier .dockerignore.
build
node_modules
Étape 3. Construisez votre fichier docker. ci-dessous est un échantillon de mon fichier docker
FROM node:6
MAINTAINER stackoverflow
ENV NODE_ENV=production
ENV PORT=3000
# use changes to package.json to force Docker not to use the cache
# when we change our application's nodejs dependencies:
ADD package.json /tmp/package.json
RUN cd /tmp && npm install
RUN mkdir -p /var/www && cp -a /tmp/node_modules /var/www
# copy current working directory into docker; but it first checks for
# .dockerignore so build will not be included.
COPY . /var/www/
WORKDIR /var/www/
# remove any previous builds and create a new build folder and then
# call our node script deploy
RUN rm -f build
RUN mkdir build
RUN chmod 777 /var/www/build
RUN npm run deploy
VOLUME /var/www/uploads
EXPOSE $PORT
ENTRYPOINT ["node","build/index.js"]
La recommandation moderne pour ce genre de chose (à partir de Docker 17.05) est d’utiliser un build multi-stage . De cette façon, vous pouvez utiliser toutes vos dépendances dev/build dans le même fichier Dockerfile, tout en optimisant le résultat final et en évitant tout code inutile.
Je ne connais pas très bien TypeScript, mais voici un exemple d'implémentation utilisant yarn et babel. En utilisant ce fichier Docker, nous pouvons créer une image de développement (avec docker build --target development .
) pour exécuter nodemon, les tests, etc. localement; mais avec un docker build .
direct, nous obtenons une image de production optimisée et allégée, qui exécute l'application avec pm2 .
# common base image for development and production
FROM node:10.11.0-Alpine AS base
WORKDIR /app
# dev image contains everything needed for testing, development and building
FROM base AS development
COPY package.json yarn.lock ./
# first set aside prod dependencies so we can copy in to the prod image
RUN yarn install --pure-lockfile --production
RUN cp -R node_modules /tmp/node_modules
# install all dependencies and add source code
RUN yarn install --pure-lockfile
COPY . .
# builder runs unit tests and linter, then builds production code
FROM development as builder
RUN yarn lint
RUN yarn test:unit --colors
RUN yarn babel ./src --out-dir ./dist --copy-files
# release includes bare minimum required to run the app, copied from builder
FROM base AS release
COPY --from=builder /tmp/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
CMD ["yarn", "pm2-runtime", "dist/index.js"]
Je viens de publier une excellente application source pour TypeScript et Node.js à l'aide de Docker.
Vous pouvez le trouver sur GitHub .
Le projet explique toutes les commandes utilisées par le fichier Dockerfile et combine tsc
avec gulp
pour quelques avantages supplémentaires.
Si vous ne voulez pas consulter le dépôt, voici les détails:
FROM node:8
ENV USER=app
ENV SUBDIR=appDir
RUN useradd --user-group --create-home --Shell /bin/false $USER &&\
npm install --global tsc-watch npm ntypescript TypeScript gulp-cli
ENV HOME=/home/$USER
COPY package.json gulpfile.js $HOME/$SUBDIR/
RUN chown -R $USER:$USER $HOME/*
USER $USER
WORKDIR $HOME/$SUBDIR
RUN npm install
CMD ["node", "dist/index.js"]
version: '3.1'
services:
app:
build: .
command: npm run build
environment:
NODE_ENV: development
ports:
- '3000:3000'
volumes:
- .:/home/app/appDir
- /home/app/appDir/node_modules
{
"name": "docker-node-TypeScript",
"version": "1.0.0",
"description": "",
"scripts": {
"build": "gulp copy; gulp watch & tsc-watch -p . --onSuccess \"node dist/index.js\"",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Stephen Gardner ([email protected])",
"license": "ISC",
"dependencies": {
"express": "^4.10.2",
"gulp": "^3.9.1",
"socket.io": "^1.2.0"
},
"devDependencies": {
"@types/express": "^4.11.0",
"@types/node": "^8.5.8"
}
}
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "ES6"
},
"include": [
"**/*.ts"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
Pour en savoir plus sur la réponse à votre question - le ts est en cours de compilation à partir de l'appel de docker-compose.yml
dans le fichier npm run build
qui appelle ensuite tsc
. tsc
copie ensuite nos fichiers dans le dossier dist
et une simple commande node dist/index.js
exécute ce fichier. Au lieu d'utiliser nodemon, nous utilisons tsc-watch
et gulp.watch
pour surveiller les changements dans l'application et relancer node dist/index.js
après chaque nouvelle compilation.
J'espère que ça aide :) Si vous avez des questions, faites le moi savoir!
Pour le moment, j'utilise un workflow où:
npm install
et tsd install
localementgulp
construire localementnpm install --production
De cette façon, je n’obtiens que les fichiers voulus dans l’image, mais il serait plus intéressant que le fichier Dockerfile puisse créer le fichier lui-même.
Dockerfile:
FROM node:5.1
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Bundle app
COPY package.json index.js /usr/src/app/
COPY views/ /usr/src/app/views/
COPY build/ /usr/src/app/build/
COPY public/ /usr/src/app/public/
# Install app dependencies
RUN npm install --production --silent
EXPOSE 3000
CMD [ "node", "index.js" ]
Je suppose qu'une automatisation complète du "processus de création d'image" pourrait être établie en intégrant le script Dockerimage, puis en supprimant les fichiers indésirables avant de procéder à une nouvelle installation.
Dans mon projet, toutefois, les sources ne peuvent pas être exécutées directement, elles doivent être compilées à partir de ES6 et/ou de TypeScript. J'utilise gulp pour construire avec babel, browserify et tsify, avec différentes configurations de navigateur et de serveur. Quel serait le meilleur flux de travail pour la construction et l'automatisation des images de menu fixe dans ce cas?}
Si je vous ai bien compris, vous souhaitez déployer votre application Web dans un conteneur Docker et fournir différentes variantes pour différents environnements cible (vous avez mentionné différents navigateurs et serveurs). (1)
Si le fichier Dockerfile doit faire la construction - l’image devra contenir toutes les dépendances dev, qui ne sont pas idéales?
Ça dépend. Si vous souhaitez fournir une image prête à l'emploi, elle doit contenir tout ce dont votre application Web a besoin pour s'exécuter. L’un des avantages est que, par la suite, vous n’avez plus qu’à démarrer le conteneur, à transmettre certains paramètres et à commencer.
Pendant la phase de développement, cette image n'est pas vraiment nécessaire, à cause de votre environnement de développement généralement prédéfini. Si vous générez une telle image après chaque modification, cela coûte du temps et des ressources.
Approche suggérée: Je suggérerais une configuration à deux sens:
Conteneur Docker pour les phases de développement et de déploiement: Je voudrais faire référence à un projet mien et à un collègue: https://github.com/k00ni/Docker-Nodejs-environment
Ce menu fixe fournit un environnement complet de développement et de déploiement en maintenant:
et autres helpers JavaScript à l'intérieur du conteneur de menu fixe. Vous venez de lier votre dossier de projet via un volume à l'intérieur du conteneur de menu fixe. Il initialise votre environnement (par exemple, déploie toutes les dépendances de package.json) et vous êtes prêt à partir.
Vous pouvez l'utiliser à des fins de {développement} _ afin que vous et votre équipe utilisiez le même environnement (version de Node.js, version de NPM, ...). Un autre avantage est que les modifications de fichiers entraînent une nouvelle compilation. des fichiers ECMA6/ReactJS/... en fichiers JavaScript (inutile de le faire à la main après chaque modification). Nous utilisons Babel pour cela.
À des fins déploiement _, il vous suffit d'étendre cette image Docker et de modifier les pièces requises. Au lieu de lier votre application à l'intérieur du conteneur, vous pouvez la tirer via Git (ou quelque chose comme ça). Vous utiliserez le même sous-sol pour tout votre travail.