Docker(二)—— Docker核心功能全解析:网络、资源控制、数据卷与镜像构建实战
文章目录
- 前言
- 一、Docker网络管理
- 1.1 Docker网络实现原理
- 1.2 Docker端口映射
- 1.2.1 随机端口映射
- 1.2.2 指定端口映射
- 1.2.3 Docker端口映射总结
- 1.3 Docker的网络模式
- 1.3.1 host模式(与宿主机共享网络栈)
- 1.3.2 Container模式(与其他容器共享网络栈)
- 1.3.3 无网络模式(none)
- 1.3.4 桥接模式(bridge)
- 1.3.5 自定义模式
- 1.3.6 Docker网络模式总结
- 二、Docker资源控制(Docker优化)
- 2.1 CPU资源控制
- 2.1.1 设置CPU使用率上限(--cpu-period / --cpu-quota)
- 2.1.2 设置CPU占用比(权重 — --cpu-shares)
- 2.1.3 绑定指定CPU(--cpuset-cpus)
- 2.1.4 CPU压力测试与验证示例
- 2.1.5 CPU资源控制注意事项
- 2.2 内存使用限制
- 2.2.1 --memory与--memory-swap规则
- 2.2.2 内存限制示例命令
- 2.2.3 内存限制验证与OOM行为
- 2.2.4 内存限制建议与注意
- 2.3 磁盘IO(blkio/io)控制
- 2.3.1 常用Docker磁盘IO限制参数
- 2.3.2 磁盘IO限制验证(dd测试)
- 2.3.3 磁盘IO控制注意事项
- 2.4 清理Docker占用的磁盘空间
- 2.5 Docker资源控制常见命令速查
- 2.6 Docker资源控制常见陷阱与建议
- 三、Docker数据卷容器(Data Volumes Containers)
- 3.1 数据卷
- 3.1.1 创建与挂载数据卷
- 3.1.2 在数据卷中写入数据
- 3.1.3 查看宿主机同步的数据
- 3.2 数据卷容器
- 3.2.1 创建数据卷容器
- 3.2.2 在数据卷容器中写入数据
- 3.2.3 使用--volumes-from共享数据卷
- 3.2.4 在新容器中验证共享数据
- 3.3 数据卷与数据卷容器总结
- 四、Docker容器互联(使用CentOS镜像)
- 4.1 创建并运行源容器web1
- 4.2 创建并运行接收容器web2(--link互联)
- 4.3 在接收容器web2中测试连接
- 4.4 Docker容器互联总结
- 五、Docker镜像的创建
- 5.1 基于现有镜像创建
- 5.1.1 启动容器并进行修改
- 5.1.2 提交容器为新镜像
- 5.2 基于本地模板创建
- 5.2.1 下载操作系统模板
- 5.2.2 导入模板为Docker镜像
- 5.3 基于Dockerfile创建(重点)
- 5.3.1 Docker镜像的分层结构与UnionFS
- 5.3.2 Dockerfile操作常用指令详解(重点)
- 5.3.3 Dockerfile实战示例(构建Apache镜像)
- 5.3.4 基于Dockerfile构建镜像
- 5.3.5 Docker镜像分层与缓存机制解析
- 5.4 Docker镜像创建方式总结
- Docker 命令无法补全解决方案
- 总结
前言
在容器化技术飞速发展的今天,Docker作为最主流的容器引擎,已经成为开发、测试和运维岗位的必备技能。但很多同学在使用Docker时,往往只停留在“拉取镜像、启动容器”的基础操作,对Docker的网络通信、资源限制、数据持久化以及镜像定制等核心功能理解不深,导致在实际项目中遇到各种“卡壳”问题。
本文将从实战角度出发,系统梳理Docker的六大核心模块:网络管理、资源控制、数据卷容器、端口映射、容器互联和镜像创建,每个模块都包含原理讲解、命令示例和注意事项,帮你打通Docker使用的“任督二脉”。无论你是刚接触Docker的新手,还是需要巩固基础的开发者/运维工程师,这篇文章都能为你提供实用的指导。
一、Docker网络管理
Docker容器的网络通信是容器化应用部署的核心环节——容器之间如何通信?容器如何被外部网络访问?不同场景下该选择哪种网络模式?这些问题都需要通过Docker的网络管理机制来解决。
1.1 Docker网络实现原理
Docker的网络通信依赖于Linux桥接技术,其核心是宿主机上虚拟的docker0
网桥。具体实现逻辑如下:
- 虚拟网桥(docker0):Docker启动时会在宿主机创建一个名为
docker0
的虚拟网桥,它相当于一个“虚拟交换机”,负责连接宿主机上的所有Docker容器。 - Container-IP分配:启动容器时,Docker会从
docker0
的网段(默认通常是172.17.0.0/16
)中分配一个唯一的IP地址(即Container-IP
)给容器,并将docker0
设置为容器的默认网关。 - 容器间通信:同一宿主机内的所有容器都接入
docker0
网桥,因此容器之间可以通过各自的Container-IP
直接通信(无需经过宿主机的物理网卡)。 - 外部网络访问限制:
docker0
是宿主机虚拟的网络设备,并非真实的物理网卡,外部网络无法直接通过Container-IP
访问容器。若需外部访问,需通过端口映射将容器端口映射到宿主机端口,再通过“宿主机IP:宿主机端口”访问容器。
1.2 Docker端口映射
容器内部的端口默认无法被外部网络访问(因docker0
是虚拟网桥),需通过端口映射将容器端口映射到宿主机端口,实现外部访问。Docker支持两种端口映射方式:随机映射(-P)和指定映射(-p)。
1.2.1 随机端口映射
使用-P
(大写)参数,Docker会自动从32768
开始分配一个未使用的宿主机端口,映射到容器暴露的端口(如Nginx的80端口)。适用于无需固定端口的场景(如测试环境)。
实战示例:
# 启动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服务)。
实战示例:
# 启动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和端口。 - 特点:网络性能最优(无额外网络转发开销),但容器会占用宿主机的端口,存在端口冲突风险。
- 适用场景:需要高性能网络的应用(如数据库)
- 实战命令:
# 启动Nginx容器,使用host模式 docker run -d --name nginx-host --network host nginx # 访问容器:直接通过宿主机IP(无需端口映射,容器用宿主机80端口) # http://宿主机IP:80
1.3.2 Container模式(与其他容器共享网络栈)
-
原理:新创建的容器不与宿主机共享网络,而是与已存在的某个容器共享同一个
Network Namespace
。两个容器共享IP、端口范围,但文件系统、进程列表等其他资源仍保持隔离;可通过lo
回环网卡直接通信。 -
适用场景:需要两个容器紧密协作(如“应用容器+日志收集容器”),且希望避免端口映射的场景。
-
实战案例:
# 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]
# 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
回环网卡(用于容器内部进程通信)。 - 特点:完全封闭的网络环境,无法联网,安全性极高。
- 适用场景:不需要网络通信的离线任务(如本地数据处理、加密计算)。
- 实战命令:
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
网桥与宿主机通信,具体流程,如下:- Docker启动时创建
docker0
虚拟网桥; - 为每个容器分配
Container-IP
,并设置docker0的IP地址为容器的默认网关。 - 在主机上创建一对
veth pair
(虚拟网卡对)(连接两个不同的网络命名空间
):一端作为容器内的eth0
网卡,另一端放在主机中, 以 * 这样类似的名字命名,并将这个网络设备加入到docker0
网桥; - 通过
iptables
的nat
表配置端口转发,实现容器与外部网络的通信。
- Docker启动时创建
- 补充说明:
virbr0
与docker0
的区别——virbr0
是Linux虚拟化技术(如KVM、VirtualBox)创建的虚拟网桥,用于虚拟机通信;docker0
仅用于Docker容器。 - 总结:bridge网络模式中,容器运行在同一宿主机内的虚拟网桥上,可以通过容器互相通信,与宿主机网络隔离。
1.3.5 自定义模式
Docker允许用户创建自定义网络,支持三种驱动:
bridge
:类似默认bridge
模式,但增加了DNS解析(容器可通过名称通信)、网络隔离等功能。单机网络模式,适合在一台宿主机、内容器互联;overlay
:用于跨主机容器通信(Docker Swarm集群场景);macvlan
:容器像一台物理机一样直接获取宿主机的所在的网络的IP(容器像物理机一样直接连接局域网)
实战:创建自定义bridge网络并指定IP
默认bridge
模式不支持手动指定Container-IP
,需通过自定义网络实现:
# 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
# 管理 Linux 桥接网络
brctl show
# 管理 Docker 容器网络
docker network ls
1.3.6 Docker网络模式总结
网络模式 | 核心特点 | 适用场景 |
---|---|---|
host | 共享宿主机网络栈,无端口映射 | 对网络性能要求高,无端口冲突风险的场景 |
Container | 共享其他容器网络,隔离其他资源 | 容器间紧密协作(如应用+日志收集) |
none | 无网络配置,仅lo网卡 | 离线任务、高安全性需求 |
bridge | 默认模式,独立网络栈+docker0网桥 | 单主机容器通信,常规场景 |
自定义 | 支持DNS、跨主机、MAC模拟 | 集群部署、复杂网络隔离 |
额外附加:
- Overlay:这是Docker Swarm模式的网络,主要用于在多个主机上创建一个分布式网络。容器间即便在不同的主机也能完成通信,相当于在跨主机的容器之间创建了一个覆盖网络。
- 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)。
-
实战示例:
# 方式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。
- 实战示例:
# 创建两个不同权重的容器 docker run -itd --name c1 --cpu-shares 512 centos:7 docker run -itd --name c2 --cpu-shares 1024 centos:7
# 验证:在容器内运行压力测试工具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核心(硬亲和性),避免进程在不同核心间切换,提升性能稳定性。
- 实战示例:
# 将容器绑定到宿主机第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高负载,验证资源限制效果:
-
在容器内创建一个繁忙循环脚本:
# /cpu.sh #!/bin/bash i=0 while true; do let i++; done
-
在容器内运行
./cpu.sh
,在宿主机观察top
与docker stats
:docker exec -it <container> bash ./cpu.sh
-
分别进入容器,进行压力测试
yum install -y epel-release yum install -y stress stress -c 4 #产生四个进程,每个进程都反复不停的计算随机数的平方根
-
修改
cgroups
手工测试(示例):# 找到容器对应的 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资源控制注意事项
--cpu-shares
是权重,非硬限制(CPU空闲时,容器可使用超过权重的资源);--cpu-quota/--cpus
是硬限制,容器无法突破设定的CPU上限(quota = -1 表示无限制);--cpuset-cpus
适合对性能稳定性要求高的场景(避免与其他进程抢核)(如数据库容器)。- 在多核宿主机上理解
quota/period
的含义(单位是 “相对于 1 个 CPU 的份额”)。
2.2 内存使用限制
内存限制通过-m/--memory
和--memory-swap
参数实现,避免容器耗尽宿主机内存。
2.2.1 --memory与–memory-swap规则
--memory
:限制容器可使用的物理内存(如-m 512m
表示512MB);--memory-swap
:限制容器可使用的物理内存+交换分区(swap) 总量,需与--memory
配合使用;- 核心规则:
- 示例
-m 300m --memory-swap=1g
:物理内存300MB,swap可用700MB(1G-300M); - 不设置
--memory-swap
:默认swap为--memory
的2倍(如-m 512m
,swap默认1024MB); --memory-swap=-1
:swap无限制(使用宿主机所有可用swap);--memory-swap=--memory
:容器不可使用swap(物理内存用尽触发OOM)。
- 示例
2.2.2 内存限制示例命令
# 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行为
-
验证内存限制:
# 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 内存限制建议与注意
- 生产环境必须为容器设置内存限制,避免单个容器把宿主机内存耗尽;
- 内存敏感型应用(如Redis、MySQL)建议禁用swap(
--memory-swap=--memory
),避免swap导致性能下降; - 配合容器健康检查和重启策略(如
--restart=on-failure
),应对OOM后的容器恢复。 - 在 cgroup v2 环境下内存控制文件名/行为可能与 v1 略有不同(检查
/sys/fs/cgroup
结构)。
2.3 磁盘IO(blkio/io)控制
Docker基于cgroups
的blkio
控制器(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
docker run -it --name iotest --device-write-bps /dev/sda:1MB centos:7 /bin/bash
2.3.2 磁盘IO限制验证(dd测试)
通过dd
命令(跳过文件系统缓存)测试写速率:
# 容器内执行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控制注意事项
- 限制需指定具体块设备路径(如
/dev/sda
),路径错误会导致限制失效; - 云环境或虚拟化环境中,底层存储(如AWS EBS、阿里云云盘)可能有自身IO限制,容器层限制需结合底层限制配置;
- cgroup v2环境中,IO限制参数不同(如使用
--device-io-max
),需参考对应Docker版本文档。
2.4 清理Docker占用的磁盘空间
Docker运行一段时间后,会积累停止的容器、未使用的镜像、网络和构建缓存,可通过以下命令清理:
# 清理所有未使用的资源(停止的容器、未使用的镜像、网络、缓存)
docker system prune -a
# 注意:此命令会删除所有未活跃的资源,执行前确认无需保留!
2.5 Docker资源控制常见命令速查
# 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资源控制常见陷阱与建议
- 权重vs限额:
--cpu-shares
是相对权重(争用时生效),--cpu-quota/--cpus
是硬限额(强制限制); - cgroup版本差异:CentOS 7默认用cgroup v1,CentOS 8+或新内核可能用cgroup v2,控制器名称和参数不同(如blkio→io);
- IO 限制依赖底层设备:在云/虚拟机上测试时,注意底层虚拟磁盘的行为。
- 生产环境监控:建议搭配Prometheus+Grafana+cAdvisor监控容器资源,及时发现资源瓶颈;
- 权限控制:修改
/sys/fs/cgroup
目录下的配置需root权限,优先使用docker run
参数配置,避免直接修改系统文件。
三、Docker数据卷容器(Data Volumes Containers)
容器的文件系统是临时的——容器删除后,内部数据会丢失。Docker的数据卷(Data Volumes)机制解决了这一问题,实现数据持久化;而数据卷容器则进一步简化了多容器间的数据共享。
3.1 数据卷
数据卷是容器内的特殊目录,可与宿主机目录或其他容器共享,具备以下特性:
- 数据持久化:容器删除后,数据卷中的数据不会丢失;
- 双向同步:宿主机与容器对数据卷的修改实时同步;
- 跨容器共享:多个容器可挂载同一个数据卷。
3.1.1 创建与挂载数据卷
通过docker run
的-v
或--mount
参数挂载数据卷,格式:-v 宿主机目录:容器内目录
。
# 挂载宿主机/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
)中创建文件,数据会同步到宿主机:
# 容器内执行(写入数据到/data1/abc.txt)
echo "this is web1" > /data1/abc.txt
exit
此时,容器中的 /data1/abc.txt
文件与宿主机中的 /var/www/abc.txt
文件是同步的。即使容器退出,宿主机上的数据依然存在。
3.1.3 查看宿主机同步的数据
退出容器后,查看宿主机/var/www
目录,数据已同步:
# 宿主机执行
cat /opt/www/abc.txt # 输出:this is web1
3.2 数据卷容器
数据卷容器是专门用于提供数据卷的容器,不运行应用程序,仅作为“数据载体”,供其他容器通过--volumes-from
挂载其数据卷。适用于多容器共享数据的场景(如微服务中的配置共享、日志存储)。
3.2.1 创建数据卷容器
# 创建数据卷容器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
容器,在数据卷中写入测试数据:
# 进入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
的数据卷:
# 创建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
中写入的数据:
# 进入web3容器
docker exec -it web3 bash# 查看数据
cat /data1/abc.txt # 输出:this is web2
cat /data2/ABC.txt # 输出:THIS IS WEB2
3.3 数据卷与数据卷容器总结
- 数据卷 (Data Volumes):解决“容器数据持久化”问题,通过
-v
挂载宿主机目录; - 数据卷容器(Data Volumes Containers):解决“多容器数据共享”问题,通过
--volumes-from
继承数据卷,无需每个容器都挂载宿主机目录; - 注意:数据卷容器删除后,其数据卷(匿名卷)不会被删除,需手动清理(
docker volume rm <卷ID>
)。
四、Docker容器互联(使用CentOS镜像)
容器互联是指通过网络让两个或多个容器直接通信,Docker早期通过--link
选项实现,适用于简单的单主机容器通信场景(复杂场景建议使用自定义网络)。
4.1 创建并运行源容器web1
首先创建“源容器”web1
,作为通信的发起方:
# 启动web1容器(后台运行,随机端口映射)
docker run -itd -P --name web1 centos:7 /bin/bash
# 参数说明:
# -itd:交互式+后台运行;
# -P:随机映射端口;
# --name web1:指定容器名称(互联需依赖名称)。
4.2 创建并运行接收容器web2(–link互联)
创建“接收容器”web2
,通过--link
选项连接web1
,格式:--link 源容器名称:源容器别名
。
# 启动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
的通信(无需知道web1
的Container-IP
):
# 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容器互联总结
- 核心原理:
--link
会在接收容器的/etc/hosts
文件中添加源容器的别名与Container-IP
的映射,因此接收容器可通过别名通信; - 局限性:
--link
仅支持单主机容器互联,且不支持动态更新(源容器IP变化后,接收容器的/etc/hosts
不会自动更新); - 替代方案:复杂场景(如跨主机、动态容器)建议使用Docker自定义网络(如
overlay
),支持DNS自动解析和动态IP更新。
五、Docker镜像的创建
Docker镜像是容器的“模板”,包含容器运行所需的程序、库、配置等。创建Docker镜像主要有三种方式:基于现有容器创建
、基于本地模板创建
、基于Dockerfile创建
(最常用、最灵活)。
5.1 基于现有镜像创建
该方式是“先修改容器,再将修改后的容器提交为新镜像”,适用于快速定制简单镜像。
5.1.1 启动容器并进行修改
# 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 新镜像名:标签
。
# 提交容器(容器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为例):
# 使用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
命令导入模板:
# 将模板文件导入为镜像(镜像名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指令按功能可分为基础镜像指令、镜像操作指令、容器启动指令等,以下是常用指令:
-
FROM
指定新镜像基于的基础镜像,Dockerfile 的第一条指令必须为FROM
。FROM centos:7
-
MAINTAINER
指定镜像的维护者信息:MAINTAINER "Simon Cai <simoncwh@example.com>"
-
RUN
执行命令并将结果提交到镜像中。常用来安装软件包、修改配置等。RUN yum install -y httpd
-
ENTRYPOINT
设置容器启动时默认执行的命令(不可被覆盖,优先级高于CMD):ENTRYPOINT ["httpd", "-D", "FOREGROUND"]
-
CMD
容器启动时执行的默认命令。CMD
指令会被docker run
命令后指定的命令覆盖。CMD ["httpd", "-D", "FOREGROUND"]
-
EXPOSE
声明容器内的端口(仅文档说明,不实际映射):EXPOSE 80
-
ENV
设置环境变量(容器内可使用):ENV MY_VAR=my_value
-
ADD
将文件或目录从宿主机复制到镜像中,支持从 URL 下载文件,并能自动解压归档文件:ADD myfile.tar.gz /app
-
COPY
将本地文件或目录复制到镜像中(仅本地文件,无解压):COPY . /app
-
VOLUME
声明容器内的挂载点(创建匿名卷):VOLUME ["/data"]
-
USER
设置容器内运行命令时的用户:USER root
-
WORKDIR
设置后续指令的工作目录(类似cd):WORKDIR /app
-
ONBUILD
设置当该镜像作为基础镜像时,后续 Dockerfile 执行的命令:ONBUILD RUN echo "Building from base image"
-
HEALTHCHECK
设置容器的健康检查:HEALTHCHECK CMD curl --fail http://localhost:8080 || exit 1
关键指令区别:
RUN
vsCMD
vsENTRYPOINT
:RUN
:构建镜像时执行(如安装软件);CMD
:容器启动时执行,可被docker run
后的命令覆盖(如docker run xxx bash
会覆盖CMD);ENTRYPOINT
:容器启动时执行,不可被覆盖,若需传参需用docker run --entrypoint
。
ADD
vsCOPY
:ADD
支持URL下载和自动解压(如.tar.gz),功能更丰富;COPY
仅复制本地文件,功能简单,推荐优先使用(避免意外解压)。
5.3.3 Dockerfile实战示例(构建Apache镜像)
以构建一个基于CentOS 7的Apache(httpd)镜像为例,完整流程如下:
- 创建工作目录:
mkdir -p /opt/apache && cd /opt/apache
- 编写Dockerfile:
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"]
准备执行脚本(方式一)
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 镜像名:标签 构建上下文路径
(.
表示当前目录为构建上下文)。
# 构建镜像(镜像名httpd:centos,标签centos)注意末尾的.
docker build -t httpd:centos ./# 查看构建的镜像
docker images
# 输出示例:
# REPOSITORY TAG IMAGE ID CREATED SIZE
# httpd centos 9e154b3b93e6 8 seconds ago 493MB
# 新镜像运行容器
docker run -d -p 1216:80 httpd:centos# 测试
http://192.168.10.14:1216/
5.3.5 Docker镜像分层与缓存机制解析
- 分层机制:Dockerfile中的每条指令对应一层镜像,例如:
FROM centos:7
→ 基础层;RUN yum install -y httpd
→ 安装层;COPY index.html /var/www/html/
→ 复制层。
- 缓存机制:
- 若Dockerfile指令未变化,且依赖的文件(如
index.html
)未修改,Docker会复用缓存层; - 若某一层指令或文件变化,该层及之后的所有层都会重新构建(缓存失效)。
- 若Dockerfile指令未变化,且依赖的文件(如
- 镜像层是不可变的
- 删除容器时只会删除其上面的读写层,底层的镜像层不会丢失。
- 优化建议:
- 频繁变化的指令(如
COPY
)放在Dockerfile末尾,减少重新构建的层数; - 合并
RUN
指令(用&&
连接),减少镜像层数(如合并yum install
和yum clean
); - 使用
.dockerignore
文件排除不需要的文件(如node_modules
、日志文件),减小构建上下文体积。
- 频繁变化的指令(如
5.4 Docker镜像创建方式总结
- 基于现有镜像创建:通过修改容器并提交为新的镜像。
- 基于本地模板创建:从模板文件导入创建镜像。
- 基于 Dockerfile 创建:通过编写 Dockerfile 来定制镜像,支持自动化构建。
Docker 命令无法补全解决方案
- 首先安装 bash-completion 包:
yum install -y bash-completion
- 重新加载 bash 配置使补全生效:
source /etc/profile.d/bash_completion.sh
- 之后测试 Docker 命令补全是否正常工作:
docker run --cpu- # 按Tab键应该会显示补全选项
总结
本文系统梳理了Docker的六大核心功能:网络管理、资源控制、数据卷容器、端口映射、容器互联和镜像创建,每个模块都从“原理+实战”角度出发,提供了可直接复用的命令和配置示例。
Docker的核心价值在于“隔离”与“标准化”——通过网络隔离实现容器间通信控制,通过资源限制避免宿主机资源耗尽,通过数据卷实现数据持久化,通过Dockerfile实现镜像的标准化构建。这些功能看似独立,实则相辅相成,共同构成了Docker容器化应用的基础。
建议大家在学习过程中“边学边练”:每掌握一个知识点,就动手执行命令验证效果(如创建自定义网络、设置CPU限制、编写Dockerfile),只有通过实战才能真正理解Docker的底层逻辑。后续我还会分享Docker Compose、Docker Swarm等容器编排工具的使用,欢迎持续关注!