web-dev-qa-db-fra.com

Node and docker - comment gérer la construction de Babel ou de TypeScript?

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.

16
Jørgen Tvedt

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.

9
gerichhome

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" ]
9
Lukas Hechenberger

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"]
5
Derese Getachew

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"]
4
Greg

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:

Dockerfile

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"]

docker-compose.yml

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

package.json

{
  "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"
  }
}

tsconfig.json

{
  "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!

3
Augie Gardner

Pour le moment, j'utilise un workflow où:

  1. npm install et tsd install localement
  2. gulp construire localement
  3. Dans Dockerfile, copiez tous les fichiers du programme, mais pas typings/node_modules to docker image
  4. Dans Dockerfile, npm 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.

2
Jørgen Tvedt

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:

  1. Pendant le développement: utilisez un environnement fixe pour développer votre application. Tous les logiciels peuvent s'exécuter localement ou à l'intérieur d'un menu fixe/VM. Je suggère d'utiliser un conteneur Docker avec votre dev-setup, surtout si vous travaillez en équipe et que tout le monde doit avoir le même dev-basement.
  2. Déployer l'application Web: Si j'ai bien compris (1), vous souhaitez déployer l'application pour différents environnements et vous devez par conséquent créer/fournir différentes configurations. Pour réaliser quelque chose comme ça, vous pouvez commencer avec un script Shell qui empaquette votre application dans un conteneur de menu fixe différent. Vous exécutez le script avant votre déploiement. Si Jekyll est en cours d'exécution, votre script Shell est appelé après chaque validation, après l'exécution de tous les tests.

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:

  • Node.js
  • MNP
  • Gorgée
  • Babel (transposition automatique d'ECMA6 en JavaScript lors d'un changement de fichier)
  • Webpack

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.

0
k00ni