Skip to content

Commit 18841d5

Browse files
committed
Docker multistage
1 parent f2cc844 commit 18841d5

File tree

10 files changed

+311
-76
lines changed

10 files changed

+311
-76
lines changed

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/bzPrOe11)
2+
23
# CS3219 Project (PeerPrep) - AY2425S1
4+
35
## Group: G14
46

5-
### Note:
6-
- You can choose to develop individual microservices within separate folders within this repository **OR** use individual repositories (all public) for each microservice.
7-
- In the latter scenario, you should enable sub-modules on this GitHub classroom repository to manage the development/deployment **AND** add your mentor to the individual repositories as a collaborator.
8-
- The teaching team should be given access to the repositories as we may require viewing the history of the repository in case of any disputes or disagreements.
7+
### Note:
8+
9+
- You can choose to develop individual microservices within separate folders within this repository **OR** use
10+
individual repositories (all public) for each microservice.
11+
- In the latter scenario, you should enable sub-modules on this GitHub classroom repository to manage the
12+
development/deployment **AND** add your mentor to the individual repositories as a collaborator.
13+
- The teaching team should be given access to the repositories as we may require viewing the history of the repository
14+
in case of any disputes or disagreements.
15+
16+
Run `docker compose up --build`

backend/Dockerfile

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,95 @@
11
# syntax=docker/dockerfile:1
22

3-
FROM golang:1.23
3+
# Comments are provided throughout this file to help you get started.
4+
# If you need more help, visit the Dockerfile reference guide at
5+
# https://docs.docker.com/go/dockerfile-reference/
46

5-
# Set destination for COPY
6-
WORKDIR /backend
7+
# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
78

8-
# Download Go modules
9-
# TODO: don't include the .env file in the COPY
10-
# TODO: multistage build
11-
COPY go.mod go.sum ./
12-
RUN go mod download
9+
################################################################################
10+
# Create a stage for building the application.
11+
ARG GO_VERSION=1.23
12+
FROM golang:${GO_VERSION} AS build
13+
WORKDIR /src
1314

14-
# Copy the source code. Note the slash at the end, as explained in
15-
# https://docs.docker.com/reference/dockerfile/#copy
16-
COPY . .
15+
# Download dependencies as a separate step to take advantage of Docker's caching.
16+
# Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds.
17+
# Leverage bind mounts to go.sum and go.mod to avoid having to copy them into
18+
# the container.
19+
RUN --mount=type=cache,target=/go/pkg/mod/ \
20+
--mount=type=bind,source=go.sum,target=go.sum \
21+
--mount=type=bind,source=go.mod,target=go.mod \
22+
go mod download -x
1723

18-
# Build
19-
RUN CGO_ENABLED=0 GOOS=linux go build -o /backend/app
24+
# This is the architecture you're building for, which is passed in by the builder.
25+
# Placing it here allows the previous steps to be cached across architectures.
26+
ARG TARGETARCH=amd64
27+
28+
# Build the application.
29+
# Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds.
30+
# Leverage a bind mount to the current directory to avoid having to copy the
31+
# source code into the container.
32+
RUN --mount=type=cache,target=/go/pkg/mod/ \
33+
--mount=type=bind,target=. \
34+
CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/server .
35+
36+
37+
FROM build AS dev
38+
39+
40+
COPY --from=build /bin/server /bin/server
2041

21-
# Optional:
22-
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
23-
# But we can document in the Dockerfile what ports
24-
# the application is going to listen on by default.
25-
# https://docs.docker.com/reference/dockerfile/#expose
2642
EXPOSE 9090
2743

2844
# Run
29-
CMD ["/backend/app"]
45+
CMD ["/bin/server"]
46+
47+
48+
################################################################################
49+
# Create a new stage for running the application that contains the minimal
50+
# runtime dependencies for the application. This often uses a different base
51+
# image from the build stage where the necessary files are copied from the build
52+
# stage.
53+
#
54+
# The example below uses the alpine image as the foundation for running the app.
55+
# By specifying the "latest" tag, it will also use whatever happens to be the
56+
# most recent version of that image when you build your Dockerfile. If
57+
# reproducability is important, consider using a versioned tag
58+
# (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff).
59+
FROM alpine:latest AS final
60+
61+
# Install any runtime dependencies that are needed to run your application.
62+
# Leverage a cache mount to /var/cache/apk/ to speed up subsequent builds.
63+
RUN --mount=type=cache,target=/var/cache/apk \
64+
apk --update add \
65+
ca-certificates \
66+
tzdata \
67+
&& \
68+
update-ca-certificates
69+
70+
# Create a non-privileged user that the app will run under.
71+
# See https://docs.docker.com/go/dockerfile-user-best-practices/
72+
ARG UID=10001
73+
RUN adduser \
74+
--disabled-password \
75+
--gecos "" \
76+
--home "/nonexistent" \
77+
--shell "/sbin/nologin" \
78+
--no-create-home \
79+
--uid "${UID}" \
80+
appuser
81+
USER appuser
82+
83+
# Copy the executable from the "build" stage.
84+
COPY --from=build /bin/server /bin/
85+
86+
# Expose the port that the application listens on.
87+
EXPOSE 9090
88+
89+
90+
ENV MONGODB_URI="mongodb+srv://nxtms3:[email protected]/?retryWrites=true&w=majority&appName=questions"
91+
ENV PORT=":9090"
92+
ENV CORS_ORIGIN="http://host.docker.internal:3000"
93+
94+
# What the container should run when it is started.
95+
ENTRYPOINT [ "/bin/server" ]

backend/database/initialise_database.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import (
1212
)
1313

1414
func InitialiseDB() (*mongo.Client, error) {
15-
// Load environment variables
16-
err := godotenv.Load(".env")
1715

18-
if err != nil {
19-
log.Fatal("Error loading environment variables: " + err.Error())
16+
if os.Getenv("ENV") == "" {
17+
err := godotenv.Load(".env")
18+
if err != nil {
19+
log.Fatal("Error loading environment variables: " + err.Error())
20+
}
2021
}
2122

2223
mongoURI := os.Getenv("MONGODB_URI")

backend/go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ module peerprep
22

33
go 1.23
44

5-
require github.com/joho/godotenv v1.5.1 // indirect -allows to load environment variables from a .env file instead of hardcoding them in the code
6-
75
require (
86
github.com/bytedance/sonic v1.11.6 // indirect
97
github.com/bytedance/sonic/loader v0.1.1 // indirect
@@ -46,6 +44,7 @@ require (
4644

4745
require (
4846
github.com/gin-contrib/cors v1.7.2
47+
github.com/joho/godotenv v1.5.1
4948
github.com/microcosm-cc/bluemonday v1.0.27
5049
)
5150

backend/main.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ import (
2121
func main() {
2222
//initialise logger file and directory if they do not exist
2323

24-
err := godotenv.Load(".env")
25-
if err != nil {
26-
log.Fatal("Error loading environment variables: " + err.Error())
24+
// In Docker, the godotenv is not needed as the environment variables are set in the Dockerfile
25+
if os.Getenv("ENV") == "" {
26+
err := godotenv.Load(".env")
27+
if err != nil {
28+
log.Fatal("Error loading environment variables: " + err.Error())
29+
}
2730
}
2831

2932
ORIGIN := os.Getenv("CORS_ORIGIN")
@@ -39,19 +42,24 @@ func main() {
3942

4043
logDirectory := "./log"
4144

42-
if err := os.MkdirAll(logDirectory, 0755); err != nil {
43-
logger.Log.Error("Failed to create log directory: " + err.Error())
44-
}
45-
46-
logFile, err := os.OpenFile("./log/question_api.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
45+
if os.Getenv("ENV") != "prod" {
46+
if err := os.MkdirAll(logDirectory, 0755); err != nil {
47+
logger.Log.Error("Failed to create log directory: " + err.Error())
48+
}
49+
logFile, err := os.OpenFile(
50+
"./log/question_api.log",
51+
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
52+
0666,
53+
)
4754

48-
if err != nil {
49-
logger.Log.Warn("Failed to log to file, using default stderr")
50-
}
55+
if err != nil {
56+
logger.Log.Warn("Failed to log to file, using default stderr")
57+
}
5158

52-
defer logFile.Close()
59+
defer logFile.Close()
5360

54-
logger.Log.Out = logFile
61+
logger.Log.Out = logFile
62+
}
5563

5664
//initialise the database and handle errors
5765
server, err := apidatabase.InitialiseDB()
@@ -63,7 +71,7 @@ func main() {
6371
//create a new instance of the questionDB
6472
questionDB := apidatabase.NewQuestionDB(server)
6573

66-
f, _ := os.Create("log/gin.log")
74+
f, _ := os.Create("./log/gin.log")
6775
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
6876

6977
router := gin.Default()

compose.yaml

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,126 @@
1+
x-common-environment:
2+
environment:
3+
- ENV=${ENV:-dev}
4+
15
services:
2-
peerprep:
3-
build: peerprep
6+
peerprep-dev:
7+
# profiles:
8+
# - [ dev ]
9+
depends_on:
10+
- user-service-dev
11+
- backend-dev
12+
build:
13+
context: peerprep
14+
target: dev
415
env_file:
516
- peerprep/.env
17+
environment:
18+
- ENV=${ENV:-dev}
19+
volumes:
20+
- ./peerprep:/frontend
621
ports:
7-
- "3000:3000"
8-
extra_hosts:
9-
- "host.docker.internal:host-gateway"
10-
22+
- "${PEERPREP_PORT:-3000}:3000"
23+
# extra_hosts:
24+
# - "host.docker.internal:host-gateway"
25+
networks:
26+
- backend
27+
- user-service
28+
####### Dockerfile Node is distroless, no curl
29+
# healthcheck:
30+
# test: [ "CMD", "curl", "-f", "http://localhost:3000/" ]
31+
restart: on-failure
1132
develop:
1233
watch:
1334
- action: sync
1435
path: peerprep
1536
target: /frontend
1637

17-
user-service:
18-
build: user-service
19-
volumes:
20-
- /user-service/node_modules
38+
39+
user-service-dev:
40+
# profiles:
41+
# - [ dev ]
42+
build:
43+
context: user-service
44+
target: dev
2145
env_file:
2246
- user-service/.env
23-
extra_hosts:
24-
- "host.docker.internal:host-gateway"
47+
environment:
48+
- ENV=${ENV:-PROD}
49+
volumes:
50+
- ./user-service:/user-service
2551
ports:
26-
- "3001:3001"
52+
- "${USER_SERVICE_PORT:-3001}:3001"
53+
networks:
54+
- user-service
55+
# extra_hosts:
56+
# - "host.docker.internal:host-gateway"
57+
# healthcheck:
58+
# test: [ "CMD", "curl", "-f", "http://localhost:3001/" ]
59+
restart: on-failure
2760
develop:
2861
watch:
29-
- action: rebuild
62+
- action: sync
3063
path: user-service
3164
target: /user-service
3265

66+
# user-service-prod:
67+
# extends:
68+
# service: user-service-dev
69+
# profiles:
70+
# - [ prod ]
71+
# environment:
72+
# - ENV=prod
73+
# build:
74+
# target: final
3375

34-
backend:
35-
build: backend
76+
backend-dev:
77+
# profiles:
78+
# - [ dev ]
79+
build:
80+
context: backend
81+
target: dev
3682
env_file:
3783
- backend/.env
84+
environment:
85+
- ENV=${ENV:-dev}
86+
87+
volumes:
88+
- logs:/log
89+
- ./backend:/src
3890
ports:
39-
- "9090:9090"
91+
- "${BACKEND_PORT:-9090}:9090"
92+
networks:
93+
- backend
94+
healthcheck:
95+
test: [ "CMD", "curl", "-f", "http://localhost:9090/health" ]
96+
restart: on-failure
4097
develop:
4198
watch:
4299
- action: rebuild
43100
path: backend
44101
target: backend/app
45102

103+
104+
# backend-prod:
105+
# extends:
106+
# service: backend-prod
107+
# profiles:
108+
# - [ prod ]
109+
# environment:
110+
# - ENV=prod
111+
# build:
112+
# target: final
113+
46114
# mongo:
47115
# image: "mongo:latest"
48116
# ports:
49-
# - "27017:27017"
117+
# - "27017:27017"
118+
119+
networks:
120+
backend:
121+
driver: bridge
122+
user-service:
123+
driver: bridge
124+
125+
volumes:
126+
logs:

0 commit comments

Comments
 (0)