vault backup: 2026-05-18 00:17:59
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
---
|
||||
tags: [docker, container, dockerfile, buildkit, image-optimization]
|
||||
create time: 2026-05-18 00:45
|
||||
---
|
||||
|
||||
# Dockerfile 最佳实践 — 教程
|
||||
|
||||
## 概述
|
||||
|
||||
Dockerfile 是微服务交付的标准起点。本文档从多阶段构建出发,详解层缓存技巧、BuildKit 高级特性和常见问题排查。更多话题:多架构构建见 [[../01-容器化/02-多架构构建]],镜像安全见 [[../01-容器化/03-镜像安全]]。
|
||||
|
||||
## Go 多阶段构建(推荐)
|
||||
|
||||
```dockerfile
|
||||
# ========== 阶段 1: 构建 ==========
|
||||
FROM golang:1.22-alpine AS builder
|
||||
|
||||
RUN apk --no-cache add git ca-certificates
|
||||
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
ARG LDFLAGS="-s -w -extldflags '-static'"
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "$LDFLAGS" -o server .
|
||||
|
||||
# ========== 阶段 2: 运行时 ==========
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk --no-cache add ca-certificates tzdata && \
|
||||
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
echo "Asia/Shanghai" > /etc/timezone
|
||||
|
||||
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||
USER appuser
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/server .
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -qO- http://localhost:8080/healthz || exit 1
|
||||
|
||||
CMD ["./server"]
|
||||
```
|
||||
|
||||
> [!question] 为什么要用多阶段构建?
|
||||
>
|
||||
> 单阶段构建中,编译工具和源码都在最终镜像里——一个 Go 项目的镜像轻松超过 800MB。**多阶段构建**把构建和运行拆成两个独立的镜像层,第二阶段只 COPY 二进制文件,最终镜像缩小到十几 MB。
|
||||
|
||||
## 关键优化点速查
|
||||
|
||||
| 优化项 | 方法 | 效果 |
|
||||
|--------|------|------|
|
||||
| **多阶段构建** | 编译和运行分离 | 镜像从 800MB → 15MB |
|
||||
| **alpine 基础镜像** | 替代 debian/ubuntu | 减小体积 |
|
||||
| **非 root 运行** | `USER appuser` | 安全合规 |
|
||||
| **静态链接** | `CGO_ENABLED=0` | 不依赖系统库 |
|
||||
| **layer cache** | `go.mod` 先 COPY | CI 加速构建 |
|
||||
| **健康检查** | HEALTHCHECK 指令 | K8s 原生支持 |
|
||||
|
||||
## `.dockerignore` — 别忽略的文件
|
||||
|
||||
```
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
vendor/
|
||||
tests/
|
||||
*.log
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
```
|
||||
|
||||
> [!tip] 为什么 .dockerignore 很重要?
|
||||
>
|
||||
> 如果不排除 `.git` 目录,整个版本历史都会被打包进镜像(增加数百 MB)。如果排除不当,可能遗漏必要的配置文件。建议每个项目根目录都包含此文件。
|
||||
|
||||
## 层缓存技巧
|
||||
|
||||
### ❌ 差的缓存策略
|
||||
|
||||
```dockerfile
|
||||
# 差:任何文件改动都会让后续所有 layer 失效
|
||||
COPY . .
|
||||
RUN go build
|
||||
```
|
||||
|
||||
### ✅ 好的缓存策略
|
||||
|
||||
```dockerfile
|
||||
# 好:只有 go.mod/go.sum 变化时才重新下载依赖
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN go build
|
||||
```
|
||||
|
||||
核心原则:**经常变动的内容靠近 COPY,少变的放前面**。这样当代码频繁变更时,依赖下载和构建步骤仍能命中缓存。
|
||||
|
||||
## BuildKit 特性
|
||||
|
||||
BuildKit 是新一代构建引擎,默认在 Docker 18.09+ 和所有 Docker Desktop 中启用:
|
||||
|
||||
```bash
|
||||
# 启用 BuildKit 加速
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
# 利用远程缓存 (需配合 registry)
|
||||
docker build --cache-from=harbor.example.com/app/cache:latest .
|
||||
|
||||
# SSH agent forwarding(拉取私有依赖用)
|
||||
docker build --ssh default .
|
||||
|
||||
# 秘密变量注入(不进 Docker history)
|
||||
docker build --secret id=token,env=GITHUB_TOKEN .
|
||||
```
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
# ^^^ 必须声明语法版本以启用 BuildKit
|
||||
|
||||
FROM golang:1.22-alpine AS builder
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go mod download && go build -o server .
|
||||
```
|
||||
|
||||
> [!info] `--mount=type=cache` vs RUN 缓存
|
||||
>
|
||||
> Docker 默认的层缓存会在源文件变化时跳过整层;而 `type=cache` 是增量更新本地缓存目录,不受构建上下文变化影响,适合依赖下载和编译缓存。
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
| 症状 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
| `exec: "app": not found` | 多阶段 COPY 路径错误 | 检查绝对路径和文件名拼写 |
|
||||
| `standard_init_linux.go: exec user process caused: permission denied` | 没有执行权限或缺少换行符 | `chmod +x`, 确保 Linux 换行 |
|
||||
| `service unavailable` (K8s) | HEALTHCHECK 未就绪 | 增大 `--start-period` |
|
||||
| 镜像体积异常大 | `.git` 未排除或多层 RUN 未清理 | 检查 `.dockerignore`,合并 RUN 并 `rm -rf` |
|
||||
| `cannot execute: executable file not found` | 架构不匹配(arm64 → amd64) | 确认 `TARGETARCH` 或手动指定 `--platform` |
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[../01-容器化/02-多架构构建]] — 一次构建全平台镜像
|
||||
- [[../01-容器化/03-镜像安全]] — Trivy 扫描与 distroless 镜像
|
||||
- [[../hhs/MS/05-部署运维/02-Kubernetes]] — K8s 以 Pod 为部署单元,镜像来自 Docker
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
tags: [docker, multi-arch, buildx, cross-platform]
|
||||
create time: 2026-05-18 00:45
|
||||
---
|
||||
|
||||
# 多架构构建 — Buildx 实战
|
||||
|
||||
## 概述
|
||||
|
||||
随着 Apple Silicon 和 ARM 服务器普及,一个平台只能构建一种架构的镜像已无法满足需求。Docker Buildx 让我们**一次构建、全平台运行**。本文档讲解多架构构建的核心操作和原理。
|
||||
|
||||
## Buildx 快速上手
|
||||
|
||||
```bash
|
||||
# 启用 buildx 并创建 multi-platform builder
|
||||
docker buildx create --name mybuilder --use
|
||||
docker buildx inspect --bootstrap
|
||||
```
|
||||
|
||||
## 跨平台构建推送
|
||||
|
||||
```bash
|
||||
# 为 amd64 + arm64 同时构建并推送到仓库
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
-t harbor.example.com/app/server:v1.2.3 \
|
||||
--push \
|
||||
.
|
||||
```
|
||||
|
||||
> [!note] manifest list 原理
|
||||
>
|
||||
> 多架构镜像本质上是一个 **manifest list**——它不直接包含二进制文件,而是记录每个架构对应的镜像 digest。拉取时,客户端根据自身平台自动选择正确的变体。可以用 `docker buildx imagetools inspect <image>` 查看完整列表。
|
||||
|
||||
## 不同场景的构建策略
|
||||
|
||||
| 场景 | 策略 | 命令 |
|
||||
|------|------|------|
|
||||
| 开发阶段 | 只构建本机架构 (`linux/arm64`),速度最快 | `docker buildx build --platform linux/arm64 .` |
|
||||
| CI 流水线 | 构建全平台 (`linux/amd64,linux/arm64`),统一推送 | `--platform linux/amd64,linux/arm64 --push` |
|
||||
| 临时调试 | `--load` 仅构建本地可用 | `docker buildx build --load .` |
|
||||
|
||||
## Go 跨平台编译提示
|
||||
|
||||
在 Dockerfile 中使用 `ARG TARGETARCH`(Buildx 自动传入):
|
||||
|
||||
```dockerfile
|
||||
FROM golang:1.22-alpine AS builder
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN echo "Building for: $TARGETARCH ($TARGETPLATFORM)"
|
||||
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH \
|
||||
go build -a -ldflags "-s -w" -o server-$TARGETARCH .
|
||||
```
|
||||
|
||||
> [!info] 环境变量对照
|
||||
>
|
||||
> | 变量 | 示例值 | 说明 |
|
||||
> |------|--------|------|
|
||||
> | `TARGETARCH` | `amd64`, `arm64` | CPU 架构 |
|
||||
> | `TARGETOS` | `linux`, `darwin` | 操作系统 |
|
||||
> | `TARGETPLATFORM` | `linux/amd64`, `linux/arm64` | 完整平台标识 |
|
||||
|
||||
## 补充:QEMU 模拟(CI 中的备用方案)
|
||||
|
||||
当你的 CI 环境不支持 Buildx QEMU 插件时,可以手动安装:
|
||||
|
||||
```bash
|
||||
# 注册 QEMU 模拟器
|
||||
docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
|
||||
# 之后即可通过 qemu 模拟不同平台构建(速度较慢)
|
||||
docker buildx build --platform linux/arm64 -t my-app:arm64 .
|
||||
```
|
||||
|
||||
> [!warning] 模拟构建 vs 原生交叉编译
|
||||
>
|
||||
> QEMU 模拟可以运行在其他架构上,但构建速度极慢(通常比原生慢 5~10 倍)。生产环境推荐使用 **Go 原生交叉编译**(`GOOS=linux GOARCH=arm64`),零依赖且秒级完成。只在需要 C 扩展的场景才考虑 QEMU 模拟。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[../01-容器化/01-Dockerfile最佳实践]] — 多阶段构建 + BuildKit 特性
|
||||
- [[../hhs/MS/05-部署运维/02-Kubernetes]] — K8s 节点混合架构时的调度考量
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
tags: [docker, image-security, trivy, distroless, registry]
|
||||
create time: 2026-05-18 00:45
|
||||
---
|
||||
|
||||
# 镜像安全 — 扫描、基线 & 仓库管理
|
||||
|
||||
## 概述
|
||||
|
||||
镜像安全不仅仅是"不要有 CVE"——它是一个完整的供应链安全体系,涵盖基础镜像选型、漏洞扫描、仓库分级管理和推送门禁。更多内容参见 [[../01-容器化/01-Dockerfile最佳实践]]。
|
||||
|
||||
## 镜像扫描流程
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Scan["镜像扫描 (Trivy/Snyk)"] --> Clean{"漏洞等级?"}
|
||||
|
||||
Clean -- "CRITICAL/HIGH" --> Block["❌ 阻止推送"]
|
||||
Clean -- "MEDIUM" --> Review["⚠️ 人工审核"]
|
||||
Clean -- "LOW/INFO" --> Allow["✅ 允许推送"]
|
||||
|
||||
style Block fill:#ffebee
|
||||
style Review fill:#fff3e0
|
||||
style Allow fill:#e8f5e9
|
||||
```
|
||||
|
||||
### Trivy 常用命令
|
||||
|
||||
```bash
|
||||
# 扫描镜像
|
||||
trivy image harbor.example.com/app/server:v1.2.3
|
||||
|
||||
# 只报告 HIGH 及以上级别
|
||||
trivy image --severity HIGH,CRITICAL harbor.example.com/app/server:v1.2.3
|
||||
|
||||
# 输出 JSON 供 CI 集成
|
||||
trivy image --format json --output report.json harbor.example.com/app/server:v1.2.3
|
||||
|
||||
# 扫描 Dockerfile 中的潜在问题
|
||||
trivy config Dockerfile
|
||||
```
|
||||
|
||||
## 安全检查清单
|
||||
|
||||
- [ ] 不使用 `latest` tag(永远用具体版本号)→ 精确回滚
|
||||
- [ ] 不安装不必要的软件包(`apk del --purge .build-deps`)→ 最小攻击面
|
||||
- [ ] 定期更新基础镜像(修复 CVE)→ 跟上安全补丁节奏
|
||||
- [ ] 使用非 root 用户运行 → `USER appuser`
|
||||
- [ ] 镜像不包含密钥、密码、私钥 → CI/CD Secret 挂载代替 ENV
|
||||
- [ ] 使用最小基础镜像(scratch / distroless)→ 减少暴露面
|
||||
|
||||
## Distroless 镜像
|
||||
|
||||
Google 推出的**无 shell、无包管理器**的极简运行时镜像:
|
||||
|
||||
```dockerfile
|
||||
# 最极致的精简
|
||||
FROM gcr.io/distroless/static-debian12
|
||||
COPY --from=builder /app/server .
|
||||
USER nonroot
|
||||
CMD ["./server"]
|
||||
```
|
||||
|
||||
> ⚠️ 注意:distroless 镜像没有 shell (`/bin/sh`),调试时需要额外工具(如 debug 镜像或 `kubectl exec` 到 sidecar)。
|
||||
|
||||
### 常用 Distroless 变种
|
||||
|
||||
| 镜像 | 适用场景 | 大小 |
|
||||
|------|---------|------|
|
||||
| `gcr.io/distroless/static-debian12` | 纯静态二进制 | ~2MB |
|
||||
| `gcr.io/distroless/base-debian12` | 需要 glibc 的动态链接程序 | ~30MB |
|
||||
| `gcr.io/distroless/java17` | Java 应用(含 JRE) | ~150MB |
|
||||
| `gcr.io/distroless/nodejs18` | Node.js 应用 | ~100MB |
|
||||
|
||||
## 镜像压缩对比参考
|
||||
|
||||
| 基础镜像 | 典型 Go 服务大小 | 备注 |
|
||||
|---------|-----------------|------|
|
||||
| `ubuntu:22.04` | ~35 MB | 完整发行版 |
|
||||
| `debian:bookworm-slim` | ~20 MB | 裁剪后较常用 |
|
||||
| `alpine:3.19` | ~7 MB | musl libc,偶有兼容问题 |
|
||||
| `distroless/static` | ~5 MB | 无 shell,生产推荐 |
|
||||
| `scratch` | <取决于二进制 | 仅静态链接二进制可运行 |
|
||||
|
||||
> [!warning] Alpine 与 musl libc
|
||||
>
|
||||
> Alpine 使用 musl libc 而非 glibc。某些 C 扩展(如 `node-gyp`、部分 Python wheel)可能无法编译或运行时行为不一致。Go 服务通过 `CGO_ENABLED=0` 规避了此问题,但 Node.js/Python 应用需谨慎评估。
|
||||
|
||||
## 镜像仓库管理与流转
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Dev["开发者本地"] -->|"docker push"| DEV_REPO["dev 仓库<br/>v1.2.3-dev"]
|
||||
|
||||
Staging["Staging 验证"] -->|"通过"| PROD_REPO["prod 仓库<br/>v1.2.3"]
|
||||
|
||||
K8s["K8s Cluster"] -->|"pull"| PROD_REPO
|
||||
|
||||
PR["PR Merge"] --> Tag["打标签 v1.2.3"]
|
||||
Tag --> ProdRepoMove["移动到 prod 仓库"]
|
||||
|
||||
style DEV_REPO fill:#fff3e0
|
||||
style PROD_REPO fill:#e8f5e9
|
||||
```
|
||||
|
||||
| 仓库方案 | 特点 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| **Harbor** | 自托管,RBAC + 扫描 | 国内企业首选 |
|
||||
| **ECR / ACR / GCR** | 云厂商托管 | 全云环境 |
|
||||
| **Docker Hub** | 公共免费 | 开源项目 |
|
||||
|
||||
### Harbor 最佳实践
|
||||
|
||||
| 实践 | 说明 |
|
||||
|------|------|
|
||||
| **项目隔离** | dev/prod 独立项目,不同团队有不同权限 |
|
||||
| **自动扫描** | 推送时自动触发 Trivy 扫描,阻断高危漏洞 |
|
||||
| **复制规则** | dev 仓库 → prod 仓库自动复制已通过测试的镜像 |
|
||||
| **生命周期策略** | 自动清理过期镜像和垃圾数据 |
|
||||
|
||||
## 补充:镜像签名与 Cosign
|
||||
|
||||
为了防止中间人攻击或被篡改的镜像被拉到集群,可以使用 Sigstore Cosign 对镜像签名:
|
||||
|
||||
```bash
|
||||
# 签名镜像
|
||||
cosign sign --key cosign.key harbor.example.com/app/server:v1.2.3
|
||||
|
||||
# 在 CI 中验证签名
|
||||
cosign verify --key cosign.pub harbor.example.com/app/server:v1.2.3
|
||||
|
||||
# 不依赖密钥的验证( rekor 透明日志)
|
||||
cosign verify --certificate-identity=email@company.com \
|
||||
--certificate-oidc-issuer=https://accounts.google.com \
|
||||
harbor.example.com/app/server:v1.2.3
|
||||
```
|
||||
|
||||
> [!info] 镜像签名 vs 漏洞扫描
|
||||
>
|
||||
> 两者解决不同问题:
|
||||
> - **漏洞扫描** = 这个镜像有没有已知漏洞?
|
||||
> - **镜像签名** = 这个镜像确实是我们构建的,没有被篡改?
|
||||
>
|
||||
> 生产环境应该两者都有。签名可以作为 Gatekeeper/Admission Webhook 的一部分,在 Pod 启动前强制验证镜像签名。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[../01-容器化/01-Dockerfile最佳实践]] — Dockerfile 中的安全加固
|
||||
- [[../01-容器化/02-多架构构建]] — Buildx 构建全平台镜像
|
||||
- [[../hhs/MS/05-部署运维/03-CICD与GitOps]] — CI 流水线中的安全扫描环节
|
||||
Reference in New Issue
Block a user