07月11日
DevOps 工程师成长日记系列五:部署

原文地址:https://medium.com/@devfire/how-to-become-a-devops-engineer-in-six-months-or-less-part-5-deploy-83e790545c23
原文作者:Igor Kantor
翻译君:CODING 戴维奥普斯

让我们简要回顾下我们的 DevOps 之旅:
在第一篇,我们介绍了 DevOps 文化以及相关的基础技能;
在第二篇,我们讨论了如何为将来的代码部署奠定基础;
在第三篇,我们讨论了如何有组织地管理代码;
在第四篇,我们讨论如何简单地打包代码。
以下是我们贯穿前后的路线图:

图片

如果在上图每列的技术栈上花费一个月左右的话,那么我们现在处于第 4 个月。基于前文的学习,我们已经知道了如何配置将要运行代码的服务器基础架构、如何正确地对代码进行版本管理、如何将代码打包以备部署。今天我们要讨论如何部署代码。

部署代码

注意到了吗?我没有说“如何轻松地部署代码”,因为代码从开发环境到正确部署仍然是一个充满了错误和失败的痛苦过程。

原因很多,但在我看来, 这主要归结为差异。具体而言, 创建代码的环境与实际代码运行的环境之间存在差异。我认为减少这些差异意味着你不仅可以在整体代码部署中实现最大的改进,还可以在代码部署后的运行时达到一定的优化。那么,我们如何减少或消除生产和非生产环境之间的差异呢?

在我的机器上明明是可以跑的

如果你的开发基础设施是这样的:

图片
用“温柔的爱和关怀”手工组装而成的开发基础设施

但你的生产环境基础设施看起来像这样:

图片

那你就会遇到麻烦。

如果你使用 基础设施即为代码的方式而不是手动配置,那么差异这事儿你已经搞定得七七八八了。如果不是,请不要绝望 —— 你并不孤单。花一个下午,找出你所碰到的所有差异(培训、文化、人员、流程等),并逐一消除它们。

最重要的是,如果你仍在手动配置,那你可能很难去管理现代技术栈。因此你需要做的第一件事是确保涉及产品的所有内容都是由部署服务器构建的版本化软件包。假设上述事情你已经完成,我会告诉你 部署代码的最佳方法是不部署代码

现代化的代码部署

将代码部署到生产环境机器是一件非常 90 年代的事情。

图片
现有技术的“代码部署装置”

将代码部署到一组固定生产环境机器的最大问题是:你的生产环境服务器(代码运行的地方)与你的开发环境服务器(编写代码的地方)不同。这就难怪在部署后会立即出现大量问题。

因此,你需要尽一切可能确保构建产物(而不是一小段代码)一直处在运行环境当中。换句话说, 将代码一次性部署到开发环境,克隆运行代码的整个机器环境,然后将其复制到需要的任何位置。这被称为“不可变部署”,是一个非常强大的模式,可以避免你数小时部署后的头痛。当然,如果你运行容器,同样的想法也是适用的:在任何地方部署相同的容器即可。

“但是我的生产环境和开发环境就是不同的!”你可能会说。数据库用户名密码,连接字符串,S3 存储桶位置等等,这些都是不同的。解决这个问题的方法是使用 12 因子应用配置原则。 所有配置都需要外部化并作为环境变量传递到服务器。

例如,如果在 AWS,可以使用 SSM 作为外部参数存储,它很好地集成了 CloudFormation。直接通过 aws ssm cli 命令行工具设置环境变量也非常容易。当然,其它云厂商也提供了类似的机制。

当出现问题时,你需要压制“修理”生产环境机器的冲动。这些机器是不可变的,这意味着你所做的任何修复都必须来自开发环境。 事实上,你的终极目标应该是根本不允许任何在生产环境服务器上的接入。没有 ssh、没有 scp、没有人有任何访问权限,不是你,更不是觊觎中的黑客。

但如果我需要日志来解决问题呢? 所以日志也应该外部化。理想情况下可以通过ElasticSearch / Logstash / Kibana(ELK)技术栈或商业软件(如 SumoLogic 或Datadog)将日志转储到其它地方。

无论你做什么,你的产品都是“黄牛” —— 它们会在出现最轻微的不健康信号时就被替换。它们不是“宠物”,需要耗费数小时进行故障排除来恢复健康。我知道这个比喻被太多人使用了,并且我听到那些真正养牛的人说过实际上他们的工作原理和我们刚所讨论的不同,但重点事务确实如此。 不要“修复”你的生产环境机器,而是修复你的开发环境并重新部署。

代码部署机制

所以你知道要做些啥了,但怎么做呢?

“不幸”的是,这就是 Jenkins 的用武之地,Jenkins 是最受欢迎的开源部署自动化服务器之一。我说“不幸”是因为 Jenkins(及其前任 Hudson)已经存在了近十年,并且在漫长的使用过程当中我们发现了:它的设置很复杂,维护起来更复杂。它带有数以百万计的可疑质量插件。这些插件往往会在最不合适的时候崩溃,把所有事情搞砸。实际上,真正具有弹性的分布式 Jenkins 设置很少见,通常只有最大的研发组织里才能看到。

那为什么我还建议你从 Jenkins 开始呢?因为尽管存在各种缺陷,它仍然非常受欢迎,并且在我们的行业中被大量使用。了解 Jenkins,特别是 Jenkinsfile 的结构,对就业前景会是一个巨大并且不容忽视的好处。当你学习 Jenkins 时,请确保你遵循较新的 Pipeline BlueOcean 技术路径,而不是更旧的“Jenkins jobs”。

参考阅读:
Jenkinsfile:https://jenkins.io/doc/book/pipeline/jenkinsfile/
Pipeline BlueOcean:https://jenkins.io/doc/book/blueocean/

当你希望 CI/CD 流水线被直接包含在代码仓库当中后,持续集成工具会变得非常重要,这样的话流水线本身就是一段版本化的代码。

一切都是代码

你的应用程序如何被部署、监控、配置等等——说到底最终都化作为存储在代码仓库里被正确版本化的代码片段。

我们的目标是 为核心开发人员(编写功能代码的软件工程师)创建一个真正无摩擦的环境。例如,我应该能够编写我自己的微服务、添加我认为必要的测试、添加监控即代码的配置、在一些“env.yaml” 文件中指定我的参数、将它们全部存储在一个代码仓库中;通过 CI/CD 流水线自动触发构建、测试、部署(金丝雀发布或蓝绿发布),并在完成后给我发送电子邮件。事实上,这是 DevOps 工程师核心使命的本质。

Jenkins 的替代品

就像我之前说过的那样,Jenkins 已经被广大开发者使用很长一段时间了。现在还有其它的工具,在我看来更好,即使没有 Jenkins 那么为人所知。

  • 一个是 AWS 自己的 CodeDeploy 服务。它有局限性,但 CodeDeploy 背后的开发人员在过去一年做了很大的改进,如果你在用 AWS,建议你试一试。
  • 另一个是 GitLab CI。如果你的研发组织运行在 GitLab 上,你可以考虑使用,因为它与 GitLab 的其它部分良好地集成在一起。
  • 去年十月 GitHub 宣布了 Actions(目前仍处在公测中),用于 GitHub 用户的自动化工作流场景。

我认为这里的工具并不是最重要的。重要的是要记住包括代码部署流水线在内的所有内容都是版本化的软件部件,它首先得来自于开发环境,而不是生产环境。

如果你从 Jenkins 开始学习持续集成,请尝试将其设置为容器模式。这并不是一件非常困难的事情,反而会是一个很不错的学习机会,你可以找到弹性的、容器化的 Jenkins 机器节点来部署容器化的 Jenkins。

你也完全可以从简单的、没有任何容器编排的方式开始,这是下一篇文章的主题,敬请关注。

写在最后

原文作者给我们介绍了一个非常重要的实践:在部署环境遇到的问题要去调整和修改开发环境,让生产环境与开发环境保持一致,修改生产环境治标不治本。同时也给我们介绍了一些他喜欢的国外持续集成工具。 针对国内的工具,译者推荐你还可以考虑使用 CODING 持续集成,它是 CODING 提供的一站式 DevOps 解决方案中重要的一环。其构建脚本在语法上全面兼容 Jenkins,又免除开发者部署和维护持续集成环境的繁杂事务。

同时 CODING 支持包括 Docker 镜像、Jar、APK 等软件包的构建, 预置了主流开发语言的构建环境:Java、PHP、Go、Python 等等;开启缓存加速功能可以平均提高 300% 的构建速度。对于包括 Maven,NPM 在内的 主流镜像源有专用网络优化,保证拉取速度; 支持单项目并行构建,以满足重度持续集成用户的需求。对不太喜欢脚本的同学还提供了 完善的图形化编排能力,以降低使用门槛。

图片

CODING 还对构建产物提供了统一的 制品库管理,目前制品库已支持 Docker 镜像、NPM、Helm 等常见包类型的制品管理。后续 CODING 会逐步支持多种主流的软件包类型来进一步完善 DevOps 工作流,敬请期待。