Building Modules
This guide covers building TinySystems modules for production deployment.
Container Build
Dockerfile
Standard multi-stage Dockerfile:
dockerfile
# Build stage
FROM golang:1.21-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /app
# Cache dependencies
COPY go.mod go.sum ./
RUN go mod download
# Build
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-w -s" -o module ./cmd/main.go
# Runtime stage
FROM alpine:3.18
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/module .
# Non-root user
RUN adduser -D -u 1000 appuser
USER appuser
ENTRYPOINT ["/app/module"]Build Commands
bash
# Local build
docker build -t my-module:dev .
# With build args
docker build \
--build-arg VERSION=1.0.0 \
--build-arg COMMIT=$(git rev-parse HEAD) \
-t my-module:1.0.0 .
# Multi-platform
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.io/my-module:1.0.0 \
--push .Using CLI
bash
# Simple build
tinysystems build --tag my-module:1.0.0
# Build and push
tinysystems build --tag registry.io/my-module:1.0.0 --push
# Multi-platform
tinysystems build \
--tag registry.io/my-module:1.0.0 \
--platform linux/amd64,linux/arm64 \
--pushBuild Optimizations
Layer Caching
Optimize for Docker layer cache:
dockerfile
# Cache go mod download
COPY go.mod go.sum ./
RUN go mod download
# Only copy source after mod download
COPY . .Binary Size
Reduce binary size:
dockerfile
RUN CGO_ENABLED=0 GOOS=linux \
go build -ldflags="-w -s -X main.version=${VERSION}" \
-o module ./cmd/main.go| Flag | Effect |
|---|---|
-w | Remove DWARF debug info |
-s | Remove symbol table |
-X | Set variable at link time |
Multi-Stage Builds
Minimal runtime image:
dockerfile
# Use distroless for even smaller images
FROM gcr.io/distroless/static-debian11
COPY --from=builder /app/module /module
ENTRYPOINT ["/module"]Version Injection
Build-Time Variables
go
// main.go
package main
var (
version = "dev"
commit = "unknown"
date = "unknown"
)
func main() {
log.Info("starting module",
"version", version,
"commit", commit,
"date", date,
)
// ...
}Build with Version
bash
go build -ldflags="-X main.version=1.0.0 -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -o module ./cmd/main.goIn Dockerfile
dockerfile
ARG VERSION=dev
ARG COMMIT=unknown
ARG BUILD_DATE=unknown
RUN go build -ldflags="-w -s \
-X main.version=${VERSION} \
-X main.commit=${COMMIT} \
-X main.date=${BUILD_DATE}" \
-o module ./cmd/main.goCI/CD Build
GitHub Actions
yaml
name: Build and Push
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}
ghcr.io/${{ github.repository }}:latest
build-args: |
VERSION=${{ steps.version.outputs.VERSION }}
COMMIT=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=maxGitLab CI
yaml
build:
stage: build
image: docker:24-dind
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use
- docker buildx build
--platform linux/amd64,linux/arm64
--build-arg VERSION=$CI_COMMIT_TAG
--build-arg COMMIT=$CI_COMMIT_SHA
--push
-t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
.
only:
- tagsBuild Validation
Verify Image
bash
# Check image layers
docker history my-module:1.0.0
# Check image size
docker images my-module:1.0.0
# Run smoke test
docker run --rm my-module:1.0.0 --version
# Scan for vulnerabilities
trivy image my-module:1.0.0Test Before Push
yaml
# In CI
- name: Test image
run: |
docker run -d --name test-module my-module:$VERSION
sleep 5
docker logs test-module
docker exec test-module wget -q -O- http://localhost:8080/healthz
docker stop test-moduleMakefile
Common build tasks:
makefile
VERSION ?= $(shell git describe --tags --always --dirty)
COMMIT := $(shell git rev-parse HEAD)
DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
REGISTRY ?= ghcr.io/myorg
IMAGE := $(REGISTRY)/my-module
.PHONY: build docker-build docker-push
build:
go build -ldflags="-w -s \
-X main.version=$(VERSION) \
-X main.commit=$(COMMIT) \
-X main.date=$(DATE)" \
-o bin/module ./cmd/main.go
docker-build:
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
--build-arg BUILD_DATE=$(DATE) \
-t $(IMAGE):$(VERSION) .
docker-push: docker-build
docker push $(IMAGE):$(VERSION)
docker tag $(IMAGE):$(VERSION) $(IMAGE):latest
docker push $(IMAGE):latest
docker-buildx:
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg VERSION=$(VERSION) \
--push \
-t $(IMAGE):$(VERSION) .Next Steps
- Helm Charts - Package for deployment
- Registry Publishing - Push to registries
- Versioning - Version strategy