属性
Capistrano 中的服务器对象本质上由名称和哈希组成:名称是 DNS 名称(或 IP 地址),哈希包含服务器的“属性”。这些属性分为两种:Capistrano 需要的属性(Capistrano 属性)和应用程序可用的属性(自定义属性)。它们共享相同的命名空间(只有一个底层哈希!),因此自定义属性的名称受到限制。
Capistrano 属性
Capistrano 属性是用于 SSH 登录服务器的属性,以及支持基本角色功能的属性。它们是
:user
- 服务器的 SSH 用户名:password
- SSH 用户的密码:port
- 服务器上 SSH 守护进程的端口号:roles
- 角色名称数组:ssh_options
- SSH 参数的哈希(见下文):primary
- 一个布尔值,指示服务器是否应被视为主服务器。
可以按以下方式指定 :user
、:port
和 :password
- 作为主机名的一部分,格式为 'user@host:port',不带密码。
- 在属性
:user
、:password
和:port
中。 - 在属性
:ssh_options
中(使用相同的键)。
优先级
SSH 相关属性的设置优先级如下,从最高优先级开始。
- 服务器或角色上的属性声明。最后一个属性声明会覆盖所有之前的服务器或角色声明。
- 在主机名字符串中指定的数值。
- 服务器或角色
:ssh_options
属性中的数值。 - 阶段全局变量
:ssh_options
。 - SSHKit 后端
ssh_options
。 - 本地
~/.ssh/config
文件中的设置。
但是请注意,从这些地方获取的默认值 *不会* 反映回服务器属性,因此如果使用的是较低优先级的默认值,则 host.user
将为 nil。
自定义属性
当使用 Capistrano 作为通用部署框架(超出其在 Rails 部署中的传统用途)时,能够存储额外的参数变得很重要。您可以将 Capistrano 视为部署的 *MVC* 框架,其中阶段文件(表示所有应用程序组件之间的关系)是 *模型*,任务(使模型更改能够被执行)是 *控制器*,而实际的物理体现(通常是运行服务器上的配置文件)是 *视图*。
从任务中访问属性
可以从 Capistrano 任务中以编程方式访问 Capistrano 服务器上的属性。*Capistrano* 属性可以通过主机对象本身的方法访问,而 *自定义* 属性可以通过主机 properties
属性的方法访问。
这些方法具有预期的名称:user
、port
等等。例外情况是 ssh_config
,它可以通过 netssh_options
方法访问。
以下功能是 Capistrano 3.3.6 及更高版本中的新功能。
在 on()
块的范围内,所产生的主机是底层主机的 *副本*,这允许您通过调用 setter 方法来临时覆盖任何属性。例如:
on roles(:all) do |host|
host.user = 'root'
host.password = 'supersecret'
execute :yum, 'makecache'
end
这会将 SSH 用户临时设置为 'root'(并使用相应的密码),而不会影响在配置中为服务器定义的 SSH 用户。
复杂配置中的属性设置
当配置涉及更多服务器时,能够在角色级别定义一组属性,并让这些属性在服务器级别被后来的定义覆盖,这将有助于保持配置尽可能地 DRY。一个典型的需求是定义一组 Redis 服务器,这些服务器都具有相同的端口参数,并且除了一个主服务器之外,其他服务器都是从服务器。
为了允许这样做,属性可以在服务器和角色级别设置。指导原则是在合并属性,并且最后定义优先。在实践中,我们根据属性值的类型略微调整这一点。
- 标量值将被覆盖。
- 哈希值的键将被合并,重复的键将采用最后一个键的值。
- 数组值将把后续条目追加到数组中。
服务器和角色属性示例
以上 Redis 需求可以使用阶段文件中的以下声明来满足。
role :redis, %w{ r1.example.com r2.example.com r3.example.com }, redis: { port: 6379, master: false },
server 'r1.example.com', redis: { port: 6380, master: true }
角色属性约定
这会因为一台机器可能服务于多个角色,实际上一台机器可能需要执行两次相同的角色而变得复杂!例如,在开发环境中,您可能希望一台机器同时作为数据库服务器、主 Redis 服务器和从 Redis 服务器。
为了解决这个问题,我们采用了一种关于服务器属性使用的约定。
-
给定角色的服务器属性应使用与角色相同的键名存储。属性的内容可以是标量、数组或哈希。
-
同一服务器上角色的多次出现应将内容设置为数组,其中后续元素表示每个实例。
以下示例显示了在同一服务器上具有多个 Redis 和 Sentinel 角色的配置。
server 'dev.local', roles: %w{db web redis sentinel worker}, primary: true,
redis: [ { name: 'resque', port: 6379, db: 0, downtime: 10, master: true },
{ name: 'resque', port: 6380, db: 0, downtime: 10 } ],
sentinel: [ { port: 26379 }, { port: 26380 }, { port: 26381 } ]
这些属性可以通过普通方式访问,但为了帮助获取它们,您可以使用 role_properties()
函数(见下文)。
设置属性
属性可以在角色和服务器级别设置。
角色属性
角色的声明接受一个服务器名称数组和一个属性的尾部哈希。按照惯例,角色声明中的第一个服务器被视为主服务器,但 :primary
属性在这种情况下实际上不会被设置。
服务器属性
服务器的声明需要一个服务器名称和一个属性的尾部哈希。其中一个属性必须是 :role
,并且其值必须是一个角色名称数组。
访问属性
roles()
方法
roles()
方法接受一个或多个角色名称(或角色数组),后面跟着一个可选的 属性过滤器,并返回一个包含属于这些角色的 Capistrano::Configuration::Server
对象的数组。这些对象具有以下有用的属性
hostname
- 字符串properties.keys
- 可用属性的名称properties
- 一个类似哈希的对象,用于存储属性。它使用 Ruby 的“method_missing”为每个有效键提供一个方法。roles
- 一组角色名称,以符号表示
通过此方法检索的服务器不受任何主机或角色过滤器的影响。
role_properties()
方法
此方法接受一个角色列表(后面跟着一个可选的 属性过滤器),并返回一个包含属性的哈希数组,其中添加了 :hostname
和 :role
键。
task :props do
rps = role_properties(:redis, :sentinel)
rps.each do |props|
puts props.inspect
end
end
# Produces...
{:name=>"resque", :port=>6379, :db=>0, :downtime=>10, :master=>true, :role=>:redis, :hostname=>"dev.local"}
{:name=>"resque", :port=>6380, :db=>0, :downtime=>10, :role=>:redis, :hostname=>"dev.local"}
{:port=>26379, :role=>:sentinel, :hostname=>"dev.local"}
{:port=>26380, :role=>:sentinel, :hostname=>"dev.local"}
{:port=>26381, :role=>:sentinel, :hostname=>"dev.local"}
或者,您可以提供一个代码块,它将生成主机名、角色和属性。
task :props_block do
role_properties(:sentinel) do |hostname, role, props|
puts "Host: #{hostname}, Role: #{role}, #{props.inspect}"
end
end
# Produces...
Host: dev.local, Role: sentinel, {:port=>26379}
Host: dev.local, Role: sentinel, {:port=>26380}
Host: dev.local, Role: sentinel, {:port=>26381}
请注意,与 on()
不同,此函数不会导致任何远程执行,它纯粹用于配置目的。