Skip to content

Commit 3b5447e

Browse files
committed
Flush out the image build and add lots of explanatory comments
1 parent 4ee1ebb commit 3b5447e

File tree

2 files changed

+156
-8
lines changed

2 files changed

+156
-8
lines changed

Dockerfile

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,53 @@
1-
FROM node:20-alpine AS base
1+
###################################################
2+
# Stage: base
3+
#
4+
# This base stage ensures all other stages are using the same base image
5+
# and provides common configuration for all stages, such as the working dir.
6+
###################################################
7+
FROM node:20 AS base
28
WORKDIR /usr/local/app
39

10+
###################################################
11+
# Stage: dev
12+
#
13+
# This stage is used for local development. It is expected that the files
14+
# are mounted into the container at runtime, hence there are no COPY
15+
# statements. The default CMD will install the yarn dependencies and then
16+
# start the development server.
17+
#
18+
# This stage is the specified in the compose.yaml file (see the target field).
19+
###################################################
420
FROM base AS dev
521
CMD ["yarn", "dev"]
622

23+
###################################################
24+
# Stage: client-build
25+
#
26+
# This stage builds the client application, producing static HTML, CSS, and
27+
# JS files that can be served by the backend.
28+
#
29+
# This build seeks to optimize the build cache as much as possible by
30+
# installing the dependencies before copying in the main source code.
31+
###################################################
32+
FROM base AS client-build
33+
COPY client/package.json client/yarn.lock ./
34+
RUN --mount=type=cache,id=yarn,target=/root/.yarn yarn install
35+
COPY client/.eslintrc.cjs client/index.html client/vite.config.js ./
36+
COPY client/public ./public
37+
COPY client/src ./src
38+
RUN yarn build
39+
40+
###################################################
41+
# Stage: final
42+
#
43+
# This stage is intended to be the final "production" image. It sets up the
44+
# backend and copies the built client application from the client-build stage.
45+
###################################################
746
FROM base AS final
8-
COPY package.json yarn.lock ./
9-
RUN yarn install --production
10-
COPY . .
47+
ENV NODE_ENV=production
48+
COPY backend/package.json backend/yarn.lock ./
49+
RUN --mount=type=cache,id=yarn,target=/root/.yarn yarn install --production --frozen-lockfile
50+
COPY backend/src ./src
51+
COPY --from=client-build /usr/local/app/dist ./src/static
52+
EXPOSE 3000
1153
CMD ["node", "src/index.js"]

compose.yml

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,42 @@
1+
###################################################
2+
# This Compose file provides the development environment for the todo app.
3+
#
4+
# Seeing the final version of the application bundles the frontend with the
5+
# backend, we are able to "simulate" that by using a proxy to route requests
6+
# to the appropriate service. All requests to /api will be routed to the
7+
# backend while all other requests will be sent to the client service. While
8+
# there is some overlap in the routing rules, the proxy determines the service
9+
# based on the most specific rule.
10+
#
11+
# To support easier debugging and troubleshooting, phpMyAdmin is also included
12+
# to provide a web interface to the MySQL database.
13+
###################################################
14+
15+
###################################################
16+
# Services
17+
#
18+
# The services define the individual components of our application stack.
19+
# For each service, a separate container will be launched.
20+
###################################################
121
services:
22+
23+
###################################################
24+
# Service: proxy
25+
#
26+
# This service is a reverse proxy that will route requests to the appropriate
27+
# service. Think of it like a HTTP router or a load balancer. It simply
28+
# forwards requests and allows us to simulate the final version of the
29+
# application where the frontend and backend are bundled together. We can
30+
# also use it to route requests to phpMyAdmin, which won't be accessible at
31+
# localhost, but at db.localhost.
32+
#
33+
# The image for this service comes directly from Docker Hub and is a Docker
34+
# Official Image. Since Traefik can be configured in a variety of ways, we
35+
# configure it here to watch the Docker events for new containers and to use
36+
# their labels for configuration. That's why the Docker socket is mounted.
37+
#
38+
# We also expose port 80 to connect to the proxy from the host machine.
39+
###################################################
240
proxy:
341
image: traefik:v2.11
442
command: --providers.docker
@@ -7,14 +45,29 @@ services:
745
volumes:
846
- /var/run/docker.sock:/var/run/docker.sock
947

48+
###################################################
49+
# Service: backend
50+
#
51+
# This service is the Node.js server that provides the API for the app.
52+
# When the container starts, it will use the image that results
53+
# from building the Dockerfile, targeting the dev stage.
54+
#
55+
# The code is mounted into the container, allowing the backend to be
56+
# automatically reloaded when code changes are made (thanks to nodemon).
57+
#
58+
# The environment variables configure the application to connect to the
59+
# database, which is also configured in this Compose file. We obviously
60+
# wouldn't hard-code these values in a non-production environment. But, in
61+
# dev, these values are fine.
62+
#
63+
# Finally, the labels are used to configure Traefik (the reverse proxy) with
64+
# the appropriate routing rules. In this case, all requests to localhost/api/*
65+
# will be forwarded to this service's port 3000.
66+
###################################################
1067
backend:
1168
build:
1269
context: ./
1370
target: dev
14-
depends_on:
15-
- mysql
16-
ports:
17-
- 3000:3000
1871
volumes:
1972
- ./backend:/usr/local/app
2073
environment:
@@ -26,6 +79,20 @@ services:
2679
traefik.http.routers.backend.rule: Host(`localhost`) && PathPrefix(`/api`)
2780
traefik.http.services.backend.loadbalancer.server.port: 3000
2881

82+
###################################################
83+
# Service: client
84+
#
85+
# The client service is the React app that provides the frontend for the app.
86+
# When the container starts, it will use the image that results from building
87+
# the Dockerfile, targeting the dev stage.
88+
#
89+
# The code is mounted into the container, allowing the client to be automatically
90+
# reloaded when code changes are made (thanks to vite).
91+
#
92+
# The labels are used to configure Traefik (the reverse proxy) with the
93+
# appropriate routing rules. In this case, all requests to localhost will be
94+
# forwarded to this service's port 5173.
95+
###################################################
2996
client:
3097
build:
3198
context: ./
@@ -36,6 +103,23 @@ services:
36103
traefik.http.routers.client.rule: Host(`localhost`)
37104
traefik.http.services.client.loadbalancer.server.port: 5173
38105

106+
107+
###################################################
108+
# Service: mysql
109+
#
110+
# The MySQL service is used to provide the database for the application.
111+
# The image for this service comes directly from Docker Hub and is a Docker
112+
# Official Image.
113+
114+
# The data is persisted in a volume named todo-mysql-data. Using a volume
115+
# allows us to take down the services without losing the data. When we start
116+
# the services again, the data will still be there (assuming we didn't delete
117+
# the volume, of course!).
118+
#
119+
# The environment variables configure the root password and the name of the
120+
# database to create. Since these are used only for local development, it's
121+
# ok to hard-code them here.
122+
###################################################
39123
mysql:
40124
image: mysql:8.0
41125
volumes:
@@ -44,6 +128,21 @@ services:
44128
MYSQL_ROOT_PASSWORD: secret
45129
MYSQL_DATABASE: todos
46130

131+
###################################################
132+
# Service: phpmyadmin
133+
#
134+
# This service provides a web interface to the MySQL database. It's useful
135+
# for debugging and troubleshooting data, schemas, and more. The image for
136+
# this service comes directly from Docker Hub and is a Docker Official Image.
137+
#
138+
# The environment variables configure the connection to the database and
139+
# provide the default credentials, letting us immediately open the interface
140+
# without needing to log in.
141+
#
142+
# The labels are used to configure Traefik (the reverse proxy) with the
143+
# routing rules. In this case, all requests to db.localhost will be forwarded
144+
# to this service's port 80.
145+
###################################################
47146
phpmyadmin:
48147
image: phpmyadmin/phpmyadmin
49148
environment:
@@ -54,5 +153,12 @@ services:
54153
traefik.http.routers.phpmyadmin.rule: Host(`db.localhost`)
55154
traefik.http.services.phpmyadmin.loadbalancer.server.port: 80
56155

156+
###################################################
157+
# Volumes
158+
#
159+
# For this application stack, we only have one volume. It's used to persist the
160+
# data for the MySQL service. We are only going to use the default values,
161+
# hence the lack of any configuration for the volume.
162+
###################################################
57163
volumes:
58164
todo-mysql-data:

0 commit comments

Comments
 (0)