更新和重启最佳实践

Best Practices for Updates and Restarts

在现代社会,保持系统运行不仅是基本要求,更是不可谈判的。

当涉及到软件更新及其对系统的影响时,情况变得更加复杂。一方面,安全是当今更新的主要驱动力,这一点也是不可谈判的。为了维持最强的安全性,补丁需要尽快实施并应用于所有系统。

对于包含重要错误修复、性能改进和增强功能的软件更新,同样适用此原则。为了利用这些改进,也应及时实施这些更新。

但停机时间怎么办?根据您的环境和操作流程,应用安全补丁和软件更新时,可能会因服务重启而导致短暂的停机,也可能会引入导致重大停机时间的错误代码,直到回滚或更新为止。如何在不造成停机的情况下及时更新和修补这一难题,您需要做出正确的架构决策,否则您的环境将无法实现高可用性。

解决方案是依赖于能够保证亚秒级重启时间的软件,即使在数百个节点之间也是如此。这是我们投入大量精力研究的领域,因此,即使是大规模升级 MinIO 也不会造成中断。在这篇文章中,我们将概述我们方法背后的理念,并向您展示如何执行高可用对象存储所需的不间断升级。

MinIO 专注于不间断重启,因为我们了解您的数据对您有多重要。数据是现代企业的血液。这意味着数据存储就是心脏。如果它停止了,血液就会停止流动——那么,就会发生不好的事情。由于从应用程序、ETL、工作流、数据库、AI/ML——甚至 CDN——都依赖于数据,因此数据存储系统必须始终可用——即使在它使用安全和发布更新时也是如此。无法实现不间断升级会导致技术债务累积,并产生不利的升级激励,从而降低软件的实用性并增加数据隐私和持续业务运营的风险。

一般原则

对于 MinIO,就像时间一样,稳定性是单向的。最新的 MinIO 版本将始终是最稳定的。面向现代 CI/CD 的开发人员会理解这一点,而老派的架构师可能不会——但我们向您保证,这是真的。MinIO 发布频繁,但有目的性,并且每个版本都经过精心规划、开发和测试。我们始终鼓励我们的客户和社区使用最新版本。

发现的错误会被修复并合并到最新版本发布的的上游代码中。如果特定客户需要修复,可以根据请求进行回退,这些是应用于旧版本的补丁。每周都会从上游发布最新版本。因此,我们的客户和社区可以放心,最新版本包含您当前正在使用的版本的必要修复,无论该版本有多旧。

虽然每个人都应该使用最新版本,但我们也认识到,我们的发布节奏和您的部署节奏,再加上必要的内部流程,可能并不一致。在一个每个人都运行大量软件包和开源框架的世界里,所有发布和部署节奏重叠的情况很少见。

您的节奏不应该影响您的停机时间——并且在 MinIO 中,它不会。

那么 MinIO 如何处理所有这些呢?首先,MinIO 的最新二进制版本确保它与尽可能旧的版本向后兼容。如果数据以旧格式写入,例如使用以前的元数据结构,新版本将具有读取它的逻辑并就地升级该数据到新格式。每个版本的迁移逻辑都内置在代码本身中,因此您无需执行额外的步骤即可更新。

在 Linux 中,包管理器管理配置更改。但这可能会变得很麻烦,因为我们需要向包管理器说明每个更改。这是一个每个人都必须记住的所有更改链以及应用它们的脚本。

MinIO 的滚动升级速度很快,并且新版本处理了与任何旧版本的兼容性所有知识。例如,最近一位客户使用的是 2.0.9 版本,他们想知道是否可以直接更新到 4.5.2 版本,或者是否必须进行完全重新安装或一次更新一个版本。我们建议他们升级到 4.5.2,他们很快就能够启动并运行。MinIO 始终确保向后兼容性,我们永远不会让您陷入困境。

正如我们在教程中将展示的那样,您只需下载较新版本,将其放置在旧二进制文件的位置并重新启动服务即可。您可以从版本 B 无缝跳转到版本 X。实际上,MinIO 通过依赖链确保功能,该依赖链保证升级会考虑先前版本,更重要的是,一次升级一个版本,例如 B->C->D….X。从用户的角度来看,也许更重要的是从管理员的角度来看,连接的基础设施可以正常工作。MinIO 执行此迁移并处理兼容性的复杂性。结果是不需要手动通过多个版本进行升级——这是一种过时且传统的升级方法,可以追溯到运行单片客户端-服务器应用程序的时代,当您落后几个版本或拥有数百个节点时,这在当今的云原生微服务世界中根本不可行。

如果要求依次升级,那就像谚语(顺便说一下,是错误的)一样,需要两年时间才能粉刷金门大桥,但您必须每两年粉刷一次。关键是,如果您需要逐个版本依次升级,您将把所有资源都用于升级,而没有剩余资源用于改进、优化和定制。

事实上,这就是旧版遗留系统中发生的情况。它们会停滞,然后在自身重量下崩溃。

我们大多数管理过系统的人都知道流程。支持和依赖的系统将离线。然后,为了最大程度地减少总停机时间,每个节点都会被关闭,应用更新或安全补丁,节点会重新启动,然后整个循环会在下一个节点上重复。

如果您有 2-3 个节点,这不是什么大问题,但如果集群中有 50 个节点,这可能需要一整天(或一夜)。不仅数据量,而且这些节点中的数据量也会使流程变得更长。同样,实践者知道这个过程很难跟踪,如果没有准确的跟踪,他们就有可能不知道每个节点处于什么状态。不完整升级的后果可能很严重。使用旧版二进制版本的现有 MinIO 服务器将接收请求,但不会理解包含新功能的请求。如果存在集群负载均衡器,则请求会被随机发送到一个节点,因此很难控制请求发送到哪里。

更新时间越长,ETL 作业积压的时间就越长,恢复所需的时间就越长。这就是我之前提到的累积的技术债务。作为一名前 DevOps 工程师,我对此深有体会。有时,整个系统需要完整的 24 小时循环才能恢复并稳定下来。当系统重新启动时,不可避免地会出现配置不匹配或设置更改,因为更新的性质以及应用它所需的时间会导致流程在各处中断。MinIO 缓解了这些问题,因为在下载二进制文件和重新启动服务之间,没有太多可能导致漂移的活动部件。

首先,让我们讨论 MinIO 如何让您实现不间断更新和快速重启。稍后,我们将向您展示如何在教程中跨环境升级和重启 MinIO。此 MinIO 升级过程可以扩展到 50 节点集群,每个节点都运行一个在端口 9000 上侦听的 MinIO 二进制文件。

无缝、简单、即时

发布新版本的 MinIO 二进制文件后,实际的升级本身非常简单。我们将向您展示如何升级二进制文件以及 Kubernetes 运算符。此过程将同时在所有 50 个节点上进行升级。这种升级方式节省了时间,并激励您的 DevOps 团队更频繁地升级二进制文件,因为此任务可以轻松自动化。在架构上,所有内容都包含在一个在单个进程 ID 中运行的二进制文件中。

二进制文件就位后,我们将同时在所有节点上重新启动 MinIO 服务。同样,您不需要一次一个节点地执行此操作,因为它们都在后端使用单个二进制文件和数据格式。当新进程重新启动时,后端进程知道如何读取旧版本正在管理的驱动器,因为迁移逻辑也编码在新版本中。重启速度非常快,实际上是亚秒级的,因为 MinIO 效率很高。MinIO 几乎不使用任何 CPU 资源,并且整个代码库都捆绑在一个二进制文件中,使其保持精简和简单,并消除了管理大量活动部件的需要。

同时,我们建议您同时更新所有节点。传统上,不间断升级只能通过滚动更新来实现,因为每个节点都需要时间来更新,并且节点相互依赖,因此必须一次更新一个。在更新 MinIO 集群时,随着每个 MinIO 节点的更新,集群将继续使用旧版本运行,直到集群中的所有节点都更新为止。这减轻了担心不同的请求显示不同版本的负担,因为所有请求都转到相同的版本,直到完全切换为止。

通过并行执行更新,我们使其成为原子操作;要么所有节点都运行二进制文件的较新版本,要么所有节点都运行旧版本。MinIO 确保您不会遇到某些节点运行旧版本而某些节点运行新版本的潜在灾难性情况。

最棒的是,使用 MinIO 的应用程序并不知道此升级过程,因为 HTTP/API 调用甚至不知道服务器正在重启。我们不是在谈论需要节点通知其他节点和客户端它们正在重启的传统应用程序或文件共享。MinIO 通过在指定端口上继续接收请求来维护一致性,并且当服务重启时,它会将请求路由到已启动的较新版本。

无论您是在物理服务器上运行 MinIO 在数据中心 还是在运行 Kubernetes 的 Pod 上运行,都可以使用这种升级和重启方法。在下一部分中,我们将向您展示一个简单的教程,教您如何在现实世界中运行的基础架构中实时实现这些概念。

执行升级

执行实际升级本身非常简单。我们将向您展示两种升级方式:使用二进制文件和 Kubernetes 运算符。

二进制文件

务必确保集群中运行的所有节点都是 MinIO 部署节点,而不是其他与 S3 兼容的服务。

要更新所有节点上的 MinIO,请运行以下命令

mc admin update ALIAS

此命令更新 MinIO 部署中的所有服务器。更新后,它还将同时重启所有 MinIO 部署节点上的 MinIO 服务。MinIO 操作是原子且严格一致的,因此重启过程不会中断应用程序。

这避免了 MinIO 节点的“滚动”重启,并符合 MinIO 方式的更新和重启理念。

Kubernetes 运算符

如果您正在运行 Kubernetes 运算符,则可以按如下方式进行升级。

在开始升级之前,请验证运算符中资源的状态

kubectl get all -n minio-operator

也请验证运算符的版本

kubectl get pod -l 'name=minio-operator' -n minio-operator -o json | jq '.items[0].spec.containers'

验证完成后,您可以使用 krew 插件进行升级,或者在本例中,我们将向您展示如何手动执行。

下载 MinIO Kubernetes 插件,并将其替换为系统路径中现有的插件

curl https://github.com/minio/operator/releases/download/v4.5.8/kubectl-minio_4.5.8_linux_amd64 -o kubectl-minio

chmod +x kubectl-minio

mv kubectl-minio /usr/local/bin/

验证插件是否已安装在正确的路径中

kubectl minio version

以下命令是实际升级运算符的命令

kubectl minio init

再次运行前面提到的 jq 命令以验证新安装的运算符的版本

kubectl get pod -l 'name=minio-operator' -n minio-operator -o json | jq '.items[0].spec.containers'

还可以通过登录到运算符控制台来验证

kubectl minio proxy

关于无中断升级的最终想法

现代企业始终在线,永不停机。当然,在一个互联互通的世界中,企业并不具备端到端的控制权——问问亚马逊自己就知道了。话虽如此,但需要消除自我造成的停机时间——并且借助 MinIO 的功能套件,这成为可能。

但是,不要只听我们说,请亲眼见证。创建一个 MinIO 集群,然后下载 MinIO(50 个节点对于测试来说可能有点多 :)),并执行升级——您将亲眼见证并行性、弹性和二进制文件的力量,该二进制文件足够小,可以在一秒钟内重新加载,但功能强大到可以为地球上一些最大的企业提供支持。

无法立即实现无中断升级会导致技术债务的累积。这会产生连锁反应,为您的整个工程和 DevOps 团队增加额外的技术债务。维护后,启动这些系统是一项完全不同的挑战。通常,它们必须按正确的顺序启动,并且需要进行广泛的测试以确保它们与脱机前的工作方式相同。

您可以确信,每个 MinIO 版本在您的环境中都能完美运行,无论是在开发、QA、暂存还是生产环境中。我们尽一切努力确保每个新版本都是改进,并且我们为您提供工具,以便在您的环境中快速验证这一点。

在未来的博文中,我们将更详细地介绍我们关于如何设置这些不同环境的建议。例如,如果您没有足够的节点来构建不同的环境,那么您可以在同一节点上但在不同的端口上运行多个 MinIO 二进制文件;这样,您可以重新利用生产数据并在暂存环境端口上对其进行测试,以确保在升级生产端口上运行的二进制文件之前一切正常。如果您有多个 MinIO 租户,那么您可以先升级免费层级,然后随着信心的增强,可以一次在一个区域中将其推广到其他区域。


如果您有任何问题,请随时在 Slack 上与我们联系!