Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions config/makefile/db.mk
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,8 @@ db\:delete:
docker compose down -v --remove-orphans

db\:chmod:
#ostgreSQL has a strict rule for security. The SSL private key file (server.key) cannot be owned by a regular user.
# When you mount the file from your host server, the file inside the container is still owned by your user (gocanto),
# not by root or the postgres user. PostgreSQL sees this as a security risk and refuses to start.
#sudo chown root:root ./database/infra/ssl/server.key
#sudo chmod 600 ./database/infra/ssl/server.key
chmod 600 $(DB_INFRA_SERVER_KEY)
sudo chmod 600 $(DB_INFRA_SERVER_KEY)
sudo chmod 644 $(DB_INFRA_SERVER_CRT)

db\:secure:
rm -f $(DB_INFRA_SERVER_CRT) $(DB_INFRA_SERVER_CSR) $(DB_INFRA_SERVER_KEY)
Expand Down
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ services:
context: .
dockerfile: ./docker/dockerfile-api
args:
- APP_VERSION=v1.0.0-release
- APP_VERSION=0.0.0.1
- APP_HOST_PORT=${ENV_HTTP_PORT}
- APP_USER=${ENV_DOCKER_USER}
- APP_GROUP=${ENV_DOCKER_USER_GROUP}
- APP_DIR=/app
- BINARY_NAME=oullin_api
container_name: oullin_api
restart: unless-stopped
secrets:
Expand Down
166 changes: 99 additions & 67 deletions docker/dockerfile-api
Original file line number Diff line number Diff line change
@@ -1,112 +1,144 @@
# --- Build Arguments
ARG GO_VERSION=1.24
ARG ALPINE_VERSION=latest

ARG APP_VERSION="0.0.0-dev"
#
# ----------------------------------------------------------------------------------------------------------------------
# GLOBAL BUILD ARGUMENTS
# ----------------------------------------------------------------------------------------------------------------------
#
# These args can be overridden at build time with `--build-arg NAME=value`.
# Otherwise, you could use you docker-compose file to achive the same purpose.
#
# ----------------------------------------------------------------------------------------------------------------------
#

ARG APP_VERSION=0.0.0.1
ARG BUILD_TAGS="posts,experience,profile,projects,social,talks,gus,gocanto"
ARG BINARY_NAME=oullin_api

ARG BINARY_NAME=server
ARG APP_HOST_PORT=8080
# Non-root user/group settings.
ARG APP_USER=appuser
ARG APP_GROUP=appgroup
ARG APP_HOME=/home/${APP_USER}

ARG BUILD_DIR=/app
# Container runtime port.
ARG APP_HOST_PORT=8080

# Application directory inside container.
ARG APP_DIR=/app

# Storage directories (relative to APP_DIR).
ARG STORAGE_DIR=storage
ARG LOGS_DIR=logs
ARG MEDIA_DIR=media
ARG FIXTURES_DIR=fixture

# --- Build Stage
FROM golang:${GO_VERSION}-alpine AS builder
# ----------------------------------------------------------------------------------------------------------------------
# BUILDER STAGE
# ----------------------------------------------------------------------------------------------------------------------
FROM golang:1.24-alpine AS builder

# --- Docker Args always go before ENV vars.
# Forwards build-time arguments into this specific stage so they can be referenced.
ARG BUILD_DIR
# Bring in the build args needed in this stage.
ARG APP_DIR
ARG BINARY_NAME
ARG APP_VERSION
ARG BUILD_TAGS

# --- Go env vars.
# Tell Go to keep its module & build caches under /app
ENV GOPATH=${BUILD_DIR}/.gopath
ENV GOMODCACHE=${BUILD_DIR}/.gopath/pkg/mod
ENV GOCACHE=${BUILD_DIR}/.gocache
RUN mkdir -p ${GOMODCACHE} ${GOCACHE}
# Configure Go build cache and module cache under our APP_DIR.
ENV GOPATH=${APP_DIR}/.gopath
ENV GOMODCACHE=${APP_DIR}/.gopath/pkg/mod
ENV GOCACHE=${APP_DIR}/.gocache

# Install timezone data so Go’s time.* calls work correctly.
RUN #apk add --no-cache tzdata
# Create the Go module & build cache directories.
RUN mkdir -p ${GOMODCACHE} ${GOCACHE}

# Sets the primary working directory for this stage of the build.
WORKDIR ${BUILD_DIR}
# Set the working directory for the build.
WORKDIR ${APP_DIR}

# Copies the Go module definition files into the builder.
# This is done first to leverage Docker's layer caching. The subsequent
# 'go mod download' step will only be re-run if these files have changed.
# Copy Go module files and download dependencies.
COPY go.mod go.sum ./

RUN go mod download

# Copies the rest of the application's source code into the builder.
# Copy remaining source code into the builder.
COPY . .

# Compiles the Go application into a single, statically-linked binary.
# -tags: Applies build constraints, allowing for conditional compilation.
# -o: Specifies the output path and name for the compiled binary.
# -ldflags: Provides flags to the linker.
# -s: Strips the symbol table, reducing binary size.
# -w: Strips DWARF debugging information, further reducing size.
# -X: Injects a value into a string variable at build time. Here, it sets
# the application's version by targeting the 'Version' variable in the 'main' package.
RUN CGO_ENABLED=0 go build -tags "${BUILD_TAGS}" -o ${BUILD_DIR}/${BINARY_NAME} -ldflags="-s -w -X main.Version=${APP_VERSION}" .

# --- Final Stage
FROM alpine:${ALPINE_VERSION}
#USER root

# Forwards build-time arguments into this final stage so they can be referenced.
# Compile a statically-linked binary.
#
# * CGO_ENABLED=0: disable CGO for static builds.
# * -tags: apply build tags.
# * -o: output binaries path/name.
# * -ldflags: strip symbols and inject version.
#
RUN CGO_ENABLED=0 go build \
-tags "${BUILD_TAGS}" \
-o ${APP_DIR}/${BINARY_NAME} \
-ldflags="-s -w -X main.Version=${APP_VERSION}" \
.

# ----------------------------------------------------------------------------------------------------------------------
# FINAL STAGE
# ----------------------------------------------------------------------------------------------------------------------
FROM alpine:3.22

# Bring in the runtime args.
ARG APP_USER
ARG APP_GROUP
#ARG APP_HOME
ARG BUILD_DIR
ARG APP_HOME
ARG APP_DIR
ARG BINARY_NAME
ARG STORAGE_DIR
ARG LOGS_DIR
ARG MEDIA_DIR
ARG FIXTURES_DIR
ARG APP_HOST_PORT

# Creates a dedicated, non-root user and group to run the application with.
#RUN addgroup -S ${APP_GROUP} && adduser -S ${APP_USER} -G ${APP_GROUP} -h ${APP_HOME}

# Install timezone data so Go’s time.* functions work correctly.
RUN apk add --no-cache tzdata
ENV TZ=Asia/Singapore

# Make sure the home exists & switch into it.
WORKDIR /app
# Create the system group for our non-root user.
RUN addgroup -S ${APP_GROUP}

# Create the system user, assign to group, set its home.
RUN adduser -S ${APP_USER} \
-G ${APP_GROUP} \
-h ${APP_HOME} \
${APP_USER}

# Ensure the user’s home directory actually exists.
RUN mkdir -p ${APP_HOME}

# Switch to the application directory (implicitly creates it if missing).
WORKDIR ${APP_DIR}

# Creates the necessary storage directories inside the container.
RUN mkdir -p ${STORAGE_DIR}/${LOGS_DIR} ${STORAGE_DIR}/${MEDIA_DIR}
RUN mkdir -p ${STORAGE_DIR}/${FIXTURES_DIR} ${STORAGE_DIR}/${FIXTURES_DIR}
# Create storage subdirectories under the APP_DIR.
#
# * logs/: for application logs
# * media/: for uploaded/static media
# * fixture/: for static fixtures data
#
RUN mkdir -p ${STORAGE_DIR}/${LOGS_DIR}
RUN mkdir -p ${STORAGE_DIR}/${MEDIA_DIR}
RUN mkdir -p ${STORAGE_DIR}/${FIXTURES_DIR}

# Copies the 'fixture' files from the local project directory into the container.
COPY ${STORAGE_DIR}/${FIXTURES_DIR} ./${STORAGE_DIR}/${FIXTURES_DIR}/
# Copy fixture files from builder stage.
COPY ${STORAGE_DIR}/${FIXTURES_DIR} ${STORAGE_DIR}/${FIXTURES_DIR}/

# Copies the compiled application binary from the 'builder' stage.
COPY --from=builder ${BUILD_DIR}/${BINARY_NAME} ${BUILD_DIR}/
RUN chmod +x ${BUILD_DIR}/${BINARY_NAME}
# Copy the compiled binary and mark it executable.
COPY --from=builder ${APP_DIR}/${BINARY_NAME} ${APP_DIR}/
RUN chmod +x ${APP_DIR}/${BINARY_NAME}

# Copy timezone data from builder so Go’s time.* calls work correctly.
#RUN apk add --no-cache tzdata
#COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# Give ownership of home and app directories to our non-root user.
RUN chown -R ${APP_USER}:${APP_GROUP} ${APP_HOME}
RUN chown -R ${APP_USER}:${APP_GROUP} ${APP_DIR}

# Recursively sets the ownership of all files in the application's home directory.
#RUN chown -R ${APP_USER}:${APP_GROUP} ${APP_HOME}
# Switch to non-root user for all subsequent container processes.
USER ${APP_USER}:${APP_GROUP}

#USER ${APP_USER}
# Arguments do not exist at container runtime, so ${APP_DIR} and ${BINARY_NAME} are empty when the entryoint is called.
# Therefore, we need to send those values as environment variables for the shell to pick them up at runtime.
ENV APP_DIR=${APP_DIR}
ENV BINARY_NAME=${BINARY_NAME}

# Expose the application port.
EXPOSE ${APP_HOST_PORT}

#CMD ["./server"]
#CMD ["/app/${BINARY_NAME}"]
ENTRYPOINT ["/app/server"]
# Launch the binary (shell-form so that environment variables are expanded).
ENTRYPOINT ["/bin/sh", "-c", "exec ${APP_DIR}/${BINARY_NAME}"]
Loading