容器相关的技术

艺帆风顺 发布于 2025-04-02 14 次阅读


一、容器镜像

镜像是容器运行的基础,容器引擎服务可使用不同的镜像启动相应的容器。在容器出现错误后,能迅速通过删除容器、启动新的容器来恢复服务,这就需要以容器镜像作为支撑技术。

1.1、镜像简介

镜像是由按层封装好的文件系统和描述镜像的元数据构成的文件系统包,包含应用所需要的系统、环境、配置和应用本身等。镜像由开发者构建好之后上传至镜像仓库,使用者获取镜像之后就可以使用镜像直接构建自己的应用。

与虚拟机所用的系统镜像不同,容器镜像不仅没有Linux系统内核,同时在格式上也有很大的区别。虚拟机镜像是将一个完整系统封装成一个镜像文件,而容器镜像不是一个文件,而是分层存储的文件系统。

1.2、镜像原理

分层存储

分层存储是容器镜像的主要特点之一,每个镜像都是由一系列的“镜像层”组成。当需要修改镜像内的某个文件时,只会对最上方的读写层进行改动,不会覆盖下层已有文件系统的内容。当提交这个修改生成新的镜像时,保存的内容仅为最上层可读写文件系统中被更新过的文件,这样就实现了在不同的容器镜像间共享镜像层的效果。

写时复制

容器镜像使用了写时复制的策略,在多个容器之间共享镜像,每个容器在启动的时候并不需要单独复制一份镜像文件,而是将所有镜像层以只读的方式挂载到一个挂载点,再在上面覆盖一个可读写的容器层。在未更改文件内容时,所有容器共享同一份数据,只有在容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。写时复制配合分层机制减少了镜像对磁盘空间的占用和容器启动时间。

内容寻址

内容寻址存储机制,是根据文件的内容来索引镜像和镜像层,Docker-1.10版本后引入了这个机制。对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值作为镜像层的唯一标识。该机制提高了镜像的安全性,并在pull、push、load和save操作后检测数据的完整性。

1.3、镜像存储

镜像仓库是镜像存储的位置,也是用来获得镜像的重要渠道之一,镜像仓库根据用途不同分为公共仓库和私有仓库。

公共仓库

公共仓库是面向整个互联网用户的仓库,典型的代表为DockerHub。大部分常用应用的镜像,都可以通过在DockerHub中直接下载使用。另外,用户也可以将自己创建好的镜像上传至公共仓库供其它的用户使用,软件厂商也可以以镜像形式发布自己的软件。

私有仓库

不是所有的镜像都适合在互联网进行发布共享,有时候有些含有敏感信息的镜像只能在团队内部共享使用。因此,考虑到镜像的敏感性与网络的可用性和稳定性等因素,出现了私有仓库的使用场景,或者可以称之为本地仓库。私有仓库是在一定范围内可访问的镜像仓库,典型的私有仓库实现代表为Harbor。

二、容器存储

2.1、镜像元数据

在Linux系统中Docker的数据默认存放在/var/lib/docker中,基于不同的系统又有不同的存储驱动、不同的目录结构,常用的标准格式是OCI。

镜像每一层的ID是该文件内容的哈希校验值,作为该层的唯一标识。获取镜像后,会使用以下方式索引镜像:首先读取镜像的manifests,根据manifests文件中config的sha256码,得到镜像config文件,遍历manifests里面的所有layer,根据其sha256码在本地查找,拼出完整的镜像。

2.2、存储驱动

理想情况下,使用挂载卷来存储高读写的目录,很少将数据直接写入容器的可写层。但是,总有些特殊需求需要直接写入容器的可写层。这时候就需要存储驱动来作为容器和宿主机之间的媒介。Docker依靠驱动技术来管理镜像和运行它们的容器间的存储和交互。

目前,Docker主要支持AUFS、BtrFS、Device Mapper、OverlayFS、ZFS五种存储驱动。没有单一的存储驱动适合所有的应用场景,要根据不同的场景选择合适的存储驱动,才能有效提高Docker的性能。

2.3、数据卷

通常,有状态的容器都有数据持久化存储的需求。文件系统的改动都是发生在最上面的可读写层,在容器的生命周期内它是持续的,包括容器被停止后。但是,当容器被删除后,该数据层也就随之被删除了。

因此,Docker 采用数据卷(Volume)的形式向容器提供持久化存储。数据卷是Docker容器数据持久化存储的首选机制,绑定挂载依赖于主机的目录结构,但数据卷是由Docker管理。

与绑定挂载相比,数据卷有以下几个优点:

● 与绑定挂载相比,数据卷更容易备份或迁移;
● 可以使用Docker CLI命令或Docker API管理数据卷;
● 数据卷在Linux和Windows上均可使用;
● 数据卷可以在多个容器之间更安全地共享;
● 数据卷驱动程序允许在远程主机或云上存储数据卷、加密卷的内容或添加其它功能;
● 新数据卷的内容可以由容器预填充。

另外,与使用容器的读写层保存数据相比,数据卷通常是更好的选择。因为使用数据卷存储不会增加容器的大小,并且数据卷是持久化的,不会依赖于容器的生命周期。

三、容器网络

3.1、容器网络支撑技术

容器网络虽然有多种形态,但多数使用了若干种支撑技术,例如网络命名空间、Linux网桥以及虚拟网络接口对。

网络命名空间

网络命名空间是一种实现网络隔离的技术,创建一个网络命名空间后就有一个包括网络接口、路由、访问控制规则(Iptables)等网络资源的独立网络环境,该命名空间的网络与其它网络隔离。

Linux网桥

Linux网桥是Linux系统中的虚拟网桥,它可以将不同主机的网络接口连接,从而实现主机间的通信。Docker启动后,会默认创建名为docker0的Linux网桥。在未创建任何容器时,可以看到docker0网桥没有接口连接。当创建容器时,Docker会为容器创建虚拟网络接口,并将其连接到docker0网桥上。

虚拟网络接口对

为了实现容器与宿主机网络、外部网络之间的通信,需要通过虚拟网络接口对容器与Linux网桥连接。当Docker启动一个容器时,会创建一个虚拟网络接口对,即两个相连的虚拟网络接口。其中一个连接容器,成为容器的网卡eth0;另一个被连接到docker0网桥上,从而容器内部的数据包先后经过eth0和另一网卡到达docker0网桥,实现在同一子网内不同容器之间的通信。

3.2、主机网络

None网络模式

None网络模式下,容器拥有自己的网络命名空间,但是并不为容器进行任何的网络配置。创建的容器只有loopback接口,需要用户为容器添加网卡、配置IP等。

Bridge网络模式

Bridge(网桥)网络模式主要是利用Iptables进行NAT和端口映射,从而提供单主机网络。与虚拟机下的NAT网络类似,这种网络模式下同一主机上的容器之间是可以互相通信的,但是分配给每个容器的IP地址从主机外部不能访问。

Bridge网络是Docker默认的网络类型,在安装完Docker后会默认创建docker0网桥,并通过虚拟网络接口对连接容器和docker0网桥,这样主机上的所有容器就处在了一个二层网络中。

Host网络模式

Host网络模式下,Docker服务启动容器时并不会为容器创建一个隔离的网络环境,容器将会被加入主机所在网络,共享主机的网络命名空间(/var/run/docker/netns/default)。其网络配置(网络地址、路由表和Iptables等)和主机保持一致,容器通过主机的网卡和IP,实现与外部的通信。

由于容器并没有独立的网络命名空间,容器服务的端口没有经过端口映射就直接暴露在主机上,因此容器中服务的端口号不能与主机上已经使用的端口号冲突。以这种网络模式创建的容器虽然可以访问主机的所有网络接口,但除非在特权模式下部署,否则容器可能不会重新配置主机的网络堆栈。

Container网络模式

Container(容器)网络模式比较特殊,新创建的容器和已经存在的某个容器共享同一个命名空间。该新容器不会创建自己的网卡或配置自己的IP,而是和一个指定容器共享IP、端口范围等。这两个容器只有网络方面共享数据,文件系统、进程列表等,其它方面还是隔离的。两个容器的进程可以通过loopback网卡通信。

3.3、集群网络

我们以Kubernetes为例来具体阐述容器集群中的网络实现。

Kubernetes设计了Pod对象,对应某特定应用中的逻辑主机,将每个服务按任务拆分,分别将相应进程包装到相应的Pod中。一个Pod中包含一个或多个相关的容器,这些容器都会运行在同一个主机中,并且共享相同的网络命名空间和相同的Linux协议栈。

一个Kubernetes集群通常会涉及到以下三种通信:

1、同一个Pod内,容器和容器之间的通信;
2、同一个主机内不同Pod之间的通信;
3、跨主机Pod之间通信。

同一Pod内容器之间的通信,由于其共享网络命名空间、共享Linux协议栈,因此它们之间的通信是最简单的,这些容器好像是运行在同一台机器上,直接使用Linux本地的IPC进行通信,它们之间的互相访问只需要使用localhost加端口号就可以。

同一个主机上不同的Pod通过veth连接在同一个docker0网桥上,每个Pod从docker0动态获取IP地址,该IP地址和docker0的IP地址是处于同一网段的。这些Pod的默认路由都是docker0的IP地址,所有非本地的网络数据都会默认开到docker0网桥上,由docker0网桥直接转发,相当于一个本地的二层网络。

跨主机的Pod之间通信较复杂,每个Pod的地址和其所在主机的docker0在同一个网段,而docker0和主机的物理网络是不同的网段,如何保证Pod内的网络数据能够通过物理网络,找到对端Pod所在物理主机地址,并且完成数据传输是通信成功的关键。

因此在Kubernetes的网络中,需要有一个全局网络地址规划的模块,当Pod1向Pod2发送数据时,能够知道目的Pod2所在主机的IP地址。这样数据从Pod1发出,经Host1的docker0网桥路由到Host1的物理网卡eth0,然后通过物理网络到达Host2的物理网卡eth0、docker0网桥,进而送达Pod2。

四、容器运行时

容器运行时负责管理容器运行的整个生命周期,包括但不限于指定容器镜像格式、构建镜像、上传和拉取镜像、管理镜像、管理容器实例、运行容器等。

4.1、容器运行时结构

针对云原生容器运行时结构,可以简单汇总为三层抽象,由左至右分别为编排API层、容器API层、内核API层,每一层均有着不同的实现方式,其中Kubernetes为实现编排API的标准,在Kubernetes中,CRI标准运行时实现了容器API层,而OCI标准运行时实现了内核API层,因而若要在Kubernetes中启动一个容器,需要使用CRI标准运行时启动OCI标准运行时实现。

4.2、容器运行时分类

OCI层面的容器运行时主要是:Kata Containers、gVisor等;
CRI层面的容器运行时主要是:Docker、Containerd等。

    版权声明:本文内容来自CSDN:武天旭,遵循CC 4.0 BY-SA版权协议上原文接及本声明。本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行可。原文链接:https://blog.csdn.net/wutianxu123/article/details/127914241?spm=1001.2014.3001.5501如有涉及到侵权,请联系,将立即予以删除处理。在此特别鸣谢原作者的创作。此篇文章的所有版权归原作者所有,与本公众号无关,商业转载建议请联系原作者,非商业转载请注明出处。