Task K: Deployment and Production

部署一直都被视作一个应用的幸福时刻。因为亲手将自己精心制作的代码上传至服务器,而且还有千千万万的人将使用它。此时应该已经摆好了庆功宴就等我们回去了。一段时间后,我们制作的应用也将被载入 Wired 杂志,一夜之间我们就在极客社区中名声大噪。

不过部署应用是需要一些计划以帮助我们可以更加顺畅和重复进行部署工作的。

通过此章节,我们将建立如下的部署步骤:

Application deployment road map.png

部署时,上述所有工作将在一台机器上完成,我们会通过一台物理隔离的机器操作 web 服务器完成所有工作。在图示中,用户机器在中心,左边是 WEBRick web 服务器。这台服务器操作的是 SQLite3、需要安装的 gem 和应用代码。现在你的代码可能还没有放置在 Git 中,不过没有关系,在本章的最后应用代码和使用到的 gem 都会被推送至 Git。

此 Git 仓库也将复制到生产服务器中,生产服务器可以是另一台机器,不过目前没有必要。服务器中将会运行一个由 Apache httpd 和 Phusion Passenger 组成的服务。而代码将访问可能部署于第四台机器的 MySQL 数据库。

然后我们将通过 Capistrano 工具远程操作部署服务器,这样就可以通过开发机器进行安全和重复地部署。

看起来要处理的环节还不少。

相比一次处理所有任务我们更愿意将工作分成三个迭代完成。迭代 K1 会启动 Depot 应用,并且是在运行了 Apache、MySQL 和 Passenger 的真实生产环境中。

第二个迭代中将使用 Git、Bundler 和 Capistrano。这些工具可以将开发操作从部署环境中分隔开。也就是说部署动作我们需要重复两次,这只是第一次,目的是为了确认部署的每个部分都可以独立动作。这种方式让我们每次只用专心处理较少的事情,也使遇到问题时的处理流程更加简化。

最后一次迭代将学习管理和清理任务。现在我们开始工作吧。

迭代 K1:使用 Phusion Passenger 和 MySQL 部署

我们已经在本地机器中部署过 Rails 应用了,不过在运行应用的时候是通过 WEBrick。绝大多数时候没什么问题。rails server 命令会选择最适合的方式在开发模式下通过 3000 端口运行服务。不过用于部署的 Rails 会有些许不同。我们不会只开启一个 Rails 服务器并让它处理所有的工作。虽然可以只使用一个 Rails 服务不过这与理想环境相去甚远。因为 Rails 是单线程运行,一次只能处理一个请求。

Joe 提问:可以在微软 Windows 系统中部署吗?

尽管也可以在 Windows 环境中部署应用,但大量的 Rails 工具和资料都是关于 Linux 或 Mac OS X 等基于 Unix 的操作系统。比如 Ruby on Rails 团队强烈推荐并且将在本章使用的 Phusion Passenger 就是如此。

而且本章描述的技术都是在 Linux 或 Mac OS X 环境中使用。

但是 web 环境又格外需要并发环境。一些 web 服务,比如 Apache、Lighttpd 和 Zeus 都可以同时处理多个请求,基于成百上千个请求。一个单线程的基于 Ruby 的 web 服务不可能处理这样的情况。幸运的是,它也并不是必须要处理这样的情况。实际上我们在生产环境中是将 Rails 应用通过前置服务器方式部署,让类似 Apache 的工具处理来自客户端的请求。然后再通过 Passenger 使用 HTTP 代理向几个应用进程中的一个发送请求即可。

配置第二台机器

如果你已经有另一台可以使用的机器最好不过,如果没有也可以通过虚拟机实现。有许多软件都可以实现此目的,比如 VirtualBox 和 Ubuntu。如果你打算使用 Ubuntu 我们推荐 12.04 LTS。

按照第一章中的步骤配置虚拟机中的 Ruby 及 Rails 环境。你也可以跳过安装 Rails 的步骤直接安装 Bundler。

gem install bundler

接下来,从原来的机器中将 Depot 应用的整个文件夹都拷贝至第二台机器中。在第二台机器中进入应用文件夹中,通过 Bundler 安装应用需要的依赖。

bundle install

通过下列的命令组合验证依赖安装没有问题:

rake about
rake test
rails server

此时,启动浏览器应该可以操作应用。验证没有问题后将服务停止。

当前拷贝应用文件夹、开启和停止应用都是让开发者自己动手,在这章内容后面需要将此步骤自动化。不过现在,通过了解相应的操作步骤以及相应的正确结果可以帮助我们确认开发环境的基础配置已经复刻至另一台机器中。

安装 Passenger

下一步是确认 Apache web 服务器已经在第二台机器上安装及运行。Linux 用户已经在 1.3 节时安装过,而 Mac OS X 用户也已经在操作系统中附带,不过需要启用一下。如果是使用 Mac OS X 10.8 之前版本的系统,需要在 Preferences > Sharing and enabling Web Sharing 中设置。然后在终端窗口中运行下面命令即可。

sudo apachectl start
sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist

接着安装 Passenger。

gem install passenger --version 4.0.8
passenger-install-apache2-module

如果必要的依赖没有安装成功,稍后的命令会告知如何处理。比如在 Ubuntu 13.04 系统中,你会发现需要先安装 libcurl4-openssl-dev、apache2-prefork-dev、libapr1-dev 及 libaprutil1-dev。如果出现类似问题,按照提供的步骤进行,然后再尝试 Passenger 的安装命令。

如果依赖都正常安装,此命令将使大量源文件被编译,以及许多配置文件被更新。在此过程中,我们还会被告知需要修改 Apache 的配置。首先需要允许构建新的 module,也将包括添加如下代码至 Apache 配置中。(注意:Passenger 会告知你将一些代码拷贝至此文件中,那就按提示建议进行,不要使用下面的代码。我们已经略去了 LoadModule 路径以适应文章展示。你还需要确认 Passenger 提供的相应路径。)

LoadModule passenger_module /home/rubys/.rvm/.../ext/apache2/mod_passenger.soPassengerRoot /home/rubys/.rvm/gems/ruby-2.0.0-p0/gems/passenger-4.0.1PassengerDefaultRuby /home/rubys/.rvm/wrappers/ruby-2.0.0-p0/ruby

找到 Apache 配置文件路径,并将下列命令内容与路径进行比对。

apachectl -V | grep HTTPD_ROOT
apachectl -V | grep SERVER_CONFIG_FILE

有些系统中命令是 apache2ctl,还有些系统中是叫 httpd。不论如何,找到你系统中相应的命令。

与修改文件路径不同的是,许多现代系统十分方便,允许你分离式地维护相应的扩展。比如在 Mac OS X 中,你会在 httpd.conf 文件的结尾看到如下代码:

Include /private/etc/apache2/other/*.conf

如果你在 httpd.conf 中看见这样的代码,便可以将 Passenger 提供的配置填写在上述路径的 passenger.conf 文件中。在 Ubuntu 中可以将配置填写在 /etc/apache2/conf.d/passenger 文件中。

本地部署应用

接着是部署应用。上一步骤需要在每个服务器中都进行一次,而现在的步骤需要在每个应用中都操作一次。将主机名和应用文件夹路径填写在下列 ServerName 配置中:


  ServerName depot.yourhost.com
  DocumentRoot /home/rubys/deploy/depot/public/
  
    AllowOverride all
    Options -MultiViews
    Order allow,deny
    Allow from all
  

DocumentRoot 是设置 Rails 应用中的公共路径,所以需要将公共路径设置为可读。

也许你的 Apache 已经按照约定将这些配置都已经放入指定地方。在 Mac OS X 中,可以查看 httpd.conf 中的下列代码(可能是被注释掉的):

#Include /private/etc/apache2/extra/httpd-vhosts.conf

如果有上述代码,可以考虑将注释去掉,并将主机名称替换为 dummy-host.example.com

在 Ubuntu 中,通常是在 /etc/apache2/sites-available 文件中的类似代码,可以列举允许的网站地址。例如,如果将文件命名为 depot,可以通过下列命令启用站点:

sudo a2ensite depot

如果有多个应用,就在每个应用中重复 VirtualHost 部分操作,并调整每个应用的 ServerNameDocumentRoot 配置。还需要确认下列代码已经存在于配置文件中:

NameVirtualHost *:80

如果此配置不存在,在包含「Listen 80」字眼的配置前将上述配置添加。

最后是重启 Apache web 应用。

sudo apachectl restart

接着要配置客户端,让其可以配置你指定的机器。这个配置可以在 /etc/hosts 中处理。在 Windows 中,对应的文件为 C:\Windows\system32\drivers\etc\。需要以管理员权限打开此文件才可以编辑。

要在 /etc/hosts 中配置如下代码:

127.0.0.1 depot.yourhost.com

添加此配置后,我们便可以通过指定的主机(或者虚拟主机)访问应用了。如果你没有配置使用 80 之外的端口,在通过 URL 访问应用时就不需要添加额外的端口号。

不过还有些事情需要小心。

  • 当你重启服务后看到「The address or port is invalid」类似的提示,这意味着 NameVirtualHost 已经存在,可能是在相同路径下的其他配置文件中。如果发生类似的问题,请删除你添加的配置,因为同样的配置只需要出现一次。

  • 如果想要在以非生产环境的方式运行应用,只需要在 Apache 配置中的 VirtualHost 项添加相应 Rails 配置即可。

RailsEnv development

  • 可以通过更新或创建应用中 tmp 路径下的 restart.txt 文件重启应用而不用重启 Apache。

touch tmp/restart.txt

  • passenger-install-apache2-module 命令的输出结果会告知我们如何查找到其他文档。

使用 MySQL 作为数据库

SQLite 官网已经明确地表述了该数据库擅长及不擅长的领域。其中特别说到 SQLite 不适合高数据量、高并发的网站,但我们的网站恰恰是 SQlite 不擅长的领域。

其实除了 SQLite 外还有大量免费并适于商业使用的选择。我们打算使用 MySQL。通过 Linux 自带的包管理工具即可获得,而且 MySQL 网站已经为 OS X 提供了相应的安装包。

Mac OS X ver. 10.7(x86, 64-bit). DMG Archive 版本在 10.8 的系统上能正常运转。如果你不想登录通过页面底部的链接可以直接下载。

除了安装 MySQL 数据库外,也需要在 Gemfile 中添加 mysql gem 依赖。

# Gemfile

group :production do
  gem 'mysql2'
end

将 mysql gem 依赖配置在 production 分组中是为了在开发和测试环境运行时不会加载它。如果你需要,也可以将 sqlite3 依赖分别配置在 developmenttest 分组中。

然后通过 bundle install 命令安装依赖。也许你首先要安装 MySQL 数据库的开发文件。在 Ubuntu 中,你需要安装 libmysqlclient-dev。

通过 mysql 命令行即可创建数据库,也可以使用 phpmyadmin 或 CocoaMySQL 这样更加方便的工具。

mysql -u root
CREATE DATABASE depot_production DEFAULT CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON depot_production.* TO 'username'@'localhost' IDENTIFIED BY 'password';
EXIT;

如果你给数据库取了其他的名字请记住它,因为在修改配置文件时也需要此名字。现在我们来看看配置文件。

config/database.yml 包含了连接的数据库信息。其中有三个部分,每个部分分别是开发、测试和生产环境的数据库配置。生产环境的配置有如下部分:

# config/database.yml

production:
  adapter: sqlite3
  database: db/production.sqlite3
  pool: 5
  timeout: 5000

我们要将此部分配置替换为:

# config/database.yml

production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: depot_production
  pool: 5
  username: depot
  password: 123456
  host: localhost

记得修改 usernamepassword 和必要的数据库连接属性。

加载数据库

下一步运行 migration。

rake db:setup RAILS_ENV="production"

两种可能中的其中一定会发生。如果一切正常你将看见成功结果的输出。

如果有一些错误信息输出,也不要慌张。可能只是因为一些简单的配置问题,可以按如下建议排查:

  • 检查 database.yml 中 production 部分的数据库名称。它应该与你创建的数据库名称一致(可以通过 mysqladmin 或其他的数据库管理工具查看)。

  • 检查 database.yml 中的用户名及密码是否与创建数据库时一致。

  • 检查数据库是否正常运转。

  • 检查是否可以通过命令行进行连接。如果使用的是 MySQL 可以运行 mysql depot_production

  • 如果能够通过命令行连接,检查是否可以创建测试表。(进行此项测试的数据库用户需要有相应的数据库权限)。

create table dummy(i int);
drop table dummy;
  • 如果你可以通过命令行创建表,而 rake db:migrate 命令依然失败,请再仔细检查 database.yml 文件。如果文件中有 socket: 项配置,尝试将此配置注释。

  • 如果错误信息是「No such file or directory...」,并且其中的文件名是 mysql.sock,就意味着 Ruby MySQL 库无法找到 MySQL 数据库。此错误出现有两种可能,一是在安装数据库前你已经将安装了相关库,另一种是你是通过二进制文件安装的库,此方式会导致相关库对 MySQL 数据库的 socket 文件是猜测的错误地址。想修复此问题,需要重新安装 Ruby MySQL 库。如果不是这个问题,就请再次检查 database.yml 文件中的 socket: 配置是否为系统中 MySQL socket 的正确地址。

  • 如果错误是「Mysql not loaded」,表示运行的 Ruby MySQL 库是旧版的。Rails 的最低版本为 2.5。

  • 还有些读者遇到的错误是「Client does not support authentication protocol requested by server; consider upgrading MySQL client」。要解决这个 MySQL 版本与访问库版本不一致的问题可以根据 http://dev.mysql.com/doc/mysql/en/old-client.html 的步骤解决,然后输入 MySQL 命令,比如 set password for 'some_user'@'some_host'=OLD_PASSWORD('newpwd');

  • 如果是在 Windows 的 Cygwin 中使用 MySQL,并且指定主机为 localhost 可能会出现问题,可以尝试替换为 127.0.0.1。

  • 最后,可能会因 database.yml 的格式上出现问题。YAML 库读取此类文件时对 tab 字符十分敏感。如果文件中包含了 tab 字符,就会导致问题。(你想想自己选择 Ruby 而不是 Python 是不是因为不喜欢 Python 中的有意义的空格符?)。

回到 rake db:setup 命令,许多时候都可能需要确认是否配置出现问题。

虽然听起来很可怕,但也不要过度担心。事实上,数据库连接多数时候都如同魔咒一般,一旦 Rails 正常与数据库连接上就不用再为其担心。

现在,应用已经正常运行。这与为一个用户运行并没有区别,只有当拥有一些并发用户或者大量数据时才会有所区别。

下一步要将生产环境从开发环境中分离出去。

迭代 K2:通过 Capistrano 远程部署

如果你有一个大型商铺,同时需要管理大量详细的服务,保证运行的必要软件都处于同一版本才是正道。为了更多类似的需求共享服务器就诞生了,不过我们还要注意处理在开发环境安装的软件版本与生产环境中安装的版本不一致问题。

不必担心,其实可以采用下面提出的解决方案。

准备部署服务器

在开发环境中对相应软件进行版本管理,而不是直接进行相应部署是理想方式,所以我们可以选择 Capistrano 管理部署环境。

许多软件配置管理系统(SCM)也不错。比如 subversion 就是比较好的选择。但如果你不曾使用过任何相关软件,建议使用 Git,它易于安装也不需要分离服务器进程。下面的例子都将基于 Git 讲解,但如果你已经采用了其他的 SCM 系统,也不需担心,Capistrano 只关心你使用了 SCM,但不注重你使用的是具体哪种。

首先创建一个可以通过部署服务器访问的空仓库。实际上,如果只拥有一台部署服务器就无法将其作为 Git 服务器。所以登录服务器,然后输入以下命令:

mkdir -p ~/git/depot.git
cd ~/git/depot.git
git --bare init

下面要注意的是即使 web 服务器和 SCM 服务器都为同一个物理机器,但 Capistrano 远程时都将访问 SCM 软件。为了更加流畅地进行工作,我们可以通过生成公钥的方式(如果你还不曾拥有公钥),接下来可以通过公钥获取访问服务器的权限。

test -e ~/.ssh/id_dsa.pub || ssh-keygen -t dsa
cat ~/.ssh/i8d_dsa.pub >> ~/.ssh/authorized_keys

在自己的机器中通过 ssh 测试,通过这些操作后将会确认你的 known_hosts 文件已经被更新。

现在还有最后一件事需要处理,Capistrano 将在应用路径和 Rails 路径之间创建一个 current 文件夹,此文件夹将包含 public 子文件夹。也就是说,如果你在控制自己的服务器或者在共享主机的控制面板中,就需要调整 httpd.conf 文件中的 DocumentRoot 和 Directory。

DocumentRoot /home/rubys/deploy/depot/current/public/

重启 Apache 服务器。此时会出现 depot/current/public 路径不存在的警告,这没有任何问题,因为稍后我们将创建此路径。

最后,要确认 Gemfile 和 config/database.yml 文件的修改都已经从第二台机器的 Depot 应用拷贝至第一台机器的 Depot 应用中。

服务器就处理完了。此时从服务器退出,接下来的工作我们将在开发机上处理。

远程获取应用程序

首先就是通过 Capistrano 将 Gemfile 进行修改。

# Gemfile

gem 'rvm-capistrano', group: :development

所有的用户都需要去除此行注解,RVM 用户需要添加 rvm- 字符。

现在通过 bundle install 安装 Capistrano,在 G3 迭代中我们曾通过此命令对 bcrypt-ruby 进行过安装。

如果尚未准备好将应用置于配置的控制中,按下面的命令操作。

cd your_application_directory
git init
git add .
git commit -m "initial commit"

下一步是可选择的,不过如果你对部署服务器没有完整的控制,或者有大量的部署服务器需要管理,跳过此步骤并不是一个好主意。即将使用的是 Bundler 的第二个特性,也就是 package 命令。此命令的作用是将应用依赖的软件版本都放置进仓库中。

bundle package
git add Gemfile.lock vendor/cache
git commit -m "bundle gems"

在 24.3 节我们还会对此功能的更多细节进行讨论。

此时,所有代码都将被推送出服务器。

git remote add origin ssh://user@host/~/git/depot.git
git push origin master

将其中的 userhost 替换为远程服务器上的用户和主机名。

通过这几个步骤后,你已经赋予了被部署构件的控制权。你将能够操纵提交至本地仓库的代码,当代码被推送出当前服务器后就可以实施控制。接着,就是控制代码放置到生产环境中。

远程部署应用获取

之前我们已经在本地服务器中部署过应用,现在将第二次部署,不过此次是通过远程的方式。

准备工作已经完成,现在代码都已经在 SCM 服务器中,我们可以通过应用服务器进行访问。而且,两台服务器相不相同并不重要,重要的是都已经有了可以进行执行操作的角色。

在项目中添加必要的文件使 Capistrano 可以正常操作,执行下列命令:

capify .

通过输出信息可以看出 Capistrano 创建了两个文件。第一个文件 Capfile,它是 Rakefile 的相似品。你要将其中的一行代码去除注释,完成此工作后就不同需要接触此文件了。

# Capfile

load 'deploy'
# Uncomment if you are using Rails' asset pipeline
#START_HIGHLIGHT
load 'deploy/assets'
#END_HIGHLIGHT
load 'config/deploy' # remove this line to skip loading any of the default tasks'

第二个文件是 config/deploy.rb,它包含了部署应用的方法。Capistrano 向我们提供的是这个文件的最小版本,下面是此文件的完全版,你可以下载并使用它。

# config/deploy.rb

#---
# Excerpted from "Agile Web Development with Rails",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/rails4 for more book information.
#---
require 'bundler/capistrano'

# be sure to change these
set :user, 'rubys'
set :domain, 'depot.pragprog.com'
set :application, 'depot'

# adjust if you are using RVM, remove if you are not
set :rvm_type, :user
set :rvm_ruby_string, 'ruby-2.0.0-p247'
require 'rvm/capistrano'

# file paths
set :repository,  "#{user}@#{domain}:git/#{application}.git" 
set :deploy_to, "/home/#{user}/deploy/#{application}" 

# distribute your applications across servers (the instructions below put them
# all on the same server, defined above as 'domain', adjust as necessary)
role :app, domain
role :web, domain
role :db, domain, :primary => true

# you might need to set this if you aren't seeing password prompts
# default_run_options[:pty] = true

# As Capistrano executes in a non-interactive mode and therefore doesn't cause
# any of your shell profile scripts to be run, the following might be needed
# if (for example) you have locally installed gems or applications.  Note:
# this needs to contain the full values for the variables set, not simply
# the deltas.
# default_environment['PATH']=':/usr/local/bin:/usr/bin:/bin'
# default_environment['GEM_PATH']=':/usr/lib/ruby/gems/1.8'

# miscellaneous options
set :deploy_via, :remote_cache
set :scm, 'git'
set :branch, 'master'
set :scm_verbose, true
set :use_sudo, false
set :normalize_asset_timestamps, false
set :rails_env, :production

namespace :deploy do
  desc "cause Passenger to initiate a restart"
  task :restart do
    run "touch #{current_path}/tmp/restart.txt"
  end

  desc "reload the database with seed data"
  task :seed do
    deploy.migrations
    run "cd #{current_path}; rake db:seed RAILS_ENV=#{rails_env}"
  end
end

不过需要修改几个属性,让信息与我们的应用匹配。需要修改的有 :user:domain:application 几项。:repository 需要填写之前我们放置在 Git 中代码的地址。:deploy_to 为之前让 Apache 可以查找到的应用的 public 路径。

其中还有几行配置是告诉大家如何使用 Capistrano 制作可用的 RVM 的。

如果是通过 root 用户安装的 RVM,就修改 set :rvm_type 值为 :system。修改 :rvm_ruby_string 为想要使用的 Ruby 解释器版本。

如果没有使用 RVM,请将此行删除。

default_run_optionsdefault_environment 只是为解决遇到的特定问题。其它杂项是基于 Git 提供的,它们可以禁用一些资源流程逻辑,也就是表示 Rails 版本的优先级。

然后还定义了两个任务。一个是告诉 Capistrano 如何启动 Passenger。另一个是通过 seed 数据重启数据库。你也可以根据自己的需要调整相应的任务。

第一次部署时,我们必须执行其他步骤,在服务器中为了部署建立基本的目录结构。

cap deloy:setup

当执行上述命令,Capistrano 将提示我们输入服务器密码。如果密码验证失败也就登录失败,此时需要将 deploy.rb 中的 default_run_options 配置注释再进行尝试。一旦连接成功,它将创建必要的目录。在命令工作完成后,我们再检查其他有问题的配置。

cap deploy:check

运行命令之前,我们需要去除 deploy.rb 中的 default_environment 代码的注释。我们可以不断重复执行此命令,直到它完全显示成功,我们以此定位它显示出的问题。

最后一项工作,我们加载包含了商品信息的 seed 数据。

cap deploy:seed

现在我们完成了相应的工作。

反复冲洗

当我们进行到此时,服务器已经准备好了新版应用。接下来就是在仓库中检查我们做出的修改,然后重新部署。现在,还有两个 Capistrano 文件没有检查。尽管应用服务器并不需要它们,但我们可以通过它们检测部署流程。

git add .
git commit -m "add cap files"
git push
cap deploy

前三个命令都是更新 SCM 服务器。在你熟悉 Git 后,你肯定会想了解和控制添加哪些文件以及何时添加,甚至会想在部署前增量提交多修改,等等这样需求。最后一个命令会更新应用服务器、web 服务器和数据库服务器。

如果因为某些原因我们需要回退至之前版本的应用,可以使用下列命令:

cap deploy:rollback

现在我们可以完整地部署应用以及更新服务器中运行的代码。每次部署应用时,相应的新版本都会被摘取至服务器中,更新一些符号连接,然后重启 Passenger 进程。

迭代 K3:检查部署完成的应用

当应用被部署后,有时会疑虑想检查一下应用运行的情况。可以通过两个简单的方法做到,第一个是监控前置 web 服务器的输入日志,也就是运行应用的 Apache 服务器。第二种方式是通过 rails console 连接应用。

查看日志文件

想要快速了解应用的情况,可以通过 tail 命令查看向应用发起的请求。令人感兴趣的数据一般都来自应用的日志文件本身。即使 Apache 运行了多个应用,输出的日志也会记录在相应应用的 production.log 文件中。

假设应用是部署在之前展示的路径中,下面我们就看看运行中的日志:

cd /home/rubys/deploy/depot/current
tail -f log/production.log

有时,我们需要低级别的信息,也就是应用中的数据信息。关于这个需求,是时候使用最有用的实时服务器 debug 工具了。

通过控制台查看应用实时情况

在应用的 model 类中我们创建了许多函数。当然,这些函数都是通过应用的 controller 进行调用,但我们也可以直接操作。可以通过 rails console 脚本实现:

cd /home/rubys/deploy/depot/current/
rails console production

p = Product.find_by(title: "CoffeeScript")
p.price = 29.00
p.save

当控制台会话开启,便可以仔细研究 model。并且还可以创建、查看以及删除数据。从某种角度看来,它就像应用的 root 控制台。

当应用被部署在生产环境后,为了让应用运行流畅我们还需要关注一些配置杂项。这些活并不会自动处理,不过我们可以让它们自动化。

处理日志文件

当应用运行时会源源不断地向日志文件输出。最后,日志文件将变成庞然大物。为了解决这个问题,许多日志解决方案都可以创建按日期增加方式堆积的日志文件。这样即可以将巨大的日志文件拆散,也可以在日期超过一定限制后将其删除。

Logger 类就支持这种方式。它需要指定日志文件的数量(或者频次)以及每个日志的大小,在 config/environments/production.rb 中配置如下信息即可:

config.logger = Logger.new(config.paths['log'].first, 'daily')

或者这样配置:

require 'active_support/core_ext/numberic/bytes'
config.logger = Logger.new(config.paths['log'].first, 10, 10.megabytes)

注意上述示例中明确引入了 active_support,因为这个声明在应用初始化之前,也就是在 Active Support 库被引入之前。实际上,Rails 提供的一个配置项并不需要 Active Support 库。

config.active_support.bare = true

或者可以直接将日志使用机器的系统日志。

config.logger = SyslogLogger.new

可以在 http://rubyonrails.org/deploy 中查找更多配置项。

继续运行并调优

当建立基本的部署后,我们准备结束应用的开发并将它在生产环境启动。甚至有可能建立其他的部署服务器,不过从第一次部署的课程中学习到的知识将对以后的部署有所帮助。比如,我们会发现 Rails 是系统中比较慢的组件,多数请求时间都会花费在 Rails 上,而不是对数据库或文件系统的等待上。可以通过按比例增加机器的方式减缓 Rails 的加载。

然后,还可能发现大量的请求时间都消耗在数据库中。如果是这种情况,就要看看如何优化数据库操作了。也许可以修改访问数据库的方式,或者需要定制一些 SQL 将默认的 Active Record 操作替换。

有一件事情是确定的,每个应用都需要根据自己的情况调整配置。最重要的是监控它的超时情况并发现需要处理的地方。并不是运行应用之后我们的工作就结束了,它只是个开始。

完成应用在生产环境的第一次部署后我们的工作才真正开始,不过关于 Depot 应用的旅程已经结束。在完成此章后,我们将回头看看其中重要的一些代码。

总结

本章中聊到了许多环境。在本地开发环境为单独的用户运行过应用,然后以将它运行在其他机器上,运行了另一个 web 服务器,访问其他类型的数据库,甚至还可能在其他操作系统中运行。

完成这些任务使用了许多产品。

  • 安装和配置了 Phusion Passenger 和 Apache httpd,搭建了生产级的 web 服务器。

  • 安装和配置了 MySQL,一个生产级的数据服务器。

  • 通过 Bundler 和 Git 远程获取了应用依赖

  • 安装及配置了 Capistrano,使我们可以安心和重复地部署应用

自习天地

下面的知识需要你自己练习:

  • 如果有多名人员进行合作开发,将数据库的详细配置信息放置在配置管理系统中好像不太合适(特别是密码)。为了解决这个问题,将完整的 database.yml 拷贝至 shared 路径,并且编写 Capistrano 任务在每次部署时都将文件拷贝至 current 路径中。

  • 这章主要关注平稳、尝试和正确,或许还有一些保守开发选择,不过在此领域已经有了许多大胆的创新。Capistrano 和 Git 也出现了一些争论,每个相关领域都出现了竞争,下面就是一些相关方法:

    • 尝试使用 rbenv 和 ruby-build 替换 rvm

    • 尝试使用 PostgreSQL 替换 mysql

    • 尝试使用 Unicorn 和 nginx 替换 Phusion Passenger 和 Apache httpd

敏捷的意义不止是做正确的选择,它即需要遵从计划,又需要快速灵活地响应变化。


本文翻译自《Agile Web Development with Rails 4》,目的为学习所用,如有转载请注明出处。

你可能感兴趣的:(Task K: Deployment and Production)