Writing Skills
writing-skills
效率与协作技能,来自 obra/superpowers,查看原文获取完整用途和操作说明。
deployment-patterns【部署与运维】Deploys Go applications with build optimization, multi-stage Docker builds, binary sizing, and deployment strategies for cloud and on-prem.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: deployment-patterns description: Deploys Go applications with build optimization, multi-stage Docker builds, binary sizing, and deployment strategies for cloud and on-prem. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: go role: implementation scope: implementation output-format: code content-types:
Senior DevOps engineer deploying optimized Go applications with multi-stage Docker builds, binary sizing, cross-compilation, and deployment strategies. This skill covers building minimal binaries, container optimization, and production deployment patterns.
-ldflags "-s -w", CGO_ENABLED=0, GOOS=linuxdistroless, scratch, or alpine) for the final stageUse this skill when:
Avoid this skill for:
CGO_ENABLED=0 is not compatibleOptimize the Build — Enable compiler optimizations and strip debug symbols. Checkpoint: Binary is stripped and smaller than 20MB for simple applications.
Create a Multi-Stage Dockerfile — Build in one stage, copy only the binary in the final stage. Checkpoint: Final image contains only the binary and required certificates.
Cross-Compile for Target Platforms — Build for the deployment target, not the build machine.
Checkpoint: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 produces a portable binary.
Configure the Runtime Environment — Set non-root user, health check, and resource limits. Checkpoint: Container runs as non-root, has a health check, and respects resource limits.
Use Build Tags for Variants — Conditionally include code for different environments. Checkpoint: Build tags don't create circular dependencies or break imports.
Multi-stage builds produce minimal deployment images.
# ❌ BAD: includes Go toolchain, source code, and all build dependencies
FROM golang:1.22
# Set working directory
WORKDIR /app
# Copy everything (including .git, tests, docs)
COPY . .
# Install dependencies (cache is not optimized)
RUN go mod download
# Build with no optimizations
RUN go build -o myapp
# Run the app (as root!)
CMD ["./myapp"]
What's wrong:
.git directory are in the image — security risk# Stage 1: Build
FROM golang:1.22-alpine AS builder
# Install build dependencies (only what's needed)
RUN apk add --no-cache git ca-certificates
# Set working directory
WORKDIR /app
# Copy go mod files first (leverage Docker layer caching)
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build with optimizations
# -ldflags "-s -w" strips debug symbols and DWARF info
# -ldflags "-X main.Version=1.2.3" embeds version info
ARG VERSION=dev
ARG COMMIT=unknown
ARG BUILD_TIME=unknown
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build \
-ldflags="-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildTime=${BUILD_TIME}" \
-o /usr/local/bin/myapp \
./cmd/server/
# Stage 2: Runtime
FROM gcr.io/distroless/static-debian12:nonroot
# Copy the binary from the builder stage
COPY --from=builder /usr/local/bin/myapp /usr/local/bin/myapp
# Distroless images run as non-root by default (uid 1000)
# No shell, no package manager — minimal attack surface
# Expose the HTTP port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/usr/local/bin/myapp", "health"]
# Run the application
ENTRYPOINT ["/usr/local/bin/myapp"]
Why this works:
CGO_ENABLED=0 produces a fully static binary — no libc dependency-ldflags="-s -w" strips debug symbols — smaller binaryEXPOSE 8080 documents the portHEALTHCHECK enables Kubernetes liveness/probeBuild flags control binary size and runtime behavior.
# ❌ BAD: default build — includes debug symbols, DWARF info, and race detector
go build -o myapp ./cmd/server/
# Binary size: ~15MB with debug info
# No version info embedded
# Includes race detector overhead (if -race flag is used)
What's wrong:
-race flag adds ~10x memory overhead — only for testingCGO_ENABLED=1 (default) produces dynamically linked binary — not portable# Production build with all optimizations
VERSION=$(git describe --tags --always)
COMMIT=$(git rev-parse --short HEAD)
BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
go build \
-trimpath \
-ldflags="-s -w \
-X main.Version=${VERSION} \
-X main.Commit=${COMMIT} \
-X main.BuildTime=${BUILD_TIME} \
-extldflags '-static'" \
-o dist/myapp \
./cmd/server/
# Binary size: ~8-12MB (stripped, static, no debug info)
Build flags explained:
| Flag | Effect |
|---|---|
-trimpath | Removes file system paths from the binary — builds are reproducible |
-s | Strips symbol table — smaller binary, no debug info |
-w | Strips DWARF debug information — smaller binary |
-X main.Version=... | Sets a main.Version variable at link time — embeds version info |
-extldflags '-static' | Links all C libraries statically (requires CGO_ENABLED=0 for full static) |
CGO_ENABLED=0 | Disables CGO — produces fully static, portable binary |
GOOS=linux GOARCH=amd64 | Cross-compiles for Linux AMD64 |
// package: main
// Build-time variables set via -ldflags
var (
Version = "dev"
Commit = "unknown"
BuildTime = "unknown"
)
// VersionInfo returns structured version information for the /version endpoint.
func VersionInfo() map[string]string {
return map[string]string{
"version": Version,
"commit": Commit,
"build_time": BuildTime,
"go_version": runtime.Version(),
"os": runtime.GOOS,
"arch": runtime.GOARCH,
}
}
Why this works:
-trimpath makes builds reproducible — same source produces the same binary-s -w strips all debug info — binary is 30-40% smaller-X flags embed build metadata — version info is available at runtimeCGO_ENABLED=0 produces a fully static binary — no libc dependency, fully portableVersionInfo() serves structured version data for /version endpointCross-compilation and build tags enable platform-specific builds.
# ❌ BAD: separate build scripts for each platform — hard to maintain
# build-linux.sh
go build -o myapp-linux ./cmd/server/
# build-darwin.sh
go build -o myapp-darwin ./cmd/server/
# build-windows.sh
go build -o myapp-windows.exe ./cmd/server/
# build-arm.sh
go build -o myapp-arm ./cmd/server/
What's wrong:
.exe — Linux builds don't# build.sh — unified cross-compilation script
#!/usr/bin/env bash
set -euo pipefail
VERSION=$(git describe --tags --always)
COMMIT=$(git rev-parse --short HEAD)
# Target platforms: os:arch:output
PLATFORMS=(
"linux:amd64:myapp"
"linux:arm64:myapp-arm64"
"darwin:amd64:myapp-darwin"
"darwin:arm64:myapp-darwin-arm64"
"windows:amd64:myapp.exe"
)
for platform in "${PLATFORMS[@]}"; do
IFS=':' read -r goos goarch output <<< "$platform"
# Normalize the architecture
case "$goarch" in
amd64) goarch="amd64" ;;
arm64) goarch="arm64" ;;
esac
# Construct the target name
target="${goos}_${goarch}"
echo "Building for ${target} -> ${output}..."
# Set environment variables for cross-compilation
CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" \
go build \
-trimpath \
-ldflags="-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT}" \
-tags="${BUILD_TAGS:-}" \
-o "dist/${output}" \
./cmd/server/
# Show binary size
ls -lh "dist/${output}" | awk '{print " Size: " $5}'
done
echo "Build complete. Artifacts in dist/"
ls -lh dist/
// package: internal/platform
//go:build linux
// Platform-specific initialization for Linux.
func initPlatform() error {
// Linux-specific setup (e.g., cgroups, epoll)
return nil
}
//go:build darwin
// Platform-specific initialization for macOS.
func initPlatform() error {
// macOS-specific setup
return nil
}
//go:build windows
// Platform-specific initialization for Windows.
func initPlatform() error {
// Windows-specific setup
return nil
}
Why this works:
//go:build linux) enable platform-specific codeCGO_ENABLED=0 ensures all targets produce static binariesGOOS and GOARCH control cross-compilation — no need for native toolchain-ldflags across all platformsCGO_ENABLED=0 for all production builds — produces static, portable binaries-ldflags="-s -w" for all production builds — strip debug symbols-trimpath for reproducible builds — removes file system paths from the binarydistroless, scratch, or alpine) for the final Docker stageHEALTHCHECK directive in the Dockerfile.git, or test files in the Docker imagegolang as the final base image — it includes the toolchain (~800MB)USER 1000 or distroless's default non-rootCGO_ENABLED=1 in production unless absolutely necessary (CGO binaries are not portable)-trimpath flag — builds must be reproducible| Skill | Purpose |
|---|---|
cloud-development | Cloud-native deployment with Kubernetes manifests and rolling updates |
best-practices | Go idioms and code organization for maintainable applications |
modular-design | Package structure that supports build tags and platform-specific code |
Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.