2014-04-01

这部分的文章主要聊聊部署,作为交付的重要环节,自动化部署一直是持续交付的核心。

Part I: Brief Introduction To Deploy

部署的常见方式

自动化部署主要分两个阶段:

  1. 代码分发
  2. 配置管理

下面来具体看看这些阶段使用的常见方式。

  • 代码分发

    • 代码仓库CheckOut

    直接从版本控制系统CheckOut/Clone源代码或者二进制程序。这是最简单直接的方式,也是上线的本质所在:就是把写好的源代码或者编译好的二进制程序放在线上规范的路径下。可以通过SCP, Rsync等同步手段进行文件的同步分发。这种方式简单直接,如果再辅以类P2P式传输,同步还是很快的。但最大的问题在于:1.不能解决依赖;2.不好版本控制(线下维护源代码,线上是软件包)。 + 包管理与自建源

    为了更好的组织需要分发的代码或者二进制文件,于是包管理工具诞生了,将同模块的代码,配置,数据打包到同一软件包中,模块的更新回滚本质是软件包版本的变化。当然如果你想提高包管理工具的效率,可以考虑自建软件源,软件包下载时提供CDN加速这些策略。当然这个需要对软件源对规划和改进,比如解决大规模并发安装可能会失败,增加Yum插件检查机制等。软件安装的自动化基本是围绕软件包安装进行的。

    关于代码的分发为什么基于软件包会比直接CO代码或二进制文件有优势还有个简单的原因,让新环境的部署如同新版本的更新一样操作上没有差异:没有依赖困扰,可以增量更新。

  • 配置管理

    • Fabric VS Puppet

    这是两种自动化工具的思路:过程型和状态型。以Fabric,Capistrao等工具为代表的过程型工具,基于SSH,将软件部署的步骤定义为任务。通过完成一步步任务,完成编译,打包,分发,安装这些步骤。也有以MCollective这样的基于消息队列的工具,异步实时并发,但需要安装相应客户端。而以Puppet,Chef等这些为代表的状态型工具,定义机器的状态:比如需要安装什么版本的软件包,需要什么用户,配置文件的内容是什么。然后让机器达到预定义的状态。它们将软件包、配置文件这些抽象为资源。定义该角色机器需要安装的软件包,每次更新指定新的版本号然后机器就会更新至新版本。

    如果能有机器的状态管理,那就不存在所谓的恢复环境的说法。只是机器有没有达到预定义的状态而已。新兴的SaltStack/Ansible均有上述两种思路。

  • 部署新技术

    • Image/Vagrant

    对于很多通用的软件,尤其是IAAS平台,可以制作基础的镜像,从而实现基础环境的统一标准化配置,根据需求,不同的类型的集群可以基于基础镜像继承各自的基础镜像。只需要定制相应类型的系统,大部分软件已经安装,在此之上辅以之前的配置管理工具,可以很容易实现模块部署。Vagrant是一款镜像管理软件,可以快速build机器,只需要简单的配置文件,并且提供整套完整的流程管理。 + Container/Docker

    虚拟机隔离性和资源使用率还有待提高,而且使用起来显得太重。所以Container的技术迅速发展,这种轻量级操作系统级别的虚拟化技术,可以是虚拟机技术的很好补充。启动实例能在秒级完成,每个实例如同机器的一个进程一样,却能提供很好的资源隔离和限制。可以将每类模块制作一个Container镜像,每次的更新就是新启动新实例。真正实现一次部署,随处可用。这方面Docker(LXC+CGroup+NameSpace)是代表,并且提供类Git的流程管理。

    docker 这种操作系统级虚拟化技术极大地弥补了硬件虚拟化的不足并且满足了对资源隔离的需求。启动实例如同启动应用程序一样快速。对于大批量部署能够很好地保证一致性。是未来部署的新方向。基于Docker部署之上还有新的项目,值得关注的是CoreOS的发展,它生来就是为了大规模部署。Docker也可以和OpenStack这样的工具结合,作为OpenStack的Hypervisor(应该是container), 这样通过管理OpenStack就可实现大规模的集群管理。

这些常见方式也反应了部署手段变化的轨迹,从最初的脚本标准化(CO代码或者打包安装),引入配置管理做到状态管理,到Immutable Infrastructure技术的发展。是采用PASS还是IAAS,没有统一的答案,适合的才是最好的。 Immutable Deployment的思路其实很简单,当需要升级软件时,那么很好Build一个新的升级后版本的实例,需要新软件时,同样Build新的实例,而不是在之前的实例上升级和安装。这个概念得益于Vagrant/Docker这些技术的成熟。


理想的部署应该有什么特点

  • 部署是多阶段的流水线的

    部署是个协作的过程,开发测试运维相互合作的结果。软件产品从完成开发之后进入交付阶段:构建-编译-打包-测试-分发-上线,整个过程就像计算机指令:取指-译码-执行一样是流水线的。这个流水线涉及多个阶段,代码经历Dev, Stage, Production多个阶段最终在线上部署。

  • 部署是自动化的快速化的

    将部署分阶段可以很好自动化、过程化,当定义好过程间的接口后,就可以专注在每个过程本身,从而很好实现自动化。而快速化的实现取决于将每个阶段的耗时缩到最短,如果没法各阶段攻破,比如很容易发现分发这步其实是很耗时间的,尤其是当规模变大的时候,那是不是就应该考虑提高自己的分发能力或者采用Yum这类包管理+CDN加速这样的方式。分发之外Packaging的过程也是很耗时的。

  • 部署流水线是并行的

    就像超标量计算机的出现允许指令的并行执行一样,部署也是允许并行的。这体现在两个方面:

1. 流水线间的并行,当流水线处于分阶段状态,多个模块可以处于同一个阶段,也就是多模块的并行上线
2. 流水线内的并行,比如测试:单元测试功能测试这些的并行,测试和分发的并行
  • 部署是随时可进行的

    传统交付的一个很大限制就是往往会限制变更次数,固然从运维的角度来说能减少变更次数是最好,但是变更次数的减少也存在一次变更内容的增加,其实风险也是增加的。所以在敏捷和稳定之间需要尽可能的平衡,凡是准备好的变更就应该随时进行,快速验证,提高交付速度和频率也可以降低交付风险。

  • 部署是幂等的可重复的

    就像软件开发中幂等的接口一样,运维操作也需要幂等考虑,在变更前会先检查变更是否已存在,比如如果已经更新至新版本就不需要安装新版本。这通常是借助Puppet/Chef这样的配置管理工具,当然过程型工具和Shell脚本也能这样做,就是需要提前做更多检查,但这确实不是一种好的思路,因为不确定什么时候就会忘记,而脚本本身的维护也是一个问题,又是一堆脚本。

    为什么提倡要使用状态型配置管理工具,一个很简单的原因就是线上经常有机器发现某软件包没装,版本不对等,尤其是那些很基础的软件包。这些基础依赖的重要性也是不言而喻的。也就是说系统是收敛的。


开源CD工具GO

  • Thoughtworks开源工具Go

    以典型的交付流程为例:Complie- Package - Test - Deliver -Test Environment - Production Environment

    Pipeline - Stage - Job(Unit Test/Function Test 并行) - Task 树形结构 树形结构

    每一次的发布就是一个Pipeline, 这个Pipeline有多个阶段(编译,打包,测试,预发布,发布),每个阶段又分多个工作(比如测试:单元测试,功能测试,集成测试等,这些Job有些又是可以并行的,每个Job跑在独立的Agent上),每个Job又细分为多个任务(比如功能测试需要测试一个个的测试用例,每一项测试就是一个任务,当然任务间如无依赖可以并行)

    Go是一个C/S架构的CD工具,Server负责安排每个Job在具体的Agent上执行。这里的Agent有不同类型,有的用于编译,有的用于打包,有的用于测试,功能不一而足,环境也不尽相同

    Go工具目前的问题问题,也在于对Agent的管理上,Agent属于IAAS层面问题,比如打包,需要Rhel5和Rhel6不同系统机器,测试不同模块不同产品对运行条件要求也不一样。而这些Agent问题的解决,除了传统虚拟化解决方案,Contianer是很好的选择。最显而易见的就是这些环境可以批量快速构建。在不用的时候随时可以销毁而不像虚机一样太笨重。

  • 这里面涉及三个管理:

    • 流程管理 正如前面所描述的那样
    • 依赖管理 阶段间的依赖,任务间的依赖
    • 统计图表 各阶段各任务耗时统计进行进一步优化,测试各项数据统计等


blog comments powered by Disqus