暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

一文读懂容器技术

老柴杂货铺 2025-03-31
152

传统进程管理的困境

当我们登录到操作系统后,通过 ps 等命令,能看到各式各样的进程,涵盖系统自带服务与用户应用进程。这些进程具有三个显著特点:相互可见并能通信、共享同一文件系统、共用相同系统资源。

然而,这些特点却引发了诸多问题。高级权限进程可利用相互可见性攻击其他进程;同一文件系统下,进程间数据易被修改、删除,依赖冲突也给运维带来极大压力;系统资源被共享使用,应用间常出现资源抢占,影响其他应用正常运行。例如,当一个资源密集型应用运行时,可能导致其他应用因资源不足而无法提供服务。

为解决这些问题,一种全新的进程运行环境应运而生,那就是容器。

容器:进程的理想家园

隔离与独立

为解决进程使用同一文件系统的问题,Linux 和 Unix 操作系统借助 chroot 系统调用,将子目录转变为根目录,实现视图级别的隔离,使进程拥有独立文件系统,其对文件系统的操作不会影响其他进程。

同时,利用 Namespace 技术,实现进程在资源视图上的隔离,让进程彼此不可见。再通过 Cgroup 限制进程资源使用率,如设置 CPU 和内存使用量,避免进程过度消耗资源。

容器,就是这样一个具备视图隔离、资源可限制、拥有独立文件系统的进程集合。它宛如一个独立的小世界,将系统其他资源隔离开来,拥有自己独特的资源视图。

容器的文件系统

容器拥有独立文件系统,因其使用系统资源,所以在该文件系统内无需内核相关代码或工具,只需具备运行所需的二进制文件、配置文件及依赖即可运行。

镜像:容器的灵魂伴侣

什么是镜像

我们将容器运行时所需的所有文件集合,称作容器镜像。它就像是容器的蓝图,包含了容器运行所需的一切。

镜像的构建

通常,我们采用 Dockerfile 来构建镜像。Dockerfile 提供了便捷的语法糖,能清晰描述构建的每个步骤。每个构建步骤都会对文件系统进行操作,产生的变化称为 changeset。

将构建步骤产生的变化依次作用于空文件夹,就能得到完整镜像。changeset 的分层与复用特性带来诸多优势:提高分发效率,大镜像拆分后可并行下载;减少数据下载量,本地已有部分数据时,只需下载缺失部分;节约磁盘空间,以 golang 镜像基于 alpine 镜像构建为例,复用后可节省大量空间。

以下是一个构建 golang 应用的 Dockerfile 示例:

    # 指定构建基于的镜像,体现镜像复用性
    FROM golang:1.12-alpine
    # 指定后续构建步骤所在目录,类似 Shell 中的 cd 命令
    WORKDIR /go/src/app
    # 将宿主机文件拷贝到容器镜像内
    COPY . .
    # 在文件系统内执行相应操作,完成应用构建与安装
    RUN go get -d -v./...
    #build the application and install it
    RUN go install -v./...
    # 指定使用镜像时的默认程序
    CMD ["app"]

    有了 Dockerfile 后,使用 docker build 命令即可构建应用,构建结果存储在本地。

    镜像的存储与使用

    构建好的镜像需存储与管理,docker registry(镜像仓库)应运而生,它负责存储所有镜像数据。通过 docker push 可将本地镜像推送到镜像仓库,在生产或测试环境中,就能从仓库下载镜像并运行。

    容器的运行之旅

    运行容器一般分为三步:

    从镜像仓库下载相应镜像。

    用 docker images 查看本地镜像列表,从中选择所需镜像。

    使用 docker run 运行选中镜像,得到容器,可多次运行同一镜像获得多个容器。镜像如同模板,容器则是具体运行实例,这体现了镜像一次构建、到处运行的特点。

    容器的生命周期

    运行时生命周期

    容器由一组具有隔离特性的进程集合组成。使用 docker run 时,选择镜像提供独立文件系统并指定运行程序(initial 进程)。initial 进程启动,容器随之启动;initial 进程退出,容器也退出,二者生命周期一致。

    但容器内不止 initial 进程,它还可产生子进程,或通过 docker exec 进行运维操作,这些都由 initial 进程管理。initial 进程退出时,所有子进程随之退出,防止资源泄漏。

    数据持久化

    应用程序往往有状态,会产生重要数据。容器退出删除后数据易丢失,为解决此问题,引入数据卷。数据卷是特殊目录,用于容器数据持久化,其生命周期独立于容器。将数据卷挂载到容器内,容器可将数据写入,容器退出数据也不会丢失。

    数据卷管理主要有两种方式:

    bind 方式:直接将宿主机目录挂载到容器内,简单但依赖宿主机目录,运维成本高。

    目录管理交给运行引擎。

    容器项目架构

    moby 容器引擎架构

    moby 是目前最流行的容器管理引擎,moby daemon 负责容器、镜像、网络及 Volume 的管理。其依赖的重要组件是 containerd,containerd 是容器运行时管理引擎,独立于 moby daemon,提供容器和镜像相关管理。

    containerd 底层的 containerd shim 模块类似守护进程,设计原因如下:

    管理容器生命周期,为不同容器运行时提供灵活插件化管理,shim 针对不同运行时开发,以插件形式管理。

    实现动态接管,防止 moby daemon 或 containerd daemon 意外退出时容器无人管理而消失,影响应用运行。

    支持 moby 或 containerd 原地升级,不影响业务。

    容器 VS VM

    差异对比

    VM 利用 Hypervisor 虚拟化技术模拟 CPU、内存等硬件资源,在宿主机上建立 Guest OS,每个 Guest OS 有独立内核,如 Ubuntu、CentOS、Windows 等,应用在 Guest OS 下相互独立,隔离效果好。但需占用大量计算资源和磁盘空间,启动慢,如 Windows 安装需 10 - 30G 磁盘空间,Ubuntu 需 5 - 6G。

    容器针对进程,无需 Guest OS,仅需独立文件系统提供文件集合,文件隔离为进程级别,启动快,所需磁盘空间小。不过,进程级隔离效果相对 VM 较差。

    总体而言,容器和 VM 各有优劣,目前容器技术正朝着强隔离方向发展。

    总结

    容器作为一个独特的进程集合,拥有独立的视图视角;镜像为容器提供所需的全部文件集合,具备一次构建、到处运行的特性;容器的生命周期与 initial 进程紧密相连;与 VM 相比,容器在某些方面具有优势,同时也在不断发展完善。

    随着技术的持续进步,容器技术必将在未来的数字化世界中发挥更为重要的作用,为我们带来更加高效、便捷的应用部署与运行体验。

    文章转载自老柴杂货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论