Skip to content

Commit

Permalink
Merge pull request #282 from sysadminsmedia/katos/docker-arm-refactor
Browse files Browse the repository at this point in the history
Refactor Docker files for ARM performance improvements
  • Loading branch information
katosdev authored Oct 13, 2024
2 parents a1e6685 + d45c8b2 commit 9b72419
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 85 deletions.
34 changes: 16 additions & 18 deletions .github/workflows/docker-publish-arm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,42 @@ env:
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}


jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
attestations: write
id-token: write

steps:
# Step 1: Checkout repository
- name: Checkout repository
uses: actions/checkout@v4

# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
# Step 2: Set up Buildx without specifying driver
# Let it use default settings to avoid the 'no remote endpoint' issue
- name: Set up Docker Buildx
uses: docker/[email protected] # v3.0.0
uses: docker/[email protected]
with:
install: true # Ensure Buildx is installed and set up properly
use: true # Use Buildx instance directly for this job

# Login against a Docker registry except on PR
# https://github.com/docker/login-action
# Step 3: Login against Docker registry except on PR
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/[email protected] # v3.0.0
uses: docker/[email protected]
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
# Step 4: Extract metadata for Docker images
- name: Extract Docker metadata
id: meta
uses: docker/[email protected] # v5.0.0
uses: docker/[email protected]
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
Expand All @@ -80,11 +78,10 @@ jobs:
flavor: |
suffix=-arm,onlatest=true
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
# Step 5: Build and push the Docker image
- name: Build and push Docker image
id: build-and-push
uses: docker/[email protected] # v5.0.0
uses: docker/[email protected]
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
Expand All @@ -95,8 +92,9 @@ jobs:
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
COMMIT=${{ github.sha }}
COMMIT=${{ github.sha }}
# Step 6: Attest built image to prove build provenance
- name: Attest
uses: actions/attest-build-provenance@v1
id: attest
Expand Down
32 changes: 14 additions & 18 deletions .github/workflows/docker-publish-rootless-arm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,51 +24,47 @@ on:
- '.dockerignore'
- '.github/workflows'


env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}


jobs:
build-rootless:

runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
attestations: write
id-token: write

steps:
# Step 1: Checkout repository
- name: Checkout repository
uses: actions/checkout@v4

# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
# Step 2: Set up Buildx without specifying driver
- name: Set up Docker Buildx
uses: docker/[email protected] # v3.0.0
uses: docker/[email protected]
with:
install: true # Ensure Buildx is installed and set up properly
use: true # Use Buildx instance directly for this job

# Login against a Docker registry except on PR
# https://github.com/docker/login-action
# Step 3: Login to Docker registry except on PR
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/[email protected] # v3.0.0
uses: docker/[email protected]
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
# Step 4: Extract metadata for Docker images
- name: Extract Docker metadata
id: metadata
uses: docker/[email protected] # v5.0.0
uses: docker/[email protected]
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
Expand All @@ -80,12 +76,11 @@ jobs:
type=schedule,pattern=nightly
flavor: |
suffix=-rootless-arm,onlatest=true
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
# Step 5: Build and push the Docker image
- name: Build and push Docker image
id: build-and-push
uses: docker/[email protected] # v5.0.0
uses: docker/[email protected]
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
Expand All @@ -98,6 +93,7 @@ jobs:
VERSION=${{ github.ref_name }}
COMMIT=${{ github.sha }}
# Step 6: Attest built image to prove build provenance
- name: Attest
uses: actions/attest-build-provenance@v1
id: attest
Expand Down
76 changes: 49 additions & 27 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,69 +1,91 @@
# Node dependencies
FROM node:18-alpine AS frontend-dependencies
# Node dependencies stage
FROM --platform=$TARGETPLATFORM node:18-alpine AS frontend-dependencies
WORKDIR /app

# Install pnpm globally (caching layer)
RUN npm install -g pnpm

# Copy package.json and lockfile to leverage caching
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --shamefully-hoist

# Build Nuxt
FROM node:18-alpine AS frontend-builder
WORKDIR /app
# Build Nuxt (frontend) stage
FROM --platform=$TARGETPLATFORM node:18-alpine AS frontend-builder
WORKDIR /app

# Install pnpm globally again (it can reuse the cache if not changed)
RUN npm install -g pnpm
COPY frontend .

# Copy over source files and node_modules from dependencies stage
COPY frontend .
COPY --from=frontend-dependencies /app/node_modules ./node_modules
RUN pnpm build

FROM golang:alpine AS builder-dependencies
# Go dependencies stage
FROM --platform=$TARGETPLATFORM golang:alpine AS builder-dependencies
WORKDIR /go/src/app
COPY ./backend .

# Copy go.mod and go.sum for better caching
COPY ./backend/go.mod ./backend/go.sum ./
RUN go mod download

# Build API
FROM golang:alpine AS builder
# Build API stage
FROM --platform=$TARGETPLATFORM golang:alpine AS builder
ARG BUILD_TIME
ARG COMMIT
ARG VERSION

# Install necessary build tools
RUN apk update && \
apk upgrade && \
apk add --update git build-base gcc g++
apk add --no-cache git build-base gcc g++

WORKDIR /go/src/app

# Copy Go modules (from dependencies stage) and source code
COPY --from=builder-dependencies /go/pkg/mod /go/pkg/mod
COPY ./backend .

# Clear old public files and copy new ones from frontend build
RUN rm -rf ./app/api/public
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
COPY --from=builder-dependencies /go/pkg/mod /go/pkg/mod
RUN --mount=type=cache,target=/root/.cache/go-build \

# Use cache for Go build artifacts
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-o /go/bin/api \
-v ./app/api/*.go

FROM gcr.io/distroless/java:latest

# Production Stage
FROM alpine:latest

# Production stage
FROM --platform=$TARGETPLATFORM alpine:latest
ENV HBOX_MODE=production
ENV HBOX_STORAGE_DATA=/data/
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_pragma=busy_timeout=2000&_pragma=journal_mode=WAL&_fk=1

RUN apk --no-cache add ca-certificates
# Install necessary runtime dependencies
RUN apk --no-cache add ca-certificates wget

# Create application directory and copy over built Go binary
RUN mkdir /app
COPY --from=builder /go/bin/api /app

RUN chmod +x /app/api
RUN apk add --no-cache wget

# Labels and configuration for the final image
LABEL Name=homebox Version=0.0.1
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"

# Expose necessary ports
EXPOSE 7745
WORKDIR /app
HEALTHCHECK --interval=30s \
--timeout=5s \
--start-period=5s \
--retries=3 \
CMD [ "/usr/bin/wget", "--no-verbose", "--tries=1", "-O -", "http://localhost:7745/api/v1/status" ]

# Healthcheck configuration
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD [ "wget", "--no-verbose", "--tries=1", "-O", "-", "http://localhost:7745/api/v1/status" ]

# Persist volume
VOLUME [ "/data" ]

# Entrypoint and CMD
ENTRYPOINT [ "/app/api" ]
CMD [ "/data/config.yml" ]
47 changes: 25 additions & 22 deletions Dockerfile.rootless
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,67 @@ RUN pnpm install --frozen-lockfile --shamefully-hoist

# Build Nuxt
FROM node:18-alpine AS frontend-builder
WORKDIR /app
RUN npm install -g pnpm
COPY frontend .
WORKDIR /app
COPY frontend ./
COPY --from=frontend-dependencies /app/node_modules ./node_modules
RUN pnpm build

# Build Go dependencies
FROM golang:alpine AS builder-dependencies
WORKDIR /go/src/app
COPY ./backend .
COPY ./backend/go.mod ./backend/go.sum ./
RUN go mod download

# Build API
FROM golang:alpine AS builder
ARG BUILD_TIME
ARG COMMIT
ARG VERSION
RUN apk update && \
apk upgrade && \
apk add --update git build-base gcc g++

RUN apk update && apk upgrade && apk add --no-cache git build-base gcc g++

WORKDIR /go/src/app
COPY ./backend .
RUN rm -rf ./app/api/public
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
COPY --from=builder-dependencies /go/pkg/mod /go/pkg/mod
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-o /go/bin/api \
-v ./app/api/*.go

FROM gcr.io/distroless/java:latest
# Use cache for Go build
RUN --mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
-o /go/bin/api ./app/api/*.go

# Production Stage
# Production stage with distroless
FROM gcr.io/distroless/static:latest

ENV HBOX_MODE=production
ENV HBOX_STORAGE_DATA=/data/
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1

# Copy the binary and the (empty) /data dir and
# change the ownership to the low-privileged user
# Copy the binary and data directory, change ownership
COPY --from=builder --chown=nonroot /go/bin/api /app
COPY --from=builder --chown=nonroot /data /data

RUN apk add --no-cache wget
# Add wget to the image
# Note: If using distroless, this may not be applicable
# as distroless images do not include package managers.
# This line may be omitted if you're relying on another way to handle healthchecks.
COPY --from=alpine:latest /bin/wget /usr/bin/wget

LABEL Name=homebox Version=0.0.1
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"
EXPOSE 7745

HEALTHCHECK --interval=30s \
--timeout=5s \
--start-period=5s \
--retries=3 \
CMD [ "/usr/bin/wget", "--no-verbose", "--tries=1", "-O -", "http://localhost:7745/api/v1/status" ]
VOLUME [ "/data" ]
CMD ["/usr/bin/wget", "--no-verbose", "--tries=1", "-O", "-", "http://localhost:7745/api/v1/status"]

VOLUME ["/data"]

# Drop root and run as low-privileged user
# Drop root and run as a low-privileged user
USER nonroot
ENTRYPOINT [ "/app" ]
CMD [ "/data/config.yml" ]
ENTRYPOINT ["/app"]
CMD ["/data/config.yml"]

0 comments on commit 9b72419

Please sign in to comment.