当前位置: 首页 > news >正文

Docker 容器(二)

Docker

  • 四、Docker容器数据卷
      • 1.数据卷的主要特点
      • 2.卷的共享与继承
        • (1)卷的共享(Sharing)
        • (2) 卷的继承(Inheritance)
      • 3.数据卷运行实例
  • 五、Dockerfile
    • 1.Dockerfile
    • 2. 创建一个名为 myubuntu的自定义镜像
        • 第 1 步:准备并编写 Dockerfile 文件
        • 第 2 步:构建镜像
        • 第 3 步:运行镜像
    • 3、什么是虚悬镜像?​

四、Docker容器数据卷

Docker 容器数据卷(Volume)​​ 是一种​​持久化数据​​和​​共享数据​​的机制,用于解决容器内数据生命周期的问题。它本质上是绕过容器联合文件系统(UnionFS)的​​一个特殊目录​​,可以存在于一个或多个容器中,但其数据直接存储在宿主机上或由外部服务管理。

1.数据卷的主要特点

  1. 数据持久化 (Persistence)
    • 卷中的数据是独立于容器生命周期的。即使容器被删除、重启或停止,卷中的数据依然安全地保留在宿主机上。一直到没有容器使用它为止。
    • 这是数据卷最核心的价值。
  2. 数据共享与复用 (Sharing & Reuse)
    • 一个数据卷可以同时被多个容器挂载和使用,是实现容器间数据共享的最佳方式。
    • 一个容器也可以挂载多个数据卷
  3. 高性能 (Performance)
    • 相对于容器内联合文件系统的写时复制(CoW)机制,直接对数据卷的读写性能更高,更接近原生文件系统I/O。
  4. 解耦应用与数据 (Decoupling)
    • 将动态变化的、需要持久化的数据(如数据库文件)与静态的应用环境(如已安装的程序)分离开,使容器更加轻量和专注于业务逻辑。

2.卷的共享与继承

(1)卷的共享(Sharing)

这是数据卷最直接和常用的功能。多个容器(无论是否同时运行)可以挂载同一个预先创建好的数据卷(通常是命名卷),从而实现数据的共同访问和交换。

工作机制
多个容器的挂载点(target)指向同一个卷源(source)。

操作示例:容器间共享数据

  1. 创建一个命名卷 (shared-data)

    docker volume create shared-data
    
  2. 启动第一个生产者容器 (producer),向共享卷写入数据

    docker run -it --name producer --mount source=shared-data,target=/data ubuntu bash
    

    在容器内执行:

    echo "Hello from Producer Container!" > /data/message.txt
    exit
    
  3. 启动第二个消费者容器 (consumer),从同一个共享卷读取数据

    docker run -it --name consumer --mount source=shared-data,target=/app ubuntu bash
    

    在容器内执行:

    cat /app/message.txt # 输出: Hello from Producer Container!
    
  4. 验证

  • 你可以在 consumer 容器中看到 producer 容器创建的文件。
  • consumer 中修改 /app/ 下的文件,producer 重新挂载后也能看到。
  • 即使这两个容器都停止了,shared-data 卷里的数据依然存在。
(2) 卷的继承(Inheritance)

卷的继承主要通过 --volumes-from 参数实现。它允许一个新容器自动继承另一个容器所挂载的所有数据卷,包括挂载点和权限。

重要提示--volumes-from 继承的是挂载规则,而不是复制数据。最终,继承的容器和被继承的容器会挂载到同一个物理卷上,因此它们操作的也是同一份数据,本质上也构成了一种共享。

操作示例:继承挂载规则

  1. 启动一个“数据卷容器” (data-container)
    这个容器本身可以不运行任何应用,它的唯一目的就是定义要挂载的卷。

    docker run -d --name data-container \-v /db-data \          # 定义一个匿名卷-v /config \           # 定义另一个匿名卷ubuntu sleep infinity  # 让它执行一个无害的长期命令
    
  2. 通过继承启动一个新容器 (app-container)

    docker run -d --name app-container \--volumes-from data-container \ # 关键参数:继承 data-container 的所有卷my-app-image
    

    此时,app-container 容器会自动拥有两个挂载点:/db-data/config,它们与 data-container 中的对应卷是同一个。

  3. 再启动一个容器也继承它 (backup-container)

    docker run -d --name backup-container \--volumes-from data-container \backup-image
    

    现在,data-containerapp-containerbackup-container 都共享着相同的 /db-data/config 卷。

3.数据卷运行实例

  1. ​​直接命令添加​​宿主机与容器的卷映射。
  2. 设置不同的​​读写规则​​(rw与 ro)。
  3. 实现容器间的​​卷的继承和共享​​。

场景模拟​​
我们创建两个容器:

  • 容器1 (container1)​​:作为一个数据生产者,与宿主机建立映射,并创建数据。
  • ​​容器2 (container2)​​:继承容器1的卷规则,作为数据消费者来读取数据。

步骤 1: 直接命令添加容器卷(宿主机 vs 容器映射)

我们使用 docker run -v命令直接创建映射。

语法-v <宿主机目录>:<容器目录>:<读写规则>

执行命令:创建容器1并完成映射

# 在宿主机上创建一个目录用于映射
sudo mkdir -p /docker_volume_example/data# 运行容器1,并建立映射关系
# -v /docker_volume_example/data:/data:rw 表示将宿主机的目录映射到容器的/data目录,读写规则为读写(默认)
docker run -itd \--name container1 \-v /docker_volume_example/data:/data:rw \ubuntu:22.04 \bash
  • -v /docker_volume_example/data:/data:rw:这就是图中的“直接命令添加”。
    • 宿主机目录/docker_volume_example/data
    • 容器目录/data
    • 读写规则rw(读写,为默认模式,可省略)

步骤 2: 读写规则映射添加说明

现在我们在容器1内进行操作,验证读写权限,并模拟数据生产。

进入容器1并写入数据:

# 进入容器1
docker exec -it container1 bash# 此时,我们已在容器1的内部Shell中
# 查看映射的目录
ls /data # 此时应该是空的,因为宿主机目录是空的# 向数据卷中写入一个文件,模拟生产数据
echo "This is important data created by Container1." > /data/data_from_container1.txt# 退出容器1
exit

在宿主机上验证数据共享:

# 检查宿主机映射目录,应该能看到容器1创建的文件
cat /docker_volume_example/data/data_from_container1.txt

输出结果This is important data created by Container1.

这证明了宿主机和容器1之间的数据是实时共享的。


步骤 3: 卷的继承和共享

现在,我们启动容器2,让它继承容器1的卷规则,实现数据共享。

执行命令:创建容器2并继承容器1的卷

# 使用 --volumes-from 参数继承容器1的所有卷映射规则
docker run -itd \--name container2 \--volumes-from container1 \ # 这是实现继承的关键!ubuntu:22.04 \bash

验证继承与共享:

# 进入容器2
docker exec -it container2 bash# 检查容器2的 /data 目录,应该能看到容器1创建的文件
ls /data
cat /data/data_from_container1.txt

输出结果This is important data created by Container1.

成功!容器2没有直接映射宿主机目录,但通过 --volumes-from继承了容器1的映射,因此也能访问到同一份数据。

测试只读(ro)规则:

假设我们想让容器2只能读,不能写,我们可以在继承时重新定义挂载的读写规则。

1. 先删除旧的容器2

docker stop container2
docker rm container2

2. 以只读模式重新运行容器2

# 注意命令中的 :ro
docker run -itd \--name container2 \--volumes-from container1 \ # 先继承-v /docker_volume_example/data:/app_data:ro \ # 再重新挂载并覆盖为只读规则ubuntu:22.04 \bash
  • 这里我们做了一个调整,将容器2的挂载点从 /data改到了 /app_data,并设置了 :ro(只读)。

3. 验证容器2的只读权限

docker exec -it container2 bash# 可以成功读取
cat /app_data/data_from_container1.txt# 尝试写入会报错:Read-only file system
echo "Try to write from Container2" > /app_data/data_from_container2.txt

输出结果bash: /app_data/data_from_container2.txt: Read-only file system

这证明了只读(ro)规则已生效,完美实现了“只读”的说明。

五、Dockerfile

1.Dockerfile

Dockerfile 是一个文本文件​​,里面包含了一系列的​​指令(Instruction)​​ 和​​参数​​。每一条指令都会在镜像上构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
它将基础镜像一步步变成自定义镜像。

一个典型的 Dockerfile 分为四部分:

  1. ​​基础镜像信息​​:使用 FROM指令指定从哪个镜像开始构建。
  2. ​​元数据信息​​:使用 LABEL、MAINTAINER等指令添加镜像的描述信息。
  3. ​​构建指令​​:运行命令、复制文件、设置环境变量等,按顺序逐步构建镜像层。
  4. ​​容器启动指令​​:指定镜像构建完成后,启动容器时默认要运行的命令。

Dockerfile 的常用保留字

指令功能描述
FROM指定基础镜像,必须是第一条指令。
LABEL为镜像添加元数据(键值对),如版本、描述等。替代旧的 MAINTAINER
RUN在镜像构建过程中执行命令,用于安装软件、下载依赖等。
COPY宿主机上的文件或目录复制到镜像中。
ADD类似 COPY,但功能更多(如自动解压 tar 包、支持 URL)。推荐优先使用 COPY
WORKDIR设置后续指令的工作目录(如果不存在则创建)。类似 cd
EXPOSE声明容器运行时监听的网络端口(只是一个说明,实际映射需用 -p)。
ENV设置环境变量,后续指令和容器运行时都可以使用。
ARG设置构建时的环境变量,镜像运行时不存在。用于动态传入参数。
VOLUME创建匿名数据卷挂载点,用于持久化数据。
USER指定后续指令以哪个用户身份运行(默认为 root)。
CMD指定容器启动时默认执行的命令。一个 Dockerfile 只能有一条 CMD
ENTRYPOINT指定容器启动时的主要命令,CMD的内容会成为其参数。

2. 创建一个名为 myubuntu的自定义镜像

体验从编写 Dockerfile 到构建,最后运行的完整流程

第 1 步:准备并编写 Dockerfile 文件
  1. 创建一个空目录 作为工作空间,并进入该目录。

    mkdir myubuntu-dockerfile
    cd myubuntu-dockerfile
    
  2. 创建 Dockerfile文件(注意:没有文件扩展名)。

    touch Dockerfile
    
  3. 编辑 Dockerfile文件,输入以下内容:

    # 指定基础镜像
    FROM ubuntu:22.04# 维护者信息(可选)
    LABEL maintainer="student@example.com"# 设置环境变量,防止apt安装时交互式提示
    ENV DEBIAN_FRONTEND=noninteractive# 构建指令:更新软件包列表并安装常用工具
    RUN apt-get update && \apt-get install -y \vim \net-tools \iputils-ping \curl \&& rm -rf /var/lib/apt/lists/* # 清理缓存以减小镜像体积# 设置容器启动时默认执行的命令
    CMD ["/bin/bash"]
    

    代码解释:

    • FROM ubuntu:22.04: 基于官方 Ubuntu 22.04 镜像开始构建。
    • LABEL: 添加镜像的元数据信息。
    • RUN: 这是核心步骤。它执行命令来安装我们需要的软件包。
      • apt-get update && apt-get install -y: 更新软件源并安装指定工具(vim-编辑器, net-tools-网络工具如ifconfig, iputils-ping-ping命令, curl-传输工具)。
      • && rm -rf /var/lib/apt/lists/*: 这是一个很好的实践,清理 apt 缓存,可以显著减小镜像体积。
    • CMD ["/bin/bash"]: 指定当容器启动时,默认进入 bashshell。

第 2 步:构建镜像

使用 docker build命令,根据编写好的 Dockerfile 构建镜像。

命令格式: docker build -t 新镜像名字:TAG .

Dockerfile所在的目录下,执行以下命令:

docker build -t myubuntu:1.0 .

命令解释:

  • docker build: 构建镜像的命令。
  • -t myubuntu:1.0: -t参数用于给新镜像命名和打标签。这里我们将镜像命名为 myubuntu,标签为 1.0
  • .: 这个点 .非常重要!它指定了 构建上下文(Build Context) 的路径,即当前目录。Docker 引擎会在这个路径下寻找 Dockerfile文件。

等待构建完成,会看到类似下面的输出,表示构建成功:

Successfully built xxxxxxxxxxxx
Successfully tagged myubuntu:1.0

验证镜像是否创建成功:

docker images

你应该能在列表中看到 myubuntu镜像。


第 3 步:运行镜像

使用 docker run命令来启动并进入我们新创建的容器。

命令格式: docker run -it 新镜像名字:TAG

执行以下命令:

docker run -it myubuntu:1.0

命令解释:

  • docker run: 运行容器的命令。
  • -it: 这是两个参数的组合。
    • -i(--interactive): 保持标准输入流(STDIN)打开。
    • -t(--tty): 分配一个伪终端(pseudo-TTY)。
    • 组合使用 -it可以让我们以交互模式进入容器,就像登录一台虚拟机一样。
  • myubuntu:1.0: 指定要运行的镜像名称和标签。

验证自定义功能:

命令执行后,你的终端提示符会发生变化,表示你已经进入了容器内部,形如:

root@a1b2c3d4e5f6:/#

现在,可以测试我们在 Dockerfile 中安装的工具是否可用:

# 测试 ping 命令
ping -c 4 github.com# 测试 ifconfig 命令(来自net-tools)
ifconfig# 测试 vim 编辑器
vim --version

退出容器:输入 exit即可退出容器并回到宿主机的命令行。

3、什么是虚悬镜像?​

虚悬镜像​​是指没有标签(TAG)、并且没有被任何容器引用的镜像。它在 Docker 的镜像列表中通常显示为 <none>:<none>

产生场景:

  1. 镜像构建过程中
    当你使用 docker build重新构建一个同名的镜像时,Docker 会为新的构建过程创建新的镜像层。构建成功后,它会给​​新生成的镜像​​打上你指定的标签(如 my-app:latest)。

    而​​旧版本的镜像​​就会失去这个标签,变成 :,即虚悬镜像。

  2. 删除镜像后​​
    如果你删除了一个镜像的​​某个标签​​,而这个镜像还有其他标签,那么它不会变成虚悬镜像。但如果你删除了它的​​最后一个标签​​,这个镜像本身就会变成虚悬镜像。

http://www.dtcms.com/a/360570.html

相关文章:

  • 机器视觉学习-day15-图像轮廓特征查找
  • Wi-Fi技术——OSI模型
  • 深度学习量化双雄:PTQ 与 QAT 的技术剖析与实战
  • 开源协作白板 – 轻量级多用户实时协作白板系统 – 支持多用户绘图、文字编辑、图片处理
  • globals() 小技巧
  • C++ 模板全览:从“非特化”到“全特化 / 偏特化”的完整原理与区别
  • Prometheus之启用--web.enable-remote-write-receiver
  • 基于muduo库的图床云共享存储项目(三)
  • 前端常见安全问题 + 防御方法 + 面试回答
  • 「数据获取」《中国工会统计年鉴》(1991-2013)(获取方式看绑定的资源)
  • 【人工智能99问】Qwen3简介(33/99)
  • 浅析NVMe协议:DIF
  • 多线程使用场景一(es数据批量导入)
  • 林曦词典|老死不相往来
  • 洛谷p2392kkksc03考前临时抱佛脚 详解(回溯,深度搜索法)
  • 大模型参数到底是什么?
  • CUDA与图形API的深度互操作:解锁GPU硬件接口的真正潜力
  • C++内存序不迷茫:从CPU缓存一致性理解Memory Order
  • 如何将剪贴板内容存为文件?Paste As File支持文本/图片转换
  • 批处理脚本操作 JSON 文件
  • centos7挂载iscis存储操作记录
  • Java学习笔记(前言:开发环境配置)
  • 五分钟聊一聊AQS源码
  • 【系统架构师设计(五)】需求工程上:需求开发与需求管理概述、结构化需求分析法
  • 【PyTorch】基于YOLO的多目标检测(一)
  • Trae接入自有Deepseek模型,不再排队等待
  • C# .Net8 WinFormsApp使用日志Serilog组件
  • 【IO学习】IO基础和标准IO函数
  • 生物学自然主义:心灵哲学中的生物性探索
  • 《程序员修炼之道》第七八九章读书笔记