系列综述:
💞目的:本系列是个人整理为了云计算学习
的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
🥰来源:材料主要源于–Docker视频教程从入门到进阶,docker视频教程详解–进行的,每个知识点的修正和深入主要参考各平台大佬的文章,其中也可能含有少量的个人实验自证。
🤭结语:如果有帮到你的地方,就点个赞和关注一下呗,谢谢🎈🎄🌷!!!
请先收藏!!!,后续继续完善和扩充👍(●’◡’●)
文章目录
- 一、Docker简介
- Docker综述
- 二、Docker镜像
- 镜像概述
- 镜像分层
- ImageID和镜像摘要
- 三、Docker引擎
- 概述
- 容器
- 容器操作指令
- 四、Dockerfile
- 简介
- 指令详解
- 五、数据持久化
- 简介
- 数据卷相关命令
- 六、Docker网络通信
- 简介
- CNM
- Docker网络相关指令
- 参考博客
😊点此到文末惊喜↩︎
一、Docker简介
Docker综述
- DevOps的核心理念/目标/作用
- 整合
开发和运维
团队间的协作关系,提高软件交付速度、质量和稳定性 - 流程主要包含
开发code、构建build、测试test、发布release、部署deploy、维护operator、监控monitor、迭代plan
- 整合
- Docker发展历程
- 首发版本(2013年)
- 依赖于第三方工具LXC(Linux Containers)作为其容器管理的基础,可能被禁止使用且无法跨平台
- Docker Daemon设计复杂庞大,出现问题难以维护和更新
- Docker 0.9版本(约2014年)
- 采用自研的Libcontainer工具,直接利用Linux内核的特性(如cgroups和namespaces)来管理容器,提高了效率和跨平台能力
- Docker 1.1及OCI规范(2016年)
- 将容器运行时代码从Docker Daemon中分离出来,形成了符合Docker运行时规范Open Container Initiative (OCI)的Runc项目,负责实际的容器创建和管理任务。
- Containerd作为中间层,进一步管理镜像生命周期和容器运行,加强了Docker的底层架构。
- 首发版本(2013年)
- 虚拟化技术Hypervisor
- 原理:通过抽象化硬件资源,使得每个虚拟机都相信自己独占了一套完整的硬件,从而能够在单一物理主机上运行多个独立的操作系统实例。
- 分类
- Type-1 Hypervisor(
裸金属虚拟化
):直接安装并运行在物理硬件
上,支持完整的硬件虚拟化 - Type-2 Hypervisor:运行在标准
操作系统
之上,作为一个应用程序。
- Type-1 Hypervisor(
- 常见实例
- KVM (Kernel-based Virtual Machine):集成在Linux内核中的
Type 1 Hypervisor
,支持完整的硬件虚拟化 - Xen:是一个较早的开源Hypervisor,支持多种操作系统
- Hyper-V:Windows上的
Type 1 Hypervisor
,提供安全高效的虚拟化服务
- KVM (Kernel-based Virtual Machine):集成在Linux内核中的
- Docker底层支持技术
- Linux Namespace(命名空间):容器隔离的基础,包括User,Mnt,Network,UTS,IPC,Pid
- Linux CGroups(控制组):cgroups 容器资源统计和隔离, 主要是cpu,device,memory等
- unionFS( 联合文件系统):aufs/overlayfs,是镜像分层实现的基础
- Docker的三要素
- Image(镜像):
应用程序
+依赖环境
- Container(容器):
镜像
+可写层
+运行资源
(命名空间等) - Repository(仓库):
多架构多版本镜像
的存储中心
- Image(镜像):
二、Docker镜像
镜像概述
- 定义:由
应用程序
及其依赖环境
构成的一个静态只读的模板文件
,该文件是一堆静态只读数据层
的统一视角,基于统一文件系统技术隐藏了多层 (这个定义需要进行统一的处理)
- 作用(每一个都可以进行原理的拓展)
只读
保证可重复
:Docker镜像是包含应用程序及其依赖环境的只读文件,所以开发和测试团队可以通过同一个镜像构建相同的程序执行实例
分层
保证可拓展
:Docker镜像由多个只读镜像层堆叠
而成,除基础镜像层外,每个镜像新层基于一条dockerfile指令(如RUN, COPY, ADD等)进行增量构建跨平台支持
保证可移植
:Docker在不同计算机架构(例如x86、ARM等)提供了相应的镜像和运行时环境分层存储
保证快速部署
:Docker镜像由多个只读层
堆叠组成,只读
有利于镜像层的复用
,分层
有利于不同镜像间的共享
,从而实现存储优化
,提高缓存命中率内核机制
保证隔离性
:Docker使用Linux内核的cgroup和namespace机制,实现了容器
与宿主机
或其他容器
间的隔离容器编排工具
保证可伸缩性
:Docker Swarm和Kubernetes等容器编排工具提供了自动化容器部署、伸缩和管理的功能,从而实现负载均衡和自动扩缩容
- 镜像仓库类型
- 官方仓库(Docker Official Image):由Docker官方团队维护和管理,具有高可靠性和稳定性,对个人用户有一定的速率限制
- 已验证仓库(Verified Publisher):由Docker官方认证的大型组织,镜像质量有一定的保证
- 赞助仓库(Sponsred OSS):由Docker公司赞助开发的镜像仓库,质量有保证
- 无认证仓库(无标识):使用时确实需要仔细评估来源和维护记录,以确保安全性和可靠性
- Docker镜像仓库标签
- 镜像中心Registry:存放着很多由官方、其他机构或个人创建的Docker仓库
- 仓库 Repository:Docker镜像仓库保存相关的一组镜像,这组镜像都与镜像仓库名称相同
- 标签Tag:通过:即可唯一定位一个宿主机上的具体镜像
镜像分层
- 镜像分层构成
- 基础层/顶层(Base Layer):处于镜像的最底层,基本是一个完整的rootfs,包含操作系统运行时所需的基本组件,通常是各种
Linux系统镜像
,如Ubuntu、CentOS等 - 构建层 / 中间层(Build Layers):基于
相邻的镜像下层
,每次执行Dockerfile中的指令
(如RUN, COPY, ADD等)都会创建一个新的只读镜像层
,每个层只存储了与父层不同的增量变化
,从而实现存储优化 - 可写层 / 容器层(Writable Layer):处于
容器
的最顶层,Docker通过联合文件系统(UnionFS)
实现在只读层中进行读操作
,在可写层中执行写操作
,从而实现统一视角下容器可读可写的文件系统
- 核心:Docker镜像由一些松耦合的只读镜像层组成,DockerDaemon负责堆叠这些镜像层,并将它们关联为一个统一的整体, 即对外表现出的是一个独立的对象,镜像在被拉取时,按照自底向上顺序加载
- 基础层/顶层(Base Layer):处于镜像的最底层,基本是一个完整的rootfs,包含操作系统运行时所需的基本组件,通常是各种
- 每个镜像层的组成
- 层叠的文件系统(Layered File System):在Dockerfile中每执行一个指令(如RUN、COPY、ADD等),都会创建一个包含该指令产生的对父层增量修改部分的新的文件系统,主要包含文件和目录等内容。
- 元数据(Metadata):是一个JSON文件,主要包含容器运行时的配置信息,如环境变量、工作目录、入口命令等。
- 父层指针(Parent Layer Pointer):除了最底层外,每个层都有一个父层指针,指向其相邻下层,从而方便共享相同的底层镜像。
- ImageID:Docker镜像在宿主机的唯一标识符,基于镜像层内容及构建历史计算而来,确保了内容的唯一性和完整性
- 镜像分层的优点
- 只读保护:镜像的所有分层是只读的,通过
创建新层实现增量修改
,保障镜像分层数据的完整性
- 节省存储空间:每个镜像分层只记录
增量变化
,而不是镜像下层的完整副本 - 高效共享与增量更新:多个镜像可以
共享相同的底层镜像层
,当拉取或更新镜像时,若本地已缓存则可直接使用
,若本地未缓存可只拉取有变化的层
,从而加快了镜像的构建和部署,降低了网络资源的占用。 - 灵活构建:
基于Dockerfile指令序列创建新层
的机制,有利于构建和优化镜像 - 统一视图:基于联合文件系统实现多层镜像的统一处理,从而对外表现出统一的可读可写
- 只读保护:镜像的所有分层是只读的,通过
- linux中文件系统的启动流程(整理镜像的文件系统层面的改进)
- bootfs:系统加电后,从磁盘第一个扇区读取MBR,MBR加载bootloader,再由bootloader加载内核并转移控制器kernel。
- rootfs:linux的基础文件系统,系统所需的目录结构、工具和库文件,并负责挂载其他文件系统,为用户提供操作系统的功能和接口。
- Docker如何对文件进行增删改查
- 写时复制/增量修改(Copy-On-Write,简称COW):当修改
共享数据
时,先复制将要修改的部分,然后在该副本上进行修改。(?) - 联合文件系统(UnionFS):通过上层文件系统覆盖下层相同路径文件的机制,实现了多层镜像的堆叠。结合写时复制(Copy-On-Write, COW)策略,基于
只读镜像层
在可写层
创建副本进行增量修改,从而将所有镜像层合并为一个统一高效的文件系统(?) - 增加:只在容器最上层的
可写层
进行增加操作,不影响其他镜像层 - 删除:采用
软删除
(逻辑上标记已删除),可以快速删除和还原 - 修改:自顶向下查找,找到后先复制到可写层,然后直接修改可写层的副本
- 查找:命中内存的缓存则直接使用,若没有命中则自顶向下查找到后加载到内存后访问
- 写时复制/增量修改(Copy-On-Write,简称COW):当修改
ImageID和镜像摘要
- ImageID的计算
- 基础层镜像的diffID与chainID是一致的
- 构建层的ImageID由
文件系统的增量变更layer.tar文件哈希计算得到的diffID
+父层的chainID
再进行哈希得到。
- ImageID(镜像ID)和Digest(镜像摘要)的区别
- ImageID(镜像ID)
- 本地唯一标识符:基于
增量修改内容
和构建历史
哈希计算得到的宿主机本地唯一标识符
,即用于在宿主机上唯一的引用和管理镜像。 - 构建和拉取时生成:每次构建或从远程仓库拉取镜像时,都会生成一个新的Image ID,即使镜像的内容没有变化
- 映像缓存:镜像构建时,会先查询宿主机本地是否有相同ImageID的镜像层缓存如果有则Docker会共享和复用该层,从而减少镜像层的下载和存储。
- 本地唯一标识符:基于
- Digest(镜像摘要)
- 分布式一致性:基于
镜像内容
通过哈希算法(SHA256)
计算得出的全球唯一标识符
,内容一致的镜像
在不同宿主机
上的具有相同的digest
,可用于保证分发和部署镜像的内容完整性 - 基于内容的区分:可以区分相同
<repository>:<tag>
的不同镜像,避免标签重定向无法获取指定版本镜像的问题
- 分布式一致性:基于
- ImageID(镜像ID)
- 如何基于Digest保证镜像分发的内容一致性
- 在镜像压缩后立即计算散列值,然后将该值与压缩镜像一同发送,接收方计算压缩后镜像的分发散列值与传输的分发散列值进行比较,若相同则传输证明无误。
三、Docker引擎
概述
- 现代Docker引擎的核心组件
- Docker Client(客户端):用户与Docker引擎交互的
指令工具
,通过向Docker daemon发送指令控制和管理容器、镜像、网络和存储卷等资源。 - Docker daemon(守护进程):Dockerd是Docker的后台守护进程。
- 承上:Dockerd监听来自客户端的RESTful API请求并执行
- 启下:Dockerd通过gRPC协议与Containerd通信,执行容器和镜像的管理任务。
- Containerd:提供模块化且符合CRI标准(可与k8s直接交互)的
容器生命周期全面的管理能力
- Runc:遵循OCI(开放容器倡议)标准的容器运行时工具,直接与系统内核交互,
只专注于容器的具体执行
- Shim(垫片):是一个辅助进程,用于实现容器进程和守护进程的解耦,确保容器进程和守护进程的稳定性以及独立性
- Docker Client(客户端):用户与Docker引擎交互的
- 容器的创建流程
Docker Client
将命令通过TCP协议
发送给Docker Daemon
Docker Daemon
通过gRPC
与Containerd
通信,发送创建容器的请求。- Containerd 会fork一个
子进程Shim
,Shim再fork一个Runc进程
进行实际的容器创建 - 容器创建完成后,Runc进程退出并将控制权交给对应的Shim,从而实现daemon和Container的解耦合
容器
- 容器实现的基本机制
- Linux Namespace机制实现容器间资源的隔离,而Linux Cgroup机制容器对所需资源进行控制和管理
- UnionFS机制:每个分层文件系统都以只读的方式挂载,并且通过COW机制实现写入操作(当需要对文件进行修改时,UnionFS会在最上层的文件系统中创建一个副本,并将修改操作应用于该副本,而不影响其他分层文件系统中的只读数据)
- 容器 = 镜像 + 可写层
- 容器和虚拟机的区别
- 隔离性
- 虚拟机基于
硬件虚拟化技术Hypervisor
实现操作系统级别
的隔离 - Docker容器
共享宿主机操作系统内核
,但基于Linux的namespace和cgroups
实现了进程级别
的隔离
- 虚拟机基于
- 占用资源
- Docker是进程级别的应用程序,启动迅速,占用资源少,更适合快速部署和扩展。
- 虚拟机是是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU 资源
- 适用场景
- 虚拟机适合需要高度隔离和安全性的应用
- Docker容器则更侧重于轻量化和快速部署的场景
- 隔离性
容器操作指令
- 创建容器:
docker create <image_name>
- 原理:为指定镜像增加一个
可写层
,并完成相关资源和环境的配置,但该容器未实际运行。 - 每一个容器只有一个进程隔离空间,只能运行一个主进程
- 通常会将指定的命令或默认的入门点(entrypoint)作为该容器的主进程
- 原理:为指定镜像增加一个
- 启动容器:
docker start <container_name>
- 原理:恢复已创建但处于停止状态的容器的运行状态(类似进程上下文恢复)
- 运行容器:
docker run <image_name>
- 创建并启动容器,docker run = docker creat + docker start
- 列出所有容器(包含非运行态):
docker ps –a
- 所有镜像顶层(top-level)
docker images
- 仓库名称(Repository):镜像的名称,可以包括仓库和标签,如ubuntu:latest
- 标签(Tag):镜像的版本标签,如latest,如果没有指定标签,默认显示为
<none>
- 镜像ID(Image ID):镜像的唯一标识符,每个镜像有一个唯一的长ID
- 创建日期(Created):镜像创建的时间。
- 大小(Size):镜像的大小,注意显示的大小通常不包括共享层的大小。
- 显示本地存储的所有镜像(包括所有镜像分层及没有标记的镜像):
docker images -a
- 查看指定镜像的构建历史步骤:
docker history <image_name>
- 停止容器
docker stop <container_name>
:向容器内的主进程发送SIGTERM
信号,给予10秒响应信号,若超时则会自动发送一个SIGKILL
信号来强制停止容器docker kill <container_name>
:立即向容器内的主进程发送SIGKILL
信号,这是一种不可忽略的信号,用于强制终止
进程docker pause <container_name>
:通过linux cgroup技术进行容器上下文的保持,等待被docker unpause命令恢复
- 删除指定容器:
docker rm <container_name>
- 原理:删除可读写层和相关的配置文件
- 只能删除运行中的容器,如果强制删除使用
-f
,Docker会先发送SIGKILL信号来停止容器,然后再进行删除(不推荐使用)
- 删除指定镜像:
docker rmi <image_name/id>
- 原理:删除镜像最顶层,-f可以删除镜像的中间层
- 默认无法删除正在被容器使用的镜像,可使用
-f
强制删除,但是尽量不要使用,以防止意外删除正在使用中的资源。 - 在执行删除操作之前,使用
docker images
命令查看镜像列表,可以帮助你确定要删除的镜像ID或确认镜像是否被使用
- 创建新镜像:
docker commit -a <author_message> -m <submit_information> <container_name>
- 更改持久化:可以基于运行中的容器生成当前状态容器的镜像
- 非最佳实践:commit可能将非必要更改包含在其中,而dockerfile具有明确的构建步骤
- 使用场景:在开发过程中快速测试配置或在没有Dockerfile的情况下快速保存工作状态
docker build -t <container_name>:<tag> \path\dockerfile
- 通常使用
docker build -t <container_name>:<tag> .
构建,最后.
表示在当前路径构建,直接在当前路径构建可以引用相对路径编写dockerfile,避免绝对路径变更出错 - 原理:从FROM指令中获取基础镜像,然后重复执行1. run(create和start)2. 修改 3. commit,在每个循环中形成一层新的镜像
- 通常使用
- 在运行时容器中执行指令:
docker exec <container> <command>
- 只能对正在运行的容器执行docker exec命令。如果容器已停止,需要先启动容器
- 默认情况下,命令以容器内的root用户执行,但通过-u选项可以指定其他用户,以提高安全性。
- 示例:进入容器内的交互式终端
docker exec -it my_container /bin/bash
- 默认返回一个JSON格式的Docker对象详细信息:
docker inspect <container_name>
- 将镜像保存为一个tar压缩文件:
docker save -o <self_name>.tar <image_name1>:<tag> ...
- 保存的tar文件包含了镜像的所有层,以及相关的元数据
- 备份:定期备份关键的镜像,以防数据丢失。
- 迁移:传输镜像tar文件,从而实现应用程序和环境的迁移
- 将容器文件系统压缩成tar文件:
docker export my_running_container > container_data.tar
- 仅限文件系统:导出的tar文件是容器统一多层文件系统的
最顶层
(只有一层),并且不包括其他依赖镜像层及元数据等信息 - 不包含卷数据:如果容器内部使用了数据卷,这些卷的数据不会被导出,因为它们是独立于容器的。
- 运行状态:要求容器必须是运行状态或最近停止的状态,才能被导出
- 仅限文件系统:导出的tar文件是容器统一多层文件系统的
- 展示指定镜像的创建历史:
docker history <image_name>:<tag>
- 展示镜像的构建过程和每一层的详细信息
四、Dockerfile
简介
- 定义:Dockerfile 是用来构建 Docker 镜像的脚本文件。它由一系列的指令组成,这些指令按照顺序逐行执行,通过这些指令可以定义镜像的内容、环境和行为。
- 基本指令
- 基础镜像选取:通过
FROM
指令指定基础镜像,这是构建镜像的起点。 - 环境配置:使用
RUN
指令执行命令,比如安装软件包,配置环境。 - 文件复制:利用
COPY
或ADD
指令将本地文件复制到镜像中,用于构建应用。 - 命令设置:通过
CMD
或ENTRYPOINT
指令定义容器启动时运行的默认命令。 - 环境变量:使用
ENV
指令设置环境变量,供容器内应用使用。 - 工作目录:用
WORKDIR
指定容器内的默认工作目录。 - 用户管理:通过
USER
指令切换运行命令的用户。 - 健康检查:使用
HEALTHCHECK
指令定义容器健康状态的检查方法。 - 构建缓存:通过合理的指令顺序和多阶段构建策略优化构建过程,减少重复步骤,加快构建速度
- 基础镜像选取:通过
- Dockerfile脚本编写原则
- Dockerfile指令不区分大小写,但是一般约定
指令大写,参数小写
- 将
以 # 开头的行
视为注释,其他位置都视为参数 - Docker
按序执行
Dockerfile 中的指令 - 解析器指令不支持连续相同多条语句
- Dockerfile指令不区分大小写,但是一般约定
- Dockerfile文件示例
vim Dockerfile
:文件名默认为Dockerfile
,以简化构建流程docker tag <old-name> <new-name>:<tag>
:镜像tag重命名
# 用于指定基础镜像,且必须在指令序列中排在第一位,其中若tag省略默认为latest FROM <image>:tag # 在构建Docker镜像时指定容器的挂载点 VOLUME ["/app/data"] # 以键值对形式代替MAINTAINER表达文件其他详细信息 LABEL version="1.0" author="Sen" email="xxx@qq.com" # 容器打开后的默认目录,后续的WORKDIR都是基于前面的相对路径 WORKDIR path # 复制宿主机中的指定文件到容器中的指定目录,相对路径相对于docker build命令所指定的路径, # src若指定的压缩文件会自动解压,dest是绝对路径最后必须使用/作为目录结束 # 将所要拷贝的文件和Dockerfile放到同一个文件夹中,可以避免相对路径错误 ADD <host-src> <container-dest/> # 变量的定义,在下文中的可以使用$var引用变量的值,也可在docker build中使用`--build-arg var=xxx`进行指定 ARG var=xxx # 指定环境变量,可用于RUN运行 ENV <key> <value> / <key>=<value>, <key>=<value> ... # 用当前镜像的子镜像构建时会执行的指令 ONBUILD <commamd> # 默认使用的端口号,-p指定的会覆盖该端口号,一般给配置人员看 EXPOSE <端口号> # 无法被docker run后面的命令所覆盖 ENTRYPOINT ["executable","param1","param2", ...] # 容器启动后要运行的命令,有直接命令和命令-参数形式,会被docker run后面的命令替换 CMD <command> / ["executable","param1","param2", ...] # CMD和ENTRYPOINT可以连用,ENTRYPOINT后面加指令,CMD后面加命令参数
- scratch镜像(类似cpp抽象类)
- scratch镜像是一个空镜像,作为最小的基础镜像,可用于构建完全定制化的镜像。
- 不能通过pull命令拉取,也不能run,没有tag
- 只包含一个空的文件系统,镜像运行消耗资源非常低
- 作为保留关键字,用户不能使用scratch作为自己的镜像名称。
- 悬虚镜像
- 不如普通镜像有用,没有:标识
- 删除方式:docker rmi / docker system prune(删除四类) / docker image prune(仅删除一类)
- 构建新镜像的方式
- docker build
- docker commit
- docker import
- docker compose
- docker hub 中完成Automated Builds.
- 镜像构建示例及剖析
# 定义基础镜像层为ubuntu:14.04 FROM ubuntu:14.04 # 将当前目录下的run.sh文件复制到新构建的镜像的根目录下 ADD run.sh /# /*********************************/ # 以下两条指令的执行不会创建新镜像层,仅会修改镜像JSON文件中的相关元数据信息 # /*********************************/ # 在容器的/data目录下创建一个挂载点, # 任何写入该路径下的数据都会被保存宿主机目录,从而实现数据持久化 VOLUME /data # 设置容器默认的启动命令为执行根目录下的run.sh脚本 CMD [“./run.sh”]
指令详解
- 构建基本镜像:
FROM <image>:<tag>
- 指定基础镜像:是基础镜像的名称,是版本标签,默认为latest
- 环境变量支持:可以使用ARG指令预先声明的环境变量
- 默认行为:如果指定的基础镜像不存在于本地,Docker会尝试从Docker Hub或其他配置的注册表中拉取。
- 描述镜像的元数据信息:
LEABEL <key>="<value>"
- 参数信息通常使用
键值对
形式 - 提供额外的镜像描述信息,从而便于维护和筛选
LABEL email="example@example.com" \author="SampleApp" \version="1.2.3" \description="This is a sample Docker image."
- 参数信息通常使用
- 指定容器内的计算机目录为匿名数据卷:
VOLUME /container/path
- 在运行容器时使用
docker run -v <主机绝对路径>:<容器相对路径>
时,会比较<容器相对路径>
与Dockerfile中VOLUME指定的路径
,当:- 路径一致:宿主机目录和对应的容器目录会建立直接映射
- 路径不一致:两者各自独立挂载,VOLUME会创建一个匿名数据卷并挂载到宿主机默认docker管理目录下(通常/var/lib/docker/volumes/)
- 在运行容器时使用
- 设置容器内部工作目录:
WORKDIR /work/directory
- 设置WORKDIR后,Dockerfile中后续的命令都将相对于该路径执行
- 多次使用WORKDIR,可以基于上次目录继续生成相对目录
WORKDIR /app + WORKDIR data = /app/data
- 定义参数变量:
ARG KEY_NAME=value
- 变量名一般使用
大写和下划线
,例如BASE_IMAGE_VERSION - 变量的使用需要使用
$+变量名
- ARG多次定义的相同变量名,后面会覆盖前面的值
- 变量名一般使用
- 用于定义环境变量:
ENV VAR_NAME=value
- 在容器运行时,通过命令
docker run -e
覆盖dockerfile中的ENV,eg:docker run -e VAR_NAME=value
- 在容器运行时,通过命令
- 将
宿主机的源文件或目录
复制到容器目标路径
下:COPY <宿主机的源文件或目录> <容器的目标路径>
- 若要复制整个目录的内容,只需指定目录名即可,eg:
COPY host /docker/directory
- <宿主机的源文件或目录>:尽量将所要复制的内容放在当前目录下使用相对路径,避免绝对路径的更改
- <容器的目标路径>:可以是绝对路径(以斜杠/开始)或相对于WORKDIR的相对路径(优先使用)
- COPY复制文件时会保留源文件的元数据,包括权限、时间戳等
- 如果目标路径不存在,COPY会自动创建必要的目录结构。
- 复制多个源文件到单个目标时,目标必须是一个目录
- 与ADD指令相比,COPY不提供解压文件或从URL下载文件的功能
- 由于ADD可能会从URL下载文件,这增加了潜在的安全风险,比如下载恶意软件。而COPY仅处理本地文件,避免了网络相关的安全问题,使得构建过程更加安全可靠。
- 使用COPY时,每次复制的文件内容变化都会导致一个新的镜像层,但不会因为额外的处理(如解压)而引入不可预知的层。这有助于保持镜像的精简和可追踪性。
- 根据Docker官方文档的推荐,COPY是构建镜像时的首选指令,除非确实需要ADD的特殊功能,这表明在大多数场景下,COPY是更符合最佳实践的选择
- 若要复制整个目录的内容,只需指定目录名即可,eg:
- 将宿主机的源文件/目录/网络文件拷贝到容器目标路径下:
ADD <源文件/目录/网址> <容器的目标路径>
- 支持拷贝同时解压文件,支持从URL下载,支持通配符匹配。以上均存在安全性或不确定性,所以推荐使用COPY
- 在使用ADD时,要确保源文件或URL是可靠的,以避免潜在的安全风险。
- 用于构建镜像过程中切换指定用户:
USER <用户名UID>[:<用户组GID>]
- 确保应用程序仅拥有执行其任务所需的最小权限,遵循最小权限原则
- 避免以root用户运行容器,减少潜在的安全风险
# 创建并切换到用户态进行执行 FROM ubuntu:latest RUN useradd -ms /bin/bash myuser USER myuser # 创建并切换到指定组的用户进行执行 FROM python:3.8-slim RUN useradd -r -u 1000 -G users appuser USER 1000:users
- 定义容器健康状态的检查配置:
HEALTHCHECK <OPTIONS> CMD <command>
FROM nginx:latest # check_health.py是一个Python脚本,用于检查应用的健康状态 # 每5分钟执行一次健康检查,命令执行时间不超过3s,连续5次执行失败则标记为不健康 HEALTHCHECK --interval=5m \--timeout=3s \--retries=5 \ CMD /app/check_health.py
- 设置容器启动时默认执行的命令:
CMD ["executable", "param1", "param..."]
- 覆盖性:docker run在启动容器时指定命令可以覆盖CMD指定的命令
- 唯一性:Dockerfile中的多个 CMD 指令,但只有最后一个CMD命令会生效,因为默认行为是唯一的
- 设置容器启动时默认执行的命令:
ENTRYPOINT ["executable", "param1", "param..."] (最有通用性)
- 仅可自定义参数:docker run无法覆盖ENTRYPOINT指定的启动命令,只能添加参数。
- 用户可以通过 docker run --entrypoint 选项来指定新的命令来替代原有的 ENTRYPOINT,但不常用
五、数据持久化
简介
- 可写层与容器生命周期绑定,若容器被删除或重启,将会丢失数据。常见的数据持久化方式:
- 实时同步方式
- volumes(数据卷)
- Bind mounts(绑定挂载)
- tmpfs(临时文件系统):动态存储在内存上,提供高效的实时同步,但是无法避免系统重启带来的风险
- 非实时同步方式
- 定制镜像: 将这个修改过的容器复制生成一个新的镜像,但是无法实现实时同步
- 实时同步方式
- 什么是 Docker 数据卷(Docker volumes)?
- Docker 数据卷是一种持久化数据的机制,它允许在容器和宿主机之间创建一个独立于容器生命周期的存储区域。数据卷是一个特殊的目录,存在于容器内部,但直接映射到宿主机的文件系统上。这样设计的主要目的是为了数据的持久化和共享,确保即使容器被删除或重启,数据仍然存在且可访问
- 数据持久化:数据卷属于宿主机文件系统,独立于容器,所以容器的删除不会影响数据卷中的数据
- 即时同步:通过
宿主机数据卷(文件/目录)
映射到容器挂载点
,从而实现目录修改内容的即时同步 - 共享与重用:多个容器可以挂载同一个数据卷,实现数据共享
- 镜像解耦:数据卷中的数据不会因为镜像的更新而丢失或被覆盖
- Docker 数据卷是一种持久化数据的机制,它允许在容器和宿主机之间创建一个独立于容器生命周期的存储区域。数据卷是一个特殊的目录,存在于容器内部,但直接映射到宿主机的文件系统上。这样设计的主要目的是为了数据的持久化和共享,确保即使容器被删除或重启,数据仍然存在且可访问
- 什么是主机目录绑定挂载(bind mounts)?
- 主机目录绑定(bind mounts)是将宿主机上的一个目录或文件直接映射到容器内的一个路径。这种绑定挂载允许容器直接访问宿主机的文件系统路径,提供了更直接的共享方式。bind mounts的特点包括:
- 直接宿主机路径:bind mounts可以使用宿主机的绝对路径,访问其目录或文件
- 非移植性:由于bind mounts依赖于宿主机的特定路径,这可能影响容器的可移植性。
- bind mounts则更简单直接,适合快速共享或临时数据共享场景。
- 主机目录绑定(bind mounts)是将宿主机上的一个目录或文件直接映射到容器内的一个路径。这种绑定挂载允许容器直接访问宿主机的文件系统路径,提供了更直接的共享方式。bind mounts的特点包括:
数据卷相关命令
- 数据卷的创建:
docker volume create <volume_name>
- 数据卷的挂载
docker run -it -v </host/path>:</container/path> <image_name>
docker run -it -v </host/path>:</container/path>:ro <image_name>
:挂载点只读,即容器不可修改- 数据卷与挂载点实时共享,与容器是否启动无关
- 查看系统中所有的数据卷:
docker volume ls
- 删除数据卷(不可逆操作):
docker volume rm <volume_name>
六、Docker网络通信
简介
docker exec <container-name> ip a
:查看容器的ip地址- 同一宿主机的容器间,容器和宿主机间以及容器和宿主机外联的互联网间都可以进行通信
- Docker网络的底层原理是Linux的Network Namespace,对Namespace的理解很重要!
- Network Namespace是Linux内核提供的功能,用于实现网络虚拟化,它可以创建多个隔离的网络空间。每个网络命名空间都是独立的,具有自己的防火墙、网卡、路由表、邻居表和协议栈。无论是虚拟机还是容器,当它们运行在独立的命名空间时,就像是独立的主机一样,彼此之间的网络环境互不影响。
- 两个网络命名空间之间的通信,实质就是两个容器间的通信
- 创建虚拟网卡+绑定IP+设置为通信状态+
- 创建虚拟网卡+绑定IP+设置为通信状态+
ip link add veth-ns1 type veth peer name veth-ns2
: 创建一对相互关联的虚拟网卡- 虚拟网卡绑定网络命名空间
ip link set veth-ns1 netns ns1
ip link set veth-ns1 netns ns1
ip netns exec ns1 ip a
:查看ns1的网卡配置信息- 在命名空间中给虚拟网卡绑定ip地址
ip netns exec ns1 ip addr add 192.168.1.1/24 dev veth-ns1
ip netns exec ns1 ip addr add 192.168.1.1/24 dev veth-ns2
- 启动虚拟网卡
ip netns exec ns1 ip link set dev veth-ns1 up
ip netns exec ns2 ip link set dev veth-ns2 up
CNM
- Docker网络框架由三个主要部分组成:CNM、Libnetwork和Driver组成
- 定义:CNM,即Container Network Model,是Docker容器网络的核心设计规范,它定义了一套标准化的框架来管理容器的网络连接。以下是CNM模型的关键要素及其详细说明
- 沙盒(Sandbox):
- 沙盒代表一个独立的网络栈,为每个容器提供隔离的网络环境。
- 它包含一系列网络配置,如以太网接口、端口配置、路由表条目以及DNS设置。
- Linux Network Namespace是实现沙盒隔离的标准方式,确保每个容器的网络资源独立。
- 终端(Endpoint):
- 终端是虚拟的网络接口,负责建立沙盒与特定网络之间的连接。6
- 每个终端连接到一个特定的网络,是容器与网络交互的接口。
- 一个终端仅能连接到一个网络,但一个沙盒可以有多个终端,从而连接到不同的网络。
- 网络(Network):
- 在CNM模型中,网络是逻辑上的集合,它定义了终端如何交互,可以理解为一组终端共享的通信平面。
- 实际上,它通常通过软件实现的802.1d网桥来模拟,允许容器间的通信就像它们连接在同一个物理交换机上一样。
- 网络可以配置特定的IP地址范围、子网、网关等,为连接到该网络的终端提供一致的网络环境。
- 沙盒(Sandbox):
- 一个主机可以有多个网卡,并且每个网卡都可以配备一个IP地址
- Docker引擎调用API发出请求,然后由Libnetwork做出框架性处理,然后将请求转发给相应的驱动Driver
- 在Linux主机上,Docker的bridge网络是通过Bridge驱动创建的,它使用了Linux Bridge技术。在创建时会自动创建一个默认的网桥docker0。容器与网桥之间使用veth pair技术进行连接,网桥与外网之间使用网络地址转换NAT技术进行连接,实现将内网地址转换为外网地址的功能。
Docker网络相关指令
- 创建Docker网络
docker network create -d bridge <bridge-name>
:默认不指定则网卡类型仍然为bridgedocker run --name <container-name> --network <bridge-name> busybox
:给网桥添加容器网络接口docker inspect <container-name>
:查看容器配置docker exec <container-name> ip a
:查看容器的ip地址docker exec <container1> ping <container2>
:同一个bridge网络中的可以使用容器名称代替IP地址docker run -d --name <container-name> --network host
:指定创建容器为host网络
- 容器的网络类型
- none网络:容器仍然属于一个独立的Namespace,但没有网络接口和IP地址
- host网络:与宿主机host共用一个Network Namespace,没有独立的IP,与host共用
🚩点此跳转到首行↩︎
参考博客
- Docker系列五——Docker容器隔离原理及网络通信
- 10张图带你深入理解Docker容器和镜像
- Docker——镜像原理之联合文件系统和分层理解
- Docker中镜像层的diffID如何计算?
- docker imageid 和 digest区别
- 联合文件挂载——待参考完善
- Docker原理——待参考完善
- 什么是容器技术
- 数据卷——待参考完善