Skip to content

Commit 2daf42d

Browse files
authored
Merge pull request #32 from CS3219-AY2425S1/docker-multistage
Docker multistage
2 parents f2cc844 + 102ac5a commit 2daf42d

File tree

14 files changed

+464
-80
lines changed

14 files changed

+464
-80
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/.air.toml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
root = "."
2+
testdata_dir = "testdata"
3+
tmp_dir = "tmp"
4+
5+
[build]
6+
args_bin = []
7+
bin = "tmp/main.exe"
8+
cmd = "go build -o ./tmp/main.exe ."
9+
delay = 1000
10+
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
11+
exclude_file = []
12+
exclude_regex = ["_test.go"]
13+
exclude_unchanged = false
14+
follow_symlink = false
15+
full_bin = ""
16+
include_dir = []
17+
include_ext = ["go", "tpl", "tmpl", "html"]
18+
include_file = []
19+
kill_delay = "0s"
20+
log = "build-errors.log"
21+
poll = true # NOTE: set to true to work inside docker
22+
poll_interval = 0
23+
post_cmd = []
24+
pre_cmd = []
25+
rerun = false
26+
rerun_delay = 500
27+
send_interrupt = false
28+
stop_on_error = false
29+
30+
[color]
31+
app = ""
32+
build = "yellow"
33+
main = "magenta"
34+
runner = "green"
35+
watcher = "cyan"
36+
37+
[log]
38+
main_only = false
39+
time = false
40+
41+
[misc]
42+
clean_on_exit = false
43+
44+
[proxy]
45+
app_port = 0
46+
enabled = false
47+
proxy_port = 0
48+
49+
[screen]
50+
clear_on_rebuild = false
51+
keep_scroll = true

backend/.dockerignore

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Include any files or directories that you don't want to be copied to your
2+
# container here (e.g., local build artifacts, temporary files, etc.).
3+
#
4+
# For more help, visit the .dockerignore file reference guide at
5+
# https://docs.docker.com/go/build-context-dockerignore/
6+
7+
**/.DS_Store
8+
**/.classpath
9+
**/.dockerignore
10+
**/.env
11+
**/.git
12+
**/.gitignore
13+
**/.project
14+
**/.settings
15+
**/.toolstarget
16+
**/.vs
17+
**/.vscode
18+
**/*.*proj.user
19+
**/*.dbmdl
20+
**/*.jfm
21+
**/bin
22+
**/charts
23+
**/docker-compose*
24+
**/compose.y*ml
25+
**/Dockerfile*
26+
**/node_modules
27+
**/npm-debug.log
28+
**/obj
29+
**/secrets.dev.yaml
30+
**/values.dev.yaml
31+
LICENSE
32+
README.md

backend/Dockerfile

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,98 @@
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
2027

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
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
41+
42+
EXPOSE 9090
43+
44+
RUN go install github.com/air-verse/air@latest
45+
#COPY go.mod go.sum ./
46+
#RUN #go mod download
47+
48+
ENTRYPOINT ["air", "-c", ".air.toml"]
49+
50+
51+
################################################################################
52+
# Create a new stage for running the application that contains the minimal
53+
# runtime dependencies for the application. This often uses a different base
54+
# image from the build stage where the necessary files are copied from the build
55+
# stage.
56+
#
57+
# The example below uses the alpine image as the foundation for running the app.
58+
# By specifying the "latest" tag, it will also use whatever happens to be the
59+
# most recent version of that image when you build your Dockerfile. If
60+
# reproducability is important, consider using a versioned tag
61+
# (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff).
62+
FROM alpine:latest AS prod
63+
64+
# Install any runtime dependencies that are needed to run your application.
65+
# Leverage a cache mount to /var/cache/apk/ to speed up subsequent builds.
66+
RUN --mount=type=cache,target=/var/cache/apk \
67+
apk --update add \
68+
ca-certificates \
69+
tzdata \
70+
&& \
71+
update-ca-certificates
72+
73+
# Create a non-privileged user that the app will run under.
74+
# See https://docs.docker.com/go/dockerfile-user-best-practices/
75+
ARG UID=10001
76+
RUN adduser \
77+
--disabled-password \
78+
--gecos "" \
79+
--home "/nonexistent" \
80+
--shell "/sbin/nologin" \
81+
--no-create-home \
82+
--uid "${UID}" \
83+
appuser
84+
USER appuser
85+
86+
# Copy the executable from the "build" stage.
87+
COPY --from=build /bin/server /bin/
88+
89+
# Expose the port that the application listens on.
2690
EXPOSE 9090
2791

28-
# Run
29-
CMD ["/backend/app"]
92+
93+
ENV MONGODB_URI="mongodb+srv://nxtms3:[email protected]/?retryWrites=true&w=majority&appName=questions"
94+
ENV PORT=":9090"
95+
ENV CORS_ORIGIN="http://host.docker.internal:3000"
96+
97+
# What the container should run when it is started.
98+
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()

0 commit comments

Comments
 (0)