Docker is a platform to run applications in a container, which is a lightweight version of a virtual machine. The main benefit of using Docker is the ability to deploy an application on any server without having to worry about dependencies or getting into conflicts with other applications on the same server. To build a Docker image, we write instructions in a script named Dockerfile
. Once the script is completed, we build the image by running command docker build
.
Changes in Codebase
A Node server will run the Tabunganku’s code. To accomplish this, we change the adapter from adapter-auto
to adapter-node
. Open svelte.config.js
and modify the line
|
|
into
|
|
To enable application termination using ctrl+c
or docker stop
command, add these lines in src/hooks.server.js
:
|
|
Dockerfile
This is the Dockerfile script used
FROM node:20-alpine as build
RUN mkdir -p /app
WORKDIR /app
COPY package*.json /app
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --production
FROM node:20-alpine
RUN adduser -D nodeuser
RUN mkdir -p /app
RUN chown nodeuser:nodeuser /app
WORKDIR /app
COPY --from=build --chown=nodeuser:nodeuser /app/build build/
COPY --from=build --chown=nodeuser:nodeuser /app/node_modules node_modules/
COPY package.json .
EXPOSE 3000
CMD ["node", "build"]
The process uses multi-stage builds to reduce image size. Let’s look at the first build stage:
FROM node:20-alpine as build
RUN mkdir -p /app
WORKDIR /app
COPY package*.json /app
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --production
The first stage is named build
. The first line means it uses Alpine OS to install Node version 20. The reason to use Alpine OS is because the size is much smaller compared to other Linux based operating systems. In the next step, it creates a new directory /app
, change the working directory to it, then copy both package.json
and package-lock.json
files into the /app
directory. Later, it runs npm ci
(Clean Install) command to install dependencies from package-lock.json
and copy the result into /app
directory. Finally, it executes npm run build
to build the project and npm prune --production
to remove unnecessary dependencies.
FROM node:20-alpine
RUN adduser -D nodeuser
RUN mkdir -p /app
RUN chown nodeuser:nodeuser /app
WORKDIR /app
COPY --from=build --chown=nodeuser:nodeuser /app/build build/
COPY --from=build --chown=nodeuser:nodeuser /app/node_modules node_modules/
COPY package.json .
EXPOSE 3000
CMD ["node", "build"]
In the next stage, the script will create a new user nodeuser
to own the /app
directory and its subdirectories and to run the application. The script copies /app/build
and /app/node_modules
directories from the build
stage (previous stage) into the specified directories. The EXPOSE
command is to inform the reader that the application runs on port 3000. The script ends by running a command node build
to start the application.
Using Docker Compose
I use docker compose to start the service easier. Docker compose is a tool to start a docker image by reading docker-compose.yaml
file.
version: "3"
services:
frontend:
container_name: tabunganku-frontend
build:
context: .
dockerfile: Dockerfile
image: tabunganku-frontend:latest
ports:
- 3000:3000
env_file:
- .env
networks:
fe-network:
ipv4_address: 172.20.10.4
networks:
fe-network:
name: tabunganku-network
external: true
This is the content of docker-compose.yaml
in Tabunganku frontend. It defines one service and one network. In the network side, it creates a network stack named fe-network
, which refers to tabunganku-network
. tabunganku-network
is defined in the backend stack, so I add the external: true
parameter in the network definition.
There is only 1 service defined in the service stack: frontend
. I can define how to build the Docker image here. In this script, I build the Docker image by using the same directory as where docker-compose.yaml
file is currently located and reading Dockerfile
to create the image. The Docker image and container will be named tabunganku-frontend
. It reads .env
file for the environment variables, and will run on port 3000 in the fe-network with a static IP address 172.20.10.4
.
To run the container, go to the project’s main directory and run command docker compose up
.