使用 Heartbeat 构建高可用服务

Even in the future, nothing works!

— Spaceballs

一切迟早都会发生故障。高可用服务就是指当一个主机或网络线路失效时仍旧能够提供服务。 高可用性的主要技术就是冗余,另外,这个问题的解决就是以投放更多硬件设备而著称的。

虽然最终肯定会有单独的一台服务器失效,但是两台服务器同时失效的概率是不太高的, 这对大多数的应用程序提供了一个良好的冗余水平。

最简单的方法之一是建立一对冗余服务器,它们共享一个 IP 地址,并使用心跳检测。 心跳Heartbeat)是一个守护进程,它同时运行在两台机器上并且定期彼此交换信息(heartbeats)。 其中的一台是主服务器,通常它拥有资源:在本例中是一个 IP 地址。 如果辅助服务器无法从主服务器检测到心跳信号,它就接管地址以确保服务的连续性。

在下面的处方中,我们将使用 Puppet 的配置设置两台机器,并解释如何使用它提供一个高可用服务。

操作步骤

  1. 创建如下的 heartbeat 模块:

    # mkdir /etc/puppet/modules/heartbeat
    # mkdir /etc/puppet/modules/heartbeat/manifests
    # mkdir /etc/puppet/modules/heartbeat/files
    
  2. 使用如下内容创建 /etc/puppet/modules/heartbeat/manifests/init.pp 文件:

    class heartbeat {
      package { "heartbeat":
        ensure => installed,
      }
    
      service { "heartbeat":
        ensure  => running,
        require => Package["heartbeat"],
      }
    
      exec { "reload-heartbeat":
        command     => "/usr/sbin/service heartbeat reload",
        refreshonly => true,
      }
    
      file { "/etc/ha.d/authkeys":
        source  => "puppet:///modules/heartbeat/authkeys",
        mode    => "600",
        require => Package["heartbeat"],
        notify  => Exec["reload-heartbeat"],
      }
    
      file { "/etc/ha.d/haresources":
        source  => "puppet:///modules/heartbeat/haresources",
        notify  => Exec["reload-heartbeat"],
        require => Package["heartbeat"],
      }
    
      file { "/etc/ha.d/ha.cf":
        source  => "puppet:///modules/heartbeat/ha.cf",
        notify  => Exec["reload-heartbeat"],
        require => Package["heartbeat"],
      }
    }
    
  3. 使用如下内容创建 /etc/puppet/modules/heartbeat/files/haresources 文件。 将 cookbook 替换成你的主服务器的主机名。这可以通过在主服务器上运行 uname -n 命令获得。 将 10.0.2.100 替换成你要在两台主机上共享的 IP 地址(这应该是当前网络上还未使用的地址)。 最后列出的接口是分配给心跳检测的(本例中是 eth0:1)。

    cookbook IPaddr::10.0.2.100/24/eth0:1
    
  4. 使用如下内容创建 /etc/puppet/modules/heartbeat/files/authkeys 文件 (使用你自己选择的口令替换 topsecretpassword):

    auth 1
    1 sha1 topsecretpassword
    
  5. 使用如下内容创建 /etc/puppet/modules/heartbeat/files/ha.cf 文件。 替换下面的两个 IP 地址为你自己的两台机器上 eth0 接口对应的 IP 地址。 同样需替换 cookbook 和 cookbook2 为你自己的两台机器的主机名(可以通过运行 uname -n 命令获得)。

    autojoin none
    ucast eth0 10.0.2.15
    ucast eth0 10.0.2.16
    keepalive 1
    deadtime 10
    warntime 5
    udpport 694
    auto_failback on
    node cookbook
    node cookbook2
    use_logd yes
    
  6. 在两台冗余服务器上都运行 Puppet:

    # puppet agent --test
    info: Retrieving plugin
    info: Caching catalog for cookbook.bitfieldconsulting.com
    info: Applying configuration version '1311440876'
    
    notice: /Stage[main]/Heartbeat/Package[heartbeat]/ensure: created
    notice: /Stage[main]/Heartbeat/File[/etc/ha.d/authkeys]/ensure:
    defined content as '{md5}e908c869aabe519aa69acc9e51da3399'
    
    info: /Stage[main]/Heartbeat/File[/etc/ha.d/authkeys]: Scheduling
    refresh of Exec[reload-heartbeat]
    
    notice: /Stage[main]/Heartbeat/File[/etc/ha.d/ha.cf]/ensure:
    defined content as '{md5}a8d3fdd62a1172cdff150fc1d86d8a6b'
    
    info: /Stage[main]/Heartbeat/File[/etc/ha.d/ha.cf]: Scheduling
    refresh of Exec[reload-heartbeat]
    
    notice: /Stage[main]/Heartbeat/File[/etc/ha.d/haresources]/ensure:
    defined content as '{md5}0f25aefe7f6c4c8e81b3bb6c86a42d60'
    
    info: /Stage[main]/Heartbeat/File[/etc/ha.d/haresources]:
    Scheduling refresh of Exec[reload-heartbeat]
    
    notice: /Stage[main]/Heartbeat/Exec[reload-heartbeat]: Triggered
    'refresh' from 3 events
    
    notice: Finished catalog run in 27.01 seconds
    
  7. 在主服务器节点上,检查它的资源:

    # cl_status rscstatus -m
    This node is holding all resources.
    
  8. 在辅助服务器节点上,你应该可以看到如下信息:

    # cl_status rscstatus -m
    This node is holding none resources.
    
  9. 在主节点上禁用 Heartbeat 服务:

    # service heartbeat stop
    
  10. 辅助节点现在应该接管了资源:

    # cl_status rscstatus -m
    This node is holding all resources.
    

工作原理

两台服务器上都运行了心跳守护进程,彼此监听心跳信号。 如果主服务器检测到辅助服务器已宕机,什么也不会发生。 而相反地,如果辅助服务器检测到主服务器已宕机,它就接管 IP 地址。 当主服务器恢复运行后,辅助服务器将再次放弃此地址,重新由主服务器接管 IP 地址 (如果 auto_failback 设置成了 on)。 在某些情况下,例如:如果你在主数据库服务器和从数据库服务器之间共享 IP 地址, 你可能不希望这种行为发生,在这种情况下应该将 auto_failback 设置成了 off。

更多用法

现在你有一个共享的 IP 地址(真是名不副实,因为这个地址不是“共享”的,而是在两个服务器之间切换), 你可以用这个地址来提供高可用性的服务。例如,如果服务器是被托管的 web 站点, 你应该为 web 站点设置 DNS 记录指向这个共享的 IP 地址。 当主服务器宕机时,辅助服务器将接管 IP 地址并继续响应基于此地址的 HTTP 请求。

提示

如果你正在使用 SSL 站点,你需要配置基于共享 IP 地址的 SSL 虚拟主机, 否则将不能响应基于这个 IP 的 HTTPs 请求。

另外,如果这个 web 站点使用了 sessions,主服务器上的任何 sessions 在故障转移后将会丢失, 除非 sessions 存储在一个分离的共享数据库中。

共享 IP 地址也是实现双路冗余负载均衡器的一种好方式 (参考 使用 HAProxy 为多个 web 服务器实现负载均衡 一节)。 你也可以使用这种方式为 Puppetmaster 主机提供冗余服务。 在 Puppet Labs 站点给出了一个合适的模式: http://projects.puppetlabs.com/projects/1/wiki/High_Availability_Patterns