抖音技术能力大揭密!钜惠大礼、深度体验,尽在火山引擎增长沙龙,就等你来! 立即报名>> 了解详情
写点什么

不可变层,不(仅仅)是基础设施

2016 年 1 月 04 日

原文由 Kevin Fishner 发布于 2015 年 12 月 31 日

不可变的基础设施是一个有效的应用交付流程,但部署的过程会非常缓慢,且往往不能够充分利用资源。每次应用更新就构建镜像并创建新主机会导致每次部署都需要花费10 分钟甚至更多。如果主机上只有一个服务,通常不能完全利用处理器、内存和磁盘资源。如果只有20% 的资源使用率,将会造成严重的资源浪费。像 Nomad Mesos Kubernetes 这些调度器可以将应用和基础设施细分成不可变层,这将能够加快部署时间和增加资源密度,同时能够享受到不可变架构的益处。

调度器能够打包应用,例如 Docker 容器、JAR、binary 等等,然后发布到主机上。多个应用可以被放置到同一个主机上以增加资源密度,从而节约成本。例如,如果之前以 20% 的利用率使用了 100 个主机,可以换成以 80% 的资源利用率使用 25 个主机,这样就能减少 75% 的基础设施费用。由于部署过程仅仅是将打包好的应用放置到已经存在的主机上,部署的过程将会非常快。如果还要构建机器镜像,那就可能需要 10 分钟以上,构建应用的时间通常少于 5 分钟。相较于花费数分钟才能配置一台新机器,调度应用的时间是秒级的。完整的部署过程能够从 10 分钟以上减少到 2 至 3 分钟。一旦应用打包完成,部署过程仅需数秒钟。这就意味着开发团队可以更快地应对变化和迭代应用。更重要的是,应用程序仍然是不可变对象,即它是可以进行版本控制和审计的。

基础设施和应用不变性

一个调度器的工作流程能够在两个层级促进不可变基础设施。第一个是机器级,那就是使用不可变的机器镜像。第二个是应用级,即配置不可变的应用程序包。HashiCorp 工具的生态系统就是关于如何管理一个应用交付流程并同时在应用级和机器级促进不变性的例子。

首先,使用 Pacher Terraform 来配置基础环境。使用 Packer 为 Consul 和 Nomad 服务器构建镜像,然后通过 Terraform 创建 Consul 和 Nomad 集群。这样环境已经就绪,应用就能被 Nomad 调度并被 Consol 发现。如果没有像 Consul 这样的服务发现工具,又没有其他的发现方式,那么数以千计的应用程序只能被调度,而他们的位置将是未知的。

然后,需要配置服务器池,以给应用提供资源。使用 Pacher 配置含有通用包、一个 Nomad 代理和一个 Consul 代理的基础镜像。Terraform 利用先前的基础镜像配置好服务器池,同时创建好网络规则。工作流通常不被定义为有状态的服务,但许多公司在机器级管理存储,而不是应用级。Terraform 同样在机器级配置存储。

一旦主机配置好后,每台主机中的 Nomad 代理将会报告该主机中的资源总量和容量给中央 Nomad 服务器。Nomad 服务器保存服务器集群的总状态——可用资源和每台主机的容量,这些信息将被用来做调度决策。现在环境和 Nomad 服务器已经准备好接受工作和调度应用了。

为了充分利用应用开发和部署工作流,开发人员首先在本地环境中工作,一旦提交的修改被合并到主分支,Pacher 将会创建应用包,然后将自动被 Nomad 部署。当 Nomnad 将 Pacher 构建好的应用部署到主机上(详细信息请看下一节调度策略),它将把应用注册到本地的 Consul 代理中。由于应用程序是动态部署的,所以由 Consul 来处理服务发现是必不可少的。否则数以千计的应用被部署后就无法得知其 IP 和端口。Vault 能够安全地存储 Packer、Terraform 和 Nomad 配置环境和部署应用时使用的密钥。

从开发人员的角度来看,他们主要关注的是在本地测试后使用 CI 工具集成。部署过程能够完全自动化。操作人员通常负责建立自动化过程——构建、配置、部署、维护。

使用调度器声明应用依赖和部署策略:

复制代码
{
region = <span>"us"</span>
datacenters = [<span>"us-west-1"</span>, <span>"us-east-1"</span>]
update {
stagger = <span>"30s"</span>
max_parallel = <span>1</span>
}
group <span>"webs"</span> {
<span>count</span> = <span>5</span>
task <span>"frontend"</span> {
driver = <span>"docker"</span>
config {
image = <span>"hashicorp/web-frontend"</span>
}
restart {
interval = <span>"1m"</span>
attempts = <span>2</span>
<span>delay</span> = <span>"15s"</span>
}
service {
tags = [<span>"prod"</span>]
port = <span>"http"</span>
check {
type = <span>"http"</span>
path = <span>"/health"</span>
interval = <span>"10s"</span>
<span>timeout</span> = <span>"2s"</span>
}
}
env{
DB_HOST = <span>"db01.example.com"</span>
DB_USER = <span>"web"</span>
DB_PASSWORD = <span>"loremipsum"</span>
}
resources {
cpu = <span>500</span>
memory = <span>128</span>
network {
mbits = <span>100</span>
dynamic_ports = [
<span>"http"</span>,
<span>"https"</span>,
]
}
}
}
}
}

这个工作文件声明了五个 web 前端实例,Docker 容器应使用“us-west-1”和“us-west-2”数据中心来运行实例。每个任务需要 500MHz 的 CPU、128MB 内存和 100MBit 带宽。另外,Nomad 动态为每个任务分配一个端口,使得同一个任务的多个实例可运行于同一个主机上。如果上述的前端任务有一个静态分配的 80 端口,那么一台主机上将只能运行一个此任务的实例,因为每台主机中只有一个 80 端口。通过动态分配端口,一个前端任务实例可运行于 20100 端口另一个实例可运行于 20101 端口。

由于 Nomad 将不同类型的工作负载放置到了一个通用主机集群中,如果没有服务发现工具这些放置的工作负载的地址和端口将是未知的。使用像 Consul、 etcd 或者 Zookeeper 这样的服务发现工具允许在一个集群中动态放置工作负载同时被相关的依赖服务发现。“服务”模块通过在 Consul 将任务注册为名为$(job-name)-$(task-group)-$(task-name)的服务来完成它们。另外,它还为 Nomad 放置时分配的任务注册了动态端口,包括任何用于服务的健康检查任务。上述例子中的任务在 Consul 将被注册为以my-service-webs-frontend命名的服务。当然,服务发现集成需要 Consul 代理运行在 Nomad 调度的任务所运行的主机中。目前 Nomad 只有与 Consul 集成的 API,但 Nomad 的未来版本将会为集成定制服务发现解决方案暴露相应的 API。

使用调度器部署应用

可以使用如下命令将my-service任务提交至 Nomad 服务器并部署:

nomad <span>run</span> <span>my</span>-service.hcl当将一个任务提交至 Nomad 服务器后,它会开始调度决策过程。这个过程通过相关约束规则来寻找可用资源,然后使用 bin-packing 算法决定最优部署。

一起来看上述例子如何运行。前端任务的驱动类型为 Docker 容器,这意味着运行它需要主机上有 Docker 引擎。如果 Nomad 集群中有 100 个节点,其中有 40 个已经安装了 Docker 引擎,那么其余 60 个将会直接从放置选项中被去除。下一个过滤步骤是确定哪些主机有运行此任务的可用资源(CPU、内存、网络)。最终,Nomad 会生成分配可能性列表,然后基于最大化资源密度的原则做出最优决策。如果一个主机有 20% 的利用率而另一个有 60% 的利用率,Nomad 将会选择后一个来最大化密度。因为所有的这些信息存储在每个 Nomad 服务器中,调度过程将会非常迅速。不需要网络通信,它会极大地降低调度速度。

另外,Nomad 服务器可以通过并行运行的方式来增加调度吞吐量和提供高可用性。当 Nomad 运行在高可用性模式下时,Nomad 服务器会复制状态、参与调度决策和执行首领选举。首领负责运行所有的查询和事务。Nomad 是乐观并发的,意味着所有的服务器并行地参与作出调度决策。首领会提供额外必要的协调来确保安全性,同时保证客户端不被过度调度。由于 Nomad 是乐观并发的,同时运行三个 Nomad 服务器能够将吞吐量提升至三倍左右。Nomad 服务器将会找到主机中的资源并乐观地假设这些资源仍然可用。如果两个 Nomad 服务器尝试将冲突的工作负载放置到同一个主机中,第二个放置任务将会失败,任务将会被放回到调度队列中。由于 Nomad 能够在秒级作出调度决策,被放回到调度队列中的任务不会导致严重的调度延迟。

如果任务被放置成功,但由于其他原因失败了(例如 Docker 容器处于不可用状态),本地的 Nomad 客户端将需要重启任务。在上述的例子中,前端任务会试图每分钟进行两次重启,然后等待 15 秒钟直到另一个重启周期开始。Nomad 客户端会把它运行的每个任务的健康状态报告给中心 Nomad 服务器,这样服务器就能够知晓集群中所有任务的健康状态。如果本地的 Nomad 客户端不能重启任务,中心 Nomad 服务器将会把此任务放置到其他主机中。

现代化操作和正确的应用交付之路

正确地交付应用是一个复杂的过程。将工作流分为两个不可变层,应用和基础设置,能够在减少复杂性的同时加快部署速度和增加资源密度。通过构建机器镜像和使用这些镜像配置服务器来定义基础设施层。所有的这些组件可以通过代码进行配置,例如使用 Packer 模板和 Terraform 配置。应用层通过像 Docker 容器和静态二进制的应用包来进行定义,然后通过调度器或者静态分配的方式进行调度。这些组件也可以被定义为代码,例如使用 Packer 模板和 Nomad 任务文件。

可以使用像 Consul 这样的服务发现工具合并应用层和基础设施层。每个主机中都在基础设施层运行一个 Consul 代理。被调度的应用将被注册在本地代理中,所以应用可以被发现。例如,如果 200 个api任务被动态放置了,能够发现每个任务实例是非常关键的。任务所属主机中的 IP 地址和被动态分配的端口会被注册到北本地 Consul 代理中。为了负载均衡 200 个任务实例的流量,可以使用 Consul 注册数据填充一个 HA 代理配置:

以下是一个使用 Consul 注册数据来填充一个 HA 代理配置的 Consul 模板的例子:

复制代码
<span>backend webs
balance roundrobin
mode http</span><span>{{<span>range</span> <span>service</span> <span>"api"</span>}}</span><span>
server </span><span>{{<span>.Node</span>}}</span><span> </span><span>{{<span>.Address</span>}}</span><span>:</span><span>{{<span>.Port</span>}}</span><span></span><span>{{<span>end</span>}}</span><span></span>

以下代码将会渲染 HA 代理配置:

复制代码
<span>backend</span> <span>webs</span>
<span>balance</span> <span>roundrobin</span>
<span>mode</span> <span>http</span>
104<span>.131</span><span>.109</span><span>.224</span><span>:6379</span>
104<span>.131</span><span>.109</span><span>.224</span><span>:7812</span>
104<span>.131</span><span>.59</span><span>.59</span><span>:6379</span>

正如以上所展示的,由于 Nomad 能够探测端口冲突和动态分配端口来防止冲突,相同任务的多实例可被放置到同一个主机中(相同 IP)。服务发现使得调度器能够真正地最大化资源利用率,同时维护服务之间的连接。

将问题域分为两个层级,应用层和基础设施层,同时更进一步将他们分解为不同的组件:准备、配置、服务发现、安全和调度,这样就能允许组织将复杂的过程化解为期望的应用交付流程。

关于作者

Keven Fishner 是 HashiCorp 的客户部门主管。在 HashiCorp 他与开源和商业产品客户有丰富的工作经验。他有哲学学位(杜克大学)和工程贸易学学位。

查看英文原文: Immutable Layers, Not (Just) Infrastructure

2016 年 1 月 04 日 16:171512
用户头像

发布了 32 篇内容, 共 16.0 次阅读, 收获喜欢 8 次。

关注

评论

发布
暂无评论
发现更多内容

UT之最后一测

你呀不牛

2021高校IT专业大学生就业意向调查问卷

黑马腾云

跨湖跨仓场景下如何实现海量数据分钟级分析

华为云开发者社区

大数据 数据湖 数据分析 华为云FusionInsight MRS HetuEngine

云图说|数据可视化管理,搭载数据安全黑科技!华为数据安全中心,助你保障云上数据安全!

华为云开发者社区

数据安全 华为云 云图说 DSC 数据安全中心 云上数据

Jcenter 停止服务,说一说我们的迁移方案

Antway

android maven Gradle

生命中的无奈

小天同学

读书 读后感 生命 4月日更

软件 IT 专业的高校学生有关在线课程的问卷调查

程序员历小冰

ArrayList 与 LinkedList 底层结构

Kori Lin

Java

Kubernetes入门——Kubernetes实现应用的高可用

百度开发者中心

Kubernetes k8s入门 #技术课程#

高并发系列:架构优化之细说负载均衡

Coder的技术之路

负载均衡 高并发 高并发优化 负载均衡架构

C盘内存杀手,原来是这款出人意料的被闲置的软件|iTunes

彭宏豪95

效率 工具 4月日更 iTunes

浪潮云再次入围央采2021年云计算服务采购名单

浪潮云

云计算

Linux 上 定时备份postgresql 数据库

Yang

数据库 postgresql

被遗弃的 Vector 和 Stack

Kori Lin

Java

anyRTC 音视频 uni 插件集成步骤

anyRTC开发者

uni-app android 音视频 WebRTC sdk

智能小车系列-动力系统(ezPWM)

波叽波叽啵😮一口盐汽水喷死你

pwm ezPWM PWM信号

【XXX高校】软件IT专业学生(恋爱观)调查问卷

李浩宇/Alex

调查报告 大学生 恋爱

面向软件 IT 专业的高校大学生职业规划调查问卷

HaiJun

IT, 大学生

音视频编解码--编码参数CRF

Fenngton

ffmpeg 视频编解码 视频压缩 码率控制 CRF

SCA工具:开源安全威胁一手掌控

华为云开发者社区

开源 安全 测试 SCA 软件成分分析

基于Kubernetes Operator的网易数帆生产级云原生中间件实践

网易数帆

架构 Kubernetes 云原生 operator 中间件

如何从 0 到 1 开发 PyFlink API 作业

Apache Flink

flink pyflink python 3.5+

特斯拉行车数据被篡改?专家称车企很难自证清白,保留“数据指纹”的区块链技术在路上

CECBC区块链专委会

指纹

容器 & 服务: 扩容(二)

程序员架构进阶

Kubernetes 28天写作 弹性扩容 4月日更

获取chrome80谷歌浏览器存储的指定网站Cookie数据方法详解

老猿Python

Python chrome 爬虫 Cookie

5.1特辑|为何显示有票你却抢不到?技术揭秘12306如何保证车票不超卖

华为云开发者社区

数据库 GaussDB(for Redis) 五一 12306 数据强一致性

面向软件 IT 专业的高校大学生课余时间自学情况调查

xiezhr

大学生日常 IT 高校学院 问卷调查

LeetCode题解:151. 翻转字符串里的单词,栈,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

基于 HLS 创建 Golang 视频流服务器

天黑黑

go 音视频 HLS 声网

Android 设备音视频兼容性适配

网易云信

WebRTC

【劳动最光荣】TcaplusDB祝大家劳动节快乐

TcaplusDB

C# 数据库 nosql 后端 TcaplusDB

Study Go: From Zero to Hero

Study Go: From Zero to Hero

不可变层,不(仅仅)是基础设施-InfoQ