Docker核心功能解析:网络、资源控制、数据卷与镜像构建

前言

在容器化技术飞速发展的今天,Docker早已成为开发、测试和运维岗位的必备技能。它就像一个"集装箱"技术,能把应用程序及其依赖环境打包起来,实现"一次封装,到处运行",彻底解决了"开发环境能跑,生产环境报错"的痛点。

但很多人使用Docker时,往往只停留在"拉取镜像、启动容器"的基础操作,对网络通信、资源限制、数据持久化、镜像定制这些核心功能一知半解。本文就从实战角度出发,用通俗易懂的语言,搭配详细的命令解析,带你吃透Docker的五大核心模块,让你在实际项目中不再"卡壳"。

一、Docker网络管理

Docker容器的网络通信是部署应用的核心------容器之间怎么聊、外部怎么访问容器、不同场景选哪种网络模式,这些问题都得靠网络管理来解决。

1.1 Docker网络实现原理

Docker的网络通信靠的是Linux的"虚拟网桥"技术,核心是宿主机上的docker0网桥(可以理解成一个虚拟交换机)。

  • 启动Docker时,会自动创建docker0网桥,默认网段一般是172.17.0.0/16
  • 每个容器启动后,都会从这个网段分配一个专属IP(叫Container-IP),同时把docker0设为自己的默认网关。
  • 同一台宿主机上的容器,都连在docker0这个"交换机"上,能通过Container-IP直接通信,不用经过宿主机的物理网卡。
  • 外部网络没法直接访问Container-IP(因为docker0是虚拟的),想让外部访问,得做"端口映射"------把容器端口映射到宿主机端口,通过"宿主机IP:宿主机端口"就能访问容器了。

1.2 Docker端口映射

端口映射是外部访问容器的唯一途径,Docker支持两种映射方式,按需选择即可。

1.2.1 随机端口映射

-P(大写P)参数,Docker会自动从32768开始,分配一个没被使用的宿主机端口,映射到容器暴露的端口(比如Nginx的80端口)。适合测试环境,不用记固定端口。

命令解析+实战

bash 复制代码
# 启动Nginx容器,随机映射端口
docker run -d --name test1 -P nginx
  • docker run:创建并启动容器(等同于先docker createdocker start)。
  • -d:让容器在后台运行(守护模式),不会占用当前终端。
  • --name test1:给容器起个名字叫test1,方便后续操作(比如停止、删除)。
  • -P:大写P,开启随机端口映射。
  • nginx:使用官方的Nginx镜像。

查看映射结果

bash 复制代码
docker ps -a
  • docker ps:查看运行中的容器;-a参数表示查看所有容器(包括停止的)。

  • 输出示例(重点看PORTS列):

    复制代码
    CONTAINER ID   IMAGE  COMMAND       CREATED          PORTS                   NAMES
    6fd7a69e7883   nginx  "/docker-entrypoint...."   15秒前   Up 14秒   0.0.0.0:49158->80/tcp   test1

    这里49158是宿主机随机分配的端口,80是容器内Nginx的端口,外部访问地址就是http://宿主机IP:49158

1.2.2 指定端口映射

-p(小写p)参数,手动指定"宿主机端口:容器端口",适合生产环境,需要固定访问地址。

命令解析+实战

bash 复制代码
# 启动Nginx容器,指定宿主机43000端口映射到容器80端口
docker run -d --name test2 -p 43000:80 nginx
  • -p 43000:80:核心参数,前面的43000是宿主机端口,后面的80是容器端口,意思是"访问宿主机的43000端口,就等同于访问容器的80端口"。
  • 其他参数和随机映射一致。

查看映射结果

bash 复制代码
docker ps -a
  • 输出示例(PORTS列):

    复制代码
    0.0.0.0:43000->80/tcp   test2

    外部访问地址就是http://宿主机IP:43000,端口固定,方便记忆和配置。

1.2.3 Docker端口映射总结
映射方式 参数 核心特点 适用场景
随机映射 -P(大写) 自动分配宿主机端口(32768+),不用手动指定 测试环境、临时访问
指定映射 -p 宿主机端口:容器端口(小写) 手动固定端口,访问地址不变 生产环境、需要长期访问的服务

注意:宿主机端口不能被其他程序占用,否则容器启动会失败,换个未占用的端口即可。

1.3 Docker的网络模式

创建容器时,用--net--network参数指定网络模式,不同模式对应不同的网络隔离和通信能力,Docker支持5种核心模式。

1.3.1 host模式(与宿主机共享网络栈)
  • 原理:容器不创建自己的独立网络环境,直接用宿主机的网络栈(IP、端口、网卡都和宿主机共用),相当于"容器和宿主机在同一个网络里"。
  • 特点:网络性能最好(没有转发开销),但容器会占用宿主机的端口,可能出现端口冲突(比如容器用80端口,宿主机的Nginx就没法用80端口了)。
  • 适用场景:对网络性能要求高的应用(比如数据库)。

命令解析+实战

bash 复制代码
# 启动Nginx容器,使用host模式
docker run -d --name nginx-host --network host nginx
  • --network host:指定网络模式为host。
  • 访问方式:直接用http://宿主机IP(不用加端口,因为容器用的是宿主机的80端口)。
1.3.2 Container模式(与其他容器共享网络栈)
  • 原理:新容器不跟宿主机共享网络,而是跟一个已存在的容器共享同一个网络环境(IP、端口共用),但两个容器的文件系统、进程列表是隔离的,能通过回环网卡(lo)直接通信。
  • 适用场景:两个容器需要紧密协作(比如"应用容器+日志收集容器"),不想做端口映射。

命令解析+实战

bash 复制代码
# 1. 先创建一个基础容器test1-container(CentOS 7)
docker run -itd --name test1-container centos:7 /bin/bash
# 2. 创建test2-container,共享test1-container的网络
docker run -itd --name test2-container --net=container:test1-container centos:7 /bin/bash
  • --net=container:test1-container:核心参数,意思是"让test2-container共享test1-container的网络",冒号后面是要共享的容器名称。
  • 验证:进入test2-container,ping test1-container的别名(直接ping test1-container),能ping通就说明共享成功。
1.3.3 无网络模式(none)
  • 原理:容器有自己的独立网络环境,但Docker不给它配任何网络(没有网卡、没有IP、没有路由),只有一个回环网卡(lo),只能容器内部进程通信。
  • 特点:完全封闭,没法联网,安全性极高。
  • 适用场景:不需要网络的离线任务(比如本地数据处理、加密计算)。

命令解析+实战

bash 复制代码
# 启动容器,使用none模式
docker run -itd --name test-none --network none centos:7 /bin/bash
# 验证:进入容器查看网络,只有lo网卡
docker exec -it test-none ip addr
  • ip addr:查看容器的网卡信息,输出中只有lo(127.0.0.1),没有其他网卡。
1.3.4 桥接模式(bridge)
  • 原理:Docker的默认模式(不指定--net就是bridge模式),相当于"容器的专属局域网"。容器有自己的独立网络环境,通过docker0网桥和宿主机通信,容器之间也能通过Container-IP通信。
  • 适用场景:单主机上的常规容器通信(比如Web容器和数据库容器在同一台机器上)。

工作流程

  1. Docker启动时创建docker0网桥;
  2. 容器启动时,分配Container-IP,设置docker0为默认网关;
  3. 宿主机创建一对"虚拟网卡对"(veth pair),一端是容器内的eth0网卡,另一端连在docker0网桥上;
  4. 通过iptables的端口转发,实现容器和外部网络的通信。

命令解析+实战

bash 复制代码
# 启动容器,默认使用bridge模式(不用加--net参数)
docker run -itd --name test-bridge centos:7 /bin/bash
  • 验证:进入容器,ping其他同模式的容器的Container-IP,能ping通。
1.3.5 自定义模式

Docker允许用户创建自定义网络,支持三种驱动,满足复杂场景需求:

  • bridge:类似默认的bridge模式,但支持DNS解析(容器能通过名称通信,不用记IP)、网络隔离。
  • overlay:用于跨主机容器通信(比如Docker Swarm集群,多台机器上的容器互相通信)。
  • macvlan:容器像物理机一样,直接获取宿主机所在局域网的IP,看起来就是局域网里的一台物理设备。

命令解析+实战(创建自定义bridge网络)

默认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
# 2. 启动容器,指定自定义网络和固定IP
docker run -itd --name test-custom --net mynetwork --ip 172.18.0.10 centos:7 /bin/bash
  • docker network create:创建自定义网络。
  • --subnet=172.18.0.0/16:指定网络的子网(IP范围)。
  • --opt "com.docker.network.bridge.name"="docker1":指定宿主机上的网卡名(默认是br-xxx,不好记)。
  • mynetwork:自定义网络的名称。
  • --net mynetwork:使用自定义网络。
  • --ip 172.18.0.10:给容器分配固定IP(必须在子网范围内)。
1.3.6 Docker网络模式总结
网络模式 核心特点 适用场景
host 共享宿主机网络,无端口映射,性能最好 对网络性能要求高、无端口冲突风险的场景
Container 共享其他容器网络,隔离其他资源 容器间紧密协作(如应用+日志收集)
none 无网络配置,仅lo网卡,安全性极高 离线任务、高安全性需求
bridge 默认模式,独立网络+docker0网桥 单主机常规容器通信
自定义 支持DNS、跨主机、固定IP 集群部署、复杂网络隔离

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

如果不限制容器的资源,某个容器可能会占用宿主机所有的CPU、内存,导致其他容器或程序崩溃。Docker基于Linux的cgroups(Control Groups)机制,能限制容器的CPU、内存、磁盘IO使用。

cgroups是Linux内核的资源管理工具,核心功能有4个:限制资源总量、分配资源优先级、统计资源使用、控制进程(挂起/恢复)。

2.1 CPU资源控制

主要通过3种方式控制CPU使用:限制使用率上限、设置占用权重、绑定指定CPU核心。

2.1.1 设置CPU使用率上限(--cpu-period / --cpu-quota)
  • 原理:Linux用CFS(完全公平调度器)分配CPU,通过两个参数控制:
    • --cpu-period:调度周期(单位微秒,默认100000,即100毫秒)。
    • --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 test-cpu1 --cpu-quota 50000 centos:7 /bin/bash

# 方式2:用--cpus(更直观,Docker 1.13+支持)
docker run -itd --name test-cpu2 --cpus="0.5" centos:7 /bin/bash
  • --cpu-quota 50000:100毫秒周期内,最多用50毫秒CPU,即50%单核。
  • --cpus="0.5":直接指定可用CPU核心数(0.5核),等价于--cpu-period=100000 --cpu-quota=50000
  • 注意:如果宿主机有4个逻辑核,0.5核相当于整机CPU的12.5%(0.5/4)。
2.1.2 设置CPU占用比(权重 --- --cpu-shares)
  • 原理:--cpu-shares指定CPU使用的相对权重(默认1024),只有CPU资源紧张时才生效(不是硬限制)。比如容器A权重512,容器B权重1024,CPU不够用时,两者分配比约1:2。
  • 适用场景:给重要容器分配更高权重,保证其CPU使用优先级。

命令解析+实战

bash 复制代码
# 创建两个不同权重的容器
docker run -itd --name cpu-a --cpu-shares 512 centos:7 /bin/bash
docker run -itd --name cpu-b --cpu-shares 1024 centos:7 /bin/bash
  • --cpu-shares 512:容器cpu-a的权重是512。
  • --cpu-shares 1024:容器cpu-b的权重是1024(默认值)。
  • 验证:在两个容器内同时运行CPU压力测试(后面会讲),用docker stats查看,cpu-b的CPU使用率约是cpu-a的2倍。
2.1.3 绑定指定CPU(--cpuset-cpus)
  • 原理:把容器进程绑定到宿主机的指定CPU核心上,避免进程在不同核心间切换,提升性能稳定性。
  • 适用场景:对性能稳定性要求高的应用(比如数据库、缓存)。

命令解析+实战

bash 复制代码
# 把容器绑定到宿主机第1、3个核心(核心编号从0开始)
docker run -itd --name test-cpu-bind --cpuset-cpus "1,3" centos:7 /bin/bash
  • --cpuset-cpus "1,3":核心编号用逗号分隔,支持范围(比如0-2表示0、1、2号核心)。
  • 验证:宿主机运行top命令,按键盘1键查看各核心使用率,会发现1、3号核心使用率较高。
2.1.4 CPU压力测试与验证示例

通过压力测试工具验证CPU限制是否生效:

bash 复制代码
# 1. 进入容器,安装压力测试工具stress
docker exec -it test-cpu1 bash
yum install -y epel-release && yum install -y stress

# 2. 运行压力测试(启动4个CPU密集型进程)
stress -c 4

# 3. 宿主机查看CPU使用情况
docker stats
  • stress -c 4:创建4个进程,每个进程反复计算随机数的平方根,占用CPU资源。
  • docker stats:实时监控容器的CPU、内存使用率,能看到test-cpu1的CPU使用率被限制在50%左右。
2.1.5 CPU资源控制注意事项
  1. --cpu-shares是权重,不是硬限制:CPU空闲时,容器能使用超过权重的资源。
  2. --cpu-quota/--cpus是硬限制:容器无法突破设定的CPU上限(quota=-1表示无限制)。
  3. --cpuset-cpus适合性能敏感场景:避免容器和其他进程抢核心,提升稳定性。

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(物理内存用尽会崩溃)。
2.2.2 内存限制示例命令
bash 复制代码
# 1. 限制物理内存512MB(swap默认1024MB)
docker run -itd --name test-mem1 -m 512m centos:7 /bin/bash

# 2. 限制物理内存300MB,swap总量1GB(swap可用700MB)
docker run -itd --name test-mem2 -m 300m --memory-swap=1g centos:7 /bin/bash
  • -m 512m:等价于--memory 512m,限制物理内存512MB。
  • --memory-swap=1g:物理内存+swap总量不超过1GB。
2.2.3 内存限制验证与OOM行为

验证内存限制

bash 复制代码
# 宿主机查看容器内存限制(替换为容器ID)
cd /sys/fs/cgroup/memory/docker/<容器ID>/
cat memory.limit_in_bytes  # 输出内存限制(单位字节,512MB=536870912字节)

OOM行为 :如果容器使用的内存超过限制,且没有可用swap,内核会触发"OOM killer",直接杀死容器。可以通过docker logs <容器ID>查看OOM日志。

压力测试验证

bash 复制代码
# 进入容器,用stress测试内存
docker exec -it test-mem1 bash
stress --vm 1 --vm-bytes 600m  # 尝试使用600MB内存(超过512MB限制)
  • --vm 1:启动1个内存压力进程。
  • --vm-bytes 600m:每个进程占用600MB内存。
  • 结果:容器会被OOM杀死,docker ps -a查看容器状态为"Exited"。
2.2.4 内存限制建议与注意
  1. 生产环境必须设置内存限制:避免单个容器耗尽宿主机内存。
  2. 内存敏感应用禁用swap:比如Redis、MySQL,用--memory-swap=--memory,避免swap导致性能下降。
  3. 配合重启策略:比如--restart=on-failure,容器OOM后自动重启。

2.3 磁盘IO(blkio/io)控制

限制容器对磁盘的读写速率和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是宿主机的磁盘设备(可以用lsblk命令查看实际设备名)。
2.3.2 磁盘IO限制验证(dd测试)

dd命令测试写速率(跳过文件系统缓存,确保测试准确):

bash 复制代码
# 1. 启动容器,限制写速率1MB/s
docker run -it --name test-io --device-write-bps /dev/sda:1MB centos:7 /bin/bash

# 2. 容器内执行dd测试(写入10个1MB的文件)
dd if=/dev/zero of=test.out bs=1M count=10 oflag=direct
  • dd if=/dev/zero of=test.out:从/dev/zero(无限零字节源)写入test.out文件。
  • bs=1M:每次写入1MB。
  • count=10:总共写入10次,共10MB。
  • oflag=direct:跳过文件系统缓存,直接写磁盘。
  • 输出示例:10485760 bytes (10 MB) copied, 10.0028 s, 1.0 MB/s,写速率被限制在1MB/s。
2.3.3 磁盘IO控制注意事项
  1. 设备路径要正确:必须指定宿主机的实际磁盘设备(如/dev/sda),路径错误会导致限制失效。
  2. 云环境注意底层限制:比如阿里云、AWS的云盘本身有IO限制,容器层限制不能超过底层限制。
  3. cgroup版本差异:CentOS 7用cgroup v1(参数是--device-read-bps),CentOS 8+用cgroup v2(参数不同,需参考Docker文档)。

2.4 清理Docker占用的磁盘空间

Docker运行久了,会积累停止的容器、未使用的镜像、网络和构建缓存,占用磁盘空间,用以下命令清理:

bash 复制代码
# 清理所有未使用的资源(停止的容器、未使用的镜像、网络、缓存)
docker system prune -a
  • 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 m1 -m 512m centos:7  # 512MB物理内存
docker run -itd --name m2 -m 300m --memory-swap=1g centos:7  # 300M+700M swap

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

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

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

  1. 权重vs限额:--cpu-shares是相对权重(争用时生效),--cpu-quota/--cpus是硬限额(强制限制)。
  2. cgroup版本差异:CentOS 7用cgroup v1,CentOS 8+用v2,参数和控制器名称不同(如blkio→io)。
  3. 生产环境监控:用Prometheus+Grafana+cAdvisor监控容器资源,及时发现瓶颈。
  4. 避免直接修改系统文件:修改/sys/fs/cgroup目录需要root权限,优先用docker run参数配置。

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

容器的文件系统是临时的------容器删除后,内部数据会丢失。数据卷(Data Volumes)解决了数据持久化问题,数据卷容器则简化了多容器间的数据共享。

3.1 数据卷

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

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

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

命令解析+实战

bash 复制代码
# 挂载宿主机/opt/www目录到容器内/data1目录
docker run -it --name web1 -v /opt/www:/data1 centos:7 /bin/bash
  • -v /opt/www:/data1:核心参数,前面/opt/www是宿主机目录(不存在会自动创建),后面/data1是容器内目录(不存在会自动创建)。
  • --name web1:容器名称。
  • 作用:容器内/data1目录的所有操作,都会同步到宿主机/opt/www目录。
3.1.2 在数据卷中写入数据

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

bash 复制代码
# 容器内执行(写入数据到/data1/abc.txt)
echo "这是测试数据" > /data1/abc.txt
exit  # 退出容器
3.1.3 查看宿主机同步的数据

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

bash 复制代码
# 宿主机执行
cat /opt/www/abc.txt  # 输出:这是测试数据
  • 即使删除容器docker rm web1,宿主机/opt/www目录的数据依然存在,实现了数据持久化。

3.2 数据卷容器

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

3.2.1 创建数据卷容器
bash 复制代码
# 创建数据卷容器web2,挂载两个数据卷/data1和/data2
docker run -it --name web2 -v /data1 -v /data2 centos:7 /bin/bash
  • -v /data1 -v /data2:只指定容器内目录,不指定宿主机目录,Docker会在宿主机/var/lib/docker/volumes/下创建匿名卷(自动生成名称)。
  • 作用:web2容器作为数据载体,提供/data1/data2两个数据卷。
3.2.2 在数据卷容器中写入数据

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

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

# 在/data1和/data2中写入数据
echo "数据卷容器测试1" > /data1/test1.txt
echo "数据卷容器测试2" > /data2/test2.txt
3.2.3 使用--volumes-from共享数据卷

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

bash 复制代码
# 创建web3,共享web2的所有数据卷
docker run -it --name web3 --volumes-from web2 centos:7 /bin/bash
  • --volumes-from web2:核心参数,意思是"继承web2的所有数据卷配置",web3容器会自动拥有/data1/data2目录,且数据和web2同步。
3.2.4 在新容器中验证共享数据

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

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

# 查看数据
cat /data1/test1.txt  # 输出:数据卷容器测试1
cat /data2/test2.txt  # 输出:数据卷容器测试2
  • 补充:在web3中修改/data1/test1.txt,web2中的文件也会同步修改,实现多容器数据共享。

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

类型 核心作用 关键命令 适用场景
数据卷(Data Volumes) 数据持久化(容器删除数据不丢) -v 宿主机目录:容器目录 单容器数据持久化
数据卷容器(Data Volumes Containers) 多容器数据共享 --volumes-from 数据卷容器名称 多容器共享配置、日志等

注意 :数据卷容器删除后,其数据卷(匿名卷)不会被删除,需手动清理:docker volume rm <卷ID>(用docker volume ls查看卷ID)。

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

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

4.1 创建并运行源容器web1

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

bash 复制代码
# 启动web1容器(后台运行,随机端口映射)
docker run -itd -P --name web1 centos:7 /bin/bash
  • -itd-i(保持输入打开)+-t(分配伪终端)+-d(后台运行),组合起来让容器后台交互式运行。
  • -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是源容器名称,后面web1是源容器在web2中的别名(可自定义,比如--link web1:source,则web2中用source访问web1)。
  • 原理:--link会在web2的/etc/hosts文件中添加一行web1的Container-IP web1,所以web2能通过别名直接访问web1。

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

进入web2容器,用ping命令测试与web1的通信(不用知道web1的Container-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
  • 补充:除了ping,还能通过其他网络工具通信(比如telnet、curl),只要知道web1的端口即可。

4.4 Docker容器互联总结

  1. 核心原理:--link通过修改接收容器的/etc/hosts文件,添加源容器的IP-别名映射,实现通信。
  2. 局限性:
    • 仅支持单主机容器互联,跨主机不行。
    • 不支持动态更新:如果源容器重启后IP变了,接收容器的/etc/hosts不会自动更新,导致通信失败。
  3. 替代方案:复杂场景(跨主机、动态容器)用Docker自定义网络(如overlay),支持DNS自动解析和动态IP更新。

五、Docker镜像的创建

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

5.1 基于现有镜像创建

先启动一个现有镜像的容器,在容器内修改(比如安装软件、配置环境),再将修改后的容器提交为新镜像。适用于快速定制简单镜像。

5.1.1 启动容器并进行修改
bash 复制代码
# 1. 启动CentOS 7容器(交互式)
docker run -it --name mycentos centos:7 /bin/bash

# 2. 容器内修改(比如安装OpenJDK 8)
yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
java -version  # 验证安装成功
yum clean all  # 清理yum缓存,减小镜像体积
exit  # 退出容器
  • yum install -y java-1.8.0-openjdk:安装OpenJDK 8(开发环境用java-1.8.0-openjdk-devel)。
  • yum clean all:清理yum缓存,避免镜像体积过大。
5.1.2 提交容器为新镜像

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

bash 复制代码
# 查看容器ID(找到mycentos的容器ID)
docker ps -a

# 提交容器(替换为实际容器ID)
docker commit -m "安装了OpenJDK 8" -a "测试用户" 1450d38ffda5 openjdk:8
  • docker commit:提交容器为镜像。
  • -m "安装了OpenJDK 8":提交说明,记录镜像的修改内容。
  • -a "测试用户":作者信息(可选)。
  • 1450d38ffda5:容器ID(通过docker ps -a查看)。
  • openjdk:8:新镜像的名称和标签(标签用于区分版本,默认是latest)。

查看新镜像

bash 复制代码
docker images
  • 输出示例:

    复制代码
    REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
    openjdk      8         4ee649b7cf2b   5秒前           469MB
    centos       7         eeb6ee3f44bd   4年 ago         204MB
  • 新镜像openjdk:8包含了CentOS 7+OpenJDK 8的环境,后续可直接用它创建容器。

5.2 基于本地模板创建

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

5.2.1 下载操作系统模板

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

bash 复制代码
# 用wget下载模板(如果没有wget,先安装:yum install -y wget)
wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
  • 模板是压缩包,包含操作系统的核心文件,体积较小。
5.2.2 导入模板为Docker镜像

docker import命令导入模板:

bash 复制代码
# 将模板文件导入为镜像(镜像名debian:test)
cat debian-7.0-x86-minimal.tar.gz | docker import - debian:test
  • cat debian-7.0-x86-minimal.tar.gz:读取模板文件。
  • docker import - debian:test:导入为镜像,-表示从标准输入读取,debian:test是镜像名和标签。

查看镜像

bash 复制代码
docker images
  • 输出示例:

    复制代码
    REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
    debian       test      5120c6b1c973   4秒前           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 centos:7 必须是第一条指令,基础镜像可以是官方镜像或自定义镜像
MAINTAINER 指定镜像维护者信息 MAINTAINER "测试 test@example.com" 可选,记录作者和联系方式
RUN 构建镜像时执行命令(如安装软件) RUN yum install -y httpd 每条RUN生成一层,尽量用&&合并命令,减少层数
ENTRYPOINT 容器启动时执行的命令(不可被覆盖) ENTRYPOINT ["httpd", "-D", "FOREGROUND"] 优先级高于CMD,不可被docker run后的命令覆盖
CMD 容器启动时执行的默认命令(可被覆盖) CMD ["httpd", "-D", "FOREGROUND"] 可被docker run后的命令覆盖(如docker run xxx bash)
EXPOSE 声明容器内的端口(仅文档说明) EXPOSE 80 不实际映射端口,仅告诉使用者容器暴露的端口
ENV 设置环境变量(容器内可使用) ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk 容器内可通过$JAVA_HOME引用
ADD 复制文件/目录到镜像,支持URL下载和自动解压 ADD myfile.tar.gz /app 支持.tar.gz、.zip等自动解压,本地文件优先用COPY
COPY 复制本地文件/目录到镜像(无解压) COPY index.html /var/www/html 仅复制本地文件,功能简单,推荐优先使用
VOLUME 声明容器内的挂载点(创建匿名卷) VOLUME ["/data"] 实现数据持久化,容器启动时自动挂载匿名卷
USER 设置容器内运行命令的用户 USER root 默认是root,可切换为其他用户(需提前创建)
WORKDIR 设置后续指令的工作目录(类似cd) WORKDIR /app 后续的RUN、CMD等指令都在这个目录下执行
HEALTHCHECK 设置容器健康检查 HEALTHCHECK CMD curl --fail http://localhost:8080

关键指令区别

  1. RUN vs CMD vs ENTRYPOINT:
    • RUN:构建镜像时执行(安装软件、修改配置)。
    • CMD:容器启动时执行,可被覆盖。
    • ENTRYPOINT:容器启动时执行,不可被覆盖,需传参用docker run --entrypoint
  2. ADD vs COPY:
    • ADD:支持URL下载、自动解压,功能丰富但可能有意外行为(比如误解压)。
    • COPY:仅复制本地文件,功能简单,推荐优先使用。
5.3.3 Dockerfile实战示例(构建Apache镜像)

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

步骤1:创建工作目录

bash 复制代码
# 创建工作目录,存放Dockerfile和相关文件
mkdir -p /opt/apache && cd /opt/apache

步骤2:准备相关文件

  • 下载CentOS国内源(避免yum安装慢):wget http://mirrors.aliyun.com/repo/Centos-7.repo -O CentOS-Base.repo

  • 编写Apache启动脚本run.sh

    bash 复制代码
    vim run.sh
    # 脚本内容:
    #!/bin/bash
    rm -rf /run/httpd/*  # 清理httpd缓存
    /usr/sbin/apachectl -D FOREGROUND  # 前台运行httpd(Docker容器需要前台进程才能保持运行)
  • 编写网站首页index.html

    bash 复制代码
    echo "Apache测试页面" > index.html

步骤3:编写Dockerfile

bash 复制代码
vim Dockerfile
# Dockerfile内容:
FROM centos:7  # 基础镜像
MAINTAINER "测试 <test@example.com>"  # 维护者信息
ADD CentOS-Base.repo /etc/yum.repos.d/  # 替换为国内源
RUN yum clean all && yum -y install httpd  # 安装httpd
EXPOSE 80  # 声明80端口
COPY index.html /var/www/html/  # 复制首页文件到Apache根目录
ADD run.sh /opt/run.sh  # 复制启动脚本
RUN chmod +x /opt/run.sh  # 给脚本添加执行权限
CMD ["/opt/run.sh"]  # 容器启动时执行脚本
5.3.4 基于Dockerfile构建镜像

docker build命令构建镜像,格式:docker build -t 镜像名:标签 构建上下文路径./表示当前目录为构建上下文,Docker会读取该目录下的所有文件)。

bash 复制代码
# 构建镜像(镜像名httpd:centos,标签centos)
docker build -t httpd:centos ./
  • docker build:构建镜像。
  • -t httpd:centos:指定镜像名和标签。
  • ./:构建上下文路径(当前目录,Docker会读取该目录下的Dockerfile和相关文件)。

构建过程说明

  • Docker会按Dockerfile的指令顺序执行,每条指令生成一层镜像。
  • 若指令未变化,会复用缓存(比如第二次构建时,若没修改Dockerfile,会直接使用缓存层)。

启动镜像测试

bash 复制代码
# 启动容器,映射宿主机1216端口到容器80端口
docker run -d -p 1216:80 httpd:centos
  • 访问测试:http://宿主机IP:1216,能看到"Apache测试页面",说明镜像构建成功。
5.3.5 Docker镜像分层与缓存机制解析
  • 分层机制:Dockerfile中的每条指令对应一层镜像,比如FROM centos:7是基础层,RUN yum install -y httpd是安装层,COPY index.html是复制层。
  • 缓存机制:
    • 若Dockerfile指令未变化,且依赖的文件(如index.html)未修改,Docker会复用缓存层,加速构建。
    • 若某一层指令或文件变化,该层及之后的所有层都会重新构建(缓存失效)。

镜像优化建议

  1. 频繁变化的指令放在末尾:比如COPY(复制代码、配置文件)放在最后,减少重新构建的层数。
  2. 合并RUN指令:用&&连接多个命令,减少镜像层数(比如RUN yum install -y httpd && yum clean all)。
  3. 使用.dockerignore文件:排除不需要的文件(如node_modules、日志、临时文件),减小构建上下文体积,加速构建。

5.4 Docker镜像创建方式总结

创建方式 核心步骤 优点 缺点 适用场景
基于现有容器 启动容器→修改→提交 操作简单,快速定制 镜像分层不清晰,不可重复构建 临时测试、简单镜像定制
基于本地模板 下载模板→导入镜像 快速获取特定系统环境 模板来源有限,灵活性低 快速搭建特定系统环境
基于Dockerfile 编写Dockerfile→构建镜像 可重复构建、自动化、分层清晰 需学习Dockerfile指令 生产环境、复杂镜像定制

总结

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

相关推荐
init_23612 小时前
MPLS跨域optionA 配置案例
java·开发语言·网络
朱 欢 庆2 小时前
在docker容器里 使用Jenkins部署前端项目
前端·经验分享·docker·jenkins
G_H_S_3_2 小时前
【网络运维】KVM基础使用
linux·运维·网络·kvm
lkbhua莱克瓦242 小时前
面向编程3-UDP通信程序
java·网络·网络协议·udp
小尧嵌入式2 小时前
CANOpen协议
服务器·网络·c++·windows
代码游侠2 小时前
学习笔记——网络基础
linux·c语言·网络·笔记·学习·算法
少云清2 小时前
【接口测试】2_代码实现 _设置http请求语法
网络·网络协议·http
梁辰兴11 小时前
计算机网络基础:使用集线器的星型拓扑
服务器·网络·计算机网络·集线器·计算机网络基础·梁辰兴·星型拓扑
TG:@yunlaoda360 云老大12 小时前
如何在华为云国际站代理商控制台进行SFS Turbo的性能与容量核查?
服务器·网络·数据库·华为云