Writting the Dockerfile

Source code example
  • Write it from scratch

  • Understand the most used Dockerfile instructions

  • Having an optimized Docker layers

  • Building in parallel with Docker multi-stage feature

Creating the node-build stage


							FROM node:18.16
						

Creating the node-build stage


							FROM node:18.16

							WORKDIR /opt/my-app/
						

Creating the node-build stage


							FROM node:18.16 AS node-build ## defining the stage name as node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/
						

Creating the node-build stage


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install
						

Creating the install-heroku stage


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku
						

Creating the install-heroku stage


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl
						

Creating the install-heroku stage


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name

							WORKDIR /opt/my-app/
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name

							WORKDIR /opt/my-app/

							COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name

							WORKDIR /opt/my-app/

							COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules

							COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name

							WORKDIR /opt/my-app/

							COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules

							COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin

							COPY . /opt/my-app/
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name

							WORKDIR /opt/my-app/

							COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules

							COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin

							COPY . /opt/my-app/

							ENTRYPOINT [ "/usr/local/bin/npm" ]
							## or enforce a single parameter when running the container
							## [ "/usr/local/bin/npm", "start" ]
						

Creating the application image


							FROM node:18.16 AS node-build

							WORKDIR /opt/my-app/

							COPY package.json /opt/my-app/

							RUN npm install

							FROM alpine AS install-heroku ## defining the stage name as install-heroku

							RUN apk update \
							&& apk add curl

							ENV HEROKU_VERSION="stable"
							RUN curl -LO "https://cli-assets.heroku.com/channels/${HEROKU_VERSION}/heroku-linux-x64.tar.gz" \
							&& tar xf heroku-linux-x64.tar.gz

							FROM node:18.16 ## in this case we don't need to define the stage name

							WORKDIR /opt/my-app/

							COPY --from=node-build /opt/my-app/node_modules /opt/my-app/node_modules

							COPY --from=install-heroku /heroku/bin/heroku /usr/local/bin

							COPY . /opt/my-app/

							ENTRYPOINT [ "/usr/local/bin/npm" ]
							## The user can run the docker and pass any argument, the default parameter will be start
							## e.g. to run `npm test` instead of `npm start`:
							## docker run -p 5001:5001 -it zero-to-hero-docker-example test
							CMD [ "start" ]
						

Building and running it

Source code example
  • Build the Docker based on the previous example

  • Running the docker

  • Exposing a local port and accessing it from your local machine

Going to the directory of the example


							cd zero-to-hero/docker-container/docker-example/node-js-getting-started
						

Building it


							cd zero-to-hero/docker-container/docker-example/node-js-getting-started

							docker build -t zero-to-hero-docker-example .
						

Running it


							cd zero-to-hero/docker-container/docker-example/node-js-getting-started

							docker build -t zero-to-hero-docker-example .

							docker run --rm -p 5001:5001 -it zero-to-hero-docker-example
							## or run the `node test`
							## docker run --rm -p 5001:5001 -it zero-to-hero-docker-example test
						

Explaning Build


							cd zero-to-hero/docker-container/docker-example/node-js-getting-started

							docker build -t zero-to-hero-docker-example .
							## docker build [OPTIONS] PATH | URL | -
							## `-t`: Same as `--tag`, name and optionally a tag in the 'name:tag' format
							## `zero-to-hero-docker-example`: A image name without specifying a tag.
							## The tag will be latest if not specified
							## `.`: The path to the Dockerfile, `.` is the current folder
						

Explaning Run


							cd zero-to-hero/docker-container/docker-example/node-js-getting-started

							docker build -t zero-to-hero-docker-example .

							docker run --rm -it -p 5001:5001 zero-to-hero-docker-example
							## or docker run --rm -p 5001:5001 -it zero-to-hero-docker-example test
							## docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
							## `--rm`: Automatically remove the container when it exits
							## `-i`: Same as `--interactive`, keep STDIN open even if not attached
							## `-t`: Same as `--tty`, allocate a pseudo-TTY
							## `test`: A command that was specified in the package.json file
						

... and more: Bad practices / anti-patterns


... and best practices


  • Best practices, like

    • Small docker image.

    • Organize your Dockerfile in a good way and guarantee the caching

    • Slim image

    • Use Multi-platform to support different CPU architectures, like ARM

    • Use multi-stage for parallel building

    • Graceful shutdown with dumb-init or tini

    • Rotless container with podman

    • Beyond the app, use it for toollbox and keep your machine clean