如何在组织范围内处理特定于环境的应用程序配置?

时间:2010-06-09 22:10:18

标签: deployment configuration

问题

您的组织有许多单独的应用程序,其中一些应用程序相互交互(形成“系统”)。您需要将这些应用程序部署到单独的环境中,以便于分阶段测试(例如,DEV,QA,UAT,PROD)。在每个环境中,给定的应用程序需要稍微不同地配置(例如,每个环境都有一个单独的数据库)。您希望通过某种自动化机制来处理此重新配置,以便您的发布管理器不必在每次部署到不同环境时手动配置每个应用程序。

所需功能

我想设计一个具有以下属性的组织范围的配置解决方案(理想情况下):

  • 支持“一键式”部署(仅需要指定环境,并且在部署期间/之后不需要手动重新配置)。
  • 应该有一个“记录系统”,其中指定了共享的依赖于环境的属性(例如,许多应用程序共享的数据库连接字符串)。
  • 支持重新配置已部署的应用程序(在特定于环境的属性需要更改的情况下),理想情况下无需重新部署应用程序。
  • 允许应用程序在同一台计算机上运行,​​但在不同的环境中运行(同时运行PROD实例和DEV实例)。

可能的解决方案

我看到了解决方案的两个基本方向:

  1. 使所有应用程序“环境感知”。您可以将命令行中的环境名称(DEV,QA等)传递给应用程序,然后该应用程序“足够智能”,足以在运行时计算出特定于环境的配置值。该应用程序可以从应用程序部署的平面文件或中央配置服务中获取值。
  2. 应用程序不是“智能”,因为它们位于#1中,只是通过应用程序部署的配置文件中的属性名称获取配置。安装程序/脚本在部署时将这些属性的值注入配置文件中。该安装脚本采用环境名称并从中央配置服务获取所有相关配置值。
  3. 问题

    您如何实现解决这些问题并支持这些所需功能的配置解决方案?我是否有两种可能的解决方案?您对这些解决方案有偏好吗?另外,请随时告诉我,我正在考虑这个问题。任何反馈将不胜感激。

5 个答案:

答案 0 :(得分:2)

我们都遇到过这类事情,特别是在大型组织中。我认为最重要的是先管理自己的期望,并询问是否确实必须告诉给定方框中的每个系统和子系统“更改为DEV模式”或“更改为PROD模式” 。我的个人建议如下:

  1. 让单个盒子负责不同的阶段 - 即“这是一个DEV盒子”,“这是一个PROD盒子”。

  2. 尽可能多地收集一个位置不同的配置,即使它需要软链接或收集信息然后打印出来的脚本。

    一个。这样,您可以在两个位置轻松“转储此框的配置”并查看不同之处,例如在新部署之后。

    B中。您还可以将配置更改与软件更改分开,至少在某种程度上,这是根据发布时发生的错误的好方法。

  3. 然后让所有内容都基于某些内容/某些未编入或硬编码的内容 - 只需确保在该位置收集并记录它。机制是什么并不重要,这是一件好事,因为有些系统不想被迫使用某些机制或其他机制。

  4. 很抱歉,如果这个答案过于笼统 - 问题非常笼统。我之前曾在几个大型软件组织工作过,这似乎是最好的方法。使用独立服务器作为“一个部署单元”是最现实的方案(尽管有时它很昂贵),因为应用程序相互影响,无论你多么小心,当你移动任何给定的齿轮或齿轮时,你会破坏整个系统的稳定性

    替代方案变得非常复杂。您需要开始重写您已控制的应用程序,以便让它们接受“DEV”开关,并最终为您无法控制的应用程序添加一层kludge。通常情况下,您无法控制至少基于系统范围内定义的属性的属性,除非他们“正在联系母舰以获取指示”。

    将人们重定向到远程位置并让他们“使用DEV”与“使用PROD”比“让这台机器像DEV一样运行”与“让这台机器像PROD一样运行”更容易。如果你把事情混合起来,比如让一个DEV任务和PROD任务一起在同一个盒子上运行,那么这不是一个真实的场景:我保证最终你将授予对PROD上某人的非法DEV访问权限,你将有一个DEV任务消灭PROD数据库。

    希望这会有所帮助。如果您想讨论更多相关细节,请告诉我。

答案 1 :(得分:0)

我个人更喜欢解决方案2(应用程序应该了解自己,通过其配置,运行的环境)。使用解决方案1(将环境名称作为启动参数传递),使用错误的环境说明符的危险性太高。如果两个安装的代码库的版本不同,那么从PROD代码访问TEST数据库(反之亦然)可能会造成混乱。通常就是这种情况。

我当前的项目使用解决方案1,但我不喜欢它。我之前使用的项目使用了解决方案2的变体:构建过程为每个环境生成了一个设置文件,确保它们包含相同的代码库,但是包含适当的配置参数。这就像一个魅力,但我知道它与“完全相同的构建文件必须到处部署”的范例相矛盾。

答案 2 :(得分:0)

在我读到这篇文章之前,我想我已经问了一个相关的,自我回答的问题:How to organize code so that we can move and update it without having to edit the location of the configuration file?。所以,在此基础上,我在这里提供答案。对于像查找环境设置这样简单的任务,我不喜欢“智能”应用程序(此处为解决方案1)的想法。对于应该简单的事情来说,这似乎是一个复杂的框架。安装脚本(这里的解决方案2)的想法很强大,但允许用户更改配置文件的内容很有用,但它是否允许更改此配置文件的位置?什么是“中央配置服务”,它位于何处?我的答案是,如果目标是设置配置文件的内容,我会选择选项2,但我觉得这个配置文件的位置问题仍未解决。

答案 3 :(得分:0)

如果您正在使用JSON来存储/传输配置(或者可以在预部署过程中使用JSON输出到其他格式),您可以使用任意或环境为环境/特定于上下文的值注释关键/属性名称特定的后缀,然后在build / deploy / run / render -time中动态选择/区分它们,同时单独留下未注释的属性。

我们已经使用它来避免重复整个配置文件(众所周知的相关问题)并减少重复。该技术也非常适合国际化(i18n) - 如果需要,即使在同一个文件中也是如此。

示例,预处理的JSON配置片段:

var config = {
    'ver': '1.0',
    'help': {
        'BLURB': 'This pre-production environment is not supported. Contact Development Team with questions.',
        'PHONE': '808-867-5309',
        'EMAIL': 'coder.jen@lostnumber.com'
    },
    'help@www.productionwebsite.com': {
        'BLURB': 'Please contact Customer Service Center',
        'BLURB@fr': 'S\'il vous plaît communiquer avec notre Centre de service à la clientèle',
        'BLURB@de': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
        'PHONE': '1-800-CUS-TOMR',
        'EMAIL': 'customer.service@productionwebsite.com'
    },
}
给定动态,浏览器环境已知的location.hostname =' www.productionwebsite.com ...并进行后处理(在本例中为呈现时间) >'和navigator.language' de '):

prefer(config,['www.productionwebsite.com','de']); // prefer(obj,string|Array<string>)

JSON.stringify(config); // {
    'ver': '1.0',
    'help': {
        'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
        'PHONE': '1-800-CUS-TOMR',
        'EMAIL': 'customer.service@productionwebsite.com'
    }
}

如果非注释('base')属性没有竞争注释属性,则它将被单独保留(可能是跨环境的全局),否则其值被替换被注释的值(如果后缀)匹配偏好/辨别功能的其中一个输入。完全删除不匹配的带注释属性。

您可以混合并匹配此行为以注释配置,以实现全局,默认,特定的区别(假设您是明智的)可读的零/最小重复。

单个递归 prefer()函数(正如我们所称的那样,缺乏制作整个项目/框架的需要或愿望)到目前为止我们已经开发了(参见jsFiddle,使用内联文档)比这个简单的示例更进一步,并且(更详细地解释here)处理深层嵌套的配置对象,以及优先排序和(如果您需要保留)扁平的)后缀的组合。

该函数依赖于JS能够将对象属性引用为字符串,动态地并且容忍@和&amp ;;属性名称中的分隔符,这些分隔符在点符号语法中无效,但因此(帮助)阻止开发人员通过在代码中意外引用预处理/注释属性来破坏此技术(除非他们,非传统上不喜欢使用点 - 注释。)

我们还没有为我们打破这一切,我们也没有受过这种技术的任何根本缺陷的教育,除了不负责任/无意的使用或对现有框架/技术的投资/喜爱之外。我们还没有对它进行性能分析(我们只建议每次构建/会话运行一次等),所以在你自己的用法中,YMMV。

客户端传输的大多数配置当然不希望包含敏感的预生产值,因此可以(应该!)使用相同的功能在预部署中生成仅生产版本(没有注释),同时仍然在您的流程上游享受SINGLE配置文件。

此外,如果您正在为i18n执行此操作,您可能不希望整个wad通过线路,因此可以处理服务器端(缓存或实时等)或在构建/部署中预处理它通过拆分成单独的文件,但STILL尽可能早地在工作流程中享受单一的事实来源。

我们还没有探索在Java(或C#,PERL等)中实现相同的功能,假设它甚至可能(可能有一些奇特的反思?)但是包含NodeJS的构建环境可以轻松地实现这一步骤。

答案 4 :(得分:0)

如果它满足您的需求,并且在将连接字符串存储在源代码控制存储库中没有问题,则可以创建以下文件:

Base.__init__

然后在部署脚本中选择合适的脚本,并将其重命名为实际的 appsettings.json ,然后由您的应用读取。