容器化应用Skill containerizing-applications

本技能专注于使用 Docker、docker-compose 和 Helm 等工具,将应用程序打包成容器并进行部署。它提供了从创建安全的 Dockerfile(包括多阶段构建和强化镜像)、配置服务编排(docker-compose),到打包为 Kubernetes Helm 图表的完整工作流。内容涵盖实战中常见的 15+ 个问题解决方案,并强调生产环境安全,如使用 Docker 强化镜像减少漏洞、Trivy 扫描等。关键词:Docker容器化,docker-compose编排,Helm图表,Kubernetes部署,多阶段构建,生产安全,DevOps,云原生。

Docker/K8s 0 次安装 2 次浏览 更新于 3/2/2026

name: 容器化应用 description: | 使用 Docker、docker-compose 和 Helm 图表对应用程序进行容器化。 适用于为 Kubernetes 创建 Dockerfile、docker-compose 配置或 Helm 图表时。 包含 Docker 强化镜像(减少 95% 的 CVE)、多阶段构建以及 15 个以上经过实战检验的常见问题。

容器化应用

快速开始

  1. 首先运行影响分析(环境变量、网络拓扑、身份验证/CORS)
  2. 使用以下模式生成 Dockerfile
  3. 创建具有适当网络配置的 docker-compose.yml
  4. 打包为 Kubernetes 的 Helm 图表

Dockerfile 模式

FastAPI/Python(使用 uv 的多阶段构建)

# syntax=docker/dockerfile:1
FROM python:3.13-slim AS builder
WORKDIR /app
RUN pip install uv
COPY pyproject.toml .
RUN uv pip install --system --no-cache -r pyproject.toml

FROM python:3.13-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY . .
RUN useradd -u 1000 appuser && chown -R appuser /app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Next.js(独立模式)

# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_API_URL
ARG NEXT_PUBLIC_SSO_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_SSO_URL=$NEXT_PUBLIC_SSO_URL
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]

docker-compose 模式

services:
  web:
    build:
      context: ./web
      args:
        # BROWSER: 打包进 JS 包
        - NEXT_PUBLIC_API_URL=http://localhost:8000
    environment:
      # SERVER: 在容器内运行时读取
      - SERVER_API_URL=http://api:8000
    ports:
      - "3000:3000"
    depends_on:
      api:
        condition: service_healthy

  api:
    build: ./api
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - CORS_ORIGINS=http://localhost:3000,http://web:3000
    ports:
      - "8000:8000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://127.0.0.1:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Helm 图表结构

helm/myapp/
├── Chart.yaml
├── values.yaml
├── templates/
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── configmap.yaml
│   ├── secret.yaml
│   └── ingress.yaml

Chart.yaml

apiVersion: v2
name: myapp
version: 1.0.0
appVersion: "1.0.0"

values.yaml 模式

api:
  replicaCount: 1
  image:
    repository: myapp/api
    tag: latest
    pullPolicy: IfNotPresent
  service:
    type: ClusterIP
    port: 8000
  resources:
    limits:
      cpu: 500m
      memory: 512Mi

Helm 命令

helm create mychart              # 创建新图表
helm template . --debug          # 渲染模板
helm install myapp ./chart       # 安装
helm upgrade myapp ./chart       # 升级
helm install myapp ./chart \
  --set api.image.tag=v2.0.0     # 覆盖值

经过实战检验的常见问题(15+)

1. 浏览器端与服务器端 URL

问题: 浏览器在主机上运行,服务器在容器内运行

build:
  args:
    - NEXT_PUBLIC_API_URL=http://localhost:8000   # 浏览器端
environment:
  - SERVER_API_URL=http://api:8000                # 服务器端

2. 健康检查 IPv6 问题

问题: wget http://localhost:3000 因 IPv6 失败

healthcheck:
  test: ["CMD", "wget", "--spider", "http://127.0.0.1:3000/"]  # 不要用 localhost!

3. MCP 服务器 421 错误请求

问题: FastMCP 拒绝 Docker 服务名称

transport_security = TransportSecuritySettings(
    allowed_hosts=["127.0.0.1:*", "localhost:*", "mcp-server:*", "0.0.0.0:*"]
)

4. SQLModel 表未创建

问题:create_all() 之前未导入模型

# 必须在 create_all() 之前导入
from .models import User, Task, Project  # noqa: F401
SQLModel.metadata.create_all(engine)

5. 数据库迁移顺序

问题: Drizzle db:push 会删除不在模式中的表 解决方案: 启动 postgres → 运行 Drizzle → 然后启动 API

6. uv 网络超时

RUN UV_HTTP_TIMEOUT=120 uv pip install --system --no-cache -r pyproject.toml

7. 缺少语法指令

# syntax=docker/dockerfile:1    # 始终在第一行
FROM python:3.13-slim

8. 容器内的 localhost

服务器端使用 Docker 服务名称(api, web, sso),而不是 localhost

9. 身份验证来源

在构建之前将 Docker 服务名称添加到 trustedOrigins

10. 服务启动顺序

使用带有 condition: service_healthydepends_on

11. 健康检查时机

对于需要时间启动的应用,使用 start_period(例如 40 秒)

12. pgAdmin 邮箱验证

使用有效的邮箱如 admin@example.com,而不是 .local 域名

13. Playwright 在依赖项中

将测试工具保留在 devDependencies 中(避免 300MB+ 的臃肿)

14. MCP 健康检查 406

通过 ASGI 中间件添加单独的 /health 端点

15. Helm 逗号解析

对于包含逗号的值,使用 values 文件而不是 --set

生产环境安全

Docker 强化镜像(推荐)

比社区镜像减少 95% 的 CVE。在 Apache 2.0 许可下免费使用。

# 之前:带有未知 CVE 的社区镜像
FROM python:3.12-slim

# 之后:Docker 强化镜像
FROM docker.io/docker/python:3.12-dhi

DHI 的五大支柱:

支柱 您将获得什么
最小攻击面 减少 98% CVE
100% 完整 SBOM SPDX/CycloneDX 格式
SLSA 构建级别 3 已验证的来源
OpenVEX 机器可读的漏洞状态
Cosign 签名 加密验证

验证签名:

cosign verify docker.io/docker/python:3.12-dhi

读取 SBOM:

docker sbom docker.io/docker/python:3.12-dhi

Trivy 扫描(CI/CD)

- name: 扫描漏洞
  run: trivy image --severity HIGH,CRITICAL --exit-code 1 ${{ env.IMAGE }}

Distroless 镜像(替代方案)

# Python - 使用 gcr.io/distroless/python3-debian12
FROM gcr.io/distroless/python3-debian12
# 无 shell,无包管理器,默认以非 root 用户运行

多架构构建

- uses: docker/build-push-action@v5
  with:
    platforms: linux/amd64,linux/arm64  # 为两者构建
    cache-from: type=gha
    cache-to: type=gha,mode=max

BuildKit 密钥

# 在构建期间挂载密钥(永远不会存储在层中)
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) npm install

完整模式请参阅 references/production-security.md

验证

运行:python scripts/verify.py

相关技能

  • operating-k8s-local - 使用 Minikube 的本地 Kubernetes
  • deploying-cloud-k8s - 云 Kubernetes 部署
  • scaffolding-fastapi-dapr - FastAPI 模式

参考资料