本节摘要:容器和 Serverless 是比 CVM 更高层次的应用部署方式。容器服务 TKE 是托管的 Kubernetes,让你用容器打包应用、用 K8s 管理部署、扩缩容和运维,而不用自己搭建维护 K8s 集群。Serverless 云函数 SCF 更进一步——连服务器的概念都隐藏了,你只写函数代码,由事件触发执行,按实际调用次数计费。两者各有运行模型:容器适合长期运行的服务、需要控制部署细节;Serverless 适合事件驱动、突发流量、低频调用的场景,但有冷启动延迟和执行时长限制。本节讲它们的工作原理和选型。
阅读完本节,你应当能够:
用 CVM 部署应用有个老问题:环境一致性。你在自己机器上跑得好好的,部署到 CVM 上就出问题——操作系统版本不同、依赖库版本不同、配置文件路径不同。这种"在我机器上能跑"的困局,是容器技术要解决的核心痛点。
容器把应用和它的所有依赖(运行时、库、配置)打包成一个镜像,这个镜像在任何装了容器运行时的机器上跑出来都一样。你不再担心"目标机器环境对不对",因为环境打包在镜像里了。这是容器相对 CVM 直接部署的根本优势。
但容器集群的管理本身很复杂——调度(把容器放到哪台机器)、扩缩容(流量大了加容器)、服务发现(容器之间怎么找到彼此)、滚动更新(不停机换新版本)。Kubernetes(K8s)是管理这些的事实标准,但自己搭一套生产级 K8s 集群是巨大的工程负担。这就是 TKE 的价值——它是托管的 K8s,控制平面(管理组件)由腾讯云维护,你只管用。
Serverless 走得更远:连"容器放哪台机器、怎么调度"都不让你管。你只写一个函数,上传上去,设置好"什么时候触发"(HTTP 请求、对象存储上传、定时器)。没请求时不计费、不占资源;有请求时自动拉起执行。它把运维负担降到最低,但代价是运行模型的限制(冷启动、执行时长、状态管理)。
容器部署的流程:把应用代码和依赖打包成镜像(Docker 镜像),镜像推到仓库,TKE 集群从仓库拉镜像、在节点上跑成容器。K8s 的控制平面负责调度(决定容器跑在哪个节点)、扩缩容(根据负载增减容器副本数)、服务发现(容器之间互相找)。
TKE 相比自己搭 K8s 的核心价值:控制平面托管。K8s 的控制平面(API server、调度器、控制器等)是集群的大脑,自己维护要处理它的升级、高可用、故障恢复——这是个专门的运维负担。TKE 把这部分托管了,你只管用 K8s 的 API 部署应用,不用管 K8s 自己怎么跑。
| 部署方式 | 你管什么 | 厂商管什么 | 适合 |
|---|---|---|---|
| CVM 直接部署 | OS、运行时、应用、运维 | 硬件 | 简单应用、特殊环境 |
| TKE 容器 | 应用镜像、部署配置 | K8s控制平面、节点 | 微服务、频繁发布 |
| Serverless SCF | 函数代码 | 一切基础设施 | 事件驱动、低频 |
K8s 之所以成为容器编排标准,是因为它解决了一组部署运维的常见问题:
自愈:某个容器挂了,K8s 自动重启或在新节点拉起。不用人工半夜起来重启服务。
滚动更新和回滚:发新版本时,K8s 逐个替换旧容器(滚动更新),全程不停机。发现问题可以一键回滚到上一版。
服务发现和负载均衡:K8s 给每组容器分配一个稳定的内部域名,请求自动负载均衡到这组容器的各个副本。容器增减时域名自动更新。
自动扩缩容:根据 CPU 利用率等指标自动增减容器副本数(HPA,水平 Pod 自动扩缩容)。和 IaaS 的弹性伸缩类似,但在容器层面、粒度更细、响应更快。
配置和密钥管理:配置项和敏感信息(密码)通过 K8s 的 ConfigMap 和 Secret 注入容器,不硬编码在镜像里。
Serverless(云函数 SCF)的运行模型和容器完全不同。你写一个函数(处理某个事件的代码片段),上传到平台,配置触发器(HTTP 请求、对象存储上传、定时器、消息队列消息等)。平时函数不运行、不计费;触发器事件来了,平台自动拉起函数实例执行,执行完释放。
冷启动是 Serverless 最关键的特性也是最大的坑。函数如果一段时间没被调用,平台会回收它的实例(省资源)。下次调用来了要重新拉起实例——下载代码、初始化运行时、加载依赖,这个过程叫冷启动,耗时从几百毫秒到几秒不等(取决于函数大小和运行时)。冷启动会让首次请求明显变慢。
Serverless 的几个特点:
按调用计费:没请求不计费,有请求按执行次数和时长计费。低频场景极省钱(一天几百次调用的功能,可能几分钱)。
自动伸缩:流量来了平台自动并行拉起多个函数实例,理论上能扛突发巨量并发。不用你配伸缩策略。
无状态:函数实例是临时的,执行完可能被回收。函数不能依赖实例内的本地状态(变量、文件),状态要存外部(数据库、对象存储)。
执行时长限制:单次函数执行有时间上限(通常几分钟),超时强制终止。不适合长时间运行的任务。
两者不是替代关系,适用场景不同:
| 维度 | 容器 TKE | Serverless SCF |
|---|---|---|
| 适合的工作负载 | 长期服务、稳定流量 | 事件驱动、突发、低频 |
| 冷启动 | 无(容器常驻) | 有(首次调用慢) |
| 执行时长 | 无限制 | 有上限(分钟级) |
| 状态管理 | 可有状态(本地卷) | 必须无状态 |
| 成本模型 | 按节点常驻计费 | 按调用次数计费 |
| 控制粒度 | 高(可控部署细节) | 低(平台托管一切) |
| 适合场景 | Web API、微服务 | 触发处理、定时任务、扩容 |
选 Serverless 的典型场景:图片上传后自动生成缩略图(对象存储触发)、定时跑数据清洗脚本(定时器触发)、Webhook 处理(HTTP 触发但调用不频繁)、IoT 设备消息处理(消息队列触发)。这些共同特点是"事件驱动、不需要常驻、流量波动大"。
选容器的典型场景:核心 Web API(要长期稳定服务、不能容忍冷启动)、复杂微服务架构(要服务发现和治理)、需要长连接的应用(WebSocket、流式)。这些特点是"需要常驻、对延迟敏感、架构复杂"。
💡 关键直觉:别因为"Serverless 听起来先进"就用它跑核心 API。冷启动对面向用户的 API 是致命伤——用户点一下要等 2 秒(冷启动)才有响应,体验崩坏。Serverless 的甜区是"非实时的、事件触发的、可容忍冷启动的"任务。面向用户的实时服务,容器或 CVM 更合适。
如果一定要用 Serverless 处理延迟敏感的场景,几个降低冷启动的办法:
缩小函数体积:函数代码和依赖越小,冷启动下载和初始化越快。砍掉不必要的依赖,别把整个大库都打包进去。
预置并发:一些平台支持"预置并发"——预先保持一定数量的函数实例热着,请求来了直接用不冷启动。代价是要为这些热实例付费(即使没请求),所以只在需要消除冷启动的少量核心函数上用。
选轻量运行时:不同运行时冷启动速度不同。通常编译型语言(Go)比解释型(Python、Node.js)冷启动快,Java 因为 JVM 启动重最慢。延迟敏感场景选轻量运行时。
优化初始化逻辑:函数的初始化代码(加载配置、建连接)在冷启动时执行。把耗时初始化放到函数外的全局作用域(只冷启动时执行一次,后续调用复用),别每次调用都重新初始化。
把应用容器化时,几个最佳实践:
镜像要小:小镜像拉取快、攻击面小。用精简基础镜像(如 Alpine 版本),别用包含一堆用不到的工具的大镜像。多阶段构建(build 阶段用大镜像编译,运行阶段只拷贝产物到小镜像)能有效缩小。
一个容器一个进程:别在一个容器里塞多个进程(比如 Web 加数据库)。容器的设计是一个容器干一件事,多进程违背这个原则且增加复杂度。多个服务用多个容器,通过 K8s 编排。
配置与镜像分离:配置(数据库地址、特性开关)通过环境变量或配置中心注入,不打进镜像。这样同一个镜像能跑在不同环境(测试、预发、生产),只改配置。
健康检查:配置 liveness(存活)和 readiness(就绪)探针。liveness 让 K8s 知道容器是否健康(不健康重启),readiness 让 K8s 知道容器是否准备好接流量(没准备好不路由流量)。这俩配置好,K8s 的自愈能力才能发挥作用。
# 概念性:Serverless 函数的初始化优化 import heavy_lib # 放全局 只冷启动时加载一次 db_connection = None # 全局 连接可跨调用复用 def handler(event, context): global db_connection # 懒初始化 首次调用建连接 后续复用 if db_connection is None: db_connection = create_db_connection() # 业务逻辑 return process(event, db_connection) # 注意 别在函数内每次都重建连接 冷启动后才建的连接要复用
⚠️ 常见坑:Serverless 函数里每次调用都新建数据库连接。冷启动后第一次调用建连接没问题,但如果每次调用都建(比如把建连接写在函数体内而非全局),连接开销叠加会拖慢响应且耗尽数据库连接数。把重资源(连接、加载大模型)放到全局作用域复用,是 Serverless 性能优化的关键。
下一章进入 AI 与数据服务,讲腾讯云的大模型、AI 开发平台和大数据产品。