第十一章:软件配置管理

发布时间 2023-07-26 15:54:10作者: 城南以南123

将一切纳入配置管理:
  配置管理目标
    可追溯性:任何人在获得授权的前提下,能够找到该软件的任何变更历史。例如:源代码版本管理系统(如Git、Subversion等)就属于软件配置管理工具,它包含代码仓库中所有代码的修订信息
    可重现性:任何人在获得授权的前提下,能够重现从过去到现在之间任意时间点的软件状态
    易操作性和高效性:可以很方便的追溯和重现软件服务在某时间点的指定状态
  配置管理的范围:需求、源代码、软件(部署)包和环境
  软件配置管理原则:
    一切皆有版本:
      需要对以下内容进行版本变更管理:
        操作系统层:与硬件打交道的操作系统、网络配置以及网络及系统通用管理服务
        标准软件层:包括应用程序所依赖的标准化软件包。
          标准化软件包是指以独立进程形式存在,并为上层的应用程序提供基础服务,如数据存储、消息通讯、网络服务等。如MySQL数据库、Redis服务器、ActiveMQ、ZooKeeper等
        应用软件层:包括自身团队负责开发的应用程序,以及该应用程序运行时所依赖的第三方组件库及相关数据等
          第三方软件服务是指由其他软件团队负责开发维护并提供的软件运行时服务,团队自身无法对其进行变更或修改
        软件能够正确启动并运行所需的软件配置信息和相关数据
        软件测试工具包或测试用类库
        测试相关的代码
        测试运行所需的测试数据
        测试相关的一些脚本
    共享唯一受信源:掌握任意时刻的软件状态,并确保所有人所获取的信息都是一致的。需要保存三种类型的数据:业务需求/缺陷、源代码和软件包
      需求仓库:保存有关产品的所有版本需求描述和验收条件,并且能够记录每一次团队对需求达成共识后的版本变更记录
      代码仓库:保存所有源代码的变更历史。除了产品源代码,还包括软件包整个生命周期中的所有以代码形式存在的内容。如测试代码、自动化脚本以及环境配置信息、软件包构建依赖信息等
      软件包仓库:自动保存部署流水线生产加工出来的软件包,满足后续环节的快速取用。可以分为三个子类型
        临时产物仓库:作为研发团队内部各角色在某个发布版本处于开发期间进行协作沟通的唯一的软件包受信源,从而避免因环境不一致或重复打包等问题而引入不必要的风险和浪费
        正式发布包仓库:用于保存那些通过质量验证,即将上线或者已经上线的所有软件包
        外部软件私服仓库:也叫第三方软件库的私有服务器,用于存储内部所有软件所需引用、包含或使用的外部第三方受信软件包
    标准化与自动化:
      软件配置管理中的一项重要工作就是基线管理。所谓“基线”,就是所有仓库在某一时刻的“快照”,即创建基线的时候,对仓库中的当前版本的一个整体标记或复制。
      为了有利于团队协作和版本追溯,软件配置管理应该制订一些相应的标准与规范(如分支策略、分支命名、源代码目录结构等)
      有标准规范后,很多日常事务性操作就可以被自动化(如基线),从而释放更多的人力资源
软件包的版本管理:
  包管理的反模式:
    以前常见做法是直接将应用程序所依赖的程序包与程序源代码放在一起,提交到版本控制库中
    好处是对该应用程序的开发团队比较方便,能够做到“开箱即用”
    坏处是:
      比较难识别软件包版本。比较好的处理方法是当把软件包提交到版本控制仓库时,最好在文件名中加入版本标识信息
      大量的软件包以二进制的形式存在于版本控制仓库中,而大多数版本控制系统对二进制文件的管理非常低效,而且有I/O瓶颈,且无法有针对性的进行传输优化
      大量重复的软件包在版本仓库中
      公共软件包的协调工作增加
  集中式包管理服务:
    建立企业级的统一软件包库管理系统,将所有软件包都放入其中,并且对企业内部团队提供稳定的查询、获取和申请等服务
      查询服务:内部人员可以方便的查询他想找的软件包
      获取服务:能够通过包管理服务下载自己所需要的软件包版本,并且做到版本正确和高速下载
      申请服务:指没有查找到所需的软件包(通常是企业外部的第三方软件)时,内部人员可以快速提出相应软件包的使用申请,并在第一时间内快速获得响应
    常见的包管理软件:Nexus、Artifactory
    好处:
      统一存储,空间占用少
      一致性。全公司使用统一的副本,是所有软件包的唯一来源
      安全省心。可以对软件包进行统一的安全扫描、法律审计管理
      下载速度快。在公司内网,软件包的获取速度较快
    坏处:
      需要有人管理维护这个企业系统服务
      将第三方软件包存储在公司内部服务器上,会占用一定的存储空间
      容易形成单点故障,需要制定相应的解决方案

  软件包的元信息:
    自身唯一标识信息:
      标识的基本要求:软件名称+版本号
        易于理解记忆
        方便查询索引
        版本号:大部分软件的版本号分为4段,形如A.B.C.D,每段由一个整数表示,段与段之间由小数点分隔,如1.0.12.1223
          A段为主版本号:当软件增加重要功能或功能改版,或者出现向后不兼容的改变时,A段数字通常会加1.当A段为“0”时,表示该软件功能尚不完备,未正式发布
          B段为次版本号:表示对现有部分功能的增强,而且功能一定是向下兼容的
          C段是修订版本:表示只有较小的修改,例如修正了一些缺陷
          D段通常是自定义段,可以由团队自行约定
    来源信息:主要说明自己来自哪里,即对应的程序代码在哪里可以找到,通常对应于源代码仓库的URI地址、分支名和RevisionID
    依赖关系信息:大多数软件并不是从零开始构建,会利用第三方软件包,从而开发软件速度
      软件包的依赖关系有3种:
        构建时依赖:指源代码构建软件包的过程需用到的第三方软件包。如Apache log4j、logback、SLF4j等
        测试时依赖:指在对软件包进行自动化测试验证时所需要依赖的软件包。如JUnit
        运行时依赖:软件包在运行时所需依赖的软件包。如C++语言的.so文件
包依赖管理:
  显式声明依赖
    指将应用软件在不同环境(如构建环境、测试环境或生产环境)中所需要的软件包以及相应的版本信息,通过事先约定的描述方式显式记录在文档(如构建脚本)中
  自动依赖管理
    通过显式声明方式对包依赖进行描述后,当我们构建、测试或部署我们所开发的软件应用时,只要有工具能够识别包依赖描述文件中的信息,帮助我们从软件包仓库中自动下载和更新这些软件包就可以啦
  减少复杂依赖
    常见的包依赖问题:
      依赖过多或链条过长:可以通过使用一个能自动解析所有依赖关系的包管理器来解决
      依赖冲突:
        指当软件包A和软件包B要在同一个机器上运行时,由于软件包A依赖于软件包m的V1.1版本,而B依赖于软件包m的V1.2版本。但是,软件包m不支持在同一机器上安装不同的版本,这就出现了依赖冲突
          1.想办法令依赖库的两个不同版本可以在同一环境下运行
          2.打平依赖:通过对产品代码的修改,使A和B依赖于m的同一个版本
      循环依赖:
        软件包A依赖于软件包B,且必须在B的某个特定版本下才能运行,而软件包B又反过来依赖A,并且也必须运行于A的某个特定版本下
          1.分裂依赖:对软件包A和B进行分析和改造,令其分类出一个或多个依赖子包,使得原有的软件包分别依赖于分离出来的子包,从而打破循环,形成链式
          2.每次升级时使用梯形升级法:先用软件包B V5.1来构建软件包A的新版本V1.3,再用A的V1.3来构建B的新版本,呈阶梯状交替升级
环境基础设施管理
  环境准备的4种状态:
    1.“蛮荒”:以“人脑+手工”为代表
      特点:
        1.开发人员自己搞定所有与软件部署相关的问题
        2.所有与环境准备相关的知识都存在于个别骨干开发人员的头脑中。他们是团队的核心,环境部署的“英雄”
    2.“规范化”:以“文档+私有脚本”为代表
      特点:
        1.要求提供正式的上线部署文档
        2.要有规范化的上线部署流程
        3.利用私有化脚本,提升个人效率
      面临挑战:
        1.流程通过人来维护,经常有遗漏
        2.文档通过邮件跟踪,查找不方便
        3.审计工作量大。由于手工工作量大,常有人绕过流程
        4.自动化脚本不规范统一,而且经常出错,导致部署过程中断
    3.“标准化”:以“办公自动化”为代表
      出现运维办公自动化平台,原来所有的规范全面固化到平台上,成为具体的执行标准
      好处:
        流程在平台固化。软件开发团队与运维团队之间的互动协作统一在同一个平台上,协作流程受控
        部分内容的标准统一。例如简单的部署操作可以通过模板配置方式由平台自动化执行
        可以部分复用。通过各种配置方式,减少工作量
        审计工作比较容易。所有信息保存在平台上
      缺点:
        由于系统部署还有很多比较复杂的操作,仍旧需要人工参与
        两次上线部署差异对比仍旧相对困难
    4.“自动化”:以“受控式自动化脚本”为代表
      由平台管理自动化脚本,即所有自动化脚本都是公司资产,被平台记录和保存
      自动化运维脚本有两种形态
        操作过程式为主
          最传统的自动化脚本,通过模拟手工执行步骤的自动化命令执行
          特点:
            在同一个环境下,自动化脚本被多次执行,每次执行后的环境状态可能不一致,或者出错
          好处:
            符合原有的思考习惯,只要将原来的手工操作步骤用脚本语言实现即可
            灵活。无论想做什么样的操作,几乎只要手工操作可以办到的,基本上这种过程式脚本都能办到,不受任何约束
          缺点:
            需要花费较多的管理精力。一是在执行脚本前,系统的原有状态是什么样子的;二是执行脚本到底做了那些具体操作
            在同一环境下多次执行同一自动化脚本,其最终可能使环境处于一种“未知”状态
        状态声明式为主
          指在脚本中指定环境的目标状态,由定义该状态声明规范的平台执行这个脚本
          特点:
            在同一个环境下,无论该自动化脚本被执行多少次,每次执行完成后的环境状态都是一致的,这就是所谓的“幂等操作”
          好处:
            可以明确的知道,无论在何种情况下,无论谁执行了这个脚本,系统最后都会到达同一种状态
            如果将其放在代码仓库,通过版本diff功能,就可以直接对比两次环境部署的差异点
          缺点:
            学习成本高。状态声明式语法就是一种DSL,用于描述环境部署领域的专有操作和状态
            当这种声明式文本数量较多时,文件管理上也同样存在困难
          声明式工具:比较主流的包括Puppet、Chef、Ansible、SaltStack
            以上四种工具的运行模式稍有不同,可分为拉模式和推模式
              拉模式的特点是在目标机器上必须事先安装有客户端,并保持与服务器端的连接“心跳”。目标机器上的客户端向控制端服务器发出请求命令,服务器将相关的部署信息打包发给目标机器,由目标机器在本机执行。Puppet和Chef属于这种工作模式
              推模式是指目标机器并不需要事先安装有客户端,只要在控制端设置目标机器列表并给予目标机器的权限,控制端就可以将环境部署要求发送到目标机器上,并在目标机器上完成环境部署工作。Ansible和SaltStack都属于推模式
            还有一类应用部署工具(如Capstrano或Fabric)通常被用于软件应用层的部署工作。这类工具通常为由特定编程语言编写的应用程序的部署提供便捷性。

  环境基础设施即代码
    将环境基础设施的一系列准备工作以脚本方式描述出来,并能够通过自动化的方式来执行这些脚本。对工程技术人员来说,环境基础设施不再是一堆插着网线的机器,而是“可编程环境”
      好处:
        无论哪类环境(构建、测试、生产)出了问题,我们都可以快速自动化地构建出一个全新的环境
        只要获得授权,任何人都可完成这项任务,不需要他人帮助
        任何对环境的修改都可以被记录和审计
        对不同环境来说,只要将其代码描述进行对比,就可以了解他们的差异,而无需登录到实际的主机上查看
      为了获得以上收益,环境基础设施的版本管理应该包含:
        操作系统名称、版本号、补丁版本号以及系统级的配置信息
        软件包所依赖的所有中间件层的第三方软件系统及其对应的版本号,以及对其的配置信息
        需要与应用程序进行交互的外部服务及其版本号,以及其所需要的配置信息
软件配置项的管理
  二进制文件包与配置项的分离:可以实现只需要构建一次,在部署流水线的不同阶段即可重复使用的目的
  配置信息的版本管理:
    根据配置项内容的不同,可以分为以下3类
      环境配置项:该类配置项的对应值与其所运行的环境相关,且常与环境本身的定义相互绑定。如其所用到的域名、其与其他系统或服务通信的服务地址与端口号等
      应用配置项:该类配置项与信息安全控制及应用程序自身相关。例如账号密码、初始分配内存的大小、数据库连接池大小
      业务配置项:该类配置项与应用程序所执行的业务行为相关,每一配置项设置有默认值。如最常见的功能特性开关、电商系统中的商品定价策略调整等
    根据使用时机的不同,可以分为以下3类
      构建时配置:静态变量,通常是与环境相关的一些配置
      部署时配置:静态变量,通常是与环境相关的一些配置
      运行时配置:可以动态调整的参数,程序会根据不同的参数值产生不同的行为就,通常是与技术性能表现相关的参数(如缓存大小),或者是与业务策略相关的参数,如折扣参数比例等
    通常不建议在构建时注入环境配置信息。
    当我们能够区分配置信息的不同类别时,即可定制不同配置项的版本管理策略
      对于静态配置的版本管理可以使用源代码方式进行管理
      动态配置项通常可以通过日志方式或者配置数据库记录,以便后续问题诊断和操作审计
  配置项的存储组织方式:
    文本文件、数据库、文件目录服务、注册表、Zookeeper、etcd
  配置漂移与治理
    配置漂移:指随时间发展,由于各种未预期原因而做出的配置修改引起计算机或软件服务偏离了我们所希望的配置状态
      往往是由于人的临时修改引起的
      治理:
        1.好的软件配置管理流程可以解决配置漂移问题。例如,将静态配置放入版本控制库,并禁止直接登录到主机环境修改配置信息,只能提交修改到代码仓库,并通过自动化方式进行生产环境中配置的修改
        2.不可变基础设施
不可变基础设施与云应用
  不可变基础设施,必须满足以下3个要求:
    1.系统运行环境(包括所有层次)的准备均已自动化方式完成
    2.一旦完成准备工作,该基础设施的任何一个层次均不得更改
    3.如果因为某种原因需要对该系统环境进行更改,则必须使用另一个不可变系统环境来替代之,而不是对原系统环境进行变更
  实现不可变基础设施
    物理机镜像技术和虚拟机镜像技术
      可以利用镜像工具对已经安装好的应用程序运行环境进行镜像备份,并将镜像文件保存到统一的镜像仓库中。在需要准备一个与原有系统相同的运行环境时,可以直接从镜像仓库中取出该系统镜像,部署到一台宿主机上,进行简单配置调整后,即可立即投入使用
      物理机镜像技术:将整个物理机的内容制作成镜像文件
      虚拟机镜像技术:将物理机的资源分配给在其上运行的多个虚拟机,尽管可能会带来一些性能上的损失,但其资源利用率得到了大幅提升,同时也提高了环境准备的便捷性
    Docker容器技术:虚拟机技术是在宿主操作系统上,运行一整套客户操作系统。而Docker则是一种更轻量级的容器技术,它利用了宿主操作系统的一部分,而不是重新安装一套客户操作系统
    物理机镜像成本最高,虚拟机技术次之,Docker容器化技术的成本相对低一些
  云原生应用:
    将软件服务放到云端。使得耗费时间和精力的环境基础设施准备工作得到了大大的简化,同时有利于资源的有效利用
    “云原生应用”的12要素
      1.一套基准代码,多环境部署
      2.显式声明依赖关系
      3.在环境中存储配置
      4.把后端服务当做附加资源
      5.严格分离构建、发布和运行
      6.应用本身应该是一个或多个无状态进程,进程之间没有数据共享
      7.通过端口绑定提供服务
      8.通过进程模型进行扩展
      9.快速启动和优雅停止
      10.尽可能让开发环境、预生产环境与生产环境等价
      11.日志作为事件流
      12.将管理/管理任务作为一次性进程运行
  优势与挑战
    优势:
      简化运维工作
      部署流程自文档:当完全自动化部署时,只需要创建一个描述性文本文件,说明如何正确生成应用程序的运行镜像即可
      持续部署不停机,故障更少
      减少错误和威胁
      多类环境基础设施的一致性
      杜绝了“配置漂移”
      被测试的即是被使用的:确保生产环境使用的一定就是被验证通过的镜像
    缺点:
      为不可变基础设施建立一整套自动化运维体系在初期就需要较高的成本
      生产环境中突发问题的修复时间可能会较长,无法直接通过SSH连接到有问题的服务器,直接修改
      对大规模软件服务来说,将大尺寸镜像分发到多台宿主机上需要消耗大量的网络资源,时间消耗也会不少,因此必须有相应的平台工具支持
      有状态存储的软件服务并不容易被直接替换,如数据库存有大量数据的时候
数据的版本管理
  数据库结构变更
    当软件诞生的第一天起,每当关系数据库的Schema变更,都必须撰写一个增量SQL脚本,并且将其放到源代码仓库中。
  数据文件
    通过类似FTP、远程文件系统的方式进行二进制数据文件的版本管理
需求与源代码的版本关联
  将需求文档进行归档,并与对应的软件包版本相关联
  更细粒度的管理,可以将需求管理平台中的每个需求条目ID与代码进行关联