动态配置log4j
管理多个环境的配置通常是许多软件交付系统的祸根。 大多数组织通过针对不同环境使用静态属性文件(例如,local.properties,dev.properties,test.properties,stage.properties和prod.properties)来管理属性。 我已经设计了许多软件交付系统,这些方法可以使我自己适应并在“ 以人为本的自动化 :通过自动化加快部署 ”中倡导这种方法。 这是我见过的最简单的方法之一,但是,如果您需要管理一个具有数百个甚至数千个针对多个应用程序和数百个实例的单个属性的软件交付系统,它将无法扩展,最终变得难以管理。 大多数故障是由于某个人在单个软件系统的数百个不同属性文件中输入了错误的值而导致的。
但是,情况并非没有希望。 通过有效地使用自动化,动态配置和云基础架构,您的软件交付系统可以大大减少在环境之间移动软件时对静态属性的需求。 您将在本文中学习方法。
典型特性
表1列出了组织内团队管理的一些典型属性:
表1.软件交付系统中的典型属性
属性 | 描述 |
---|---|
IP地址 | 节点的唯一可寻址位置 |
终点 | 任何节点的域名-负载均衡器,实例等 |
域名 | 用户用来访问应用程序的唯一可寻址名称; 与域名注册商和域名服务接口 |
数据库名称 | 应用程序使用的数据库名称 |
港口 | 0到65535之间的唯一数字,用于内部和外部访问节点 |
用户名和密码 | 用于无头访问资源的用户名和密码 |
目录和文件 | 操作系统中的目录和文件 |
SSH密钥 | 保护Shell密钥文件以访问受保护的资源 |
这不是一个详尽的列表,但是它代表了典型软件系统中更常用的类型。
传统物业管理
在由开发和运营团队组成的大多数软件组织中,开发团队(开发人员,测试人员,分析师等)通常会:
- 创建一个文件(例如local.properties)来管理与工作站相关的属性。
- 在dev.properties文件中修改“开发”环境的属性,并将其提交到版本控制存储库。 由于动态属性(例如IP地址)可以更改,因此开发人员必须在发生此类更改时不断修改此文件。
- 用相同的属性属性修改test.properties文件,但为测试人员使用不同的值 ,因为它们可能使用不同的IP地址,文件和目录。
开发团队的配置通常不会在这里结束,因为组织的开发方通常会管理“开发”和“测试”之外的其他环境。
在传统的组织,开发团队将手离开软件包运营团队(数据库管理员,系统/ OPS,发布工程中,等等)来部署软件到下游环境的软件交付周期的一部分。 该团队:
- 修改stage.properties文件中“登台”环境的属性,并将其提交到版本控制存储库。
- 在prod.properties文件中为“生产”环境修改类似的属性,并将其提交到版本控制存储库。 因为动态属性(例如IP地址)可以更改,所以当发生此类更改时,操作必须不断更新这些属性文件。
我见过具有属性层次结构的团队:本地,环境(通常至少有四个环境属性文件,但通常还有更多),应用程序,子系统和实例。 当它们具有数百个实例时,它们具有数百个不同的属性文件,从而几乎不可能管理IP地址,目录和其他属性的不断变化。 这导致了频繁的环境和部署失败,通常很难解决,因为变更管理面临着挑战-谁在何时何地改变了? —使得很难确定错误的根本原因。
外在属性
在使用传统方法设计良好的软件系统中,所有可能因环境而异的属性都被外部化为属性文件。 换句话说,不会将可能是可变的设置硬编码到应用程序代码中。 但是,属性越多,就越容易引入部署错误。 这些错误代价高昂且难以排除,并且经常导致在“构建升级”过程中延迟将软件交付给用户。
清单1是属性文件的简单示例:
清单1.在属性文件中定义的配置
jboss.home=/usr/local/jboss
jboss.server.hostname=jenkins.example.com
jboss.server.port=8080
jboss.server.name=default
在非平凡的系统中,诸如此类的属性文件通常具有数百个属性。 这些属性通常在开发,测试,登台和生产环境中重复出现,从而导致将数百个或数千个属性交付给生产环境时进行管理。
清单2显示了一个Java™代码段,该代码段从属性文件中检索值:
清单2.获取属性的Java代码示例
public PropertyReader(final String name, final ClassLoader loader) {
try {
InputStream is = loader.getResourceAsStream(convertName(name));
if (is != null) {
properties.load(is);
is.close();
} else {
throw new IOException("Couldn't find property file: "
+ convertName(name));
}
} catch (IOException problem) {
System.out.println("Property Reader: problem initializing");
}
}
这是团队在管理属性时最常用的方法。 但是,当今的软件交付系统能够以前所未有的规模运行。 多亏了硬件商品化,虚拟化和云计算,您才能在交付和管理软件系统时启动(终止)数百个环境。 您的配置管理方法必须能够相应地扩展。 而且(还感谢硬件商品化,虚拟化和云计算),它可以。
动态配置如何工作
我在本文中主张的方法是动态设置和检索属性。 当您使用的基础设施自动化工具定义环境(见“ 敏捷DevOps的:基础设施自动化 ”),您可以设置配置项-如数据库名称,以及文件和目录名-在配置数据库中 。 您可以在脚本中执行此操作,因为整个基础架构是完全在提交到版本控制存储库的脚本中构建的。 此外,在云或虚拟基础架构中,您可以动态设置和检索配置项,例如IP地址和域名,这些配置项通常在传统基础架构中静态定义。
清单3中的代码是一个Ruby脚本,它将配置项加载到SimpleDB(NoSQL)数据库中:
清单3.将动态配置项写入NoSQL数据库
AWS::SimpleDB.consistent_reads do
domain = sdb.domains["stacks"]
item = domain.items["#{opts[:itemname]}"]
file.each_line do|line|
key,value = line.split '='
item.attributes.set(
"#{key}" => "#{value}")
end
end
用户名和密码
Capistrano是用于定义脚本化部署的基于Ruby的基于领域的特定语言(DSL)。 清单4中的代码来自Capistrano脚本,该脚本根据用户一次定义的参数定义数据库用户名和密码:
清单4.在Capistrano中设置用户名和密码
set :dataSourceUsername do
item = sdb.domains["stacks"].items["wildtracks-config"]
item.attributes['dataSourceUsername'].values[0].to_s.chomp
end
set :dataSourcePassword do
item = sdb.domains["stacks"].items["wildtracks-config"]
item.attributes['dataSourcePassword'].values[0].to_s.chomp
end
因为您可以通过脚本完全控制基础结构,所以可以自动生成数据库用户名和密码并将其存储在安全的配置数据库中。
在整个软件交付系统的其余部分都使用此配置。 推送到该数据库的数据在上游环境中动态检索,并在下游环境中使用。
IP地址
清单5中的代码是AWS CloudFormation JSON脚本的一部分,该脚本动态设置和关联外部IP地址:
清单5.在CloudFormation中动态设置IP地址
"IPAddress" : {
"Type" : "AWS::EC2::EIP"
},
"IPAssociation" : {
"Type" : "AWS::EC2::EIPAssociation",
"Properties" : {
"InstanceId" : { "Ref" : "WebServer" },
"EIP" : { "Ref" : "IPAddress" }
}
},
您需要将这样的脚本与相应的自动化测试一起提交给版本控制存储库。
域名
清单6中的CloudFormation JSON代码使用AWS Route 53服务动态设置应用程序的A域记录:
清单6.在CloudFormation中设置域
"JenkinsDNS" : {
"Type" : "AWS::Route53::RecordSetGroup",
"Properties" : {
"HostedZoneName" : { "Fn::Join" : [ "", [ {"Ref" : "HostedZone"}, "." ]]},
"RecordSets" : [
{
"Name" : { "Fn::Join" : ["", [ { "Ref" : "ApplicationName" }, ".", \
{ "Ref" : "HostedZone" }, "." ]]},
"Type" : "A",
"TTL" : "900",
"ResourceRecords" : [ { "Ref" : "IPAddress" } ]
}]
}
},
该脚本使您无需使用域名服务(DNS)提供程序手动配置此项目,这是传统方法。
数据库名称
清单7中的代码来自CloudFormation脚本,该脚本定义了一个名为DatabaseName
的参数:
清单7.在CloudFormation中设置数据库名称
"Parameters": {
"DatabaseName": {
"Description" : "The name of the database",
"Type": "String"
}
}
当脚本为应用程序创建新数据库时,会将DatabaseName
参数传递给基础结构脚本(例如Puppet清单)。 整个脚本将提交到版本控制存储库,然后编写自动测试以验证数据库是否可访问。
港口
清单8中的代码为环境(和应用程序)设置了外部端口:
清单8.在CloudFormation中的安全组内设置端口访问
"FrontendGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable SSH and access to Apache and Tomcat",
"SecurityGroupIngress" : [
{"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "0.0.0.0/0"},
{"IpProtocol" : "tcp", "FromPort" : "8080", "ToPort" : "8080",\
"CidrIp" : "0.0.0.0/0"},
{"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", \
"CidrIp" : "0.0.0.0/0"}
]
}
},
在此示例中,唯一具有外部访问权限的端口是端口22(用于SSH访问),端口8080(用于Apache Tomcat)和端口80(用于Apache HTTP Server)。 可以通过无类域间路由(CIDR)表示法进一步限制对网络甚至单个节点的访问(请参阅参考资料 )。
动态地说
在本文中,您了解了在大多数软件交付系统中通常静态定义的配置可以并且应该动态定义,这样您就不必为每个目标环境都进行更改。 您看到了可以大大减少创建环境所需的可配置属性的数量。
下一部分将介绍一个开放源代码平台,用于在云中连续交付软件。 您将学习运行平台的基本步骤,如何使用交付管道以及如何运行作业以供应环境和运行部署。
翻译自: https://www.ibm.com/developerworks/opensource/library/a-devops7/index.html
动态配置log4j