界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
部署与运维 · SKILL.md

deployment-patterns

deployment-patterns

【部署与运维】Deploys Go applications with build optimization, multi-stage Docker builds, binary sizing, and deployment strategies for cloud and on-prem.

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

技能说明

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:

  • code
  • guidance
  • do-dont
  • examples triggers: go deployment, go docker build, multi stage build, go binary optimization, go cross compilation, go build tags archetypes:
  • tactical anti_triggers:
  • brainstorming
  • vague ideation response_profile: verbosity: low directive_strength: high abstraction_level: operational related-skills: cloud-development, best-practices, modular-design maturity: stable completeness: 95 exampleCount: 3

Go Deployment Patterns

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.

TL;DR Checklist

  • Use multi-stage Docker builds — never include source code or build tools in the final image
  • Enable Go build optimizations: -ldflags "-s -w", CGO_ENABLED=0, GOOS=linux
  • Use a minimal base image (distroless, scratch, or alpine) for the final stage
  • Cross-compile for target platforms — never build on the deployment machine
  • Run the binary as a non-root user in the container
  • Use build tags to conditionally include platform-specific code

When to Use

Use this skill when:

  • Building Docker images for Go applications
  • Optimizing binary size for deployment
  • Cross-compiling for different platforms
  • Setting up CI/CD pipelines for Go applications
  • Configuring build tags for feature flags or platform-specific code

When NOT to Use

Avoid this skill for:

  • Simple scripts that are run directly on the host (no containerization needed)
  • Development builds where debugging symbols are needed (don't strip in dev)
  • Applications that require CGO — CGO_ENABLED=0 is not compatible

Core Workflow

  1. Optimize the Build — Enable compiler optimizations and strip debug symbols. Checkpoint: Binary is stripped and smaller than 20MB for simple applications.

  2. 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.

  3. 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.

  4. 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.

  5. Use Build Tags for Variants — Conditionally include code for different environments. Checkpoint: Build tags don't create circular dependencies or break imports.


Implementation Patterns

Pattern 1: Multi-Stage Docker Build (❌ BAD vs ✅ GOOD)

Multi-stage builds produce minimal deployment images.

❌ BAD — Single-Stage Docker Build

# ❌ 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:

  • Final image is ~1GB (includes Go toolchain, source code, all dependencies)
  • Source code and .git directory are in the image — security risk
  • No optimization flags — binary includes debug symbols
  • Runs as root — container security risk
  • No health check — Kubernetes cannot monitor liveness

✅ GOOD — Multi-Stage Docker Build

# 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:

  • Final image is ~20MB (binary + CA certificates) — 50x smaller than single-stage
  • No source code, no Go toolchain, no shell in the final image
  • CGO_ENABLED=0 produces a fully static binary — no libc dependency
  • -ldflags="-s -w" strips debug symbols — smaller binary
  • EXPOSE 8080 documents the port
  • HEALTHCHECK enables Kubernetes liveness/probe
  • Runs as non-root (uid 1000) — container security

Pattern 2: Binary Optimization and Sizing (❌ BAD vs ✅ GOOD)

Build flags control binary size and runtime behavior.

❌ BAD — Default Build

# ❌ 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:

  • Binary includes debug symbols — unnecessarily large
  • No version information — cannot identify which version is running
  • No build metadata — no timestamp or git commit for traceability
  • -race flag adds ~10x memory overhead — only for testing
  • CGO_ENABLED=1 (default) produces dynamically linked binary — not portable

✅ GOOD — Optimized Production Build

# 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:

FlagEffect
-trimpathRemoves file system paths from the binary — builds are reproducible
-sStrips symbol table — smaller binary, no debug info
-wStrips 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=0Disables CGO — produces fully static, portable binary
GOOS=linux GOARCH=amd64Cross-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 runtime
  • CGO_ENABLED=0 produces a fully static binary — no libc dependency, fully portable
  • VersionInfo() serves structured version data for /version endpoint

Pattern 3: Cross-Compilation and Build Tags (❌ BAD vs ✅ GOOD)

Cross-compilation and build tags enable platform-specific builds.

❌ BAD — Platform-Specific Build Scripts

# ❌ 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:

  • Four separate scripts — changes to build flags must be applied in all four
  • No build tag support — platform-specific code requires runtime checks
  • Hard to automate — CI/CD needs to run four separate builds
  • No version consistency — each script could use different flags
  • Windows build produces a .exe — Linux builds don't

✅ GOOD — Unified Cross-Compilation with Build Tags

# 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:

  • Single build script handles all platforms — consistent build flags
  • Build tags (//go:build linux) enable platform-specific code
  • CGO_ENABLED=0 ensures all targets produce static binaries
  • GOOS and GOARCH control cross-compilation — no need for native toolchain
  • Build artifacts are version-consistent — same -ldflags across all platforms
  • Binary sizes are shown — easy to spot unexpected bloat

Constraints

MUST DO

  • MUST use multi-stage Docker builds — never include the Go toolchain in the final image
  • MUST set CGO_ENABLED=0 for all production builds — produces static, portable binaries
  • MUST use -ldflags="-s -w" for all production builds — strip debug symbols
  • MUST use -trimpath for reproducible builds — removes file system paths from the binary
  • MUST use a minimal base image (distroless, scratch, or alpine) for the final Docker stage
  • MUST run the container as a non-root user
  • MUST include a HEALTHCHECK directive in the Dockerfile

MUST NOT DO

  • MUST NOT include source code, .git, or test files in the Docker image
  • MUST NOT use golang as the final base image — it includes the toolchain (~800MB)
  • MUST NOT run the container as root — use USER 1000 or distroless's default non-root
  • MUST NOT use CGO_ENABLED=1 in production unless absolutely necessary (CGO binaries are not portable)
  • MUST NOT commit build scripts that hardcode platform-specific paths
  • MUST NOT skip the -trimpath flag — builds must be reproducible

Related Skills

SkillPurpose
cloud-developmentCloud-native deployment with Kubernetes manifests and rolling updates
best-practicesGo idioms and code organization for maintainable applications
modular-designPackage structure that supports build tags and platform-specific code

Live References

Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.

我的收藏