应用布局与部署的最佳实践
1. 应用布局原则与特性开关
1.1 应用布局第二原则
应用布局的第二个原则是,必须便于审查合并到代表集群真实来源的文件集中的每一项更改。
1.2 特性开关(Feature Gates)
当应用源代码和部署配置文件都处于版本控制之下时,一个常见问题是这些仓库之间的关系。对于小型项目,可以使用同一个仓库来管理应用源代码和配置;但对于大型项目,将两者分开更为合理。即便构建和部署应用的是同一批人,构建者和部署者的视角差异也使得这种关注点分离具有意义。
特性开关在连接源代码控制中的新特性开发与生产环境中的特性部署方面发挥着重要作用。当开发新特性时,开发工作完全在特性标志或开关之后进行。示例代码如下:
if (featureFlags.myFlag) {
// Feature implementation goes here
}
这种方法有诸多好处:
- 让团队在特性准备好发布之前很久就可以提交到生产分支,使特性开发与仓库的 HEAD 保持更紧密的一致,避免长期分支带来的可怕合并冲突。
- 启用特性只需进行配置更改来激活标志,能清晰显示生产环境中的更改,并且在特性激活出现问题时,回滚操作非常简单。
- 简化调试过程,禁用特性无需将代码回滚到旧版本,从而保留新版本的所有 bug 修复和其他改进。
1.3 应用布局第三原则
代码默认处于关闭状态,在特性标志之后进入源代码控制,并且仅通过对配置文件进行代码审查后的更改来激活。
2. 源代码控制中的应用管理
2.1 文件系统布局
为单个集群布局应用实例时,应按语义组件或层(如前端或批处理工作队列)来组织应用。对于使用两个服务的前端应用,文件系统布局可能如下:
frontend/
service-1/
service-2/
每个目录中存储着应用的配置文件,通常是 YAML 文件,这些文件直接代表集群的当前状态。建议在同一文件中包含服务名称和对象类型。虽然 Kubernetes 允许在同一文件中创建包含多个对象的 YAML 文件,但这通常是一种反模式,只有当对象在概念上相同时才适合分组。扩展后的文件系统布局示例如下:
frontend/
frontend-deployment.yaml
frontend-service.yaml
frontend-ingress.yaml
service-1/
service-1-deployment.yaml
service-1-service.yaml
service-1-configmap.yaml
2.2 定期版本管理
为了回顾应用部署的历史状态,并在部署稳定版本配置的同时迭代配置,需要能够同时存储和维护配置的多个版本。有两种方法可供选择:
| 方法 | 描述 | 优点 |
| ---- | ---- | ---- |
| 使用标签、分支和源代码控制特性 | 当准备发布时,在配置源代码控制系统中放置一个源代码控制标签(如
git tag v1.0
),标签代表该版本使用的配置,源代码控制的 HEAD 继续向前迭代。更新发布配置时,先将更改提交到仓库的 HEAD,然后在 v1.0 标签处创建一个名为 v1 的新分支,将所需更改挑选到发布分支(
git cherry-pick <edit>
),最后用 v1.1 标签标记该分支以表示新的点发布。 | 与人们在源代码控制中管理版本的方式相匹配,目录结构更简单 |
| 使用文件系统特性 | 每个版本化的部署存在于自己的目录中。例如: | 同时查看配置非常直观 |
frontend/
v1/
frontend-deployment.yaml
frontend-service.yaml
current/
frontend-deployment.yaml
frontend-service.yaml
service-1/
v1/
service-1-deployment.yaml
service-1-service.yaml
v2/
service-1-deployment.yaml
service-1-service.yaml
current/
service-1-deployment.yaml
service-1-service.yaml
3. 开发、测试和部署的应用结构
3.1 目标
应用在开发和测试方面有两个目标:
- 每个开发人员应能够轻松为应用开发新特性,能够在自己的环境中使用所有服务进行工作。
- 应用应便于在部署前进行准确测试,以确保在保持高可靠性的同时快速推出特性。
3.2 发布阶段
为实现上述目标,需要将开发阶段与发布版本相关联。发布阶段包括:
- HEAD:配置的前沿,最新更改。
- 开发(Development):基本稳定,但尚未准备好部署,适合开发人员构建特性。
- 预发布(Staging):测试的开始阶段,除非发现问题,否则不太可能更改。
- 金丝雀(Canary):首次向用户发布,用于测试实际流量中的问题,并让用户有机会测试即将推出的内容。
- 发布(Release):当前的生产版本。
3.3 引入开发标签
无论使用文件系统还是版本控制来构建发布版本,通过源代码控制标签来建模开发阶段是正确的方法。开发阶段的变化速度较快,仅在稳定性上稍落后于 HEAD。引入开发阶段时,在源代码控制系统中添加一个新的开发标签,并使用自动化过程将该标签向前移动。定期通过自动化集成测试来测试 HEAD,如果测试通过,则将开发标签移动到 HEAD。这样,开发人员在部署自己的环境时可以跟踪最新更改,同时确保部署的配置至少通过了有限的冒烟测试。
3.4 阶段与版本的映射
为避免版本和阶段的组合变得混乱,应引入阶段与版本之间的映射。在文件系统中,可以使用符号链接将阶段名称映射到版本;在版本控制中,只需在适当版本的同一修订处添加一个额外的标签。版本控制和阶段推进是两个同时进行的过程,一个用于创建新的发布版本,另一个用于使发布版本符合应用生命周期中的特定阶段。
4. 使用模板对应用进行参数化
4.1 参数化的必要性
当存在多个环境和阶段时,保持它们完全相同是不切实际或不可能的。为了使环境尽可能相似,减少不同环境之间的差异和漂移,使用参数化环境是很有用的。参数化环境使用模板来处理大部分配置,并混入一组有限的参数以生成最终配置。
4.2 使用 Helm 和模板进行参数化
有多种语言可用于创建参数化配置,它们通常将文件分为包含大部分配置的模板文件和可与模板组合以生成完整配置的参数文件。大多数模板语言允许参数在未指定值时使用默认值。以 Helm 为例,Helm 模板语言使用 “mustache” 语法:
metadata:
name: {{ .Release.Name }}-deployment
通过
values.yaml
文件传递参数:
Release:
Name: my-release
参数替换后结果为:
metadata:
name: my-release-deployment
4.3 参数化的文件系统布局
在文件系统布局中,将每个部署生命周期阶段视为参数文件和指向特定版本的指针的组合。例如,基于目录的布局可能如下:
frontend/
staging/
templates -> ../v2
staging-parameters.yaml
production/
templates -> ../v1
production-parameters.yaml
v1/
frontend-deployment.yaml
frontend-service.yaml
v2/
frontend-deployment.yaml
frontend-service.yaml
如果使用版本控制和标签,文件系统布局可能如下:
frontend/
staging-parameters.yaml
templates/
frontend-deployment.yaml
5. 全球范围内的应用部署
5.1 全球部署架构
每个 Kubernetes 集群通常位于一个区域,并包含应用的完整部署。全球应用部署由多个不同的 Kubernetes 集群组成,每个集群都有自己的应用配置。在文件系统中建模区域配置时,类似于添加新的生命周期阶段。例如,可能的阶段包括开发、预发布、金丝雀、美国东部、美国西部、欧洲、亚洲等。文件系统布局示例如下:
frontend/
staging/
templates -> ../v3/
parameters.yaml
eastus/
templates -> ../v1/
parameters.yaml
westus/
templates -> ../v2/
parameters.yaml
如果使用版本控制和标签,文件系统布局可能如下:
frontend/
staging-parameters.yaml
eastus-parameters.yaml
westus-parameters.yaml
templates/
frontend-deployment.yaml
为每个区域引入一个新标签,并使用该标签处的文件内容部署到该区域。
5.2 全球部署的实现
有了全球各区域的配置后,如何更新这些区域是一个关键问题。使用多个区域的主要目标之一是确保高可靠性和高可用性。软件版本更新通常是导致停机的主要原因,因此高可用系统的关键是限制任何更改的影响范围。在全球范围内推出版本时,应谨慎地从一个区域移动到另一个区域,并在进入下一个区域之前进行验证和建立信心。
可以使用 GitHub Actions 等工具自动化部署工作流。确定区域之间的推出时间间隔时,应考虑软件的 “平均冒烟时间”,即新发布版本推出到一个区域后,平均需要多长时间才能发现问题。一般来说,等待两到三倍的平均冒烟时间是一个合理的起点,但具体时间因应用而异。确定区域推出顺序时,应考虑各区域的特点,如高流量区域和低流量区域,以及某些特性在不同地理区域的受欢迎程度。通常先向低流量区域推出,以限制早期问题的影响范围;然后向高流量区域推出,验证版本在大规模下的正确性。制定发布计划后,应严格遵循,避免因加速发布而导致停机。
下面是一个简单的全球部署工作流的 mermaid 流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(更新 Staging 版本):::process --> B(部署到低流量区域):::process
B --> C{验证是否通过?}:::process
C -->|是| D(部署到高流量区域):::process
C -->|否| E(修复问题):::process
E --> B(部署到低流量区域):::process
D --> F{验证是否通过?}:::process
F -->|是| G(部署到其他区域):::process
F -->|否| E(修复问题):::process
综上所述,通过合理的应用布局、特性开关的使用、版本管理、参数化配置以及全球部署策略,可以实现高效、可靠的应用开发、测试和部署过程。这些方法和原则可以帮助团队更好地管理复杂的应用系统,提高开发效率和系统的稳定性。
6. 全球部署中的关键考量与操作流程细化
6.1 区域选择与发布顺序的深入分析
在确定全球部署的区域顺序时,除了考虑高流量和低流量区域,还需要关注其他因素。例如,不同区域的法规要求、网络基础设施、用户行为模式等。以下是一个综合考虑各因素的区域特性分析表格:
| 区域特性 | 描述 | 对部署的影响 |
| ---- | ---- | ---- |
| 流量大小 | 高流量区域能更好地测试应用在大规模负载下的性能;低流量区域可用于早期问题的发现 | 先低流量后高流量可降低风险 |
| 法规要求 | 不同地区有不同的数据保护、隐私等法规 | 需确保应用符合当地法规 |
| 网络基础设施 | 网络质量和稳定性影响应用的响应速度和可用性 | 网络好的区域可优先测试 |
| 用户行为模式 | 某些地区用户对特定功能的使用频率可能更高 | 可根据功能受欢迎程度安排部署顺序 |
6.2 部署工作流的详细步骤
使用 GitHub Actions 自动化部署工作流时,具体步骤如下:
1.
配置工作流文件
:在项目的
.github/workflows
目录下创建一个 YAML 文件,例如
deploy.yml
。
2.
定义触发条件
:可以设置为代码推送到特定分支时触发,如
push: branches: [main]
。
3.
设置环境
:指定运行工作流的环境,如
runs-on: ubuntu-latest
。
4.
步骤定义
:
- 检出代码:使用
actions/checkout@v2
动作。
- 安装依赖:根据应用需求安装必要的工具和库。
- 构建应用:执行构建命令,如
npm build
或
mvn package
。
- 部署到区域:根据区域配置,依次部署到各个区域。
以下是一个简化的 GitHub Actions 工作流示例:
name: Global Deployment Workflow
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Build application
run: npm build
- name: Deploy to low-traffic region
run: |
# 部署到低流量区域的命令
kubectl apply -f low-traffic-region-config.yaml
- name: Validate deployment in low-traffic region
run: |
# 验证部署的命令
kubectl get pods
- name: Deploy to high-traffic region
run: |
# 部署到高流量区域的命令
kubectl apply -f high-traffic-region-config.yaml
- name: Validate deployment in high-traffic region
run: |
# 验证部署的命令
kubectl get pods
6.3 平均冒烟时间的确定方法
确定软件的 “平均冒烟时间” 可以通过以下步骤:
1.
收集历史数据
:记录过去每次版本发布后,在各个区域发现问题的时间。
2.
计算平均值
:将所有发现问题的时间相加,然后除以发布次数,得到平均冒烟时间。
3.
考虑特殊情况
:对于一些特殊功能或重大更新,可能需要单独计算平均冒烟时间。
以下是一个简单的计算平均冒烟时间的 Python 代码示例:
# 假设这是历史发现问题的时间列表
discovery_times = [12, 24, 18, 30, 20]
# 计算平均冒烟时间
average_time_to_smoke = sum(discovery_times) / len(discovery_times)
print(f"平均冒烟时间: {average_time_to_smoke} 小时")
7. 应用布局与部署的综合优化建议
7.1 持续监控与反馈机制
建立持续监控系统,对应用在各个环境和区域的性能、可用性进行实时监控。可以使用 Prometheus 和 Grafana 等工具来收集和展示监控数据。同时,建立反馈机制,让用户和运维人员能够及时反馈问题,以便快速响应和解决。
7.2 自动化测试的扩展
除了自动化集成测试,还可以增加单元测试、端到端测试等多种测试类型。使用测试框架如 Jest、Selenium 等,确保应用的各个层面都得到充分测试。自动化测试可以在每次代码提交时自动触发,及时发现潜在问题。
7.3 团队协作与沟通
在应用开发、测试和部署过程中,团队成员之间的协作和沟通至关重要。可以使用项目管理工具如 Jira、Trello 来跟踪任务进度,使用 Slack、Microsoft Teams 等工具进行实时沟通。定期召开团队会议,分享进展和问题,确保团队目标一致。
7.4 文档与知识管理
完善的文档是团队高效协作的基础。编写详细的应用文档,包括架构设计、部署流程、配置说明等。使用知识管理工具如 Confluence 来存储和共享文档,方便团队成员随时查阅。
下面是一个团队协作流程的 mermaid 流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(开发人员提交代码):::process --> B(自动化测试):::process
B --> C{测试是否通过?}:::process
C -->|是| D(部署到开发环境):::process
C -->|否| E(开发人员修复问题):::process
E --> A(开发人员提交代码):::process
D --> F(测试人员进行测试):::process
F --> G{测试是否通过?}:::process
G -->|是| H(部署到预发布环境):::process
G -->|否| E(开发人员修复问题):::process
H --> I(进行用户验收测试):::process
I --> J{测试是否通过?}:::process
J -->|是| K(部署到生产环境):::process
J -->|否| E(开发人员修复问题):::process
通过以上综合优化建议,可以进一步提高应用开发、测试和部署的效率和质量,确保应用在全球范围内的稳定运行。在实际应用中,团队可以根据自身情况进行调整和改进,不断完善应用管理流程。
超级会员免费看
1843

被折叠的 条评论
为什么被折叠?



