本指南介绍了设计、实现、测试和部署 Knative serving 服务的最佳实践。如需了解更多提示,请参阅迁移现有服务。
编写高效的服务
本部分介绍设计和实现 Knative serving 服务的一般最佳实践。
避免后台活动
当 Knative serving 上运行的应用处理完请求后,容器实例对 CPU 的访问将被停用或受到严重限制。因此,您不应启动在请求处理程序范围之外运行的后台线程或例程。
运行后台线程可能会导致意外行为,因为对同一容器实例的任何后续请求都会恢复任何已暂停的后台活动。
后台活动是指在 HTTP 响应送达后发生的任何活动。建议检查您的代码,以确保所有异步操作都会在传送响应之前完成。
如果您怀疑服务中可能存在并不明显的后台活动,可以检查日志,以查找在 HTTP 请求条目后记录的任何内容。
删除临时文件
在 Cloud Run 环境中,磁盘存储空间属于内存文件系统。 写入磁盘的文件会占用供服务使用的内存,并且可在多次调用之间继续留存。 如果不删除这些文件,最终可能会导致内存不足错误,并且随后需要进行冷启动。
优化性能
本部分介绍性能优化方面的最佳做法。
快速启动服务
因为容器实例根据需要进行扩缩,所以典型的方法是完全初始化执行环境。这种初始化方法称为“冷启动”。如果客户端请求触发了冷启动,则容器实例启动会导致延迟时间增加。
启动例程包括以下步骤:
- 启动服务
- 启动容器
- 运行 entrypoint 命令以启动服务器。
- 检查开放的服务端口。
优化服务启动速度可以最大限度地缩短容器实例延迟处理请求的时间。
谨慎使用依赖项
如果您使用具有依赖项库的动态语言,例如导入 Node.js 模块,那么在冷启动期间加载这些模块会增加延迟时间。您可以通过以下方式缩短启动延迟时间:
- 最大限度地减少依赖项的数量和大小,以构建精简服务。
- 惰性加载不常用的代码(如果您所用的语言支持此方式)。
- 使用代码加载优化技术,例如 PHP 的 Composer 自动加载器优化技术。
使用全局变量
在 Knative serving 中,您不能假设服务状态会在各请求之间保持不变。但是,Knative serving 确实会重复使用独立的容器实例来处理持续流量,因此您可以在全局范围内声明一个变量,以允许后续调用重复使用其值。无法预知重复使用此变量是否会让任何单独的请求受益。
如果在每个服务请求中重新创建对象会带来很大的开销,您还可以在内存中缓存对象。将对象从请求逻辑转移到全局范围有助于提升性能。
Node.js
Python
Go
Java
对全局变量执行惰性初始化
全局变量的初始化始终在启动期间进行,因此这会增加冷启动时间。对不常用的对象使用惰性初始化可以延后其时间成本,缩短冷启动的时间。
Node.js
Python
Go
Java
优化并发
Knative serving 实例可以“同时”并发处理多个请求,但不超过可配置的最大并发请求数。这与使用 concurrency = 1
的 Cloud Functions 不同。
除非您的代码有特定的并发要求,否则您应该保留默认的最大并发数设置。
针对您的服务调整并发设置
每个容器实例可以处理的并发请求数受技术栈和所用共享资源(如变量和数据库连接)的限制。
如需优化服务以便能够稳定地并发处理尽可能多的请求,请执行以下步骤:
- 优化您的服务性能。
- 设置您在任何代码级并发配置中的预期并发支持级别。并非所有技术堆栈都要求进行此设置。
- 部署您的服务。
- 为您的服务设置等于或小于任何代码级配置的 Knative serving 并发请求数。如果没有代码级配置,请使用预期并发请求数。
- 使用支持可配置并发的负载测试工具。您需要确认您的服务在预期的负载和并发数情况下能够保持稳定。
- 如果服务运行状况不佳,请转到第 1 步来改进服务,或转到第 2 步来减少并发请求数。如果服务运行状况良好,则返回第 2 步并增加并发请求数。
继续迭代执行上述步骤,直到找到能稳定运行的最大并发请求数。
使内存与并发设置相匹配
您的服务处理的每个请求都需要一些额外的内存。 因此,当您增加或减少并发请求数时,请务必同时调整内存限制。
避免可变的全局状态
如果要在并发处理请求的情况下利用可变的全局状态,您需要在代码中执行额外的步骤来确保安全地完成此类操作。您可以将全局变量限制为一次性初始化并重复使用,以最大限度地减少争用(请参阅上文中的性能部分)。
如果在同时处理多个请求的服务中使用可变全局变量,请务必使用锁定或互斥机制,以防出现争用情况。
容器安全
许多通用软件安全做法都适用于容器化应用。 有些做法专门面向容器,或者适合容器的理念和架构。
您可以通过以下方式提高容器安全性:
定期重新构建容器映像并重新部署服务,以对您的服务应用安全更新。
仅在容器中添加运行服务所需的内容。额外的代码、软件包和工具都有可能成为安全漏洞。请参阅上文了解相关的性能影响。
实现包含特定软件和库版本的确定性构建流程。这可防止您的容器中混入未经验证的代码。
使用 Dockerfile
USER
语句将容器设置为以root
之外的用户身份运行。某些容器映像可能已经配置了特定用户。
自动执行安全扫描
启用漏洞扫描功能,以便对存储在 Artifact Registry 中的容器映像进行安全扫描。
您还可以使用 Binary Authorization 来确保仅部署安全的容器映像。
构建最小容器映像
大型容器映像可能会包含安全漏洞,因为它们包含的代码数量超出要求。
在 Knative serving 上,容器映像的大小不会影响冷启动或请求处理时间,也不会计入容器的可用内存。
如需构建最小的容器,请考虑使用精简基础映像,例如:
Ubuntu 是较大的基础映像,但很常用,可提供更全面的开箱即用服务器环境。
如果您的服务构建流程中涉及很多工具,建议使用多阶段构建来确保容器轻装上阵。
如需进一步了解如何创建精简容器映像,请参阅以下资源: