WHAT - 容器化系列(一)
这里写目录标题
- 一、什么是容器与虚拟机
- 1.1 什么是容器
- 1.2 容器的特点
- 1.3 容器和虚拟机的区别
- 虚拟机(VM):基于硬件的资源隔离技术
- 容器:基于操作系统的资源隔离技术
- 对比总结
- 应用场景
- 二、容器的实现原理
- 1. Namespace(命名空间)
- 2. Control Groups(Cgroups)
- 3. 文件系统隔离
- 4. 容器镜像
- 5. 容器运行时
- 6. 安全性
- 三、容器实现流程概述
- 四、容器发展历史
- 1. Chroot(1979)
- 2. FreeBSD Jail(2000)
- 3. Solaris Containers(2004)
- 4. cgroups和Namespaces(2006-2008)
- 5. LXC(Linux Containers,2008)
- 6. Docker(2013)
- 7. rkt(2014)
- 8. Kubernetes(2014):k8s
- 9. OCI(Open Container Initiative,2015)
- 10. containerd和CRI-O(2016)
- 11. Podman和Buildah(2018)
- 介绍
- 无守护进程:不依赖于 daemon
- 示例:Podman
- 12. eBPF和Cilium(2019)
- 总结
- 五、学习资料参考
一、什么是容器与虚拟机
1.1 什么是容器
在软件开发和运维中,容器是一种用于打包和运行应用程序及其依赖项的轻量级、可移植的环境。容器通过虚拟化操作系统的方式来实现资源的隔离和控制,从而允许多个容器在同一主机上运行而不会相互干扰。
1.2 容器的特点
- 轻量级:容器共享主机操作系统的内核,启动速度快,占用资源少。
- 可移植性和一致性:容器打包了应用程序及其依赖项,可以在任何支持容器的环境中运行,确保应用程序在不同环境下的一致性。
- 隔离性:每个容器都有自己的文件系统、网络接口和进程空间,确保应用程序之间互不干扰。
- 易于管理:容器技术(如Docker)提供了强大的工具链,用于创建、分发、管理和运行容器。
1.3 容器和虚拟机的区别
虚拟机(VM):基于硬件的资源隔离技术
- 架构:虚拟机通过在硬件之上运行一个完整的操作系统(Guest OS),并在Guest OS上运行应用程序。每个虚拟机有独立的内核和硬件虚拟化层。
- 资源消耗:由于每个虚拟机需要运行一个完整的操作系统,因此启动时间长,占用的内存和存储资源更多。
- 隔离性:虚拟机提供了高度的隔离性,几乎完全隔离了不同虚拟机之间的环境。
容器:基于操作系统的资源隔离技术
- 架构:容器通过共享主机操作系统的内核,实现应用程序及其依赖项的隔离。容器直接运行在宿主操作系统之上。
- 资源消耗:容器不需要完整的操作系统,只需要包含应用程序及其运行时环境,启动速度快,占用资源少。
- 隔离性:容器的隔离性基于操作系统级别,虽然隔离程度不如虚拟机,但足够满足大多数应用场景的需求。
对比总结
特性 | 容器 | 虚拟机 |
---|---|---|
启动速度 | 快(秒级) | 慢(分钟级) |
资源消耗 | 低 | 高 |
操作系统 | 共享主机OS内核 | 每个虚拟机有自己的完整OS |
隔离性 | 基于操作系统级别的隔离 | 基于硬件虚拟化的强隔离 |
管理工具 | Docker、Kubernetes等 | VMware、Hyper-V、KVM等 |
适用场景 | 微服务架构、DevOps、CI/CD | 需要严格隔离的多租户环境 |
应用场景
- 容器:适用于需要快速启动、快速扩展、资源利用率高的场景,如微服务架构、持续集成/持续部署(CI/CD)、开发和测试环境。
- 虚拟机:适用于需要严格隔离、多操作系统支持的场景,如多租户环境、需要完整操作系统功能的应用程序。
总的来说,容器和虚拟机各有优劣,选择使用哪种技术取决于具体的应用需求和场景。容器技术因其轻量级和高效性,正在越来越多的开发和运维中得到广泛应用。
二、容器的实现原理
容器的实现原理基于操作系统级别的虚拟化技术,通过一系列内核功能来实现应用程序及其依赖项的隔离、控制和管理。
容器是基于内核的 Control Groups 和 Namespace 技术实现的资源隔离技术。
以下是容器实现的关键技术和原理:
1. Namespace(命名空间)
命名空间是Linux内核中的一种特性,用于隔离全局系统资源。不同的命名空间类型包括:
- PID Namespace:隔离进程ID,使得容器内的进程ID从1开始,独立于主机上的其他进程。
- NET Namespace:隔离网络资源,包括网络接口、IP地址、路由表等。
- IPC Namespace:隔离进程间通信资源,如信号量、消息队列、共享内存等。
- MNT Namespace:隔离挂载点,使得每个容器可以拥有独立的文件系统视图。
- UTS Namespace:隔离主机名和域名,使得容器内可以拥有独立的主机名。
- USER Namespace:隔离用户和用户组ID,使得容器内的用户ID可以与主机上的用户ID不同。
2. Control Groups(Cgroups)
控制组(cgroups)是Linux内核提供的一种功能,用于限制、记录和隔离进程组(容器)的资源使用。
cgroups可以控制的资源包括:
- CPU:限制进程使用的CPU时间。
- 内存:限制进程使用的内存量。
- I/O:限制进程的I/O操作。
- 网络带宽:限制进程使用的网络带宽。
通过cgroups,管理员可以确保容器之间的资源互不干扰,并可以对资源使用进行精细化控制。
3. 文件系统隔离
容器通过使用Union File Systems联合文件系统(如OverlayFS)来实现文件系统的隔离和共享。
OverlayFS:一种联合文件系统,允许多个文件系统层次叠加,形成一个单一的文件系统视图。容器可以在只读层之上创建写层,实现高效的文件系统操作。
4. 容器镜像
容器镜像是一个只读模板,包含应用程序及其依赖项。镜像由多个层次组成,每一层都代表了镜像的某个历史状态。镜像层可以共享,避免了冗余存储,提高了存储利用率。
5. 容器运行时
容器运行时是负责管理容器生命周期的组件,如创建、启动、停止和删除容器。
常见的容器运行时包括:
- Docker Engine:最流行的容器运行时,实现了Docker容器的管理和运行。
- containerd:一个高效的容器运行时,由Docker孵化,现已成为CNCF(Cloud Native Computing Foundation)项目。
- CRI-O:针对Kubernetes设计的轻量级容器运行时,实现了Kubernetes CRI(Container Runtime Interface)。
6. 安全性
容器的安全性通过多种机制实现:
- AppArmor/SELinux:安全增强Linux模块,通过强制访问控制(MAC)策略来限制容器内进程的权限。
- Seccomp:安全计算模式,限制容器内进程可以调用的系统调用。
- Capabilities:细粒度的权限管理,限制容器内进程可以执行的特权操作。
通过这些技术,容器能够提供高效、轻量级的虚拟化环境,使得应用程序可以在不同的环境中一致地运行。
三、容器实现流程概述
- 创建命名空间:为新容器创建一组独立的命名空间,隔离进程ID、网络、文件系统等。
- 分配资源:通过cgroups分配CPU、内存、I/O等资源限制。
- 挂载文件系统:使用OverlayFS等技术挂载容器文件系统。
- 应用安全策略:配置AppArmor、SELinux、Seccomp等安全策略。
- 启动进程:在隔离的环境中启动应用程序进程。
四、容器发展历史
容器技术的发展历史可以追溯到几十年前,从早期的操作系统级虚拟化概念到现代的容器技术,经历了多个重要阶段。
以下是容器发展历史的关键里程碑:
1. Chroot(1979)
- 概念:Chroot命令最早在Unix操作系统中引入,通过改变根目录,使得应用程序在隔离的文件系统视图中运行。
- 意义:这是操作系统级虚拟化的初步尝试,虽然功能简单,但为后来的容器技术奠定了基础。
2. FreeBSD Jail(2000)
- 概念:FreeBSD引入了Jail机制,提供了更高级的进程和文件系统隔离,以及网络堆栈的隔离。
- 意义:Jail在Chroot的基础上增强了隔离性和安全性,是容器技术的重要里程碑。
3. Solaris Containers(2004)
- 概念:Sun Microsystems在Solaris 10中引入了Containers(又称Zones),实现了进程、网络和文件系统的全面隔离。
- 意义:Solaris Containers提供了完善的资源管理和隔离机制,对现代容器技术的发展具有重要影响。
4. cgroups和Namespaces(2006-2008)
- 概念:Google工程师开发了cgroups(Control Groups)并合并到Linux内核中,用于限制、记录和隔离进程资源。与此同时,Linux内核也引入了Namespaces,实现了不同系统资源的隔离。
- 意义:cgroups和Namespaces是现代容器技术的核心组件,为容器提供了基础设施支持。
5. LXC(Linux Containers,2008)
- 概念:LXC是第一个基于cgroups和Namespaces实现的容器管理工具,提供了轻量级的操作系统级虚拟化。
- 意义:LXC标志着现代容器技术的初步成型,但其用户体验和管理工具较为复杂。
6. Docker(2013)
随着 2013 年 PaaS 平台的兴起,利用 cgroups 和 Namespaces 技术实现了容器隔离,但仍然需要维护和管理本地环境和 PaaS 环境的差异。Docker 的出现从根本上解决本地环境和 PaaS 环境不一致的问题。
2013 年,Docker 项目诞生。2013-2014 年,Docker 团队发布容器集群管理项目 Swarm。2014-2015 年,Docker 团队收购了 Fig,之后命名为 docker-compose。
- 概念:Docker由Solomon Hykes和他的团队开发,基于LXC但提供了更易用的界面和工具链,包括Docker Engine、Docker Hub和Docker Compose等。
- 意义:Docker大大简化了容器的创建、管理和分发,使得容器技术迅速普及,并引发了DevOps和微服务架构的革命。
7. rkt(2014)
- 概念:由CoreOS开发,rkt(读作“rocket”)是一种安全性更高的容器运行时,注重容器的可移植性和标准化。
- 意义:rkt为容器生态系统提供了更多选择,促进了容器标准化的进程。
8. Kubernetes(2014):k8s
Google 和 Red Hat 牵头发起 CNCF 基金会,并以 Kubernetes 项目为基础,对抗以 Docker 公司为核心的容器商业生态。
- 概念:由Google开源的容器编排系统,Kubernetes自动化了容器的部署、扩展和管理,成为容器编排的事实标准。
- 意义:Kubernetes解决了大规模容器集群管理的难题,推动了容器技术在生产环境中的应用。
9. OCI(Open Container Initiative,2015)
- 概念:由Docker和其他行业领袖共同发起,OCI致力于制定容器格式和运行时的开放标准。
- 意义:OCI促进了容器技术的标准化,确保了不同容器运行时的兼容性和互操作性。
2015-2016 年可以说是 docker Swarm、Apache Mesos、Google Kubernetes 容器编排“三国争霸”的时代。
10. containerd和CRI-O(2016)
- 概念:containerd由Docker孵化,成为一个高效的容器运行时;CRI-O则是针对Kubernetes设计的轻量级容器运行时。
- 意义:containerd和CRI-O增强了容器运行时的灵活性和性能,进一步优化了容器生态系统。
11. Podman和Buildah(2018)
介绍
- 概念:由Red Hat开发,Podman是一种无守护进程的容器引擎,Buildah用于构建OCI兼容的容器镜像。
- 意义:Podman和Buildah提供了无Docker守护进程的容器管理工具,增强了容器技术的安全性和灵活性。
无守护进程:不依赖于 daemon
“无守护进程”(rootless 或 daemonless)指的是一种不依赖于守护进程(daemon)来管理和运行的容器技术。为了更好地理解这个概念,我们先来了解一下守护进程和容器引擎的传统运作方式。
守护进程和传统容器引擎
在传统的容器引擎(例如Docker)中,有一个名为“守护进程”的后台进程(通常称为dockerd
),负责管理和运行容器。这个守护进程拥有系统的高级权限(通常是root权限),它可以:
- 创建和销毁容器
- 管理容器网络和存储
- 监控和报告容器状态
守护进程的工作流程如下:
- 用户通过命令行界面(CLI)与Docker守护进程通信,发送指令来创建、启动或停止容器。
- Docker守护进程接收到指令后执行相应操作,并返回结果给用户。
无守护进程的容器管理
“无守护进程”模式(如Podman)则不依赖于一个中央的守护进程来管理容器,而是直接由用户进程管理和运行容器。这种模式有几个关键特点:
- 去中心化管理:每个容器由用户直接管理,而不是通过一个全局的守护进程。这使得容器管理更加灵活。
- 安全性提升:在无守护进程模式下,容器不需要以root权限运行,降低了安全风险。普通用户可以直接运行和管理容器,不需要sudo权限。
- 兼容性:无守护进程模式的工具(如Podman)通常兼容OCI(Open Container Initiative)标准的镜像和运行时规范,与传统容器引擎具有良好的互操作性。
无守护进程模式的优势
- 安全性:无守护进程模式可以避免守护进程权限过高带来的安全风险,尤其是在多用户环境下,每个用户运行的容器互相隔离,不会影响系统其他部分。
- 简化管理:不需要维护一个中央守护进程,简化了容器管理的复杂性。
- 资源效率:没有中央守护进程占用系统资源,减少了不必要的开销。
示例:Podman
Podman是一个无守护进程的容器引擎,与Docker CLI兼容,可以用来创建、管理和运行OCI兼容的容器。使用Podman时,每个容器运行在单独的进程空间中,直接由用户进程管理,而不是通过一个中央守护进程。
Podman命令示例:
# 创建并运行一个容器
podman run -d --name mycontainer nginx# 列出正在运行的容器
podman ps# 停止并删除容器
podman stop mycontainer
podman rm mycontainer
通过上述命令,用户可以在无守护进程的模式下方便地管理容器,而不需要一个中央的守护进程。
12. eBPF和Cilium(2019)
- 概念:eBPF(Extended Berkeley Packet Filter)提供了内核级别的可编程性,Cilium利用eBPF实现了高性能的容器网络和安全管理。
- 意义:eBPF和Cilium提升了容器网络和安全的可控性和性能,为云原生环境下的容器管理提供了新的工具。
总结
从Chroot到现代容器技术,容器的发展经历了长时间的演进。每个阶段的技术进步都为后来的容器技术奠定了基础。如今,容器已经成为现代软件开发和运维的重要组成部分,在提高资源利用率、加速应用部署、支持微服务架构等方面发挥着重要作用。
五、学习资料参考
-
docker官方文档
-
kubernetest官方文档
-
ingress-nginx文档
-
kernel Cgroup
-
AWS EKS用户向导
-
腾讯云TKE文档
相关文章:

WHAT - 容器化系列(一)
这里写目录标题 一、什么是容器与虚拟机1.1 什么是容器1.2 容器的特点1.3 容器和虚拟机的区别虚拟机(VM):基于硬件的资源隔离技术容器:基于操作系统的资源隔离技术对比总结应用场景 二、容器的实现原理1. Namespace(命…...

QT7_视频知识点笔记_67_项目练习(页面以及对话框的切换,自定义数据类型,DB数据库类的自定义及使用)
视频项目:7----汽车销售管理系统(登录,品牌车管理,新车入库,销售统计图表)-----项目视频没有,代码也不全,更改项目练习:学生信息管理系统。 学生信息管理系统࿱…...

windows10系统64位安装delphiXE11.2完整教程
windows10系统64位安装delphiXE11.2完整教程 https://altd.embarcadero.com/download/radstudio/11.0/radstudio_11_106491a.iso XE11.1 https://altd.embarcadero.com/download/radstudio/11.0/RADStudio_11_2_10937a.iso XE11.2 关键使用文件在以下内容:windows10…...

09.责任链模式
09. 责任链模式 什么是责任链设计模式? 责任链设计模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理者对象组成的链进行传递,直到有一个处理者对象能够处理该请求为止。这种模式的目的…...

Amazon云计算AWS(一)
目录 一、基础存储架构Dynamo(一)Dynamo概况(二)Dynamo架构的主要技术 二、弹性计算云EC2(一)EC2的基本架构(二)EC2的关键技术(三)EC2的安全及容错机制 提供的…...

十_信号4-SIGCHLD信号
SIGCHLD信号 在学习进程控制的时候,使用wait和waitpid系统调用何以回收僵尸进程,父进程可以阻塞等待,也可以非阻塞等待,采用轮询的方式不停查询子进程是否退出。 采用阻塞式等待,父进程就被阻塞了,什么都干…...

HCIP的学习(27)
RSTP—802.1W—快速生成树协议 STP缺陷: 1、收敛速度慢----STP的算法是一种被动的算法,依赖于计时器来进行状态变化 2、链路利用率低 RSTP向下兼容STP协议。(STP不兼容RSTP) 改进点1—端口角色 802.1D协议---根端口、指定端口…...

6. MySQL 查询、去重、别名
文章目录 【 1. 数据表查询 SELECT 】1.1 查询表中所有字段使用 * 查询表的所有字段列出表的所有字段 1.2 查询表中指定的字段 【 2. 去重 DISTINCT 】【 3. 设置别名 AS 】3.1 为表指定别名3.2 为字段指定别名 【 5. 限制查询结果的条数 LIMIT 】5.1 指定初始位置5.2 不指定初…...

Oracle导出clob字段到csv
使用UTL_FILE ref: How to Export The Table with a CLOB Column Into a CSV File using UTL_FILE ?(Doc ID 1967617.1) --preapre data CREATE TABLE TESTCLOB(ID NUMBER, MYCLOB1 CLOB, MYCLOB2 CLOB ); INSERT INTO TESTCLOB(ID,MYCLOB1,MYCLOB2) VALUES(1,Sample row 11…...

C++无锁(lock free)队列moodycamel::ConcurrentQueue
moodycamel::ConcurrentQueue介绍 moodycamel::ConcurrentQueue一个用C++11实现的多生产者、多消费者无锁队列。 它具有以下特点: 1.快的让人大吃一惊,详见不同无锁队列之间的压测对比 2.单头文件实现,很容易集成到你的项目中 3.完全线程安全的无锁队列,支持任意线程数的并…...

python办公自动化——(二)替换PPT文档中图形数据-柱图
效果: 数据替换前 : 替换数据后: 实现代码 import collections.abc from pptx import Presentation from pptx.util import Cm,Pt import pyodbc import pandas as pd from pptx.chart.data impo…...

vue不同页面切换的方式(Vue动态组件)
v-if实现 <!--Calender.vue--> <template><a-calendar v-model:value"value" panelChange"onPanelChange" /></template> <script setup> import { ref } from vue; const value ref(); const onPanelChange (value, mod…...

Linux下Qt Creator无法输入中文(已解决)
1. 首先确保安装了搜狗输入法,且能正常运行。 2.克隆源码到本地。 git clone https://gitcode.com/fcitx/fcitx-qt5.git 3.检查Qt Creator版本,如下图所示,为基于Qt6的。 4. 进入源码目录,建立build文件夹,修改CMak…...

Codeforces 提交Java代码(自己处理输入输出)
示例一(A. Watermelon) 题目地址 Problem - 4A - Codeforces 题目截图 提交方式 可以提交本地文件,也可以在线提交。我们这里选择在线提交方式,点击上图中的 SUBMIT 按钮,会进入如下界面。 输入Java代码效果如下&a…...

剖析vue中nextTick源码
代码逻辑梳理: callbacks 数组用于存储待执行的回调函数,waiting 变量用于标记是否有待执行的回调函数。 flushCallbacks 函数用于执行所有存储在 callbacks 数组中的回调函数,并在执行完成后将 waiting 设置为 false。 timer 函数根据环境…...

SSM牙科诊所管理系统-计算机毕业设计源码98077
目 录 摘要 1 绪论 1.1研究目的与意义 1.2国内外研究现状 1.3ssm框架介绍 1.4论文结构与章节安排 2 牙科诊所管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能…...

【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C “ 登神长阶 ” 🤡往期回顾🤡:C模板入门 🌹🌹期待您的关注 🌹🌹 ❀STL之string 📒1. string…...

go语言linux安装
下载:https://go.dev/dl/ 命令行使用 wget https://dl.google.com/go/go1.19.3.linux-amd64.tar.gz解压下载的压缩包,linux建议放在/opt目录下 我放在/home/ihan/go_sdk下 sudo tar -C /home/ihan/go_sdk -xzf go1.19.3.linux-amd64.tar.gz 这里的参数…...

vi和vim有什么不同?
vi 和 vim 都是流行的文本编辑器,它们之间有以下主要区别: 历史: vi 是一个非常古老的文本编辑器,最初由 Bill Joy 在 1976 年为 Unix 系统编写。vim(Vi IMproved)是 vi 的一个增强版,由 Bram M…...

CSS动画效果(鼠标滑过按钮动画)
1.整体效果 https://mmbiz.qpic.cn/sz_mmbiz_gif/EGZdlrTDJa5SXiaicFfsrcric7TJmGO6YddqC4wFPdM7PGzPHuFgvtDS7MIvnLHB4WFaKia0Qh8VCyUaoyHMc2Zltg/640?wx_fmtgif&fromappmsg&tpwebp&wxfrom5&wx_lazy1&wx_co1 网页设计中的按钮不仅是用户交互的桥梁&#…...

数据结构(C):从初识堆到堆排序的实现
目录 🌞0.前言 🚈 1.堆的概念 🚈 2.堆的实现 🚝2.1堆向下调整算法 🚝2.2堆的创建(堆向下调整算法) ✈️2.2.1 向下调整建堆时间复杂度 🚝2.3堆向上调整算法 🚝2.…...

ChatGLM3-6B部署
ZhipuAI/chatglm3-6b 模型文件地址 chatglm3-6B-32k-int4 量化的模型地址 ChatGLM3 代码仓库 ChatGLM3 技术文档 cpolar http xxx 端口 /anaconda3/envs/chatglm2/lib/python3.8/site-packages/gradio$ networking.py 硬件环境 最低要求: 为…...

代码随想录35期Day54-JavaScript
Day54题目 ### LeetCode739每日温度 核心思想:今天主要是学会单调栈的使用.找到比元素更大的下一个元素,如果比栈顶元素小就入栈,否则就出栈顶元素,当前元素就是比栈顶元素大的"下一个更大的元素". /*** param {number[]} temperatures* return {number[]}*/ var …...

把自己的服务器添加到presearch节点
Presearch is a scam. Before, judging by the price of the token you should have been able to get between $150-$200 after 12-13 months of regular searches. "If you use this service for the next 11 years you will have earned $30!" Presearch大约需要…...

Open3D(C++) OTSU点云二值化
目录 一、算法原理二、代码实现三、结果展示1、原始点云2、二值化本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 最大类间方差法(Between-class scatter method)是一种用于分割的方法,它通过计算图…...

浔川python社获得全网博主原力月度排名泸州地区第二名!
今日,浔川python社在查看全网博主原力月度排名泸州地区时,一看就震惊啦! 全网博主原力月度排名泸州地区排名榜单 全网博主原力月度排名泸州地区第二名为:浔川python社。 感谢粉丝们的支持!浔川python社还会继续努力&a…...

第二站:Java——集合框架的深邃海洋(续)
### Java——集合框架的深邃海洋(续) 在我们的Java集合框架探索之旅中,我们已经涉足了基本操作、高级特性,现在让我们深入探讨一些特定场景下的应用和进阶技巧,比如集合的分区操作、分组、并行流的性能考量࿰…...

linux系统下,mysql增加用户
首先,在linux进入mysql mysql -u root -p 然后查看当前用户: select user,host from user; 增加用户语句: CREATE USER 用户名host范围 IDENTIFIED BY 密码;...

Java数据结构与算法(最长回文子串中心扩散法)
前言 回文子串是练习数据结构和算法比较好的使用场景,可以同时练习到双指针、动态规划等一些列算法。 实现原理 中心扩散算法实现。这里定义最长回文子串长度的大小为maxLen,起点位置为0. 奇数个数为中心点和偶数个数为中心点分别计算回文长度大小。…...

基于Python网络招聘数据可视化分析系统的设计与实现
基于Python网络招聘数据可视化分析系统的设计与实现 Design and Implementation of Python-based Network Recruitment Data Visualization Analysis System 完整下载链接:基于Python网络招聘数据可视化分析系统的设计与实现 文章目录 基于Python网络招聘数据可视化分析系统的…...