Soy un poco nuevo en Docker y trato de entender algunos de los conceptos.
En muchos tutoriales y artículos (en realidad, casi todos), este es un Dockerfile típico para una configuración de crear-reaccionar-aplicación y nginx:
# CRA FROM node:alpine as build-deps WORKDIR /usr/src/app COPY package.json package-lock.json ./ RUN npm install COPY . ./ RUN npm run build # Nginx FROM nginx:1.12-alpine COPY --from=build-deps /usr/src/app/build /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
Suponiendo que todo funcione como se esperaba, la imagen sería enorme.
Tuve la idea de adoptar un enfoque ligeramente diferente. Ejecute npm install && npm run build
localmente y luego tenga esto como Dockerfile:
FROM nginx:1.12-alpine WORKDIR /opt/app-root COPY ./nginx/nginx.conf /etc/nginx/ COPY ./build ./src/dist/ COPY ./node_modules . USER 1001 EXPOSE 8080 ENTRYPOINT ["nginx", "-g", "daemon off;"]
¿Qué enfoque es mejor? Cada vez que ejecuto docker build -t app-test:0.0.1 .
, me parece que el segundo enfoque siempre es más rápido.
Si no necesita el árbol node_modules
(y para Nginx que aloja una aplicación de navegador, no lo necesita), el segundo enfoque de simplemente copiar en la aplicación construida está bien.
Hay un par de razones para desear específicamente el primer enfoque, para ejecutar la compilación en Docker. Si hay detalles específicos de la arquitectura en su compilación (paquetes de nodo con extensiones nativas, por ejemplo), Docker podría ser un sistema operativo y una pila de biblioteca diferentes a los de su sistema host, por lo que es posible que no pueda copiar directamente en un directorio node_modules
. Si su compilación es realmente específica para pequeñas correcciones en el tiempo de ejecución del idioma, puede forzar una versión muy específica de Node en el Dockerfile.
Casi todas las aplicaciones de navegador en las que he trabajado se construyen bien con cualquier binario de node
que tenga, y una vez que lo haya hecho, el árbol dist
son archivos estáticos independientes de la plataforma.
El estilo "normal" parece variar según los idiomas. Las aplicaciones Java en particular parecen construir la aplicación fuera de Docker y luego COPY
el archivo .jar
final (independiente de la plataforma) en la imagen. Go tiende a usar una construcción de varias etapas, copiando un binario construido en una imagen final extremadamente mínima. Si estuviera escribiendo una aplicación de navegador de Node, probablemente se vería como su primer formulario, RUN yarn build
en Docker, pero he visto muchas variaciones en el tema.
La primera opción de IMO es mejor desde la perspectiva de la creación de contenedores, porque no necesita el paquete (npm) en su computadora portátil para ejecutar sus aplicaciones. Solo necesitas la ventana acoplable.
Para eso, podría usar la compilación docker de varias etapas.
En el primer contenedor, instalaría todas las dependencias (junto con las dependencias de desarrollo) y luego ejecutaría npm run build
. Creará su aplicación, pero tendrá dependencias de desarrollo inútiles dentro de sus node_modules. No tienes que copiar ese node_modules.
En el segundo contenedor, ejecutaría npm install --production --no-audit
y copiaría el directorio dist
del primer contenedor. Ahora tendrá su código compilado y una carpeta node_modules con solo módulos de producción.
Haciéndolo más ligero pero el tiempo de construcción sería un poco más largo.
Construir dentro de un contenedor garantiza un artefacto de construcción predecible y reproducible. Ejecutar npm install
en macOS y Linux puede producir diferentes node_modules
, por ejemplo, node-gyp .
Las personas a menudo construyen node_modules
con una compilación de varias etapas (si el contenedor real que está tratando de compilar no es una aplicación Node.js). Es decir, su aplicación nginx real per se no depende de Node.js, sino del directorio node_modules
y los archivos que lo contienen. Entonces generamos node_modules
en un contenedor de nodos y lo copiamos en el nuevo contenedor (nginx).
Por lo tanto, todos los que construyan con el Dockerfile de varias etapas producirán exactamente el mismo contenedor. Si copia sus node_modules
locales en un contenedor durante la compilación, otros compañeros de trabajo no podrán predecir el contenido de node_modules
.