Linux 上的 .NET Core,马背上的 DevOps

我们尽最大努力开发 DevOps。 我们有 8 个人,Vasya 是 Windows 中最酷的。 突然 Vasya 离开了,我的任务是启动一个由 Windows 开发提供的新项目。 当我把整个 Windows 开发堆栈倒在桌子上时,我意识到情况很痛苦......

故事就是这样开始的 亚历山德拉·辛奇诺娃开发运营大会。 当这位领先的 Windows 专家离开公司时,亚历山大想知道现在该怎么办。 当然是切换到 Linux! Alexander 将通过为 100 个最终用户完成的项目示例,告诉您他如何成功开创先例,并将部分 Windows 开发转移到 Linux。

Linux 上的 .NET Core,马背上的 DevOps

如何使用TFS、Puppet、Linux .NET core轻松轻松地将项目交付到RPM? 如果开发团队第一次听到Postgres和Flyway这两个词,并且截止日期是后天,如何支持项目数据库的版本控制? 如何与Docker集成? 如何激励 .NET 开发人员放弃 Windows 和 Smoothies,转而使用 Puppet 和 Linux? 如果没有实力、没有意愿、没有资源来维护 Windows 的生产,如何解决意识形态冲突? 关于这一点,以及关于 Web 部署、测试、CI、关于在现有项目中使用 TFS 的实践,当然还有关于破拐杖和工作解决方案,都在 Alexander 的报告笔录中。


于是,Vasya离开了,任务就落在了我身上,开发者们拿着干草叉不耐烦地等待着。 当我最终意识到瓦夏无法归来时,我开始谈正事。 首先,我评估了我们的机群中 Win 虚拟机的百分比。 分数对 Windows 不利。

Linux 上的 .NET Core,马背上的 DevOps

由于我们正在积极开发 DevOps,我意识到交付新应用程序的方法需要进行一些改变。 只有一种解决方案 - 如果可能的话,将所有内容转移到 Linux。 Google 帮助了我——当时 .Net 已经被移植到 Linux 上,我意识到这就是解决方案!

为什么将 .NET core 与 Linux 结合起来?

造成这种情况的原因有几个。 在“付钱”和“不付钱”之间,大多数人会选择第二个——就像我一样。 MSDB 许可证的费用约为 1 美元;维护一组 Windows 虚拟机的费用为数百美元。 对于一家大公司来说,这是一笔很大的开支。 这就是为什么 储  - 第一个原因。 不是最重要的,而是最重要的之一。

Windows 虚拟机比 Linux 虚拟机占用更多资源 - 它们很重。 鉴于公司的规模,我们选择了Linux。

该系统只需集成到现有 CI 中。 我们认为自己是进步的 DevOps,我们使用 Bamboo、Jenkins 和 GitLab CI,所以我们的大部分工作都在 Linux 上运行。

最后一个原因是 方便的伴奏。 我们需要降低“护卫”的准入门槛,即了解技术部分、确保服务不间断、维护二线服务的人员。 他们已经熟悉 Linux 堆栈,因此他们理解、支持和维护新产品比花费额外的资源来了解 Windows 平台软件的相同功能要容易得多。

需求

首先也是最重要的—— 新解决方案为开发人员带来便利。 并不是所有人都做好了改变的准备,尤其是在 Linux 这个词被提出之后。 开发人员想要他们最喜欢的 Visual Studio、TFS 以及程序集和冰沙的自动测试。 如何交付生产对他们来说并不重要。 因此,我们决定不改变通常的流程,对Windows开发保持一切不变。

需要新项目 集成到现有的 CI 中。 轨道已经在那里,所有工作都必须考虑配置管理系统的参数、公认的交付标准和监控系统来完成。

易于支持和操作,作为来自不同部门和支持部门的所有新参与者的最低进入门槛的条件。

截止日期 - 昨天.

赢发展集团

当时 Windows 团队在与什么合作?

Linux 上的 .NET Core,马背上的 DevOps

现在我可以自信地说 身份服务器4 是 ADFS 的一个很酷的免费替代品,具有类似的功能,或者什么 实体框架核心 - 开发人员的天堂,在这里您不必费心编写 SQL 脚本,而是用 OOP 术语描述数据库中的查询。 但后来,在讨论行动计划的过程中,我把这个堆栈视为苏美尔楔形文字,只识别 PostgreSQL 和 Git。

当时我们正在积极使用 木偶 作为配置管理系统。 在我们的大多数项目中,我们使用 亚搏体育app CI, 松紧带,平衡高负载服务使用 HA代理 监控一切 ZABBIX, 韧带 格拉法纳 и 普罗米修斯, ,而这一切都在铁片上旋转 HPESXi的 上 VMware的。 每个人都知道它 - 该类型的经典。

Linux 上的 .NET Core,马背上的 DevOps

让我们看看并尝试了解在我们开始所有这些干预措施之前发生了什么。

是什么

TFS 是一个相当强大的系统,不仅可以将代码从开发人员交付到最终的生产机器,而且还具有一套与各种服务非常灵活的集成——以跨平台级别提供 CI。

Linux 上的 .NET Core,马背上的 DevOps
以前,这些都是实心窗户。 TFS 使用了多个构建代理,这些代理用于组装许多项目。 每个代理有 3-4 个工作人员来并行任务并优化流程。 然后,根据发布计划,TFS将刚刚出炉的Build交付到Windows应用服务器上。

我们想要实现什么目标?

我们使用 TFS 进行交付和开发,并在 Linux 应用程序服务器上运行应用程序,它们之间存在某种魔力。 这 魔术盒 还有前面的工作。 在拆解它之前,我先先谈谈该应用程序。

项目

该应用程序提供处理预付卡的功能。

Linux 上的 .NET Core,马背上的 DevOps

客户

有两种类型的用户。 第一 通过使用 SSL SHA-2 证书登录获得访问权限。 U 第二 使用登录名和密码进行访问。

HAProxy的

然后客户端请求去了HAProxy,解决了以下问题:

  • 主要授权;
  • SSL 终止;
  • 调整 HTTP 请求;
  • 广播请求。

客户端证书已沿链进行验证。 我们 - 权威 我们可以负担得起,因为我们自己为服务客户颁发证书。

请注意第三点,我们稍后再回到它。

后端

他们计划在 Linux 上制作后端。 后端与数据库交互,加载必要的权限列表,然后根据授权用户拥有的权限,提供签署财务文档并将其发送以供执行或生成某种报告的访问权限。

使用 HAProxy 节省成本

除了每个客户端导航的两个上下文之外,还有一个身份上下文。 身份服务器4 只允许您登录,这是一个免费且功能强大的模拟 ADFS的  - 活动目录联合服务.

身份请求分几个步骤进行处理。 第一步 - 客户 进入后台,它与该服务器通信并检查客户端的令牌是否存在。 如果没有找到,请求将返回到它来自的上下文,但会进行重定向,并且通过重定向它会转到身份。

第二步——请求已收到 到IdentityServer中的授权页面, 客户端注册的地方,等待已久的令牌出现在 IdentityServer 数据库中。

第三步—— 客户端被重定向回来 到它所来自的上下文。

Linux 上的 .NET Core,马背上的 DevOps

IdentityServer4有一个特点: 它通过 HTTP 返回对返回请求的响应。 无论我们如何努力设置服务器,无论我们如何通过文档启发自己,每次我们收到带有通过 HTTPS 发送的 URL 的初始客户端请求时,IdentityServer 返回相同的上下文,但使用 HTTP。 我们很震惊! 我们通过身份上下文将所有这些传输到 HAProxy,并且在标头中我们必须将 HTTP 协议修改为 HTTPS。

有什么改进以及在哪里节省了?

我们通过使用免费的解决方案来授权一组用户和资源,从而节省了资金,因为我们没有将 IdentityServer4 作为单独的节点放置在单独的段中,而是将其与应用程序后端运行的同一服务器上的后端一起使用。

它应该如何运作

所以,正如我所承诺的 - 魔盒。 我们已经明白,我们一定会转向 Linux。 让我们制定需要解决方案的具体任务。

Linux 上的 .NET Core,马背上的 DevOps

傀儡显现。 为了交付和管理服务和应用程序配置,必须编写很酷的配方。 一卷铅笔雄辩地表明了它的完成速度和效率。

运输方式。 标准是转速。 每个人都知道,在 Linux 中你离不开它,但项目本身在组装后是一组可执行的 DLL 文件。 大约有150人左右,工程难度相当大。 唯一和谐的解决方案是将这个二进制文件打包到 RPM 中并从中部署应用程序。

版本控制。 我们必须经常发布,并且必须决定如何形成包名称。 这是与TFS集成程度的问题。 我们在 Linux 上有一个构建代理。 当 TFS 将任务发送到处理程序(工作程序)到构建代理时,它还会向其传递一堆最终出现在处理程序进程的环境中的变量。 这些环境变量包含构建名称、版本名称和其他变量。 在“构建 RPM 包”部分中了解更多相关信息。

设置 TFS 归根结底是建立管道。 以前,我们在Windows代理上收集了所有Windows项目,但现在出现了Linux代理——一个Build代理,它需要包含在构建组中,用一些工件进行丰富,并告诉将在这个Build代理上构建什么类型的项目,并以某种方式修改管道。

身份服务器。 ADFS 不是我们的方式,我们追求开源。

让我们来看看这些组件。

魔术盒

由四部分组成。

Linux 上的 .NET Core,马背上的 DevOps

Linux 构建代理。 Linux,因为我们是为它而构建的——这是合乎逻辑的。 这部分分三步完成。

  • 配置工作人员 而且并不孤单,因为预计该项目的工作将分散进行。
  • 安装.NET Core 1.x。 当 1 已经在标准存储库中可用时,为什么还要使用 2.0.x? 因为我们开始开发的时候稳定版本是1.09,就决定基于它来做项目。
  • git 2.x.

RPM 存储库。 RPM 包需要存储在某个地方。 假设我们将使用可供所有 Linux 主机使用的同一公司 RPM 存储库。 他们就是这么做的。 存储库服务器已配置 网络挂钩 即从指定位置下载所需的RPM包。 包版本由构建代理报告给 webhook。

亚搏体育app。 注意力! 这里的 GitLab 不是由开发人员使用,而是由运营部门使用,用于控制应用程序版本、软件包版本、监控所有 Linux 机器的状态,并且它存储配方 - 所有 Puppet 清单。

木偶 — 解决了所有有争议的问题并提供了我们想要从 Gitlab 获得的配置。

我们开始潜水。 DLL 传递到 RPM 是如何工作的?

将 DDL 交付给 RPM

假设我们有一位 .NET 开发摇滚明星。 它使用 Visual Studio 并创建一个发布分支。 之后,它上传到Git,这里的Git是一个TFS实体,也就是说,它是开发人员使用的应用程序存储库。

Linux 上的 .NET Core,马背上的 DevOps

之后,TFS 发现新的提交已到达。 哪个应用程序? 在 TFS 设置中,有一个标签指示特定构建代理拥有哪些资源。 在本例中,他看到我们正在构建一个 .NET Core 项目,并从池中选择一个 Linux 构建代理。

构建代理接收源并下载必要的 依赖 来自 .NET 存储库、npm 等。 在构建应用程序本身并进行后续打包之后,将 RPM 包发送到 RPM 存储库。

另一方面,会发生以下情况。 运营部门工程师直接参与项目的推出:他更改了包的版本 希拉 在存储应用程序配方的存储库中,之后 Puppet 触发 百胜,从存储库中获取新包,并且新版本的应用程序可供使用。

Linux 上的 .NET Core,马背上的 DevOps

一切都很简单,但是构建代理本身内部会发生什么?

打包 DLL RPM

从 TFS 接收项目源和构建任务。 构建代理 开始从源代码构建项目本身。 组装好的项目可以作为一套提供 DLL文件,它们打包在 zip 存档中以减少文件系统的负载。

ZIP 存档被丢弃 到 RPM 包构建目录。 接下来,Bash 脚本初始化环境变量,查找 Build 版本、项目版本、构建目录的路径,并运行 RPM-build。 构建完成后,包将发布到 本地存储库,它位于构建代理上。

接下来,从Build代理到RPM存储库中的服务器 JSON 请求已发送 指示版本和构建的名称。 我之前谈到的 Webhook 从构建代理上的本地存储库下载这个包,并使新程序集可供安装。

Linux 上的 .NET Core,马背上的 DevOps

为什么要采用这种特定的包交付方案到 RPM 存储库? 为什么我不能立即将组装好的包发送到存储库? 事实上,这是确保安全的一个条件。 这种情况限制了未经授权的人将 RPM 软件包上传到所有 Linux 计算机均可访问的服务器的可能性。

数据库版本控制

在与开发团队协商后,发现这些人更接近 MS SQL,但在大多数非 Windows 项目中我们已经全力使用 PostgreSQL。 既然我们已经决定放弃一切付费的东西,我们也开始在这里使用 PostgreSQL。

Linux 上的 .NET Core,马背上的 DevOps

在这一部分中,我想告诉您我们如何对数据库进行版本控制以及我们如何在 Flyway 和 Entity Framework Core 之间进行选择。 让我们看看它们的优点和缺点。

缺点

Flyway只有一个方向,我们 我们无法回滚 - 这是一个显着的缺点。 您可以在其他方面将其与 Entity Framework Core 进行比较 - 就开发人员的便利性而言。 您还记得我们把它放在最前面,主要标准是不改变 Windows 开发的任何内容。

对于 Flyway 我们 需要某种包装纸这样人们就不会写 SQL查询。 它们更接近于 OOP 术语的操作。 我们编写了使用数据库对象的说明,生成了 SQL 查询并执行它。 新版本的数据库已准备就绪,经过测试 - 一切都很好,一切正常。

Entity Framework Core 有一个缺点 - 在重负载下 构建次优 SQL 查询,并且数据库中的缩减可能会很大。 但由于我们没有高负载的服务,所以我们不会以数百RPS来计算负载,我们接受了这些风险并将问题委托给未来的我们。

优点

实体框架核心 开箱即用且易于开发,和飞行路线 轻松集成到现有 CI。 但我们为开发人员提供了方便:)

卷起程序

Puppet 发现软件包版本即将发生变化,包括负责迁移的版本。 首先,它安装一个包含迁移脚本和数据库相关功能的包。 此后,与数据库一起使用的应用程序将重新启动。 接下来是其余组件的安装。 Puppet 清单中描述了安装包和启动应用程序的顺序。

应用程序使用敏感数据,例如令牌、数据库密码,所有这些都从 Puppet Master 提取到配置中,并以加密形式存储。

TFS问题

在我们决定并意识到一切确实对我们有用之后,我决定为其他项目的 Win 开发部门看看 TFS 中的程序集作为一个整体发生了什么情况 - 无论我们是否快速构建/发布,以及发现速度方面存在重大问题。

其中一个主要项目需要 12 到 15 分钟才能组装起来——那是很长的时间,你不能这样生活。 快速分析显示 I/O 严重下降,而且是在阵列上。

经过逐个组件分析后,我确定了三个焦点。 第一的 - “卡巴斯基杀毒软件”,它扫描所有 Windows 构建代理上的源。 第二 - Windows 索引器。 它没有被禁用,并且在部署过程中所有内容都在构建代理上实时索引。

第三 - npm 安装。 事实证明,在大多数管道中,我们都使用了这种确切的场景。 他为什么不好? 当依赖树形成时,运行 Npm 安装过程 包lock.json,其中记录了将用于构建项目的包的版本。 缺点是 Npm install 每次都会从互联网上获取最新版本的软件包,这在大型项目中会花费大量时间。

开发人员有时会在本地计算机上进行实验,以测试特定部分或整个项目的工作方式。 有时,事实证明一切在本地都很酷,但他们组装起来,推出来,却没有任何效果。 我们开始找出问题所在——是的,不同版本的软件包具有依赖关系。

  • AV 例外中的来源。
  • 禁用索引。
  • 去 国家管理委员会.

npm ci 的优点是我们 我们收集一次依赖树,我们有机会为开发商提供 当前的包列表,他可以随心所欲地在本地进行实验。 这 节省时间 编写代码的开发人员。

布局

现在介绍一下存储库配置。 历史上我们使用 关系 用于管理存储库,包括 内部回购协议。 这个内部存储库包含我们用于内部目的的所有组件,例如自编写的监控。

Linux 上的 .NET Core,马背上的 DevOps

我们还使用 的NuGet,因为与其他包管理器相比,它具有更好的缓存。

导致

在我们优化构建代理后,平均构建时间从 12 分钟减少到 7 分钟。

如果我们算上所有本可以用于 Windows 的机器,但在这个项目中切换到 Linux,我们节省了大约 10 美元。这还只是许可证费用,如果我们考虑到内容的话,还可以节省更多。

计划

在下个季度,我们计划致力于优化代码交付。

切换到预构建的 Docker 镜像。 TFS 是一个很酷的东西,它有许多插件,允许您集成到 Pipeline 中,包括基于触发器的组装,例如 Docker 映像。 我们想为同一个触发器创建一个触发器 包lock.json。 如果用于构建项目的组件的组成发生某种变化,我们会构建一个新的 Docker 镜像。 随后用于部署带有组装的应用程序的容器。 现在情况并非如此,但我们正计划切换到 Kubernetes 中的微服务架构,该架构正在我们公司积极开发,并且长期以来一直服务于生产解决方案。

总结

我鼓励大家扔掉Windows,但这并不是因为我不知道如何烹饪它。 原因是大多数开源解决方案都是 Linux堆栈。 你还好吗 节省资源。 在我看来,未来属于具有强大社区的 Linux 上的开源解决方案。

亚历山大·辛奇诺夫 (Alexander Sinchinov) 演讲者简介 在 GitHub 上.

开发运营大会 是一个由专业人士为专业人士举办的开发、测试和运营流程整合的会议。 这就是亚历山大谈到的项目的原因? 已实施并正在工作,演出当天有两个成功的版本。 在 RIT++ 的 DevOps 大会 27月28日、XNUMX日还会有更多类似的学员案例。 您仍然可以跳上最后一节车厢 提交一份报告 或者慢慢来 预订 票。 在斯科尔科沃与我们见面!

来源: habr.com

添加评论