Docker(二)—— Docker核心功能全解析:网络、资源控制、数据卷与镜像构建实战

文章目录

  • 前言
  • 一、Docker网络管理
    • [1.1 Docker网络实现原理](#1.1 Docker网络实现原理)
    • [1.2 Docker端口映射](#1.2 Docker端口映射)
      • [1.2.1 随机端口映射](#1.2.1 随机端口映射)
      • [1.2.2 指定端口映射](#1.2.2 指定端口映射)
      • [1.2.3 Docker端口映射总结](#1.2.3 Docker端口映射总结)
    • [1.3 Docker的网络模式](#1.3 Docker的网络模式)
      • [1.3.1 host模式(与宿主机共享网络栈)](#1.3.1 host模式(与宿主机共享网络栈))
      • [1.3.2 Container模式(与其他容器共享网络栈)](#1.3.2 Container模式(与其他容器共享网络栈))
      • [1.3.3 无网络模式(none)](#1.3.3 无网络模式(none))
      • [1.3.4 桥接模式(bridge)](#1.3.4 桥接模式(bridge))
      • [1.3.5 自定义模式](#1.3.5 自定义模式)
      • [1.3.6 Docker网络模式总结](#1.3.6 Docker网络模式总结)
  • 二、Docker资源控制(Docker优化)
    • [2.1 CPU资源控制](#2.1 CPU资源控制)
      • [2.1.1 设置CPU使用率上限(--cpu-period / --cpu-quota)](#2.1.1 设置CPU使用率上限(--cpu-period / --cpu-quota))
      • [2.1.2 设置CPU占用比(权重 --- --cpu-shares)](#2.1.2 设置CPU占用比(权重 — --cpu-shares))
      • [2.1.3 绑定指定CPU(--cpuset-cpus)](#2.1.3 绑定指定CPU(--cpuset-cpus))
      • [2.1.4 CPU压力测试与验证示例](#2.1.4 CPU压力测试与验证示例)
      • [2.1.5 CPU资源控制注意事项](#2.1.5 CPU资源控制注意事项)
    • [2.2 内存使用限制](#2.2 内存使用限制)
      • [2.2.1 --memory与--memory-swap规则](#2.2.1 --memory与--memory-swap规则)
      • [2.2.2 内存限制示例命令](#2.2.2 内存限制示例命令)
      • [2.2.3 内存限制验证与OOM行为](#2.2.3 内存限制验证与OOM行为)
      • [2.2.4 内存限制建议与注意](#2.2.4 内存限制建议与注意)
    • [2.3 磁盘IO(blkio/io)控制](#2.3 磁盘IO(blkio/io)控制)
      • [2.3.1 常用Docker磁盘IO限制参数](#2.3.1 常用Docker磁盘IO限制参数)
      • [2.3.2 磁盘IO限制验证(dd测试)](#2.3.2 磁盘IO限制验证(dd测试))
      • [2.3.3 磁盘IO控制注意事项](#2.3.3 磁盘IO控制注意事项)
    • [2.4 清理Docker占用的磁盘空间](#2.4 清理Docker占用的磁盘空间)
    • [2.5 Docker资源控制常见命令速查](#2.5 Docker资源控制常见命令速查)
    • [2.6 Docker资源控制常见陷阱与建议](#2.6 Docker资源控制常见陷阱与建议)
  • [三、Docker数据卷容器(Data Volumes Containers)](#三、Docker数据卷容器(Data Volumes Containers))
    • [3.1 数据卷](#3.1 数据卷)
      • [3.1.1 创建与挂载数据卷](#3.1.1 创建与挂载数据卷)
      • [3.1.2 在数据卷中写入数据](#3.1.2 在数据卷中写入数据)
      • [3.1.3 查看宿主机同步的数据](#3.1.3 查看宿主机同步的数据)
    • [3.2 数据卷容器](#3.2 数据卷容器)
      • [3.2.1 创建数据卷容器](#3.2.1 创建数据卷容器)
      • [3.2.2 在数据卷容器中写入数据](#3.2.2 在数据卷容器中写入数据)
      • [3.2.3 使用--volumes-from共享数据卷](#3.2.3 使用--volumes-from共享数据卷)
      • [3.2.4 在新容器中验证共享数据](#3.2.4 在新容器中验证共享数据)
    • [3.3 数据卷与数据卷容器总结](#3.3 数据卷与数据卷容器总结)
  • 四、Docker容器互联(使用CentOS镜像)
    • [4.1 创建并运行源容器web1](#4.1 创建并运行源容器web1)
    • [4.2 创建并运行接收容器web2(--link互联)](#4.2 创建并运行接收容器web2(--link互联))
    • [4.3 在接收容器web2中测试连接](#4.3 在接收容器web2中测试连接)
    • [4.4 Docker容器互联总结](#4.4 Docker容器互联总结)
  • 五、Docker镜像的创建
    • [5.1 基于现有镜像创建](#5.1 基于现有镜像创建)
      • [5.1.1 启动容器并进行修改](#5.1.1 启动容器并进行修改)
      • [5.1.2 提交容器为新镜像](#5.1.2 提交容器为新镜像)
    • [5.2 基于本地模板创建](#5.2 基于本地模板创建)
      • [5.2.1 下载操作系统模板](#5.2.1 下载操作系统模板)
      • [5.2.2 导入模板为Docker镜像](#5.2.2 导入模板为Docker镜像)
    • [5.3 基于Dockerfile创建(重点)](#5.3 基于Dockerfile创建(重点))
      • [5.3.1 Docker镜像的分层结构与UnionFS](#5.3.1 Docker镜像的分层结构与UnionFS)
      • [5.3.2 Dockerfile操作常用指令详解(重点)](#5.3.2 Dockerfile操作常用指令详解(重点))
      • [5.3.3 Dockerfile实战示例(构建Apache镜像)](#5.3.3 Dockerfile实战示例(构建Apache镜像))
      • [5.3.4 基于Dockerfile构建镜像](#5.3.4 基于Dockerfile构建镜像)
      • [5.3.5 Docker镜像分层与缓存机制解析](#5.3.5 Docker镜像分层与缓存机制解析)
    • [5.4 Docker镜像创建方式总结](#5.4 Docker镜像创建方式总结)
  • [Docker 命令无法补全解决方案](#Docker 命令无法补全解决方案)
  • 总结

前言

在容器化技术飞速发展的今天,Docker作为最主流的容器引擎,已经成为开发、测试和运维岗位的必备技能。但很多同学在使用Docker时,往往只停留在"拉取镜像、启动容器"的基础操作,对Docker的网络通信、资源限制、数据持久化以及镜像定制等核心功能理解不深,导致在实际项目中遇到各种"卡壳"问题。

本文将从实战角度出发,系统梳理Docker的六大核心模块:网络管理资源控制数据卷容器端口映射容器互联镜像创建,每个模块都包含原理讲解、命令示例和注意事项,帮你打通Docker使用的"任督二脉"。无论你是刚接触Docker的新手,还是需要巩固基础的开发者/运维工程师,这篇文章都能为你提供实用的指导。

一、Docker网络管理

Docker容器的网络通信是容器化应用部署的核心环节------容器之间如何通信?容器如何被外部网络访问?不同场景下该选择哪种网络模式?这些问题都需要通过Docker的网络管理机制来解决。

1.1 Docker网络实现原理

Docker的网络通信依赖于Linux桥接技术 ,其核心是宿主机上虚拟的docker0网桥。具体实现逻辑如下:

  1. 虚拟网桥(docker0) :Docker启动时会在宿主机创建一个名为docker0的虚拟网桥,它相当于一个"虚拟交换机",负责连接宿主机上的所有Docker容器。
  2. Container-IP分配 :启动容器时,Docker会从docker0的网段(默认通常是172.17.0.0/16)中分配一个唯一的IP地址(即Container-IP)给容器,并将docker0设置为容器的默认网关。
  3. 容器间通信 :同一宿主机内的所有容器都接入docker0网桥,因此容器之间可以通过各自的Container-IP直接通信(无需经过宿主机的物理网卡)。
  4. 外部网络访问限制docker0是宿主机虚拟的网络设备,并非真实的物理网卡,外部网络无法直接通过Container-IP访问容器。若需外部访问,需通过端口映射将容器端口映射到宿主机端口,再通过"宿主机IP:宿主机端口"访问容器。

1.2 Docker端口映射

容器内部的端口默认无法被外部网络访问(因docker0是虚拟网桥),需通过端口映射 将容器端口映射到宿主机端口,实现外部访问。Docker支持两种端口映射方式:随机映射(-P)和指定映射(-p)

1.2.1 随机端口映射

使用-P(大写)参数,Docker会自动从32768开始分配一个未使用的宿主机端口,映射到容器暴露的端口(如Nginx的80端口)。适用于无需固定端口的场景(如测试环境)。

实战示例

bash 复制代码
# 启动Nginx容器,随机映射端口
docker run -d --name test1 -P nginx

# 查看端口映射结果(重点看PORTS列)
docker ps -a
# 输出示例:
# CONTAINER ID   IMAGE  COMMAND       CREATED          PORTS                   NAMES
#6fd7a69e7883   nginx          "/docker-entrypoint...."   15 seconds ago   Up 14 seconds               0.0.0.0:49158->80/tcp, :::49158->80/tcp   test1

# 外部访问(替换为宿主机IP)
http://192.168.80.10:49158

# 查看容器日志(排查访问问题)
docker logs test1  # 或使用容器ID

1.2.2 指定端口映射

使用-p(小写)参数,手动指定"宿主机端口:容器端口",适用于需要固定访问端口的场景(如生产环境的Web服务)。

实战示例

bash 复制代码
# 启动Nginx容器,指定宿主机43000端口→容器80端口
docker run -d --name test2 -p 43000:80 nginx

# 查看端口映射结果
docker ps -a
# 输出示例:
# CONTAINER ID   IMAGE  COMMAND       CREATED          PORTS                   NAMES
# b3369e74b2a8   nginx     "/docker-entrypoint...."   25 seconds ago   Up 23 seconds   0.0.0.0:43000->80/tcp, :::43000->80/tcp   test2

# 外部访问(固定端口43000)
http://192.168.80.10:43000

# 查看容器日志(排查访问问题)
docker logs test2  # 或使用容器ID

1.2.3 Docker端口映射总结

映射方式 参数 特点 适用场景
随机映射 -P 自动分配宿主机端口(32768+) 测试环境、无需固定端口
指定映射 -p 宿主机端口:容器端口 手动指定端口,固定访问地址 生产环境、需要固定端口的服务

注意:宿主机端口需未被占用,若端口冲突,容器启动会失败,需更换宿主机端口。

1.3 Docker的网络模式

使用docker run创建容器时,可通过--net--network参数指定容器的网络模式,不同模式对应不同的网络隔离与通信能力。Docker支持5种核心网络模式,以下逐一解析。

1.3.1 host模式(与宿主机共享网络栈)

  • 原理 :类似VMware的"桥接模式",容器不创建独立的Network Namespace(网络命名空间),而是与宿主机共享同一个网络栈。因此,容器不会虚拟自己的网卡、配置独立IP,而是直接使用宿主机的IP和端口

  • 特点:网络性能最优(无额外网络转发开销),但容器会占用宿主机的端口,存在端口冲突风险。

  • 适用场景:需要高性能网络的应用(如数据库)

  • 实战命令

    bash 复制代码
    # 启动Nginx容器,使用host模式
    docker run -d --name nginx-host --network host nginx
    # 访问容器:直接通过宿主机IP(无需端口映射,容器用宿主机80端口)
    # http://宿主机IP:80


1.3.2 Container模式(与其他容器共享网络栈)

  • 原理 :新创建的容器不与宿主机共享网络,而是与已存在的某个容器 共享同一个Network Namespace。两个容器共享IP、端口范围,但文件系统、进程列表等其他资源仍保持隔离;可通过lo回环网卡直接通信。

  • 适用场景:需要两个容器紧密协作(如"应用容器+日志收集容器"),且希望避免端口映射的场景。

  • 实战案例

    bash 复制代码
    # 1. 先创建基础容器test1-container(CentOS 7)
    docker run -itd --name test1-container centos:7 /bin/bash
    
    # 2. 查看test1-container的进程号(用于后续验证网络命名空间)
    docker inspect -f '{{.State.Pid}}' test1-container  # 输出示例:1753
    
    # 3. 查看test1-container的网络命名空间(记录net对应的编号,如4026532575)
    ls -l /proc/1753/ns
    # 输出示例:
    # lrwxrwxrwx 1 root root 0 1月   7 11:27 net -> net:[4026532504]
    bash 复制代码
    # 4. 创建test2-container,指定与test1-container共享网络
    docker run -itd --name test2-container --net=container:test1-container centos:7 /bin/bash
    
    # 5. 验证网络命名空间共享(test2-container的net编号与test1-container一致)
    docker inspect -f '{{.State.Pid}}' test2-container  # 输出示例:1844
    ls -l /proc/1844/ns
    # 输出示例:
    # lrwxrwxrwx 1 root root 0 1月   7 12:27 net -> net:[4026532504]

1.3.3 无网络模式(none)

  • 原理 :容器拥有独立的Network Namespace,但Docker不为其配置任何网络(无网卡、无IP、无路由),仅保留lo回环网卡(用于容器内部进程通信)。

  • 特点:完全封闭的网络环境,无法联网,安全性极高。

  • 适用场景:不需要网络通信的离线任务(如本地数据处理、加密计算)。

  • 实战命令

    bash 复制代码
    docker run -itd --name test-none --network none centos:7 /bin/bash
    # 进入容器查看网络(仅lo网卡)
    docker exec -it test-none ip addr

1.3.4 桥接模式(bridge)

  • 原理 :Docker的默认网络模式(不指定--net时默认使用),类似VMware的"NAT模式"。容器拥有独立的Network Namespace,并通过docker0网桥与宿主机通信,具体流程,如下:
    1. Docker启动时创建docker0虚拟网桥;
    2. 为每个容器分配Container-IP,并设置docker0的IP地址为容器的默认网关。
    3. 在主机上创建一对veth pair(虚拟网卡对)(连接两个不同的网络命名空间):一端作为容器内的eth0网卡,另一端放在主机中, 以 * 这样类似的名字命名,并将这个网络设备加入到 docker0 网桥;
    4. 通过iptablesnat表配置端口转发,实现容器与外部网络的通信。
  • 补充说明virbr0docker0的区别------virbr0是Linux虚拟化技术(如KVM、VirtualBox)创建的虚拟网桥,用于虚拟机通信;docker0仅用于Docker容器。
  • 总结:bridge网络模式中,容器运行在同一宿主机内的虚拟网桥上,可以通过容器互相通信,与宿主机网络隔离。

1.3.5 自定义模式

Docker允许用户创建自定义网络,支持三种驱动:

  • bridge:类似默认bridge模式,但增加了DNS解析(容器可通过名称通信)、网络隔离等功能。单机网络模式,适合在一台宿主机、内容器互联;
  • overlay:用于跨主机容器通信(Docker Swarm集群场景);
  • macvlan:容器像一台物理机一样直接获取宿主机的所在的网络的IP(容器像物理机一样直接连接局域网)

实战:创建自定义bridge网络并指定IP

默认bridge模式不支持手动指定Container-IP,需通过自定义网络实现:

bash 复制代码
# 1. 创建自定义bridge网络(子网172.18.0.0/16,网卡名docker1)
docker network create --subnet=172.18.0.0/16 --opt "com.docker.network.bridge.name"="docker1" mynetwork
# 参数说明:
# --subnet:指定网络子网;
# --opt "com.docker.network.bridge.name":指定宿主机上的网卡名(默认是br-xxx格式,不易记);
# mynetwork:自定义网络名称。

# 2. 启动容器时指定自定义网络和IP
docker run -itd --name test4 --net mynetwork --ip 172.18.0.10 centos:7 /bin/bash

# 3. 验证IP(进入容器查看)
docker exec -it test4 ip addr  # 应显示172.18.0.10
docker inspect test4


bash 复制代码
# 管理 Linux 桥接网络
brctl show
# 管理 Docker 容器网络
docker network ls

1.3.6 Docker网络模式总结

网络模式 核心特点 适用场景
host 共享宿主机网络栈,无端口映射 对网络性能要求高,无端口冲突风险的场景
Container 共享其他容器网络,隔离其他资源 容器间紧密协作(如应用+日志收集)
none 无网络配置,仅lo网卡 离线任务、高安全性需求
bridge 默认模式,独立网络栈+docker0网桥 单主机容器通信,常规场景
自定义 支持DNS、跨主机、MAC模拟 集群部署、复杂网络隔离

额外附加

  1. Overlay:这是Docker Swarm模式的网络,主要用于在多个主机上创建一个分布式网络。容器间即便在不同的主机也能完成通信,相当于在跨主机的容器之间创建了一个覆盖网络。
  2. Macvlan:Macvlan模式可以让容器直接连接到主机的物理网络,每个Macvlan接口都有一个唯一的MAC地址,此模式使得容器看起来就像是网络上的物理设备。

二、Docker资源控制(Docker优化)

为避免单个容器占用过多宿主机资源(如CPU、内存),Docker基于Linux的cgroups(Control Groups)机制实现资源限制。

cgroups是Linux内核提供的资源管理工具,支持限制、统计、优先级分配等功能。

cgroups 的 4 大功能(简要)

  • 资源限制:限制任务使用的总资源量。
  • 优先级分配:如通过 cpu 时间片和 IO 带宽分配优先级。
  • 资源统计:统计 cpu 时长、内存用量等。
  • 任务控制:对 cgroup 中的进程执行挂起/恢复等操作。

2.1 CPU资源控制

2.1.1 设置CPU使用率上限(--cpu-period / --cpu-quota)

  • 原理:Linux使用CFS(完全公平调度器)调度CPU,通过两个参数控制容器CPU使用率:

    • --cpu-period:调度周期(单位:微秒,默认100000,即100ms);
    • --cpu-quota:容器在一个周期内可使用的CPU时间(单位:微秒,默认-1,即无限制);
    • 计算公式:可用CPU核心数 = cpu-quota / cpu-period(如50000/100000=0.5,即50%的单核CPU)。
  • 实战示例

    bash 复制代码
    # 方式1:使用--cpu-quota(限制为50%单核CPU)
    docker run -itd --name test6 --cpu-quota 50000 centos:7 /bin/bash
    
    # 方式2:使用--cpus(更直观,Docker 1.13+支持)
    docker run -itd --name cputest --cpus="0.5" centos:7 /bin/bash
    # 说明:--cpus="0.5" 等价于 --cpu-period=100000 --cpu-quota=50000
    
    # 进行压测并在宿主机查看cpu使用率
    docker stats
  • 注意:若宿主机有4个逻辑核,0.5个CPU相当于整机CPU能力的12.5%(0.5/4=0.125)。

  • 最小值/范围--cpu-period 有效范围通常 1000 ~ 1000000(单位 us)。--cpu-quota 必须 >= 1000(1 ms)或者 -1(不限制)。

2.1.2 设置CPU占用比(权重 --- --cpu-shares)

  • 原理--cpu-shares指定CPU使用的相对权重 (默认1024),仅在CPU资源紧张(容器争用CPU)时生效,并非硬限制。例如:

    • 容器A(--cpu-shares 512)与容器B(--cpu-shares 1024)争用CPU时,分配比约为1:2。
  • 实战示例

    bash 复制代码
    # 创建两个不同权重的容器
    docker run -itd --name c1 --cpu-shares 512 centos:7
    docker run -itd --name c2 --cpu-shares 1024 centos:7
    bash 复制代码
    # 验证:在容器内运行压力测试工具stress
    # 1. 进入容器安装stress(先在容器内设置阿里源)
    docker cp CentOS-Base.repo c1:/etc/yum.repos.d/
    docker exec -it c1 bash
    yum install -y epel-release && yum install -y stress
    
    # 2. 每个容器启动4个CPU压力进程
    stress -c 4  # 在c1和c2中分别执行
    
    # 3. 宿主机查看CPU分配(倾向于1:2)
    docker stats

2.1.3 绑定指定CPU(--cpuset-cpus)

  • 原理:将容器进程绑定到宿主机的指定CPU核心(硬亲和性),避免进程在不同核心间切换,提升性能稳定性。

  • 实战示例

    bash 复制代码
    # 将容器绑定到宿主机第1、3个核心(核心编号从0开始)
    docker run -itd --name test7 --cpuset-cpus "1,3" centos:7 /bin/bash
    
    # 验证:宿主机查看核心利用率
    top  # 按键盘1键,查看CPU1和CPU3的使用率
    # 或进入容器运行 taskset -p <pid>

2.1.4 CPU压力测试与验证示例

通过自定义脚本模拟CPU高负载,验证资源限制效果:

  • 在容器内创建一个繁忙循环脚本:

    bash 复制代码
    # /cpu.sh
    #!/bin/bash
    i=0
    while true; do let i++; done
  • 在容器内运行 ./cpu.sh,在宿主机观察 topdocker stats

    bash 复制代码
    docker exec -it <container> bash
    ./cpu.sh
  • 分别进入容器,进行压力测试

    bash 复制代码
    yum install -y epel-release
    yum install -y stress
    stress -c 4				#产生四个进程,每个进程都反复不停的计算随机数的平方根
  • 修改 cgroups 手工测试(示例):

    bash 复制代码
    # 找到容器对应的 cgroup 路径(容器ID替换)
    cd /sys/fs/cgroup/cpu/docker/<container-id>/
    cat cpu.cfs_period_us
    cat cpu.cfs_quota_us
    echo 50000 > cpu.cfs_quota_us   # 设置配额(临时生效)

2.1.5 CPU资源控制注意事项

  1. --cpu-shares权重,非硬限制(CPU空闲时,容器可使用超过权重的资源);
  2. --cpu-quota/--cpus硬限制,容器无法突破设定的CPU上限(quota = -1 表示无限制);
  3. --cpuset-cpus适合对性能稳定性要求高的场景(避免与其他进程抢核)(如数据库容器)。
  4. 在多核宿主机上理解 quota/period 的含义(单位是 "相对于 1 个 CPU 的份额")。

2.2 内存使用限制

内存限制通过-m/--memory--memory-swap参数实现,避免容器耗尽宿主机内存。

2.2.1 --memory与--memory-swap规则

  • --memory:限制容器可使用的物理内存 (如-m 512m表示512MB);
  • --memory-swap:限制容器可使用的物理内存+交换分区(swap) 总量,需与--memory配合使用;
  • 核心规则:
    1. 示例-m 300m --memory-swap=1g:物理内存300MB,swap可用700MB(1G-300M);
    2. 不设置--memory-swap:默认swap为--memory的2倍(如-m 512m,swap默认1024MB);
    3. --memory-swap=-1:swap无限制(使用宿主机所有可用swap);
    4. --memory-swap=--memory:容器不可使用swap(物理内存用尽触发OOM)。

2.2.2 内存限制示例命令

bash 复制代码
# 1. 限制物理内存512MB(swap默认1024MB)
docker run -itd --name memtest -m 512m centos:7 /bin/bash

# 2. 限制物理内存300MB,swap总量1GB(swap可用700MB)
docker run -itd --name memtest2 -m 300m --memory-swap=1g centos:7 /bin/bash

2.2.3 内存限制验证与OOM行为

  • 验证内存限制

    bash 复制代码
    # 1. 宿主机查看容器内存限制(替换为容器ID)
    cd /sys/fs/cgroup/memory/docker/<容器ID>/
    cat memory.limit_in_bytes  # 输出内存限制(单位:字节,512MB=536870912字节)
    cat memory.usage_in_bytes  # 输出当前内存使用量
    
    # 2. 容器内模拟内存高负载(使用stress)
    docker exec -it memtest bash
    stress --vm 1 --vm-bytes 600m  # 尝试使用600MB内存(超过512MB限制)


  • OOM行为 :若容器内存超过限制且无swap可用,内核会触发OOM Killer杀死容器,可通过docker logs查看日志或者 dmesg 来确认 OOM 事件。

2.2.4 内存限制建议与注意

  1. 生产环境必须为容器设置内存限制,避免单个容器把宿主机内存耗尽;
  2. 内存敏感型应用(如Redis、MySQL)建议禁用swap(--memory-swap=--memory),避免swap导致性能下降;
  3. 配合容器健康检查和重启策略(如--restart=on-failure),应对OOM后的容器恢复。
  4. 在 cgroup v2 环境下内存控制文件名/行为可能与 v1 略有不同(检查 /sys/fs/cgroup 结构)。

2.3 磁盘IO(blkio/io)控制

Docker基于cgroupsblkio控制器(cgroup v1)或io控制器(cgroup v2)限制容器的磁盘读写速率和IOPS(每秒输入输出次数)。

2.3.1 常用Docker磁盘IO限制参数

参数 作用 示例
--device-read-bps 限制设备读速率(单位:B/s、KB/s、MB/s) --device-read-bps /dev/sda:1M
--device-write-bps 限制设备写速率 --device-write-bps /dev/sda:1M
--device-read-iops 限制设备读IOPS(次数) --device-read-iops /dev/sda:100
--device-write-iops 限制设备写IOPS(次数) --device-write-iops /dev/sda:100

实战示例 :限制容器对/dev/sda(宿主机磁盘)的写速率为1MB/s

bash 复制代码
docker run -it --name iotest --device-write-bps /dev/sda:1MB centos:7 /bin/bash

2.3.2 磁盘IO限制验证(dd测试)

通过dd命令(跳过文件系统缓存)测试写速率:

bash 复制代码
# 容器内执行dd命令(写入10个1MB的块)跳过文件系统缓存
dd if=/dev/zero of=test.out bs=1M count=10 oflag=direct

# 输出示例(写速约1MB/s,符合限制):
# 10485760 bytes (10 MB) copied, 10.0028 s, 1.0 MB/s

2.3.3 磁盘IO控制注意事项

  1. 限制需指定具体块设备路径 (如/dev/sda),路径错误会导致限制失效;
  2. 云环境或虚拟化环境中,底层存储(如AWS EBS、阿里云云盘)可能有自身IO限制,容器层限制需结合底层限制配置;
  3. cgroup v2环境中,IO限制参数不同(如使用--device-io-max),需参考对应Docker版本文档。

2.4 清理Docker占用的磁盘空间

Docker运行一段时间后,会积累停止的容器、未使用的镜像、网络和构建缓存,可通过以下命令清理:

bash 复制代码
# 清理所有未使用的资源(停止的容器、未使用的镜像、网络、缓存)
docker system prune -a
# 注意:此命令会删除所有未活跃的资源,执行前确认无需保留!

2.5 Docker资源控制常见命令速查

bash 复制代码
# CPU相关
docker run -itd --name c1 --cpu-shares 512 centos:7  # 权重512
docker run -itd --name c2 --cpu-quota 50000 centos:7  # 50%单核
docker run -itd --name c3 --cpuset-cpus "1,3" centos:7  # 绑定核心1、3
docker run -itd --name c4 --cpus="0.5" centos:7  # 直观指定0.5核

# 内存相关
docker run -itd --name memtest -m 512m centos:7  # 512MB物理内存
docker run -itd --name memtest2 -m 300m --memory-swap=1g centos:7  # 300M+700M swap

# 磁盘IO相关
docker run -it --name iotest --device-write-bps /dev/sda:1MB centos:7  # 写速1MB/s

# 监控与验证
docker stats  # 实时监控容器资源使用
docker exec -it <容器ID> bash  # 进入容器操作
cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.cfs_quota_us  # 查看CPU配额
cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.limit_in_bytes  # 查看内存限制

# 清理资源
docker system prune -a  # 清理未使用资源

2.6 Docker资源控制常见陷阱与建议

  1. 权重vs限额--cpu-shares是相对权重(争用时生效),--cpu-quota/--cpus是硬限额(强制限制);
  2. cgroup版本差异:CentOS 7默认用cgroup v1,CentOS 8+或新内核可能用cgroup v2,控制器名称和参数不同(如blkio→io);
  3. IO 限制依赖底层设备:在云/虚拟机上测试时,注意底层虚拟磁盘的行为。
  4. 生产环境监控:建议搭配Prometheus+Grafana+cAdvisor监控容器资源,及时发现资源瓶颈;
  5. 权限控制 :修改/sys/fs/cgroup目录下的配置需root权限,优先使用docker run参数配置,避免直接修改系统文件。

三、Docker数据卷容器(Data Volumes Containers)

容器的文件系统是临时的------容器删除后,内部数据会丢失。Docker的数据卷(Data Volumes)机制解决了这一问题,实现数据持久化;而数据卷容器则进一步简化了多容器间的数据共享。

3.1 数据卷

数据卷是容器内的特殊目录,可与宿主机目录或其他容器共享,具备以下特性:

  • 数据持久化:容器删除后,数据卷中的数据不会丢失;
  • 双向同步:宿主机与容器对数据卷的修改实时同步;
  • 跨容器共享:多个容器可挂载同一个数据卷。

3.1.1 创建与挂载数据卷

通过docker run-v--mount参数挂载数据卷,格式:-v 宿主机目录:容器内目录

bash 复制代码
# 挂载宿主机/var/www目录到容器内/data1目录
docker run -v /opt/www:/data1 --name web1 -it centos:7 /bin/bash
# 参数说明:
# /var/www:宿主机目录(不存在会自动创建);
# /data1:容器内目录(不存在会自动创建);
# web1:容器名称。
  • -v /var/www:/data1: 将宿主机上的 /var/www 目录挂载到容器中的 /data1 目录。这样容器内 /data1 的修改会同步到宿主机上的 /var/www
  • --name web1: 给容器指定一个名称 web1
  • -it centos:7 /bin/bash: 使用 centos:7 镜像启动容器并进入交互式 shell。

3.1.2 在数据卷中写入数据

进入容器后,在数据卷目录(/data1)中创建文件,数据会同步到宿主机:

bash 复制代码
# 容器内执行(写入数据到/data1/abc.txt)
echo "this is web1" > /data1/abc.txt
exit

此时,容器中的 /data1/abc.txt 文件与宿主机中的 /var/www/abc.txt 文件是同步的。即使容器退出,宿主机上的数据依然存在。

3.1.3 查看宿主机同步的数据

退出容器后,查看宿主机/var/www目录,数据已同步:

bash 复制代码
# 宿主机执行
cat /opt/www/abc.txt  # 输出:this is web1

3.2 数据卷容器

数据卷容器是专门用于提供数据卷的容器 ,不运行应用程序,仅作为"数据载体",供其他容器通过--volumes-from挂载其数据卷。适用于多容器共享数据的场景(如微服务中的配置共享、日志存储)。

3.2.1 创建数据卷容器

bash 复制代码
# 创建数据卷容器web2,挂载两个数据卷/data1和/data2
docker run --name web2 -v /data1 -v /data2 -it centos:7 /bin/bash
# 说明:-v /data1 未指定宿主机目录,Docker会在宿主机/var/lib/docker/volumes/下创建匿名卷
  • --name web2: 给容器命名为 web2
  • -v /data1 -v /data2: 在容器内部挂载了两个数据卷 /data1/data2。这些数据卷不依赖于宿主机,而是仅仅存在于容器内。

3.2.2 在数据卷容器中写入数据

进入web2容器,在数据卷中写入测试数据:

bash 复制代码
# 进入web2容器
docker exec -it web2 bash

# 在/data1和/data2中写入数据
echo "this is web2" > /data1/abc.txt
echo "THIS IS WEB2" > /data2/ABC.txt

3.2.3 使用--volumes-from共享数据卷

创建新容器web3,通过--volumes-from挂载web2的数据卷:

bash 复制代码
# 创建web3,共享web2的所有数据卷
docker run -it --volumes-from web2 --name web3 centos:7 /bin/bash
# 说明:--volumes-from web2 表示继承web2的所有数据卷配置
  • --volumes-from web2: 这表示将容器 web2 中的所有数据卷挂载到新容器 web3 中。

3.2.4 在新容器中验证共享数据

进入web3容器,查看/data1/data2目录,可看到web2中写入的数据:

bash 复制代码
# 进入web3容器
docker exec -it web3 bash

# 查看数据
cat /data1/abc.txt  # 输出:this is web2
cat /data2/ABC.txt  # 输出:THIS IS WEB2

3.3 数据卷与数据卷容器总结

  1. 数据卷 (Data Volumes) :解决"容器数据持久化"问题,通过-v挂载宿主机目录;
  2. 数据卷容器(Data Volumes Containers) :解决"多容器数据共享"问题,通过--volumes-from继承数据卷,无需每个容器都挂载宿主机目录;
  3. 注意 :数据卷容器删除后,其数据卷(匿名卷)不会被删除,需手动清理(docker volume rm <卷ID>)。

四、Docker容器互联(使用CentOS镜像)

容器互联是指通过网络让两个或多个容器直接通信,Docker早期通过--link选项实现,适用于简单的单主机容器通信场景(复杂场景建议使用自定义网络)。

4.1 创建并运行源容器web1

首先创建"源容器"web1,作为通信的发起方:

bash 复制代码
# 启动web1容器(后台运行,随机端口映射)
docker run -itd -P --name web1 centos:7 /bin/bash
# 参数说明:
# -itd:交互式+后台运行;
# -P:随机映射端口;
# --name web1:指定容器名称(互联需依赖名称)。

4.2 创建并运行接收容器web2(--link互联)

创建"接收容器"web2,通过--link选项连接web1,格式:--link 源容器名称:源容器别名

bash 复制代码
# 启动web2,通过--link连接web1(别名为web1)
docker run -itd -P --name web2 --link web1:web1 centos:7 /bin/bash
# 说明:--link web1:web1 表示将web1容器暴露给web2,web2中可通过"web1"这个别名访问web1。

4.3 在接收容器web2中测试连接

进入web2容器,通过ping命令测试与web1的通信(无需知道web1Container-IP):

bash 复制代码
# 1. 进入web2容器
docker exec -it web2 bash

# 2. 测试与web1的连通性(使用别名web1)
ping web1
# 输出示例(ping通表示互联成功):
# PING web1 (172.17.0.2) 56(84) bytes of data.
# 64 bytes from web1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.068 ms

4.4 Docker容器互联总结

  1. 核心原理--link会在接收容器的/etc/hosts文件中添加源容器的别名与Container-IP的映射,因此接收容器可通过别名通信;
  2. 局限性--link仅支持单主机容器互联,且不支持动态更新(源容器IP变化后,接收容器的/etc/hosts不会自动更新);
  3. 替代方案 :复杂场景(如跨主机、动态容器)建议使用Docker自定义网络(如overlay),支持DNS自动解析和动态IP更新。

五、Docker镜像的创建

Docker镜像是容器的"模板",包含容器运行所需的程序、库、配置等。创建Docker镜像主要有三种方式:基于现有容器创建基于本地模板创建基于Dockerfile创建(最常用、最灵活)。

5.1 基于现有镜像创建

该方式是"先修改容器,再将修改后的容器提交为新镜像",适用于快速定制简单镜像。

5.1.1 启动容器并进行修改

bash 复制代码
# 1. 启动一个CentOS 7容器(交互式)
docker create -it --name mycentos centos:7 /bin/bash  # 创建容器
docker start <容器ID>  # 启动容器
docker exec -it <容器ID> bash  # 进入容器

# 2. 修改容器内部的环境(例如安装软件包、修改配置文件等),然后退出容器
# 安装OpenJDK 8
yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
# 验证java版本
java-version
yum clean all

5.1.2 提交容器为新镜像

使用docker commit命令将修改后的容器提交为新镜像,格式:docker commit -m "提交说明" -a "作者" 容器ID 新镜像名:标签

bash 复制代码
# 提交容器(容器ID替换为实际ID)
docker commit -m "openjdk8" -a "simon" 1450d38ffda5 openjdk:8

# 查看新镜像
docker images
# 输出示例:
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
# openjdk      8         4ee649b7cf2b   5 seconds ago   469MB

5.2 基于本地模板创建

通过导入现成的操作系统模板(如OpenVZ模板)创建镜像,适用于快速获取特定系统环境的场景。

5.2.1 下载操作系统模板

从OpenVZ官网下载模板(以Debian 7为例):

bash 复制代码
# 使用wget下载模板
wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
# 或使用curl下载
curl -L http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz -o debian-7.tar.gz

5.2.2 导入模板为Docker镜像

使用docker import命令导入模板:

bash 复制代码
# 将模板文件导入为镜像(镜像名debian:test)
cat debian-7.0-x86-minimal.tar.gz | docker import - debian:test

# 查看镜像
docker images
# 输出示例:
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
# debian       test      5120c6b1c973   4 seconds ago   215MB

5.3 基于Dockerfile创建(重点)

Dockerfile是一个包含一系列指令 的文本文件,每条指令对应镜像的一层,通过docker build命令自动构建镜像。这种方式支持自动化、可重复构建,是生产环境中创建镜像的首选方式。

5.3.1 Docker镜像的分层结构与UnionFS

Docker镜像基于UnionFS(联合文件系统) 构建,具有以下特点:

  • 分层存储:每一条Dockerfile指令生成一层镜像,层与层之间相互独立;
  • 只读特性:所有镜像层都是只读的,容器启动时会在镜像层之上添加一层"可读写层"(容器层);
  • 缓存机制:Docker会缓存已构建的镜像层,若指令或文件未变化,直接复用缓存,加速构建。

为什么Docker中的CentOS镜像只有200MB左右?

传统CentOS系统镜像约4GB,而Docker镜像仅保留rootfs(根文件系统,包含/bin/etc等核心目录),共享宿主机的bootfs(引导文件系统,包含内核),因此体积大幅缩小。

5.3.2 Dockerfile操作常用指令详解(重点)

Dockerfile指令按功能可分为基础镜像指令镜像操作指令容器启动指令等,以下是常用指令:

  1. FROM

    指定新镜像基于的基础镜像,Dockerfile 的第一条指令必须为 FROM

    dockerfile 复制代码
    FROM centos:7
  2. MAINTAINER

    指定镜像的维护者信息:

    dockerfile 复制代码
    MAINTAINER "Simon Cai <simoncwh@example.com>" 
  3. RUN

    执行命令并将结果提交到镜像中。常用来安装软件包、修改配置等。

    dockerfile 复制代码
    RUN yum install -y httpd
  4. ENTRYPOINT

    设置容器启动时默认执行的命令(不可被覆盖,优先级高于CMD):

    dockerfile 复制代码
    ENTRYPOINT ["httpd", "-D", "FOREGROUND"]
  5. CMD

    容器启动时执行的默认命令。CMD 指令会被 docker run 命令后指定的命令覆盖。

    dockerfile 复制代码
    CMD ["httpd", "-D", "FOREGROUND"]
  6. EXPOSE

    声明容器内的端口(仅文档说明,不实际映射):

    dockerfile 复制代码
    EXPOSE 80
  7. ENV

    设置环境变量(容器内可使用):

    dockerfile 复制代码
    ENV MY_VAR=my_value
  8. ADD

    将文件或目录从宿主机复制到镜像中,支持从 URL 下载文件,并能自动解压归档文件:

    dockerfile 复制代码
    ADD myfile.tar.gz /app
  9. COPY

    将本地文件或目录复制到镜像中(仅本地文件,无解压):

    dockerfile 复制代码
    COPY . /app
  10. VOLUME

    声明容器内的挂载点(创建匿名卷):

    dockerfile 复制代码
    VOLUME ["/data"]
  11. USER

    设置容器内运行命令时的用户:

    dockerfile 复制代码
    USER root
  12. WORKDIR

    设置后续指令的工作目录(类似cd):

    dockerfile 复制代码
    WORKDIR /app
  13. ONBUILD

    设置当该镜像作为基础镜像时,后续 Dockerfile 执行的命令:

    dockerfile 复制代码
    ONBUILD RUN echo "Building from base image"
  14. HEALTHCHECK

    设置容器的健康检查:

    dockerfile 复制代码
    HEALTHCHECK CMD curl --fail http://localhost:8080 || exit 1

关键指令区别

  • RUN vs CMD vs ENTRYPOINT
    • RUN:构建镜像时执行(如安装软件);
    • CMD:容器启动时执行,可被docker run后的命令覆盖(如docker run xxx bash会覆盖CMD);
    • ENTRYPOINT:容器启动时执行,不可被覆盖,若需传参需用docker run --entrypoint
  • ADD vs COPY
    • ADD支持URL下载和自动解压(如.tar.gz),功能更丰富;
    • COPY仅复制本地文件,功能简单,推荐优先使用(避免意外解压)。

5.3.3 Dockerfile实战示例(构建Apache镜像)

以构建一个基于CentOS 7的Apache(httpd)镜像为例,完整流程如下:

  1. 创建工作目录
bash 复制代码
mkdir -p /opt/apache && cd /opt/apache
  1. 编写Dockerfile
bash 复制代码
vim Dockerfile
#基于的基础镜像
FROM centos:7
#维护镜像的用户信息
MAINTAINER this is apache image <cwh>
#镜像操作指令安装apache软件,需提前准备国内源文件
ADD CentOS-Base.repo /etc/yum.repos.d/
RUN yum clean all
#RUN yum -y update
RUN yum -y install httpd
#开启 80 端口
EXPOSE 80
#复制网站首页文件
ADD index.html /var/www/html/index.html


//方法一:
#将执行脚本复制到镜像中
ADD run.sh /opt/run.sh
#启动容器时执行脚本
CMD sh /opt/run.sh

//方法二:
ENTRYPOINT [ "/usr/sbin/apachectl" ]
CMD ["-D", "FOREGROUND"]

//方法三
# 启动 httpd 服务
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
bash 复制代码
准备执行脚本(方式一)
vim run.sh
#!/bin/bash
rm -rf /run/httpd/*							#清理httpd的缓存
/usr/sbin/apachectl -D FOREGROUND			#指定为前台运行
#因为Docker容器仅在它的1号进程(PID为1)运行时,会保持运行。如果1号进程退出了,Docker容器也就退出了。

# 准备网站页面
echo "this is test web" > index.html

5.3.4 基于Dockerfile构建镜像

使用docker build命令构建镜像,格式:docker build -t 镜像名:标签 构建上下文路径.表示当前目录为构建上下文)。

bash 复制代码
# 构建镜像(镜像名httpd:centos,标签centos)注意末尾的.
docker build -t httpd:centos ./

# 查看构建的镜像
docker images
# 输出示例:
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
# httpd        centos    9e154b3b93e6   8 seconds ago    493MB
bash 复制代码
# 新镜像运行容器
docker run -d -p 1216:80 httpd:centos

# 测试
http://192.168.10.14:1216/


5.3.5 Docker镜像分层与缓存机制解析

  • 分层机制 :Dockerfile中的每条指令对应一层镜像,例如:
    1. FROM centos:7 → 基础层;
    2. RUN yum install -y httpd → 安装层;
    3. COPY index.html /var/www/html/ → 复制层。
  • 缓存机制
    • 若Dockerfile指令未变化,且依赖的文件(如index.html)未修改,Docker会复用缓存层;
    • 若某一层指令或文件变化,该层及之后的所有层都会重新构建(缓存失效)。
  • 镜像层是不可变的
    • 删除容器时只会删除其上面的读写层,底层的镜像层不会丢失。
  • 优化建议
    1. 频繁变化的指令(如COPY)放在Dockerfile末尾,减少重新构建的层数;
    2. 合并RUN指令(用&&连接),减少镜像层数(如合并yum installyum clean);
    3. 使用.dockerignore文件排除不需要的文件(如node_modules、日志文件),减小构建上下文体积。

5.4 Docker镜像创建方式总结

  • 基于现有镜像创建:通过修改容器并提交为新的镜像。
  • 基于本地模板创建:从模板文件导入创建镜像。
  • 基于 Dockerfile 创建:通过编写 Dockerfile 来定制镜像,支持自动化构建。

Docker 命令无法补全解决方案

  1. 首先安装 bash-completion 包:
bash 复制代码
yum install -y bash-completion
  1. 重新加载 bash 配置使补全生效:
bash 复制代码
source /etc/profile.d/bash_completion.sh
  1. 之后测试 Docker 命令补全是否正常工作:
bash 复制代码
docker run --cpu-  # 按Tab键应该会显示补全选项

总结

本文系统梳理了Docker的六大核心功能:网络管理、资源控制、数据卷容器、端口映射、容器互联和镜像创建,每个模块都从"原理+实战"角度出发,提供了可直接复用的命令和配置示例。

Docker的核心价值在于"隔离"与"标准化"------通过网络隔离实现容器间通信控制,通过资源限制避免宿主机资源耗尽,通过数据卷实现数据持久化,通过Dockerfile实现镜像的标准化构建。这些功能看似独立,实则相辅相成,共同构成了Docker容器化应用的基础。

建议大家在学习过程中"边学边练":每掌握一个知识点,就动手执行命令验证效果(如创建自定义网络、设置CPU限制、编写Dockerfile),只有通过实战才能真正理解Docker的底层逻辑。后续我还会分享Docker Compose、Docker Swarm等容器编排工具的使用,欢迎持续关注!

相关推荐
yenggd2 小时前
华为本地pbr及mqc及traffic-filter使用案例
运维·服务器·华为
福大大架构师每日一题3 小时前
docker和k3s安装kafka,go语言发送和接收kafka消息
docker·golang·kafka
林克爱塞尔达3 小时前
Linux高级技巧之集群部署(七)
linux·运维·php
Bruce_Liuxiaowei3 小时前
解锁 SSH 的完整潜力:从登录到隧道,再到自动化
运维·自动化·ssh
灰色红桃3 小时前
Docker 安装多版本Mysql+PHP+Nginx(php5.6,php7,mysql5.6,mysql8)
docker
Aczone283 小时前
驱动(二)Linux 系统移植、驱动开发框架
linux·运维·驱动开发
liu****3 小时前
负载均衡式的在线OJ项目编写(四)
运维·c++·负载均衡·个人开发
php@king3 小时前
安装xdebug调试工具(docker容器+vscode编辑器+xdebug)
vscode·docker·编辑器
余衫马4 小时前
Ubuntu24.04 安卓模拟器安装指南
android·容器·模拟器