Skip to content

Docker 容器化

运维视角的 Docker 手册:镜像怎么构建得小且安全、数据与网络怎么管理、多容器怎么编排、镜像怎么分发。容器原理(namespace/cgroup/UnionFS)见后端·容器与部署 的分工页面。示例语境:Docker Engine 27 + Docker Compose v2,命令需在真实环境验证。

先分清四个概念

  • 镜像(Image):只读模板,由分层(layer)叠加;改一层会新增层,历史层仍在;
  • 容器(Container):镜像的运行实例,可写层在其上;
  • 仓库(Registry):存放与分发镜像(Docker Hub、Harbor、云镜像仓库);
  • 引擎与守护进程dockerd + containerd 承担容器生命周期,docker CLI 只是客户端。
bash
docker version            # 分 Server 与 Client 两行,连不上先看这里
docker info               # 引擎运行状态与资源

Dockerfile:构建的纪律

层级最小化与缓存友好的顺序:变化越频繁的指令越靠后apt install 在前、COPY 代码 在后)。

dockerfile
# 多阶段构建:builder 阶段含编译工具,最终镜像只要产物
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .

FROM gcr.io/distroless/base-debian12
WORKDIR /app
COPY --from=builder /app/server .
USER nonroot
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD ["/app/server", "health"]
ENTRYPOINT ["/app/server"]

构建实践速查:

  • 永远有 .dockerignore(排除 .gitnode_modules*.log),避免把大文件送进构建上下文;
  • RUN 把包安装与缓存清理放同一层:apt-get install ... && rm -rf /var/lib/apt/lists/*
  • 跑非 root 用户(镜像内置 USER,不要依赖启动后 su);
  • EXPOSE 只是文档,真正映射在 -p / compose 里做;
  • HEALTHCHECK 给编排器(K8s 会替代它)提供就绪依据;
  • 一个容器一个主进程,ENTRYPOINT 定主程序、CMD 给默认参数,前后台混跑会让容器秒退。

数据卷:容器删了数据不能没

类型场景生命周期
Named volume数据库、应用状态由 Docker 管理,docker volume rm 才删
Bind mount挂载本机配置/开发目录随宿主机文件存活
tmpfs临时缓存、敏感小文件容器停止即清空
bash
docker volume create pgdata
docker run -d --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD_FILE=/run/secrets/pg_pw \
  postgres:16

纪律:容器可随时重建docker rm -f 无负担),因为数据都在卷里;Bind mount 目录权限要以容器内用户视角检查,否则常见的「Permission denied」来自属主不匹配。

网络:默认桥接之外要知道的

bash
docker network ls
docker run -p 8080:80 --name web nginx   # 端口映射 宿主:容器
docker network create app-net            # 自定义网络(自动 DNS 解析)
  • 同自定义网络下的容器可用服务名互相访问(内置 DNS),跨主机网络是 K8s/overlay 的领域;
  • 不要用 --network host 图省事绕过端口隔离,安全与可移植性都丢;
  • 容器里看到 127.0.0.1 是容器自己,连宿主服务要用宿主机地址或 host-gateway

资源限制、清理与运行纪律

bash
docker run -d --name api --memory 512m --cpus 1.5 image:tag
docker stats                    # 实时资源占用
docker system df                # 看镜像/容器/卷各占多少
docker system prune -a -f       # 清理悬空镜像与停止容器(-a 连未使用镜像也删,慎用)
docker logs --tail 200 api      # 看容器日志(容器内 stdout/stderr)
  • 不加限制的容器可能吃光宿主机内存触发 OOM-kill 波及无关进程;
  • 日志默认到 stdout 由 Docker 收集,长时间运行要配 --log-driver 与轮转,否则磁盘被 /var/lib/docker 撑爆。

Compose:多容器的声明式入口

yaml
services:
  app:
    build: .
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      DB_HOST: db
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      retries: 5

volumes:
  pgdata:
bash
docker compose up -d            # 起服务(-d 后台)
docker compose ps               # 健康与状态
docker compose logs -f app
docker compose down -v          # 停止并连卷一起删(-v 慎用)

注意 depends_on 默认只保证启动顺序、不保证就绪,依赖健康检查(service_healthy)才是可靠姿势。

镜像仓库与供应链安全

  • 基础镜像锁版本、尽量用带 digest 的引用;构建产物用不可变 taggit-<sha> / 构建号),弃用 latest
  • 内网/私有镜像加签名(cosign)并在部署侧校验,避免供应链投毒;
  • 发布前做镜像漏洞扫描(trivy 等),高危漏洞进发布门禁;
  • 推送凭证用凭据管理器/CI secret,不写进 Dockerfile 与 shell 历史。
bash
docker tag app:git-abc123 registry.example.com/team/app:git-abc123
docker push registry.example.com/team/app:git-abc123

常见坑速查

现象解法
容器秒退前台进程没跑主进程前台的镜像设计,查 docker logs
重启后容器没了没配 restart/自启compose restart--restart unless-stopped
挂载目录 Permission denied属主与容器内用户不符以容器用户视角改属主或用匹配的 uid
磁盘被 Docker 撑爆容器日志/镜像越积越大日志轮转驱动 + system prune 定期清理
不设资源限制宿主机 OOM--memory/--cpus 按需设置
依赖库没起来就启动应用连库失败健康检查 + condition: service_healthy
.dockerignore 缺失构建上下文巨大、泄漏文件.dockerignore
镜像 tag 用 latest生产拉到意外版本不可变 tag + digest

检查清单

  • [ ] Dockerfile 有多阶段构建、非 root 用户、健康检查与合理层顺序;
  • [ ] 数据写入 named volume / bind mount,容器可随时重建;
  • [ ] 端口仅按需映射,应用走自定义网络内服务名互通;
  • [ ] 容器都配置资源限制与日志轮转,无无限增长项;
  • [ ] 镜像使用不可变 tag,发布前完成漏洞扫描与签名校验;
  • [ ] 密钥经 secret 注入,未明文写入任何构建或运行配置。

基于 VitePress 构建 · 内容以知识共享方式沉淀