[Pacemaker] Automating Pacemaker Setup

veghead sean at studyblue.com
Fri May 27 21:56:11 CEST 2011


Todd Nine <todd at ...> writes:

>   I have a setup nearly working.  Would you be willing to share recipes?
...
> It's not quite working yet, but it's close.  Since you've managed to get
> this working, you may be able to finish these off.  I have everything
> working except the init start/stop hooks for pacemaker to set the
> Elastic IP automatically and then run chef-client to reconfigure
> everything on all the other nodes.

Wow. The example pacemaker config and the trick of starting heartbeat before 
using crm configure were the last steps I needed. Thanks!

So, here's how I got Elastic IP failover working. I can't claim credit for the 
idea... I found a basic example here: https://forums.aws.amazon.com/thread.jspa?
messageID=195373. That didn't quite work for me, so I rewrote the LSB script in 
pure ruby and leveraged the amazon-ec2 gem (https://github.com/grempe/amazon-
ec2) to handle associating the EIP with the current instance. I have included my 
script below. A couple of key things. 

First, I found that when an instance loses it's elastic ip (whether through 
"disassociate" or another instance grabbed eip), it loses public internet 
connectivity for 1-3 minutes. Apparently this is expected, according to AWS 
Support: https://forums.aws.amazon.com/message.jspa?messageID=250571#250571. As 
a result, I decided it didn't make any sense to have the "stop" method for the 
EIP LSB script do anything.

Second, my Pacemaker configure is pretty close to yours. I setup the nodes with 
ucast in almost the exact same manner. The key differences are all in setting up 
the primitives with the correct order and colocation:

primitive elastic_ip lsb:elastic-ip op monitor interval="10s"
primitive haproxy lsb:haproxy op monitor interval="10s"
order haproxy-after-eip inf: elastic_ip haproxy
colocation haproxy-with-eip inf: haproxy elastic_ip

Third, here's my elastic-ip.rb LSB script that handles the Elastic IP. Since LSB 
scripts can't take any parameters other than the usual start/stop/status/etc, I 
treat the script as a Chef template and inject the desired EIP into the 
template. The other secret is that I created a special user using AWS IAM with a 
policy that only allows the user to associate/disassociate EIP addresses. I 
store the AccessKey and SecretAccessKey in a file in /etc/aws/pacemaker_keys. 

Let me know if you have any questions. And thanks for the tip on using crm to 
initialize pacemaker.

#!/usr/bin/ruby

# Follows the LSB Spec: http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-
generic/LSB-Core-generic/iniscrptact.html

require 'rubygems'
require 'AWS'

ELASTIC_IP="<%= @elastic_ip %>"
EC2_INSTANCE_ID=`wget -T 5 -q -O - http://169.254.169.254/latest/meta-
data/instance-id`

# Load the AWS access keys
properties = {}
File.open("/etc/aws/pacemaker_keys", 'r') do |file|
  file.read.each_line do |line|
    line.strip!
    if (line[0] != ?# and line[0] != ?=)
      i = line.index('=')
      if (i)
        properties[line[0..i - 1].strip] = line[i + 1..-1].strip
      else
        properties[line] = ''
      end
    end
  end
end
AWS_ACCESS_KEY = properties["AWS_ACCESS_KEY"].delete "\""
AWS_SECRET_ACCESS_KEY = properties["AWS_SECRET_ACCESS_KEY"].strip.delete "\""

[ ELASTIC_IP, EC2_INSTANCE_ID, AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY ].each do 
|value|
  if value.nil? || value.length == 0
    exit case ARGV[0]
      when "status" then 4
      else 1
    end
  end
end

def status(ec2)
  # Typical responses look like the following:
  #   {"requestId"=>"065d1661-31b1-455d-8f63-ba086b8104de", "addressesSet"=>
{"item"=>[{"instanceId"=>"i-22e93a4d", "publicIp"=>"50.19.93.215"}]}, 
"xmlns"=>"http://ec2.amazonaws.com/doc/2010-08-31/"}
  # or
  #   {"requestId"=>"9cd3ab7e-1c03-4821-9565-1791dd1bb0fc", "addressesSet"=>
{"item"=>[{"instanceId"=>nil, "publicIp"=>"174.129.34.161"}]}, 
"xmlns"=>"http://ec2.amazonaws.com/doc/2010-08-31/"}
  response = ec2.describe_addresses({:public_ip => ELASTIC_IP})
  retval = 4
  if ! response.nil?
    if ! response["addressesSet"].nil?
      if ! response["addressesSet"]["item"].nil? && response["addressesSet"]
["item"].length >= 1
        if response["addressesSet"]["item"][0]["instanceId"] == EC2_INSTANCE_ID
          retval = 0
        else
          retval = 3
        end
      end
    end
  end
  retval
end

def start(ec2)
  # Throws exception if the instance does not exist or the address does not 
belong to us
  retval = 1
  begin
    response = ec2.associate_address({ :public_ip => ELASTIC_IP, :instance_id => 
EC2_INSTANCE_ID })
    retval = 0
  rescue => e
    puts "Error attempting to associate address: " + e
  end
  retval
end

def stop(ec2)
  0
end

def reload(ec2)
  start(ec2)
end

def force_reload(ec2)
  reload(ec2)
end

def restart(ec2)
  start(ec2)
end

def try_restart(ec2)
  start(ec2)
end

ec2 = AWS::EC2::Base.new(:access_key_id => AWS_ACCESS_KEY, :secret_access_key => 
AWS_SECRET_ACCESS_KEY)

retval = case ARGV[0]
  when "status" then status(ec2)
  when "start" then start(ec2)
  when "stop" then stop(ec2)
  when "reload" then reload(ec2)
  when "force-reload" then force_reload(ec2)
  when "restart" then restart(ec2)
  when "try-restart" then try_restart(ec2)
  else 3
end

puts "elastic-ip " + (retval == 0 ? "OK" : "FAIL")
exit retval




More information about the Pacemaker mailing list