前言
在容器化技术飞速发展的今天,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 create再docker 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容器和数据库容器在同一台机器上)。
工作流程:
- Docker启动时创建
docker0网桥; - 容器启动时,分配Container-IP,设置
docker0为默认网关; - 宿主机创建一对"虚拟网卡对"(veth pair),一端是容器内的
eth0网卡,另一端连在docker0网桥上; - 通过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资源控制注意事项
--cpu-shares是权重,不是硬限制:CPU空闲时,容器能使用超过权重的资源。--cpu-quota/--cpus是硬限制:容器无法突破设定的CPU上限(quota=-1表示无限制)。--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 内存限制建议与注意
- 生产环境必须设置内存限制:避免单个容器耗尽宿主机内存。
- 内存敏感应用禁用swap:比如Redis、MySQL,用
--memory-swap=--memory,避免swap导致性能下降。 - 配合重启策略:比如
--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控制注意事项
- 设备路径要正确:必须指定宿主机的实际磁盘设备(如
/dev/sda),路径错误会导致限制失效。 - 云环境注意底层限制:比如阿里云、AWS的云盘本身有IO限制,容器层限制不能超过底层限制。
- 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资源控制常见陷阱与建议
- 权重vs限额:
--cpu-shares是相对权重(争用时生效),--cpu-quota/--cpus是硬限额(强制限制)。 - cgroup版本差异:CentOS 7用cgroup v1,CentOS 8+用v2,参数和控制器名称不同(如blkio→io)。
- 生产环境监控:用Prometheus+Grafana+cAdvisor监控容器资源,及时发现瓶颈。
- 避免直接修改系统文件:修改
/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容器互联总结
- 核心原理:
--link通过修改接收容器的/etc/hosts文件,添加源容器的IP-别名映射,实现通信。 - 局限性:
- 仅支持单主机容器互联,跨主机不行。
- 不支持动态更新:如果源容器重启后IP变了,接收容器的
/etc/hosts不会自动更新,导致通信失败。
- 替代方案:复杂场景(跨主机、动态容器)用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 |
关键指令区别:
- RUN vs CMD vs ENTRYPOINT:
- RUN:构建镜像时执行(安装软件、修改配置)。
- CMD:容器启动时执行,可被覆盖。
- ENTRYPOINT:容器启动时执行,不可被覆盖,需传参用
docker run --entrypoint。
- 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:bashvim run.sh # 脚本内容: #!/bin/bash rm -rf /run/httpd/* # 清理httpd缓存 /usr/sbin/apachectl -D FOREGROUND # 前台运行httpd(Docker容器需要前台进程才能保持运行) -
编写网站首页
index.html:bashecho "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会复用缓存层,加速构建。 - 若某一层指令或文件变化,该层及之后的所有层都会重新构建(缓存失效)。
- 若Dockerfile指令未变化,且依赖的文件(如
镜像优化建议:
- 频繁变化的指令放在末尾:比如
COPY(复制代码、配置文件)放在最后,减少重新构建的层数。 - 合并RUN指令:用
&&连接多个命令,减少镜像层数(比如RUN yum install -y httpd && yum clean all)。 - 使用
.dockerignore文件:排除不需要的文件(如node_modules、日志、临时文件),减小构建上下文体积,加速构建。
5.4 Docker镜像创建方式总结
| 创建方式 | 核心步骤 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 基于现有容器 | 启动容器→修改→提交 | 操作简单,快速定制 | 镜像分层不清晰,不可重复构建 | 临时测试、简单镜像定制 |
| 基于本地模板 | 下载模板→导入镜像 | 快速获取特定系统环境 | 模板来源有限,灵活性低 | 快速搭建特定系统环境 |
| 基于Dockerfile | 编写Dockerfile→构建镜像 | 可重复构建、自动化、分层清晰 | 需学习Dockerfile指令 | 生产环境、复杂镜像定制 |
总结
Docker的核心价值在于"隔离"与"标准化"------通过网络隔离控制容器通信,通过资源限制避免宿主机资源耗尽,通过数据卷实现数据持久化,通过Dockerfile实现镜像的标准化构建。这些功能看似独立,实则相辅相成,共同构成了Docker容器化应用的基础。