环境无关的环境
程序员文章站
2022-06-10 16:50:19
...
环境无关的环境 Posted width = "1" height = "1" /> 问题: 在某台机器上运行正常的环境, 换到另外一台机器就gp, 或需要很繁琐的从头配置,浪费从几小时到几天不等的时间.
原因, 则通常包括环境的各个部分散落在不同角落, 不是少了这个就是少了那个, 或不同机器上版本不一样;还包括在配置中使用硬编码的绝对路径, 依赖全局环境变量或属性等.
机器环境虽然各有各的不同, 但依然有可能创建一个"环境无关的环境"
1. 使用相对路径代替绝对路径关键是如何获得当前路径, 如何确定根路径, 如何确保目录结构
4. 缺省值 + 用户自定义属性这是创建"环境无关的环境"的核心机制. 无论如何, 环境要在不同机器上部署, 总会需要修改某些配置,以适应宿主机器. 然而前面我们提到, 所有配置都已置入版本控制. 如果我们直接修改, 则每个环境中都会存在未提交的本地修改. 这是我们不希望看到的,因为当我们升级配置并更新到所有部署时, 可能会产生冲突. 这里其实是两个层面的问题:
缺省值当然可以直接定义在脚本或配置文件中.而多数常用的脚本和配置系统都提供了用户定义属性覆盖缺省值的机制. 比如:
有一个很简单的解决办法, 就是把用户自定义属性置入单独的文件,并且不要把它提交到版本控制系统中(一个理由是这一部分相对整个组织来说, 不存在也不需要唯一的官方来源)
前面例子中的 user.properties 就是一个用户自定义属性文件,只存在每个用户自己的机器上, 不在配置库中. 在Windows和Bash脚本中也可以类似处理
参考借鉴
原因, 则通常包括环境的各个部分散落在不同角落, 不是少了这个就是少了那个, 或不同机器上版本不一样;还包括在配置中使用硬编码的绝对路径, 依赖全局环境变量或属性等.
机器环境虽然各有各的不同, 但依然有可能创建一个"环境无关的环境"
1. 使用相对路径代替绝对路径关键是如何获得当前路径, 如何确定根路径, 如何确保目录结构
- Windows和Unix都有内置的环境变量来表示当前路径, 分别是 %cd% 和 $PWD
Windows批处理脚本中, 还可以使用 %~dp0% 获得脚本所在路径; 而在Unix Bash 脚本中, 则可以使用 `pwd`, 即获得 pwd 命令的输出
如果总是需要引用某个根路径的话, 则可以使用环境变量来定义根路径(参见后面的环境变量). 其实相对路径配合固定的目录结构, 大大削减了对显式定义根路径的需求
那么如何保证目录结构是固定的呢? 自然是使用配置管理系统
- 强制使用配置管理可解决固定的目录结构的问题
使用配置管理还可以解决丢三落四的问题
自然也可以解决所用工具版本不一致的问题
- 大型的系统软件如何处理, 比如 JDK, VC++编译器等, 是否也需要置入版本控制; 这个基本不用, 如果对其特定的版本有需求, 可在脚本中加入检查其版本的逻辑, 不满足则提示并退出即可
依赖的大量二进制库如何处理; 如果有必要可使用依赖管理工具如 Maven, Ivy 等, 而用于存放依赖的本地仓库依然应该置入配置管理, 哪怕不用其版本控制功能, 而只是利用其官方来源/备份存档等好处
必须放在特定位置的文件如何处理, 比如某个文件必须放在/etc目录下; 这个文件还是可以放在配置管理下的目录中, 而在/etc下创建符号链接来指向它; 并提供脚本来干这件事.
4. 缺省值 + 用户自定义属性这是创建"环境无关的环境"的核心机制. 无论如何, 环境要在不同机器上部署, 总会需要修改某些配置,以适应宿主机器. 然而前面我们提到, 所有配置都已置入版本控制. 如果我们直接修改, 则每个环境中都会存在未提交的本地修改. 这是我们不希望看到的,因为当我们升级配置并更新到所有部署时, 可能会产生冲突. 这里其实是两个层面的问题:
- 提供一种机制, 当环境与缺省配置不一致时, 允许用户修改 用户修改的文件应避免与官方文件更新的冲突
缺省值当然可以直接定义在脚本或配置文件中.而多数常用的脚本和配置系统都提供了用户定义属性覆盖缺省值的机制. 比如:
- Windows批处理: set path=my_extra_path;%path%
Bash: export PATH=my_extra_path:$PATH
Ant:
<property file="user.properties"/> <!-- user.properties 中可定义任何后面引用到的属性, 以覆盖其缺省值-->
<property name="src.dir" path="${basedir}" /> <!-- 定义"src.dir"的缺省值-->
CruiseControl:
<property name="src.dir" value="http://www.blogjava.net/chelsea/archive/2009/06/04/." /> <!-- 定义"src.dir"的缺省值-->
<property file="user.properties"/> <!-- user.properties 中可定义任何后面引用到的属性, 以覆盖其缺省值-->
注意Ant的属性是只读的, 先入为主. CruiseControl的属性则是后发制人.
有一个很简单的解决办法, 就是把用户自定义属性置入单独的文件,并且不要把它提交到版本控制系统中(一个理由是这一部分相对整个组织来说, 不存在也不需要唯一的官方来源)
前面例子中的 user.properties 就是一个用户自定义属性文件,只存在每个用户自己的机器上, 不在配置库中. 在Windows和Bash脚本中也可以类似处理
- Windows: call user_env.bat
Bash: source ./user_env.sh 或 . ./user_env.sh
参考借鉴
- 至此, 这套东西在不同的机器上部署时, 只要从版本控制系统中check out出来即可使用了. 更进一步, 还可以提供脚本, 即生成器, 自动探测用户的环境, 来生成全套配置, 类似Rails生成应用框架那样.
其实, 这是一个规范性或标准性的问题. Neal Ford 在<<Productive Programmer: 卓有成效的程序员>>中, 用一章的篇幅详述了各种解决方案, 包括配置管理, 符号链接等, 除此之外, 他还建议可以/应该使用虚拟机来统一项目组的开发环境等. 参见<<Productive Programmer: 卓有成效的程序员>>第五章.
另请参阅<<CruiseControl Enterprise 最佳实践 (3) : Configuring CruiseControl the CruiseControl way>>, 是创建环境无关的持续集成环境的实例