Dailymotion 如何使用 Kubernetes:应用程序部署
我们 Dailymotion 三年前开始在生产中使用 Kubernetes。 但是跨多个集群部署应用程序很有趣,因此在过去几年中我们一直在努力改进我们的工具和工作流程。
从哪里开始
在这里,我们将介绍如何在世界各地的多个 Kubernetes 集群上部署应用程序。
要一次部署多个 Kubernetes 对象,我们使用
我们还在 Helm 之上编写了一个小型 Python 脚本来执行检查、创建图表、添加机密和部署应用程序。 所有这些任务都是使用 docker 镜像在中央 CI 平台上执行的。
让我们进入正题吧。
笔记。 当您阅读本文时,Helm 3 的第一个候选版本已经公布。 主要版本包含大量改进,以解决我们过去遇到的一些问题。
图表开发工作流程
我们对应用程序使用分支,并决定对图表应用相同的方法。
- 分支 开发 用于创建将在开发集群上进行测试的图表。
- 当拉取请求提交到 主,他们在分期中被检查。
- 最后,我们创建一个拉取请求以将更改提交到分支 刺 并将它们应用到生产中。
每个环境都有自己的私有存储库来存储我们的图表,我们使用
不同环境下的图表存储库
值得注意的是,当开发人员推送开发分支时,他们的图表版本会自动推送到开发图表博物馆。 因此,所有开发人员都使用相同的开发存储库,并且您需要仔细指定图表的版本,以免意外使用其他人的更改。
此外,我们的小 Python 脚本使用 Kubernetes OpenAPI 规范来验证 Kubernetes 对象
图表开发工作流程的一般描述
- 根据规范设置管道任务
gazr.io 用于质量控制(lint、单元测试)。 - 使用部署我们的应用程序的 Python 工具推送 docker 镜像。
- 通过分支名称设置环境。
- 使用 Kubeval 验证 Kubernetes yaml 文件。
- 自动增加图表及其父图表(依赖于正在更改的图表的图表)的版本。
- 向与其环境相匹配的图表博物馆提交图表
管理跨集群的差异
集群联邦
曾经有一段时间我们使用
为了解决这个问题,我们开始独立管理集群,这大大简化了流程(我们使用了联邦的第一个版本;第二个版本中可能发生了一些变化)。
地理分布式平台
我们的平台目前分布在 6 个区域 - 3 个本地区域和 3 个云端区域。
全球 Helm 价值观
4 个全局 Helm 值允许您识别集群之间的差异。 我们所有的图表都有默认最小值。
global:
cloud: True
env: staging
region: us-central1
clusterName: staging-us-central1
全球价值观
这些值帮助定义我们的应用程序的上下文,并用于各种目的:监视、跟踪、日志记录、进行外部调用、扩展等。
- “云”:我们有一个混合 Kubernetes 平台。 例如,我们的 API 部署在 GCP 区域和我们的数据中心中。
- “env”:对于非生产环境,某些值可能会发生变化。 例如,资源定义和自动缩放配置。
- “region”:此信息有助于确定集群的位置,并可用于确定外部服务的附近端点。
- “clusterName”:如果我们想要为单个集群定义一个值。
这是一个具体的例子:
{{/* Returns Horizontal Pod Autoscaler replicas for GraphQL*/}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}
头盔模板示例
此逻辑在帮助程序模板中定义,以避免 Kubernetes YAML 混乱。
申请公告
我们的部署工具基于多个 YAML 文件。 下面是我们如何在集群中声明服务及其扩展拓扑(副本数量)的示例。
releases:
- foo.world
foo.world: # Release name
services: # List of dailymotion's apps/projects
foobar:
chart_name: foo-foobar
repo: [email protected]:dailymotion/foobar
contexts:
prod-europe-west1:
deployments:
- name: foo-bar-baz
replicas: 18
- name: another-deployment
replicas: 3
服务定义
这是定义我们的部署工作流程的所有步骤的概述。 最后一步将应用程序同时部署到多个工作集群。
那么秘密呢?
关于安全性,我们跟踪来自不同地方的所有秘密并将它们存储在一个独特的保险库中
我们的部署工具从 Vault 中提取秘密值,并在部署时间到来时将它们插入 Helm 中。
为此,我们定义了 Vault 中的机密与我们的应用程序所需的机密之间的映射:
secrets:
- secret_id: "stack1-app1-password"
contexts:
- name: "default"
vaultPath: "/kv/dev/stack1/app1/test"
vaultKey: "password"
- name: "cluster1"
vaultPath: "/kv/dev/stack1/app1/test"
vaultKey: "password"
- 我们定义了在 Vault 中记录机密时要遵循的一般规则。
- 如果秘密适用 到特定的上下文或集群,您需要添加特定条目。 (这里上下文 cluster1 有自己的秘密 stack-app1-password 值)。
- 否则使用该值 默认情况下.
- 对于此列表中的每一项 库伯内特的秘密 插入一个键值对。 因此,我们图表中的秘密模板非常简单。
apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
{{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
name: "{{ .Chart.Name }}"
labels:
chartVersion: "{{ .Chart.Version }}"
tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: Opaque
挑战与局限
使用多个存储库
现在我们将图表和应用程序的开发分开。 这意味着开发人员必须在两个 git 存储库中工作:一个用于应用程序,另一个用于定义其到 Kubernetes 的部署。 2 个 git 存储库意味着 2 个工作流程,新手很容易感到困惑。
管理通用图表很麻烦
正如我们已经说过的,通用图表对于识别依赖关系和快速部署多个应用程序非常有用。 但我们用 --reuse-values
以避免每次部署属于此通用图表一部分的应用程序时传递所有值。
在持续交付工作流程中,我们只有两个定期变化的值:副本数量和镜像标签(版本)。 其他的,更稳定的值是手动改变的,这是相当困难的。 此外,正如我们从自己的经验中看到的那样,部署通用图表时的一个错误可能会导致严重的失败。
更新多个配置文件
当开发人员添加新应用程序时,他必须更改多个文件:应用程序声明、机密列表、将应用程序添加为依赖项(如果该应用程序包含在通用图表中)。
Vault 中的 Jenkins 权限过于扩展
现在我们有一个
回滚过程不是自动的
要回滚,您需要在多个集群上运行该命令,这充满了错误。 我们手动执行此操作以确保指定正确的版本 ID。
我们正在迈向 GitOps
我们的目标
我们希望将图表返回到它部署的应用程序的存储库。
工作流程将与开发相同。 例如,当一个分支被推送到master时,会自动触发部署。 这种方法与当前工作流程的主要区别在于 一切都将在 git 中管理 (应用程序本身及其在 Kubernetes 中的部署方式)。
有几个优点:
- 更 更清晰 对于开发商来说。 了解如何在本地图表中应用更改会更容易。
- 可以指定服务部署定义 与代码相同的地方 服务。
- 管理通用图表的删除。 该服务将有自己的 Helm 版本。 这将允许您在最小的级别上管理应用程序生命周期(回滚、升级),以免影响其他服务。
- git 的好处 用于图表管理:撤消更改、审核日志等。如果需要撤消对图表的更改,可以使用 git 来执行此操作。 部署自动开始。
- 您可以考虑使用以下工具改进您的开发工作流程 脚手架,开发人员可以使用它在接近生产的环境中测试更改。
两步迁移
我们的开发人员已经使用此工作流程两年了,因此我们希望迁移尽可能轻松。 因此,我们决定在实现目标的过程中添加一个中间步骤。
第一阶段很简单:
- 我们保留类似的结构来设置应用程序部署,但在名为 DailymotionRelease 的单个对象中。
apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
name: "app1.ns1"
environment: "dev"
branch: "mybranch"
spec:
slack_channel: "#admin"
chart_name: "app1"
scaling:
- context: "dev-us-central1-0"
replicas:
- name: "hermes"
count: 2
- context: "dev-europe-west1-0"
replicas:
- name: "app1-deploy"
count: 2
secrets:
- secret_id: "app1"
contexts:
- name: "default"
vaultPath: "/kv/dev/ns1/app1/test"
vaultKey: "password"
- name: "dev-europe-west1-0"
vaultPath: "/kv/dev/ns1/app1/test"
vaultKey: "password"
- 每个应用程序 1 个版本(没有通用图表)。
- 应用程序 git 存储库中的图表。
我们已经与所有开发人员进行了交谈,因此迁移过程已经开始。 第一阶段仍然使用CI平台进行控制。 我很快就会写另一篇关于第二阶段的文章:我们如何迁移到 GitOps 工作流程
在这里,我们试图描述过去几年我们在应用程序部署工作流程中取得的进展,这引发了人们对 GitOps 方法的思考。 我们还没有达到目标,将报告结果,但现在我们确信,当我们决定简化一切并使其更接近开发人员的习惯时,我们做了正确的事情。
来源: habr.com