理解SaltStack(4) – 远程执行

本文介绍salt远程执行系统是如何工作的。
salt一开始就是设计成为一个远程执行工具的,salt中其它子系统对远程执行使用非常频繁。

  • salt命令设计能跨系统和跨平台工作。根据目标平台情况salt ‘*’ pkg.install git命令使用yum,apt,pacman或salt的windows包仓库安装git软件。一个命令支持多个系统。
  • salt命令以一致的数据结构返回结果。这样使得测试结果和存储结果到数据库变得容易。
  • 由于接收命令是异步的,所有的目标系统能同时执行一个任务。
  • salt使用数百个python模块库完成远程管理,并且你可以添加自己的模块(或最好贡献你的模块到项目!)。任何能通过Python,shell命令或者任何其它接口访问的程序或服务都可以封装一个salt执行模块来执行任务。
  • 远程执行系统通过salt命令行来访问。让我们来尝试执行一个简单的命令,然后通过salt系统来跟踪它:

    1. salt ‘*’ test.rand_sleep 120

    基于我们对salt通信系统的了解,下面是这个命令的一些底层行为:

  • 1.此命令通过publisher端口发送到所有已连接的minions。
  • 2.所有minon检查此命令,评估自身与命令带的target字段是否匹配以此来决定是否应该执行这个命令。
  • 3.目标系统执行命令并返回结果给request server。
  • 我们来深入了解,当在minon执行命令时发生了什么。首先每个命令分到一个单独的工作线程,所以salt minion能同时执行多个任务。
    那么在minion谁来执行test.rand_sleep 120命令?当然是salt执行模块!
    所有用来管理系统的命令都由salt执行模块来提供。这些模块就是在salt中做实际的工作的,并且你只要用一次后就大概知道它们如何工作了。
    当salt minion收到一个命令,找到正确的模块(在这个示例中是test模块),然后调用相关的函数(rand_sleep)及其参数(120)。
    运维自动化

    理解SaltStack(3) – 通信与安全

    本节介绍Salt通信模型以及认证和安全性的基本概述。

    架构模型

    salt使用server-agent通信模型,(可以作为独立的单台服务器管理工具,也能无代理通过SSH工作)。这个服务器组件称为salt master,代理称为salt minion。
    运维自动化
    salt master负责发送命令给salt minons,然后聚合显示这些命令的结果。单个salt master能管理数千个系统。

    通信模型

    Salt使用发布-订阅模式与受管系统通信。连接由salt minion发起,意味着你不需要在那些系统上监听任何端口来接收请求(从而减少攻击的可能性)。salt master使用4505和4506端口,必须向minion开放接收请求。
    运维自动化

  • Publisher: (4505端口)所有的salt minions与publisher端口建立持久连接。通过此端口在所有的连接上异步发送命令,这使得能够在大量的系统上同时执行命令。
  • Request Server:(4506端口) salt minions连接request server,当需要时发送结果给salt master和安全地请求文件,minion特定的数据值(称为salt pillar)。salt master和salt minion之间的连接是1:1的,且不是异步。
  • SALT MINION认证

    当minion首次启动时,通过网络搜索名称为salt的系统(不过这很容易更改为一个IP或者其它的主机名)。当发现后,minion发起一个握手然后发送它的公钥给salt master。
    运维自动化
    此次连接发起后,salt minion的公钥就存储在了服务器上,然后必须在salt master上执行salt-key命令接受这个minion(或者通过一些自动机制)。当salt minion的公钥被接受时,salt master才会提供解码消息所需的安全密钥(意味着在minion的key还没被接受时,minion不会运行任何命令)。
    minion的key被接受后,salt master给minion返回公钥和AES key,用来加密和解密由master发送的消息。AES key使用minion发送过来的公钥来加密,因为AES key只能由minion解密。

    安全通信

    salt master和salt minion接下来之间的通信都是使用轮换的AES key来加密。AES加密密钥使用TLS的显式初始化向量和CBC块链接算法。

    轮换的安全密钥

    轮换的AES密钥用来加密由salt master发送给salt minion的作业和加密与salt master文件服务器的连接。每次salt master重启和每次salt minion的密钥被删除都会使用salt-key命令来重新生成一个新的密钥。
    当新密码生成后,所有的salt minions必须重新发起认证请求来接收更新的AES密钥。这就使得AES的更新不会中断minion的连接。

    加密通信信道

    Salt master和Salt minion之间的发布通信使用轮换AES密钥加密。Salt master和Salt minion之间的直接通信使用唯一AES密钥来加密每个会话。
    例如,发布作业使用轮换AES密钥来加密,然后minion特定的数据(如salt pillar)使用唯一的AES密钥加密。

    用户访问控制

    在一个命令发送给minions之前,salt对Publiser ACL执行一些内部的检查以确保用户有权限执行当前的命令。如果用户被授权对指定的目标运行指定的命令,则发送命令。否则返回错误。

    理解SaltStack(2) – 插件

    没有涉及salt插件的讨论那所有关于salt方法的讨论都是不完整的。理解插件和salt的可插拔架构往往就像发现新大陆的感觉,把salt研究者变成salt的福音传道者。
    基本的解释是:salt的核心框架提供了高速通信和事件总线。
    此框架连接并验证受管系统,并为这些系统提供发送通知的方法。
    在这个核心框架上层,Salt的其余功能暴露为一组松散耦合的可插入子系统。

    可插拔的子系统

    salt包含超过20个的可插拔子系统,但大多数用户只对用于直接管理系统的少数感兴趣。下表包含Salt中一些更常见的子系统的列表:
    运维自动化

    “可插拔是什么意思”
    Salt没有定义一个内置的方式来执行任何子系统的任务,每个子系统仅仅是把它的工作委托给插件。salt为每个系统提供一系列的插件,不过更改一个插件在大多数情况下跟更新一个配置文件那样的简单。这种可插拔性使salt非常灵活。

    为了说明,下图显示了几个常见的子系统以及每个子系统最常见的插件。
    运维自动化
    此图表只显示了一些可用的子系统和插件,但应该能给你对salt一般架构有一个初步了解。

    作业运行期间的子系统

    当一个作业运行时,几个salt子系统被调用用来处理作业。下面的图表显示一个经典的状态运行或远程执行作业的子系统流程:
    运维自动化

    插件?听起来像讨论salt模块!
    在salt中,插件就是Python中的模块,既然每个插件就是一个Python模块,大多数时候,他们被简单地称为模块,或更准确地,Salt子系统模块(Salt auth模块,Salt文件服务器模块等)。只要你明白每个Salt模块都是一个扩展Salt中许多子系统之一的插件,你就会很好地理解这种关系。

    在每一步中,子系统委托它的工作给配置的插件。例如,在第7步的job returner插件可能是30个插件中的一个,包含MySQL,redis或者根本没有配置的(在第4步后job returner插件也可以直接在受管系统上运行)。
    在每一步中,有许多可用的插件用于完成一个任务,从而可以产生数百种可能的salt配置和工作流。

    伸缩性

    伸缩性使salt成为一个非常强大和可定制的工具,不过当你在学习这个工具时使得很难回答一些标准的问题。
    为了有趣些,我们采取“技术上正确”的方法,并回答几个关于Salt的常见问题:

  • 你如何开始salt作业?- 从任何可以调用Python,REST API,命令行或使用salt内置调度程序的接口
  • salt如何格式化结果? – YAML,JSON,纯文本,python数据结构和其它一些格式,你可以在任何时候使用单个参数来更改格式。
  • Salt使用什么格式来配置声明? – 根据你的使用情况来从15个支持的格式中选择一个,你还可以选择模板语言。格式是基于每个文件指定的,因此你可以同时使用多个格式。
  • 结果保存在哪里? – 你想存储的任何地方,你有30个选择。
  • 除了让你非常讨厌的对话,以这种方式回答这些问题确实说了一些关于salt方法管理的事情。你了解你的基础设施,在目前复杂的环境中,没有做任何事情最好的方式。
    没有必要为这些事情烦恼,salt为大多数用户设置了出色的默认值。主要是,当你什么时候需要伸缩性,它就在那里。

    salt组件

    有了salt的可插拔子系统的新知识,希望你开始明白,salt组件实际上是salt中的可插拔子系统,具有相应的插件。Salt grains? Salt pillar? Salt runners?所有可插拔子系统都很容易扩展。

    虚拟模块

    我们在这里已经讨论了很多,但是还有一个我们需要讨论的模块。还记得早些时候我们解释了salt如何抽象操作系统的底层细节吗?salt实现这种抽象的一种方式是使用虚拟模块。
    一些管理任务在不同的操作系统之间非常的不一样,在编写插件时,它们之间能使用的重复代码很少。
    例如,Debian系统上的包管理使用一个名为aptpkg的执行模块完成。在Redhat上,它使用一个名为yumpkg的执行模块完成(原因很明显)。如果你一直使用Salt,你会知道salt调用pkg远程执行模块进行包管理,它可以在所有操作系统上使用。
    为了启用这种类型的抽象,Salt使用虚拟模块名称加载这些类型的模块。aptpkg模块包含的说明基本上说“如果你是Debian系统,请将此模块加载为pkg。 否则,不要加载这个!类似的代码存在于对Redhat或CentOS的检查决定使用yumpkg中。这样可以存在多个模块来执行相同的任务,但只加载了一个虚拟名称。
    在阅读模块文档时,请记住这一点,因为你经常需要阅读非虚拟模块的文档以了解其行为。你可以在Salt模块中搜索__virtualname__,以找到Salt在加载该模块时使用的名称。
    下一篇文章我们讨论salt的通信。

    理解SaltStack(1) – 设计思想

    你可以通过观察saltstack的行为来了解它是如何工作的。那就是说,很多事情发生在底层你很难注意到,就像数据文件在控制台传输。此系列文章说明saltstack是如何工作的,saltstack的子系统,以及saltstack模块化架构如何使用和扩展saltstack来管理你的整个基础架构。
    在我们了解saltstack的特定组件之前,有必要了解关于SaltStack如何处理基础设施管理的方方面面。

    实时通信

    所有的salt minions都是同时接收到命令的。那意味着更新10或10000个系统所花的时间非常相近,并且能够在数秒钟内查询数千个系统。saltstack获取关于你基础设施的信息方法是实时查询的,而不是依赖数据库。

    各司其职

    salt minions只做属于它们自己的工作。Salt master发送的是一组轻量级的指令,基本上是包含”如果你是一个带有这些属性的minion:运行这些带参数的命令”。当接收到命令时,由salt minions判断是否与属性匹配。每个salt minion已经有需要存储在本地的所有命令,所以命令能够被执行和其结果很快地返回给salt master。salt master不需要为minion做任何工作,因为它能自己完成,常常能完成得更好。

    高性能和可扩展性

    Saltstack设计用于高性能和可扩展性。saltstack的通信系统在salt master与minions之间使用ZeroMQ或原始TCP建立一条持久的数据通道,给予saltstack相对于竞争方案相当大的性能优势。消息使用MessagePack进行高效序列化。在内部,salt使用Python Tornado作为一个异步网络库,而且使用了多线程和并发这些前沿的技术来进一步优化salt性能。
    用户在生产环境中的单台master管理超过10000台minions并不是不常见,目前所知有越过35000台minions由单台salt master管理!salt已经证明了其在真实环境中的速度和可扩展性。

    规范化一切

    规范化是saltstack跨平台管理功能的关键。不管你的目标机器是Linux, Windows, MacOS, FreeBSD, Solaris, AIX,物理硬件,云主机或容器,salt命令和状态都运行一致。salt抽象每个操作系统,硬件类型和系统工具的详细信息以便你能正确地管理你的基础设施。
    规范化一切还包括返回:salt命令返回以一致的数据结构的结果以方便解析和存储。

    管理一切

    salt几乎都能运行在安装有Python的所有设备。对于不支持Python的那些设备,你可以使用minion代理系统。这意味着能够被salt管理的唯一条件是支持任意的网络协议(甚至是你自己写的协议)。salt命令被发送到minion代理服务器,它将salt调用转换为本地协议,然后发送到设备。解析从设备返回的数据,然后放入数据结构并返回。

    自动化一切

    salt的事件驱动基础设施不仅让你能够自动化初始系统配置,也让你能够自动化扩展,修复和完成持续管理。salt用户自动部署和维护复杂的分布式网络应用程序和数据库,自定义应用程序,文件,用户帐户,标准包,云资源等。

    可选编程

    你不需要学习编程语言就能够使用salt的所有功能。salt的远程执行能力是CLI命令,状态系统使用YAML来描述系统的所需配置。
    你仍然可以采取”基础设施即代码”的方法 – salt有许多工具来支持这点,包括强制的必要系统与命令式和声明性执行。Salt只是帮助你不需要编写Python或Ruby代码来描述你的基础设施。

    模块化系统

    一些管理工具认为它们是可扩展的,因为它们能够运行外部脚本。在saltstack中,一切都是可扩展性的。下一篇文章会介绍,甚至是底层的通信协议都是可互换的。salt有超过20个可扩展的子系统,意味着salt能够做的事件,你也可以自定义它怎样完成任务。
    salt能够快速适应新的技术和管理新的应用,你永远都不会被别人的决定所困扰,管理你的基础设施哪一个方法最好。

    插件

    在下一篇文章,我们将介绍salt插件,以及它为什么对salt至关重要。

    代码发布系统实现

    日常运维问题

     

    在我日常运维工作中,代码发布可能是最普遍的一项工作之一,尤其是网页代码的更新,碎片化发布需求非常频繁。在前期开发人员比较少时,还可以由自己来上服务器通过脚本来发布代码。但随着公司项目的增多,更多的开发人员加入到公司,发布代码需求开始增多,这就占用了我大部分时间,经常的被打断其它工作来发布代码,非常地不爽,然后开始想解决方法。

     

    尝试解决问题

     

    当然,发布代码肯定是运维的职责之一了,但频繁的发布导致运维大部分时间浪费在重复的操作上,非常的不值得。基于此,开始限制代码发布频率,要求把不是很紧急的更新延后到一周中的几个时间点。但实施起来效果不理想,治标不治本,原因是你不能强制把需要立即上线的更改延后。实施这样的定时发布,有可能影响项目的快速迭代。

     

    最终解决方案

     

    想到这样子下去也不是办法,会造成工作很被动,于是开始着手建立以Web操作方式,结合git,rsync来实现自动代码发布。公司代码管理目前用的是svn,开发人员在发布前也没有打Tag的习惯,所以想到分布式的git来完成版本的管理,rsync当然是用来同步代码到其它服务器了。附上几张代码发布系统的截图:
    运维自动化
    运维自动化
    运维自动化

     

    开源技术使用

     

    • rsync:用来同步代码到服务器;
    • git: 用来标记版本,回滚版本;
    • tornado: python的一个web构架,提供后台服务;
    • angularjs: 前端的一个mvc框架,用来实现浏览器与后端的交互,使得后端不需要关心前端网页的渲染,专注后端逻辑的开发。前端和后端通过json数据来通信;
    • bootstrap: 让运维人员写的网站后台UI也可以很专业。

     

    代码发布流程

     

    运维自动化
    从流程图可以看到,我们只需要把审核发布的权限交给开发组负责人,运维只需要维护系统的稳定,之后代码发布就不需要运维来参与了。

     

    以上是整体的流程,现在来说详细说下具体的逻辑实现:

    • 1、开发人员提交代码更新,主要提交的字段包括“更新理由”,“svn代码路径”;
    • 2、后端收到请求后,把此数据插入到数据库,标记此更新单为“等待预发布环境更新”的状态;
    • 3、后台进程定时查询是否有等待预发布环境更新的更新单,如果有,读取svn路径,执行svn up更新代码操作,并标记此更新单为“预发布环境已更新,等待完成测试”;
    • 4、开发人员或者测试人员通过预发布环境的域名来测试功能是否正常,如果不正常,作代码修改后提交svn,再到web发布后台点击“返回修改”,对svn路径或者不做任何修改再点击“重新提交”,然后更新单又一次回到”等待预发布环境更新“状态。循环3、4步骤,直至预发布环境测试通过为止;
    • 5、在确认测试通过后,开发人员点击”测试通过“,这时更新单进入”等待审核状态“;
    • 6、负责人确认可以发布后,点击”审批“按钮,这时更新单进入”审核通过,等待执行发布操作“的状态。这时,开发人员得到发布代码的授权;
    • 7、开发人员点击”发布代码“按钮,更新单进入”已执行发布,等待系统完成发布“状态;
    • 8、后台进程查询状态为”已执行发布,等待系统完成发布“的更新单,执行git发布命令。git命令大概为,进入预发布代码目录,执行git add .;git commit -m “更新原因”;git tag 上一次版本号+1,再进入已发布代码的目录,执行git pull同步预发布代码目录的更改。最后调用rsync命令同步代码到生产环境。

     

    下面是回滚流程:

    • 1、进入web代码发布系统,选择已发布的版本,点击“申请回滚”;
    • 2、负责人审核此次回滚;
    • 3、开发人员执行回滚操作;
    • 4、后台查询“等待回滚”的记录,假如回滚的版本号为18,进入已发布代码的目录,执行git checkout -b 18 18;git checkout 18(这两条git命令作用为,以tag 18创建分支号为18的分支,并切换当前分支为18),然后再通过rsync命令来同步代码到生产环境,这样就实现了版本的回滚。

     

    最后想说的话

     

    最后想说的是,运维工作可以是枯燥的,也可以是有趣的。枯燥是因为没有意识或者懒得把重复的操作通过制定流程来使其自动化,在不断地把各种在运维工作中占用时间比较多的重复操作通过技术来使得自动化时,我们既高效完成了工作,节省了时间,又能提高编程和解决问题的能力,只有这样,我们才能让运维工作变得既有趣又有挑战性。