krbaio3 / docker-heroku

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

01 Use docker image

In this example we are going to create and run Docker images.

We will start from 01-production-bundle.

Steps to build it

  • npm install to install previous sample packages:
npm install
  • We can create our custom images. In this case, we will use the node image, the alpine version as base image to create our custom one:

./Dockerfile

FROM node:12-alpine

You can use Docker VSCode extension

  • Let's create the path where we are going to copy our app:

./Dockerfile

FROM node:12-alpine
+ RUN mkdir -p /usr/app
+ WORKDIR /usr/app

RUN: run commands inside container WORKDIR: all commands after that will be executed in this path

  • Let's add the .dockerignore to avoid unnecessary files:

./.dockerignore

node_modules
.vscode
dist
.editorconfig
.env
.env.example
.gitignore
package-lock.json
README.md

  • Copy all files:

./Dockerfile

FROM node:12-alpine
RUN mkdir -p /usr/app
WORKDIR /usr/app

+ COPY ./ ./
  • Execute install and build:

./Dockerfile

FROM node:12-alpine
RUN mkdir -p /usr/app
WORKDIR /usr/app

COPY ./ ./
+ RUN npm install
+ RUN npm run build
  • How we can run this image? We need to build our custom image before run it to be accesible by a docker container.
docker build -t my-app:1 .

-t: Give a name to image. We can use -t name:tag

  • How to run this image? Right now, we haven't a web server to resolve this static files, so we can use the interactive mode to see inside container:
docker images
docker run -it my-app:1 sh

Tag is optionally. We can see the files after build in cd ./dist && ls

  • We can create a node.js server this static files. Create server folder and:
cd ./server
npm init -y
  • Install express:
npm install express --save
  • Create server:

./server/index.js

const express = require('express');
const path = require('path');

const app = express();
const staticFilesPath = path.resolve(__dirname, '../dist');
app.use('/', express.static(staticFilesPath));

const PORT = process.env.PORT || 8081;
app.listen(PORT, () => {
  console.log(`App running on http://localhost:${PORT}`);
});
  • Let's check it:
cd ..
npm run build
node server
  • We have to do same steps in the docker file:

./Dockerfile

FROM node:12-alpine
RUN mkdir -p /usr/app
WORKDIR /usr/app

COPY ./ ./
RUN npm install
RUN npm run build

+ RUN cd server
+ RUN npm install

+ ENTRYPOINT [ "node", "server" ]

RUN vs ENTRYPOINT: I don't want to run node server when we build the image, we want to run it when run the container.

  • Run the container:
docker build -t my-app:1 .
docker run my-app:1
  • Why can't we access to http://localhost:8081? Because this process is executing itself inside container, we need to expose to our machine:

./Dockerfile

FROM node:12-alpine
RUN mkdir -p /usr/app
WORKDIR /usr/app

COPY ./ ./
RUN npm install
RUN npm run build

RUN cd server
RUN npm install

+ ENV PORT=8083
+ EXPOSE 8083
ENTRYPOINT [ "node", "server" ]
  • Run it:
docker ps
docker stop <CONTAINER ID>
docker build -t my-app:1 .
docker run --rm -p 8080:8083 my-app:1

Docker run options -p: Expose a port or a range of ports --rm: Automatically remove the container when it exits. We still have to use docker stop.

  • If we check docker images we can see dangling images, due to use same tags for each build.
docker image prune
  • On the other hand, we have an image with 361MB, too much size isn't it?. We should use multi-stage builds to decrease this size, with only the necessary info:

./Dockerfile

- FROM node:12-alpine
+ FROM node:12-alpine AS base
RUN mkdir -p /usr/app
WORKDIR /usr/app

+ # Prepare static files
+ FROM base AS build-front
COPY ./ ./
RUN npm install
RUN npm run build

- RUN cd server
- RUN npm install
+ # Release
+ FROM base AS release
+ COPY --from=build-front /usr/app/dist ./public
+ COPY ./server/package.json ./
+ COPY ./server/index.js ./
+ RUN npm install --only=production

ENV PORT=8083
+ ENV STATIC_FILES_PATH=./public

EXPOSE 8083
- ENTRYPOINT [ "node", "server" ]
+ ENTRYPOINT [ "node", "index" ]
  • Update server to consume env variable:

./server/index.js

const express = require('express');
const path = require('path');

const app = express();
- const staticFilesPath = path.resolve(__dirname, '../dist');
+ const staticFilesPath = path.resolve(__dirname, process.env.STATIC_FILES_PATH);
app.use('/', express.static(staticFilesPath));

const PORT = process.env.PORT || 8081;
app.listen(PORT, () => {
  console.log(`App running on http://localhost:${PORT}`);
});
  • Run it:
docker build -t my-app:2 .
docker run --rm -p 8080:8083 my-app:2
  • Check now the images:
docker images

About Basefactor + Lemoncode

We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.

Basefactor, consultancy by Lemoncode provides consultancy and coaching services.

Lemoncode provides training services.

For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend

About


Languages

Language:TypeScript 73.5%Language:JavaScript 21.7%Language:Dockerfile 2.9%Language:HTML 1.4%Language:Shell 0.5%