公司动态
南京大学 操作系统 (JYY) 学习笔记:从虚拟机、容器到 Serverless 的云端演进
写在前面这是本系列的第二十七篇。进程运行的程序一直以来都是操作系统中的核心抽象。作为应用程序的主体运行它的方式却在多年的发展中历经了翻天覆地的变化。本讲内容我们将跳出单台计算机的视角看看应用程序是如何打破物理机器的枷锁最终在海量数据中心里实现极致的虚拟化与云原生调度的。虚拟机和容器虚拟化与隔离Full System Emulation (全系统模拟)这个概念其实很简单就是我们在实验里做的NEMU啊取指令、译码、执行。致命的缺陷是性能: 纯软件模拟的性能通常不及 native 原生运行的 10%。NEMU一个虚拟的计算机在电脑上用纯软件模拟出其他设备或系统的运行环境。让一些程序或系统以为自己是在真实的硬件上运行但实际上它们是在电脑上通过 NEMU 模拟出来的环境中运行的。Full System Emulation 的黄金时代背景科普httpd 的 CGI通用网关接口是一种早期的 Web 开发技术允许 Web 服务器运行外部程序并将用户请求传递给这些程序然后将程序的输出返回给用户。黄金时代的起点Disco (1997): “brings back an idea popular in the 1970s: virtual machine monitors” (让 1970 年代极其流行的虚拟机监视器技术重回大众视野)。VMWare (1998): 我们能把这个硬核技术直接做成商业产品Hack 原理Guest Ring 3 直接运行在 Host Ring 3虚拟机里的应用程序依然在真实的 CPU 上全速执行但一旦触发 System call就会 trap陷入到宿主机的 VMM虚拟机监视器中去拦截处理。类似的实现Windows Subsystem for Linux (WSL 1.0, 2016)。Xen and the art of virtualization (2003): 可以把操作系统内核改一改半虚拟化来配合 VMM 提升性能。最终硬件厂商下场Intel 提供了 VT-x (2005) $ \rightarrow $ VT-d (2006) $ \rightarrow $ EPT (2008) 等强大的硬件虚拟化支持。(我们之前讲过的/dev/kvm就是利用了这些硬件特性)。/dev/kvm是 Linux 系统中的一个设备文件用于支持硬件加速的虚拟化。它是 Kernel-based Virtual Machine 的缩写允许用户空间程序利用 CPU 的硬件虚拟化特性来全速运行虚拟机。虚拟化完全体EPT 2008 (扩展页表)八级页表四级页表在物理机另外四级页表在虚拟机上。硬件 MMU 会直接在虚拟机里构建这套极其复杂的地址翻译。为什么会有黄金时代Dot-com bubble 互联网泡沫时代 (2000 纳斯达克泡沫)以前的 ISP (Internet Service Provider) 提供的是真实的物理机。虚拟机的出现改变了游戏规则它和物理机用起来完全一样但一台高配物理机能当 $ n $ 台虚拟机卖黑心商人的玩法Oversubscribe (超分/超卖)通过超量分配服务提供商可以将一台物理机当作几十台虚拟机出售大幅减少闲置资源利润起飞当然如果大家同时占用资源就会严重破坏用户体验。Everything is a State Machine…NEMU 本质上也就是一个进程。想要保存虚拟机的状态做一个 core dump 即可核心转储是程序运行时的内存快照如果你怀疑运行出了问题可以直接捕获其运行时状态然后用 GDB 回溯分析。虚拟机更容易管理状态了时间转移 (Replay):记录下虚拟机所有的非确定性输入就可以“instruction-by-instruction”完美重放整个系统的执行过程OSDI’02 ReVirt。空间转移 (Migration):可以把一台正在运行的虚拟机内存状态通过网络热迁移到另一台物理机上几分钟内无缝切换服务甚至不会中断OSDI’02。Hackers 的时代图中是华为操作系统首席科学家上海交通大学 IPADS 所长陈海波教授。他的经典论文Live updating operating systems using virtualization (VEE’06)利用虚拟化技术给操作系统内核打在线热补丁。浪潮过后容器化的崛起操作系统我自己就能虚拟化自己啊应用程序其实只能看到“系统调用 API”。操作系统如果“假装”在虚拟机里为你执行系统调用不就行了吗例子虚拟的 pstree在你的环境里你看到的pid 1是属于你的init进程。这就是容器化技术在同一个操作系统内核上运行多个隔离的用户态环境。每个环境有自己独立的文件系统、网络接口和进程空间。容器化技术底层利用了Linux 的命名空间 (Namespaces) 和控制组 (cgroups)来实现资源的绝对隔离。祝贺你发明了 Linux Namespacespid没必要是整个操作系统唯一的。给每个进程增加一个“osid”然后增加一个系统调用vos(fs_root)。创建一个新的 osid 后这个环境里的pid就可以重新从 1 开始分配。fork()出的子进程自然继承父进程的 osid。只要顺着想“操作系统里还有什么对象需要隔离”需要为不同 osid 隔离实现的对象包括pid: 进程编号。user: 用户和组 (隔离 uid 权限极其重要)。mnt: 文件系统和挂载点。ipc: 信号量、消息队列、共享内存。net: 网络设备、协议栈、端口 (让你可以在每个容器里都监听localhost:5000)。time: 系统时间和时区。uts: 主机名和域名。Linux namespaces 底层全在/proc/[pid]/ns/目录下。你甚至可以用lsns和strace去窥探这些隔离魔法。Namespaces 核心 APILinux 命名空间是一种内核特性使得每个命名空间中的进程看起来像是在一台独立的机器中运行。clone: 创建进程时带上CLONE_NEW_xxx(PID, IPC…) 选项直接生在一个新空间里。setns,unshare: 强行改变当前进程的“osid”把它丢进隔离区。Windows Subsystem for Linux (WSL 1) 听完后直呼放弃要我在 Windows 内核里翻译实现这一整套 Linux 容器的 ioctl 和 namespaces我选择死亡所以 WSL 2 干脆切成了真正的轻量级虚拟机。在今天由于这些系统调用的语义已经被容器极度依赖你想要再修改 Linux 内核相关特性变得极为困难。再进一步资源调度与 Cgroups实现了环境隔离还不够还得实现资源的控制。“圈一批进程”硬性设定它们最多能用多少 CPU 和内存。祝贺你发明了 cgroups (Control Groups)cgroups 是 Linux 内核特性用于对进程组进行物理资源分配和限制CPU 时间、内存使用量、磁盘 I/O 等。配置全在/sys/fs/cgroup目录里修改文本文件就能限制算力。这是一个和 Namespaces 完美正交的机制将 Namespaces (环境隔离) Cgroups (资源限制) 共同使用你就得到了现代的容器 (Container)例子搞一个只有busybox的“系统中的系统”。祝贺你发明了 Docker云时代的虚拟机 vs 容器如果只需要运行 Linux 环境容器在体验上和虚拟机完全一样但它的开销比虚拟机低非常多没有冗余的 Guest 内核直接复用宿主机内核虽然安全性略低。这样一台物理机上就能部署数量恐怖的服务。黑心商人的赚钱机器再一次升级Kubernetes (K8s): “容器编排”单机搞定了跨主机的海量容器怎么办K8s 提供了极其强大的跨主机弹性自动编排。自动容错检测到某个节点上的容器挂了光速在另一个健康节点重新拉起。这是云厂商最爱看到的全自动化运维。云原生与微服务Serverless 的终局舞台已经搭好了有了极度轻量的容器软件的开发和部署模式随之发生巨变以前我们把整个庞大的 Web Server 塞进一个沉重的虚拟机里。现在有了秒级启动的容器干脆把庞大的单体程序拆成成百上千个微小的Microservices (微服务)Cloud Native (云原生):云厂商会帮你把容器管理、API 网关、服务发现、负载均衡……全套搞定。Serverless (无服务器计算)连“容器”的概念都可以不要了你根本不需要去管什么操作系统、端口监听和容器镜像。你只需要实现一个简单的函数int foo() {}。剩下的网络监听、并发扩容全交给云厂商。厂商也极其开心连 oversubscribe 都不需要了直接按调用次数和毫秒时长计费最小化颗粒度最大程度榨干物理机器的每一丝性能。Function-as-a-ServiceFaaS函数即服务FaaS 是 Serverless 的核心实现形式编写/部署函数开发者写好代码片段丢到云端。事件触发当 HTTP 请求或数据库更新发生时平台光速拉起一个环境执行这个函数。释放资源跑完立刻销毁绝不占用 1 KB 多余内存。极简的资源单位换来的是极致的商业利益利用 RPC (Remote Procedure Call) 远程调度能力RPC 允许一个程序像调用本地函数一样跨网络去调用另一个云端函数而无需关心底层的 TCP/IP 通信细节。比如调用阿里云的云函数去跑笨重的ffmpeg处理音视频defget_media_data(object_key):clientfc2.Client(endpointhttps://id.cn-hangzhou.fc.aliyuncs.com,accessKeyIDxxxxxxxx,accessKeySecretyyyyyy)# 发起跨越物理机器的 RPC 调用returnclient.invoke_function(FcOssFFmpeg,GetMediaMeta,payloadjson.dumps({bucket_name:test-bucket,object_key:object_key})).data get_media_data(/object/key/to/a.mp4)再加上 CI/CD (持续集成/持续交付) 自动化开发者只需要git push剩下的一切打包、灰度测试、部署全自动流水线完成。例子我们课程的官网ics.nju.edu.cn。git push会触发 Webhook HTTP 请求带着鉴权 Token 呼叫服务器服务器瞬间完成静态网站的重新编译和无缝发布。未来“计算机”会消失吗当算力全部集中在云端我们真的只需要一块屏幕终端吗# 未来的编程可能长这样直接向 AI 索要结果responseOpenAI().responses.create(modelgpt-4.1-mini,inputGenerate an image of a cat and an otter...,tools[{type:image_generation}],)随着大语言模型的爆发“AI Inference (推理)” 占程序运行总时长的比例正在疯狂飙升。未来的操作系统会不会演变成专门用来调度和分发 AI 算力的底座总结Take-away messages:透过虚拟化几十年的发展浪潮我们看到了从笨重的全系统虚拟机到轻量的 Docker 容器再到今天大道至简的 Serverless 云函数。“计算机系统”作为最核心的支撑性技术一次次给应用世界带来了彻底的降维变革。有趣的是那些最终改变世界的技术最初往往只建立在一些极简的极客动机上——例如“如果全系统模拟真的可以运行得很快会怎样”或是“如果我们给每个操作系统对象加上一个隐形的 osid 标签会怎样”。就是这些看似微小的奇思妙想最终坚持到底彻底重塑了今天庞大的数字世界。