通常,应用的 配置 在不同 部署 (预发布、生产环境、开发环境等等) 间会有很大差异。这其中包括:
有些应用在代码中使用常量保存配置,这与 12-factor
所要求的代码和配置严格分离显然大相径庭。配置文件在各部署间存在大幅差异,代码却完全一致。
判断一个应用是否正确地将配置排除在代码之外,一个简单的方法是看该应用的基准代码是否可以立刻开源,而不用担心会暴露任何敏感的信息。
12-Factor
推荐将应用的配置存储于 环境变量 中( env vars, env )。环境变量可以非常方便地在不同的部署间做修改,却不动一行代码。更多关于12-factor
的内容请访问 12factor.net.
Rails 中大部分实践是遵守了 12-Factor 规则,除了日志和配置。Rails Way 的配置方式是 yml 文件,例如:database.yml、secret.yml 等。简单的应用使用 yml 文件和 12-factor
推荐的环境变量其实区别不大,不过当项目不断膨胀,配置数量也随着增加,yml 方式变得越来越难以维护。yml 存在的问题表现在:
下面是目前项目的配置文件,每次搭建环境修改这些配置文件是最痛苦的事情...
shared/config$ ls |grep yml
s3.yml
redis.yml
twilio.yml
paypal.yml
sunspot.yml
database.yml
icontact.yml
http_auth.yml
beanstream.yml
commercehub.yml
canada_post.yml
google_content_api.yml
最直接的方式是使用 shell 环境变量:
$ export TWILIO_ACCOUNT_SID=AC1234...
$ export TWILIO_AUTH_TOKEN=abc12...
在 Ruby 中使用 ENV 读取:
puts ENV['TWILIO_ACCOUNT_SID']
puts ENV['TWILIO_AUTH_TOKEN']
shell 环境变量由于没有持久化,又引出了 .bashrc、.bash_profile、~/.profile、/etc/environment 等方式。都不是很完美,权限问题、login shell 问题的坑都等着你呢。最重要的是,上面提到这些都不方便应付单机部署多个应用的场景。
Envyable、dotenv、Figaro等工具,在应用程序中把配置注入到ENV
中避免了上面提到的各种问题。Envyable、dotenv、Figaro 无论是实现上还是使用上其实大同小异,下面只拿 dotenv 来说:
In Gemfile:
gem 'dotenv-rails', :require => 'dotenv/rails-now'
config/application.rb
Bundler.require(*Rails.groups)
Dotenv::Railtie.load
HOSTNAME = ENV['HOSTNAME']
.env 文件:
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
gitignore 忽略.env,并且 cap 设置软链:
set :linked_files, fetch(:linked_files, []).push('config/database.yml', '.env')
调用的时候仍然是 ENV['SECRET_KEY']
。当然,dotenv 还支持多环境模式,比如 .env.production
文件只对 production 环境生效。
dotenv 已经很完美了,但用过 Heroku 的都知道这还不够,看一下 Heroku 设置环境变量的方式:
heroku config:set GITHUB_USERNAME=joesmith
Adding config vars and restarting myapp... done, v12GITHUB_USERNAME: joesmith
$ heroku config
GITHUB_USERNAME: joesmith
OTHER_VAR: production
$ heroku config:get
GITHUB_USERNAMEjoesmith
$ heroku config:unset GITHUB_USERNAME
Unsetting GITHUB_USERNAME and restarting myapp... done, v13
capistrano-twelvefactor + dotenv 可以打造出和 Heroku 一样酷的体验。
详细的步骤 capistrano-twelvefactor 上面都有写,下面只说和 dotenv 配合需要做的:
# config/deploy/production.rb
set :environment_file, deploy_path.join("shared/.env")
after 'config:set', "deploy:symlink:linked_files"
after 'config:unset', "deploy:symlink:linked_files"
然后就可以使用这几个命令查看和修改环境配置了:
注:上面需要 capistrano 3 + dotenv,Envyable 和 Figaro 目前无法使用 Heroku style.
对于单台服务器的应用,capistrano-twelvefactor + dotenv 足以应付,同时,由于 capistrano 支持集群部署,单个应用多服务器其实也是可以搞定的。但有时候不同应用之间其实也需要共享配置文件的,比如,S3 配置变化了,依赖这个帐号的所有应用都应该得到同步。另外的一个问题是,对于多服务器场景,配置存储在.env 会出现服务器之间配置不同步现象。如果要解决上述问题,可能引入etcd或 zookeeper是一个不错的选择。