1_Docker核心原理与实践

1. 核心概念

Docker 体系围绕三个核心对象展开:镜像(Image)、容器(Container)、仓库(Repository)。

1.1 镜像

镜像是一个只读的文件系统模板,包含运行应用所需的代码、运行时、系统工具、库文件和配置。

与 ISO 不同,Docker 镜像并非单一打包文件,而是由多层文件系统联合组成的虚拟概念。底层技术是 Union FS(联合文件系统),每一层只记录相对于上一层的增量变化。分层设计带来两个直接好处:

  1. 共享基础层:多个镜像可以共享相同的底层(如同一个 ubuntu:22.04 基础层),节省磁盘空间。
  2. 构建缓存:重新构建镜像时,未变更的层直接复用,加快构建速度。

1.2 容器

容器是镜像的运行实例。启动容器时,Docker 以镜像为基础层,在其上创建一个可读写的容器存储层。

容器存储层应保持无状态化,需要持久化的数据使用数据卷(Volume)或绑定挂载(Bind Mount)。这些方式跳过容器存储层,直接对宿主机(或网络存储)读写,性能和稳定性更高。数据卷的生存周期独立于容器——容器删除后,数据卷中的数据不会丢失。

1.3 仓库

仓库(Repository)用于集中存储同一软件的不同版本镜像,通过标签(Tag)区分版本。Registry(如 Docker Hub、Harbor)是仓库的托管服务,一个 Registry 下包含多个 Repository。

  • 格式:<仓库名>:<标签>,如 ubuntu:22.04
  • 不指定标签时默认使用 latest
  • 仓库名常以两段式路径出现,如 nginx/nginx-ingress,前者是用户名/组织名,后者是软件名

1.4 三者的关系

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
    R["仓库 Repository"] -->|"docker pull"| I["镜像 Image"]
    I -->|"docker run"| C["容器 Container"]
    C -->|"docker commit"| I
    I -->|"docker push"| R
    C -..->|"挂载"| V["数据卷 Volume"]

    classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef success fill:#10B981,stroke:#059669,color:#fff
    classDef warning fill:#F59E0B,stroke:#D97706,color:#fff

    class R primary
    class I success
    class C,V warning

工作流:从仓库 pull 镜像到本地 → 基于镜像 run 出容器 → 容器运行中产生的变更可通过 commit 保存为新镜像 → 新镜像 push 回仓库。容器运行时可挂载数据卷实现数据持久化。

2. 容器底层原理

容器和虚拟机有什么区别?回答这个问题,需要理解容器的两大支柱技术——Namespace 和 Cgroups。

2.1 从进程隔离说起

在 Linux 中,所有进程共享同一个内核、同一套文件系统、同一个网络栈,默认可以互相看到、互相通信。

容器的核心思路是:不启动新内核,而是通过内核提供的隔离机制,让一组进程 “ 以为 “ 自己运行在一个独立的系统中。

类比:一栋大楼(Linux 内核)中,每个房间(容器)共享水电网基础设施,但墙壁(Namespace)让住户互相看不到,电表和水表(Cgroups)限制每户能使用的资源总量。

2.2 Namespace:资源隔离

Linux Namespace 是内核提供的隔离机制,每种 Namespace 隔离一类系统资源。容器主要依赖以下六种(Linux 内核还提供 Cgroup 和 Time Namespace,Docker 20.10+ 默认启用 Cgroup Namespace):

Namespace隔离内容容器中的效果
PID进程 ID容器内进程从 PID 1 开始编号,看不到宿主机的其他进程
NET网络栈容器拥有独立的网卡、IP 地址、端口空间和路由表
MNT文件系统挂载点容器有自己的根文件系统,看不到宿主机的目录结构
UTS主机名和域名容器可以设置自己的 hostname,不影响宿主机
IPC进程间通信容器内的信号量、消息队列、共享内存与其他容器隔离
User用户和用户组 ID容器内的 root 用户可映射为宿主机的普通用户

直观验证:在容器内执行 ps aux 只能看到容器自己的进程;hostname 返回容器名称而非宿主机名。这些 “ 幻觉 “ 全部由 Namespace 制造。

2.3 Cgroups:资源限制

Namespace 解决了 “ 看到什么 “ 的问题,Cgroups 解决 “ 能用多少 “ 的问题。不加限制的容器可以吃掉宿主机所有 CPU 和内存。

Control Groups(Cgroups)用于限制、记录和隔离进程组的资源使用,主要控制以下资源:

子系统控制内容示例
cpuCPU 使用时间限制容器最多使用 2 个核心
memory内存用量限制容器最多使用 512MB,超出触发 OOM Killer
blkio块设备 I/O限制容器的磁盘读写速率
pids进程数量防止容器内 fork bomb

Docker 通过 docker run 的参数直接操作 Cgroups:

1
2
# 限制容器最多使用 2 个 CPU 核心、512MB 内存
docker run -d --cpus="2" --memory="512m" nginx

2.4 虚拟机 Vs 容器

理解了 Namespace 和 Cgroups,就能清晰区分二者的架构差异:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TB
    subgraph vm ["虚拟机架构"]
        direction TB
        HW1["硬件 Hardware"]
        HV["Hypervisor"]
        subgraph g1 ["Guest OS 1"]
            A1["App A"]
            L1["Libs"]
            K1["完整内核"]
        end
        subgraph g2 ["Guest OS 2"]
            A2["App B"]
            L2["Libs"]
            K2["完整内核"]
        end
        HW1 --> HV --> g1 & g2
    end

    subgraph ct ["容器架构"]
        direction TB
        HW2["硬件 Hardware"]
        HK["宿主机内核"]
        DR["Docker Engine"]
        subgraph c1 ["Container 1"]
            CA1["App A"]
            CL1["Libs"]
        end
        subgraph c2 ["Container 2"]
            CA2["App B"]
            CL2["Libs"]
        end
        HW2 --> HK --> DR --> c1 & c2
    end

    classDef hw fill:#64748B,stroke:#475569,color:#fff
    classDef hyper fill:#8B5CF6,stroke:#7C3AED,color:#fff
    classDef kernel fill:#EF4444,stroke:#DC2626,color:#fff
    classDef docker fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef app fill:#10B981,stroke:#059669,color:#fff

    class HW1,HW2 hw
    class HV hyper
    class K1,K2,HK kernel
    class DR docker
    class A1,A2,CA1,CA2,L1,L2,CL1,CL2 app
维度虚拟机容器
隔离机制Hypervisor + 独立内核Namespace + Cgroups(共享内核)
启动速度分钟级(需引导完整 OS)秒级(直接启动进程)
资源开销每个 VM 需独立内核 + 系统进程,通常 GB 级几乎无额外开销,只有应用本身的消耗
镜像体积GB 级(包含完整 OS)MB 级(仅包含应用及依赖)
性能有虚拟化层损耗接近原生性能
安全边界强隔离(独立内核,攻击面小)弱隔离(共享内核,内核漏洞会影响所有容器)
典型场景多租户强隔离、异构 OS 共存微服务部署、CI/CD、开发环境一致性

简言之:虚拟机虚拟的是硬件,每个 VM 运行独立内核;容器虚拟的是操作系统环境,所有容器共享宿主机内核。容器更轻更快,但安全隔离不如虚拟机。

3. 镜像原理与操作

3.1 Union FS 与分层存储

Docker 镜像底层通过存储驱动(默认 overlay2)实现分层存储,overlay2 基于内核的 OverlayFS 提供联合挂载能力,将多个只读层叠加成一个统一的文件系统视图。每一条 Dockerfile 指令(RUNCOPY 等)都会产生新的一层。

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TB
    subgraph container ["容器运行时"]
        RW["可读写层(容器层)"]
    end

    subgraph image ["镜像(只读)"]
        L3["Layer 3: COPY app.js"]
        L2["Layer 2: RUN npm install"]
        L1["Layer 1: RUN apt-get update"]
        L0["Base Layer: ubuntu:22.04"]
    end

    RW --> L3 --> L2 --> L1 --> L0

    classDef writable fill:#F59E0B,stroke:#D97706,color:#fff
    classDef readonly fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef base fill:#10B981,stroke:#059669,color:#fff

    class RW writable
    class L1,L2,L3 readonly
    class L0 base
  • 镜像层:所有层都是只读的。多个容器共享同一镜像层时,底层文件不会被复制多份。
  • 容器层:容器启动时在镜像最上方添加一个可读写层。容器内的文件修改都发生在这一层,容器删除后该层随之消失。

3.2 Copy-on-Write

当容器需要修改镜像层中的某个文件时,不会直接改动原始层,而是将该文件从只读层复制到可读写的容器层,再在容器层中修改。后续读取该文件时,从容器层读取修改后的版本,原始镜像层保持不变。

这一机制带来三个好处:

  1. 快速启动:启动容器不需要复制整个镜像文件系统,只需创建一个薄薄的可写层。
  2. 共享存储:100 个基于同一镜像的容器,底层只存一份镜像数据。
  3. 镜像不可变:镜像层永远不会被修改,保证可复现性。

3.3 镜像操作

3.3.1 获取镜像

1
docker pull [Docker Registry 地址[:端口号]/]仓库名[:标签]
  • Registry 地址:格式为 <域名/IP>[:端口号],默认为 Docker Hub。
  • 仓库名:两段式 <用户名>/<软件名>。Docker Hub 不指定用户名时默认为 library(官方镜像)。
1
2
docker pull ubuntu:22.04
# 等价于 docker pull docker.io/library/ubuntu:22.04

3.3.2 列出本地镜像

1
docker images

列表包含仓库名、标签、镜像 ID、创建时间和占用空间。镜像 ID 是唯一标识,一个镜像可对应多个标签。

3.3.3 清理虚悬镜像

docker pulldocker build 都可能产生虚悬镜像——当新旧镜像同名时,旧镜像的名称被取消,出现仓库名和标签均为 <none> 的镜像(dangling image)。

1
2
3
4
5
# 查看虚悬镜像
docker images -f dangling=true

# 清理虚悬镜像
docker image prune

3.3.4 删除镜像

1
2
3
4
5
6
docker image rm <镜像ID或名称>
# 简写
docker rmi <镜像ID或名称>

# 批量删除
docker image rm $(docker images -q ubuntu)

镜像操作掌握后,接下来看容器的完整生命周期管理。

4. 容器操作

容器的完整生命周期如下:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F'}}}%%
stateDiagram-v2
    [*] --> Created: "docker create"
    Created --> Running: "docker start"
    [*] --> Running: "docker run"
    Running --> Paused: "docker pause"
    Paused --> Running: "docker unpause"
    Running --> Stopped: "docker stop"
    Running --> Stopped: "进程退出"
    Stopped --> Running: "docker start"
    Stopped --> Removed: "docker rm"
    Removed --> [*]

4.1 新建并启动

1
docker run -it --rm ubuntu:22.04 bash
  • -i:保持标准输入打开(交互模式)。
  • -t:分配伪终端。-it 组合使用可获得交互式 Shell。
  • --rm:容器退出后自动删除,适合临时使用。
  • ubuntu:22.04:指定基础镜像。
  • bash:覆盖镜像默认的启动命令。

docker run 背后的完整流程:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
    A["docker run"] --> B{"本地存在镜像?"}
    B -->|"否"| C["从仓库下载镜像"]
    B -->|"是"| D["创建容器"]
    C --> D
    D --> E["创建 Namespace 隔离环境"]
    E --> F["通过 Cgroups 设置资源限制"]
    F --> G["挂载只读镜像层 + 可读写层"]
    G --> H["桥接虚拟网络接口 + 分配 IP"]
    H --> I["执行用户指定程序"]
    I --> J["程序退出,容器终止"]

    classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef success fill:#10B981,stroke:#059669,color:#fff
    classDef decision fill:#F59E0B,stroke:#D97706,color:#fff
    classDef danger fill:#EF4444,stroke:#DC2626,color:#fff

    class A primary
    class B decision
    class C,D,E,F,G,H,I success
    class J danger

4.2 后台运行

添加 -d 参数让容器在后台运行:

1
docker run -d --name my-nginx -p 8080:80 nginx

常用参数组合:

参数作用
-d后台运行
--name指定容器名称
-p 宿主:容器端口映射
-v 宿主:容器数据卷挂载
-e KEY=VALUE设置环境变量
--restart always容器退出后自动重启

4.3 查看容器

1
2
3
4
5
6
7
8
9
10
11
# 查看运行中的容器
docker ps

# 查看所有容器(包括已停止的)
docker ps -a

# 查看容器日志
docker container logs <容器ID或名称>

# 实时跟踪容器日志(最后 100 行)
docker logs -f --tail=100 <容器ID>

4.4 进入容器

1
docker exec -it <容器ID或名称> bash

-it 组合使用获得交互式 Shell。如果容器内没有 bash(如 Alpine 镜像),使用 sh 代替。

4.5 启动 / 停止 / 重启

1
2
3
4
docker container start <容器>    # 启动已终止的容器
docker container stop <容器> # 优雅停止(发送 SIGTERM,超时后 SIGKILL)
docker container restart <容器> # 重启
docker container kill <容器> # 强制停止(直接 SIGKILL)

4.6 导出与导入

1
2
3
4
5
6
7
8
# 导出容器快照为 tar 文件
docker export <容器ID> > ubuntu.tar

# 从 tar 导入为镜像
cat ubuntu.tar | docker import - my-ubuntu:v1.0

# 从 URL 导入
docker import http://example.com/image.tgz example/repo

docker load 导入镜像文件(保留完整构建历史),docker import 导入容器快照(仅保留当前状态,可重新指定标签等元数据)。

4.7 删除与清理

1
2
3
4
5
6
7
8
# 删除已终止的容器
docker container rm <容器名称>

# 强制删除运行中的容器
docker container rm -f <容器名称>

# 清理所有已终止的容器
docker container prune

容器操作掌握了,接下来看如何通过仓库分发和管理镜像。

4.8 文件拷贝

docker cp 用于在容器和宿主机之间拷贝文件或目录,常用于调试场景(如拷出日志、临时注入配置文件)。

1
2
3
4
5
6
7
8
# 宿主机 → 容器
docker cp foo.txt mycontainer:/foo.txt

# 容器 → 宿主机
docker cp mycontainer:/var/log/nginx/error.log ./error.log

# 拷贝整个目录
docker cp mycontainer:/etc/nginx/ ./nginx-conf/

注意:docker cp 不依赖容器是否运行,对已停止的容器同样有效。生产环境中应优先使用数据卷挂载,docker cp 仅作为临时调试手段。

4.9 容器访问宿主机服务

容器内的应用有时需要访问宿主机上运行的服务(如本地数据库、API 网关)。不同平台的处理方式有差异:

平台宿主机地址说明
Linux(默认桥接网络)172.17.0.1(docker0 网关)默认值,可能因自定义网络配置而不同
macOS / Windowshost.docker.internalDocker Desktop 内置 DNS 解析
Linux(Docker 20.10+)host.docker.internal需显式启用
1
2
3
4
5
# Linux 上显式启用 host.docker.internal
docker run --add-host=host.docker.internal:host-gateway -d my-app

# 容器内即可通过 host.docker.internal 访问宿主机服务
curl http://host.docker.internal:3306

Docker Compose 中的写法:

1
2
3
4
5
services:
app:
image: my-app
extra_hosts:
- "host.docker.internal:host-gateway"

推荐统一使用 host.docker.internal,避免硬编码 IP 地址,实现跨平台兼容。

5. 仓库与镜像分发

5.1 Docker Hub

Docker Hub 是 Docker 官方的公共镜像仓库。

1
2
3
4
5
6
7
8
# 登录
docker login

# 退出
docker logout

# 搜索镜像
docker search nginx

镜像分两类:

  • 官方镜像(如 nginxredis):Docker 官方维护,单个单词命名。
  • 用户镜像(如 bitnami/redis):带有用户名/组织名前缀。

5.2 推送镜像

1
2
3
4
5
# 给本地镜像打标签
docker tag my-app:v1.0 username/my-app:v1.0

# 推送到 Docker Hub
docker push username/my-app:v1.0

5.3 自动构建

生产环境通常通过 GitHub Actions、GitLab CI 等 CI/CD 工具实现镜像自动构建与推送,在代码提交或创建标签时触发流水线,完成构建、测试、推送到 Registry 的全流程。

5.4 私有仓库

当不希望镜像公开、或需要内网分发时,可以搭建私有仓库。

5.4.1 Docker Registry(轻量方案)

docker-registry 是官方的轻量级私有仓库工具,适合个人和小团队:

1
2
3
4
5
6
7
8
9
10
11
# 启动私有 Registry
docker run -d -p 5000:5000 --name registry \
-v /opt/registry:/var/lib/registry \
registry:2

# 推送镜像到私有仓库
docker tag my-app:v1.0 localhost:5000/my-app:v1.0
docker push localhost:5000/my-app:v1.0

# 从私有仓库拉取
docker pull localhost:5000/my-app:v1.0

5.4.2 Harbor(企业级方案)

Harbor 是 VMware 开源的企业级镜像仓库,在 Docker Registry 基础上增加了:

  • 基于角色的访问控制(RBAC)
  • 镜像漏洞扫描(集成 Trivy)
  • 镜像签名与审计日志
  • 多仓库复制(跨数据中心同步)
  • Web UI 管理界面

适合团队和生产环境使用。

会拉会推之后,还差最后一步——如何在生产环境中安全、稳定地运行容器。

6. 生产实践

6.1 容器资源限制

不限制资源的容器是生产事故的隐患——一个失控的进程可能耗尽宿主机所有 CPU 或内存,拖垮同机的其他服务。

6.1.1 CPU 限制

1
2
3
4
5
6
7
8
# 限制容器最多使用 1.5 个 CPU 核心
docker run -d --cpus="1.5" nginx

# 设置 CPU 份额(相对权重,默认 1024)
docker run -d --cpu-shares=512 nginx

# 绑定到特定 CPU 核心(第 0 和第 1 个核心)
docker run -d --cpuset-cpus="0,1" nginx

6.1.2 Memory 限制

1
2
3
4
5
# 限制最大内存为 256MB,超出时触发 OOM Killer
docker run -d --memory="256m" nginx

# 同时设置 swap 上限(memory + swap 的总量)
docker run -d --memory="256m" --memory-swap="512m" nginx

6.1.3 查看容器资源使用

1
2
3
4
5
# 实时监控所有容器的 CPU、内存、网络、磁盘 I/O
docker stats

# 只看指定容器
docker stats my-nginx

6.2 容器日志管理

容器日志默认以 JSON 文件存储在宿主机上(/var/lib/docker/containers/<容器ID>/<容器ID>-json.log)。不配置日志轮转,日志文件会无限增长直到撑满磁盘。

6.2.1 限制单容器日志大小

1
2
3
4
5
# 单个日志文件最大 10MB,最多保留 3 个轮转文件
docker run -d \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx

6.2.2 全局配置日志策略

编辑 /etc/docker/daemon.json,对所有新建容器生效:

1
2
3
4
5
6
7
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}

修改后需重启 Docker:

1
sudo systemctl restart docker

6.2.3 集中式日志收集

生产环境通常将容器日志发送到 ELK、Loki 等集中式日志平台。Docker 支持多种日志驱动:

1
2
3
4
5
# 使用 fluentd 驱动,将日志发送到日志收集器
docker run -d \
--log-driver=fluentd \
--log-opt fluentd-address=log-collector:24224 \
nginx

6.3 容器健康检查

Docker 默认只通过主进程是否存活来判断容器状态。但进程存活不代表服务可用——例如 Web 服务进入死锁状态,进程还在但不响应请求。

6.3.1 配置健康检查

1
2
3
4
5
6
7
docker run -d \
--health-cmd="curl -f http://localhost/ || exit 1" \
--health-interval=30s \
--health-timeout=5s \
--health-retries=3 \
--health-start-period=10s \
nginx

参数说明:

参数作用默认值
--health-cmd健康检查命令,返回 0 表示健康
--health-interval检查间隔30s
--health-timeout单次检查超时30s
--health-retries连续失败多少次判为 unhealthy3
--health-start-period容器启动后的宽限期,此期间失败不计数0s

对于启动较慢的应用(如 Java/Spring Boot),建议将 --health-start-period 设为合理值(如 30s-60s),避免容器在启动过程中被误判为 unhealthy。

6.3.2 健康状态流转

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F'}}}%%
stateDiagram-v2
    [*] --> starting: "容器启动"
    starting --> healthy: "检查通过"
    starting --> healthy: "宽限期内失败不计数"
    healthy --> unhealthy: "连续 N 次检查失败"
    unhealthy --> healthy: "检查恢复通过"

查看健康检查记录:

1
docker inspect --format='{{json .State.Health}}' <容器名> | jq

6.4 Docker 安全实践

容器共享宿主机内核,安全隔离不如虚拟机。以下是生产环境中应遵守的安全原则。

6.4.1 不要以 Root 运行应用

容器内默认以 root 运行,一旦容器被攻破,攻击者可能借此获得宿主机的控制权

1
2
# 以 UID 1000 运行容器
docker run -d --user 1000:1000 nginx

生产镜像通常在 Dockerfile 中通过 RUN useradd + USER 指令设置运行用户,并调整相关目录权限;--user 参数适用于镜像已做好权限适配的场景。

6.4.2 以只读模式运行

对于不需要写入文件系统的容器,启用只读文件系统:

1
2
3
4
docker run -d --read-only \
--tmpfs /tmp \
--tmpfs /run \
nginx

--tmpfs 挂载临时文件系统,允许应用写入 /tmp/run 等必要路径。

6.4.3 限制容器权限

1
2
3
4
5
# 禁止容器获取额外权限
docker run -d --security-opt=no-new-privileges nginx

# 删除所有 Linux capabilities,只保留必要的
docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE nginx

6.4.4 镜像安全扫描

定期扫描镜像中的已知漏洞:

1
2
3
4
5
# 使用 Docker Scout(Docker 官方工具)
docker scout cves nginx:latest

# 使用 Trivy(开源工具)
trivy image nginx:latest

6.5 日常使用技巧

6.5.1 远程执行 Docker 命令

DOCKER_HOST 环境变量指定 Docker 客户端连接的 daemon 地址:

1
DOCKER_HOST=ssh://root@10.133.0.21 docker ps

6.5.2 使用容器代替本地安装

无需在宿主机安装客户端工具,直接用容器:

1
2
3
4
5
6
7
8
9
10
11
# 用不同版本的 Redis 客户端连接远程服务
docker run -it --rm redis:6 redis-cli -h 10.0.114.5
docker run -it --rm redis:7 redis-cli -h 10.0.114.5

# 用容器内的 Go 编译器构建项目
docker run --rm \
-v "$(pwd)":/app \
-w /app \
-e GOPROXY=https://goproxy.cn,direct \
golang:1.24 \
go build -o server cmd/main.go

好处:保持宿主机环境清洁,轻松切换工具版本,--rm 用完即删。

6.5.3 善用 Docker Compose

对于需要多个参数的 docker run 命令,用 Docker Compose 配置文件管理更清晰、可复现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
services:
app:
image: golang:1.24
container_name: dev-server
restart: always
ports:
- "8899:8888"
volumes:
- .:/app:ro
- go-pkg-mod:/go/pkg/mod
- go-build-cache:/root/.cache/go-build
working_dir: /app
environment:
- TZ=Asia/Shanghai
command: ["go", "run", ".", "-f", "/app/etc/config.yaml"]

volumes:
go-pkg-mod:
go-build-cache:
1
2
3
docker compose up -d     # 后台启动
docker compose down # 停止并删除容器
docker compose logs -f # 查看日志

6.5.4 磁盘空间清理

1
2
3
4
5
6
7
8
# 清理虚悬镜像
docker image prune -f

# 清理所有未使用的资源(镜像、容器、网络、构建缓存)
docker system prune -a

# 查看 Docker 磁盘占用
docker system df

7. 最佳实践速查

场景✅ Do❌ Don’t
镜像选择使用官方镜像,优先 Alpine 变体使用来源不明的第三方镜像
镜像标签使用语义化版本或 Git Hash生产环境使用 latest
资源限制为每个容器设置 CPU 和内存上限让容器无限制使用宿主机资源
日志管理配置日志轮转限制,集中收集让日志文件无限增长
运行用户以非 root 用户运行应用以 root 身份运行生产容器
健康检查配置 HEALTHCHECK 检测服务可用性仅依赖进程存活判断容器健康
数据持久化使用命名数据卷或 Bind Mount把重要数据写入容器存储层
启动命令用 Docker Compose 管理多参数启动用超长 docker run 命令
安全最小权限原则 + 定期扫描镜像漏洞使用 --privileged 运行容器
磁盘清理定期执行 docker system prune从不清理虚悬镜像和停止的容器