什么是 Immutable Web Apps
不可变 Web 应用程序是一种与框架无关的方法,用于构建和部署静态单页应用程序:
最大限度地降低实时发布的风险和复杂性。
简化和最大化缓存。
最大限度地减少对服务器和运行时环境管理的需求。
通过简单、灵活的原子部署实现持续交付。
准则
该方法基于严格分离的原则:
从代码配置。
从构建任务中释放任务。
来自静态内容的动态内容。
以下概念定义了不可变 Web 应用程序的核心要求。 它们与框架和基础设施无关。
Static assets are independent of the web application environment(s)
静态资产是从 Web 应用程序代码库的构建生成的文件(javascript、css、图像)。 当它们不包含任何特定于环境的内容并且它们被发布到一个独特的、独立的位置时,它们就会变得不可变。
Static assets must not contain anything that is environment-specific
所有领先的应用程序框架(Angular CLI、Create React App、Ember CLI、Vue CLI 3)都建议在编译时定义环境变量。 这种做法要求为每个环境生成静态资产,并针对环境的任何更改重新生成静态资产。
不可变 Web 应用程序引用在全局范围内定义的环境变量并引用以下两种方式之一:
直接从窗口对象
通过包装环境变量的注入服务
一个例子:
静态资产必须托管在唯一且独立于 Web 应用程序环境的位置。
不包含任何特定环境的静态资产可以构建一次,发布到唯一位置,然后在 Web 应用程序的多个环境中使用。
这些静态资产与内容交付网络 (CDN)(Google 托管库、cdnjs、jsDelivr、UNPKG)上托管的 javascript 库具有相同的品质:
https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
上面的 jquery 库的位置,被无数的 web 应用程序引用,既独立于应用程序,又是唯一的版本。
https://assets.myapp.com/apps/1.0.2/main.js
同样,Web 应用程序 javascript 文件的位置是唯一的,并且托管在专用于静态资产的位置。 静态资产 Web 服务器是 Web 应用程序版本的存储库。
Configure the static assets for long-term caching
不包含任何特定于环境的内容并托管在唯一且永久位置的静态资产可以配置为由浏览器(几乎)无限期地缓存:
cache-control: public, max-age=31536000, immutable
index.html 是可部署的配置
单页应用程序的 HTML 文档(通常是 index.html)不是静态的。 它因环境和部署目的地的不同而有所差异。HTML 文档是特定于环境的配置和定义 Web 应用程序的不可变静态资产的组合。
index.html 包含对静态资产的完全限定引用。
看个例子:
index.html must never be cached
注意:为了允许立即更改 Web 应用程序环境,index.html 绝不能被浏览器或无法按需清除的公共缓存缓存:
cache-control: no-store
不可变 Web 应用程序将发布任务与构建任务分离为两个不同的工作流。
构建
不可变 Web 应用程序的代码库负责构建静态资产并将它们发布到静态 Web 服务器。代码库的每个状态都可以由位于唯一位置的一组静态资产表示。并非代码库的每个状态都需要发布,但代码库的任何单个状态都不需要多次发布。
通常,代码库是与持续集成系统集成的源控制代码存储库,该系统能够构建、版本控制并将静态资产发布到静态 Web 服务器。
这方面的一个例子可能是:
一个托管在 GitHub 存储库中的 Angular 项目。当提交被推送到主分支时,repo 与 TravisCI 集成以构建和版本资产。 版本化资产发布到 AWS S3 存储桶中的唯一位置。
发布
index.html 文件独立于代码库进行管理,它们充当每个环境的清单。 它们应被视为配置文件并进行相应管理。 此外,需要有一种机制来修改或替换每个 Web 应用程序环境中的 index.html。 更改 index.html 的行为实际上是一种部署。
这方面的一个例子可能是:
一组 index.html 文件,每个环境一个,托管在 Github 存储库中。该存储库与 TravisCI 集成以在修改为 AWS S3 存储桶时发布 index.html 文件。有一个专用于每个 Web 应用程序环境的 index.html 和 S3 存储桶。
这种构建和发布分离的工作流,其底层基础设置如下图所示:
支持不可变 Web 应用程序的基础架构由三部分组成:
Web 应用程序服务器:通过提供 index.html 来托管 Web 应用程序环境的静态 Web 服务器。
静态资产服务器:用于托管不可变静态资产的静态 Web 服务器。
API:一个或多个公开暴露的端点以与 Web 应用程序后端交互。
构建静态资产是一个复杂的过程,通常涉及:
依赖解析
下载库文件
Transpiling
Minifying
Bundling
Uglifying, Code Splitting, Tree Shaking, * Autoprefixing…
这些过程非常耗时,严重依赖外部依赖性,并且通常以看似不确定的方式运行。它们不是应该通过立即将生成的资产发布到生产环境而无需验证来结束的过程。即使发布多个大型静态资产的行为也是一个可能被中断并使 Web 应用程序环境处于损坏状态的过程。
不可变 Web 应用程序生成一次并发布一次到某个位置。此过程发生在实时发布之前。它们可以在暂存环境中进行验证并提升到生产环境,而无需以显着降低的风险重新生成。
Atomic live releases - 原子实时发布
不可变 Web 应用程序的实时发布是发布单个 index.html 文件的行为。 部署是即时的,所有资产都可以立即使用,而没有任何缓存在发布时被破坏的风险。
回滚与部署一样有风险,而且通常风险更大。 对于不可变 Web 应用程序,部署的相同质量适用于回滚。 值得注意的是,在回滚的情况下,大多数浏览器仍会缓存以前的资产。
万一浏览器尝试加载旧版本的 index.html,以前版本的所有资产仍然可用且未损坏。
简化的缓存策略
管理缓存控制标头可能令人生畏,尤其是当 Web 应用程序基础架构利用 CDN 使用的公共缓存时。 缓存中最简单的两个概念是:“始终缓存”和“从不缓存”。 不可变 Web 应用程序包含这些概念,将可以“始终缓存”的代码与“从不缓存”的配置完全分开。
简化的路由策略
领先的应用程序框架在其部署建议中没有将静态资产的位置与 index.html 分开。 相反,他们建议向 Web 服务器添加路由规则,为所有未解析为物理文件的路径返回 index.html。
这些路由规则的实现可能因 Web 服务器而异,错误通常会导致路径解析为错误的资源。
将 index.html 的托管和静态资产分开可以消除这种风险。 静态资产服务器始终提供由 url 表示的物理文件,而 Web 应用程序服务器始终为任何 url 提供 index.html。
不可变的 Web 应用通常与下列这些概念具有密切关联:
现代应用程序框架:Angular、React、Vue 和 Ember 使团队能够构建越来越复杂的单页静态应用程序。像 webpack 这样的工具提高了创建、优化和管理构建工件的能力。
DevOps:DevOps 文化使 Web 应用程序开发人员能够分解和重新评估其 Web 应用程序基础架构,以更好地满足其 Web 应用程序的需求。
成熟的应用程序模式和实践:后端应用程序和服务正在围绕一组支持可移植性、可扩展性和高可用性的最佳实践进行融合。 这种趋势极大地增加了可用的工具和服务,尤其是与容器和容器编排相关的工具和服务。 许多这些实践刚刚开始应用于静态单页 Web 应用程序。