Setting up HA for arbitrary services

Note

This example presumes an Apache HTTP server running on same machines as the Keepalive Daemon, but that is not a requirement.

  • What the user experiences

digraph web_service {
    rankdir="LR"
    node [shape="rectangle"]
    Request [shape="circle", color="red"]
    Landlord [color="blue", label="Landlord IP"]
    WEB [ color=blue, label="MP" ]
    Request -> Landlord -> WEB
}
  • Packet’s-eye-view

digraph ha_packet {
    rankdir="LR"
    node [shape="rectangle"]
    Request [shape="circle", color="red"]
    subgraph cluster0 {
        label="landlord on 192.168.56.200"
        #KA [ label="192.168.56.200" ]
        subgraph cluster_ethyl {
          label="ethyl on 192.168.56.201"
          color=blue
          HAproxy_ethyl
          MPethyl
        }
        subgraph cluster_fred {
          label="fred on 192.168.56.202"
          color=blue
          HAproxy_fred
          MPfred
        }
    }
    #Request -> KA
    #KA -> HAproxy_ethyl
    #KA -> HAproxy_fred
    Request -> HAproxy_ethyl [ label="192.168.56.200 master" ]
    Request -> HAproxy_fred [ label="192.168.56.200 slave", style=dotted ]
    HAproxy_ethyl -> MPethyl
    HAproxy_ethyl -> MPfred
    HAproxy_fred -> MPethyl
    HAproxy_fred -> MPfred
}
  • Under the hood…

    • This configuration envisions only two machines: fred and ethyl

    • The dashed lines are functions, not machines.

digraph ha_demo {
    rankdir="LR"
    node [shape="rectangle"]
    Request [shape="circle", color="red"]
    Landlord [color="blue", label="Landlord IP"]
    HA [ style=dashed, color=blue, label="LB" ]
    WEB [ style=dashed, color=blue, label="MP" ]
    subgraph cluster0 {
        label="fred"
        HAethyl [ label="HA ethyl" ]
        WEBethyl [ label="MP" ]
        KAmaster [ label="VRRP\nmaster" ]
    }
    KA [ style=dashed, color=blue, label="VRRP" ]
    subgraph cluster1 {
        label="ethyl"
        HAfred [ label="HA fred" ]
        KAslave [ label="VRRP\nslave" ]
        WEBfred [ label="MP" ]
    }
    Request -> Landlord [ fontcolor=red, color=red ]
    Landlord -> HA
    HA -> HAethyl [ color=blue, style=dotted, dir=back ]
    HA -> HAfred [ color=blue, style=dotted, dir=back ]
    HA -> WEB [ color=red ]
    WEB -> WEBethyl [ color=red, label="LB or failover", dir=back ]
    WEB -> WEBfred [ color=red, label="LB or failover", dir=back ]
    KAmaster -> HAethyl [ color=orange, label="monitoring" ]
    KAslave -> HAfred [ color=orange, label="monitoring" ]
    KA -> KAmaster [ style=dashed, color=orange, label="VRRP", dir=back ]
    KA -> KAslave [ style=dashed, color=orange, label="VRRP", dir=back ]
}
  • When would we add an appliance?

    • …When we use bulk encryption for a mixed-domain stack.

      • This may someday apply to us.

    • …As part of a denial-of-service protection scheme.

      • This will not apply to us anytime soon.

    • …If most of our application logic already lives on an appliance with only a database behind it.

      • This will likely never apply to us.

    • …If CI/CD is no longer an objective for the organization.

      • This better not apply to us.

  • Here is how it looks with an appliance.

Event

Resources involved with agile solution

Resources involved with rigid appliance

Networking incident

Network engineer assesses network issues. Component fault isolation is obvious and immediate.

Network engineer assesses network issues. Component fault isolation is obvious and immediate.

Application incident

System administrator validates available services in a central location. Component fault isolation is obvious and immediate.

Network engineer and system administrator simultaneously test possibly conflicting configration. Fault isolation is a separate task, delaying actual break-fix.

Configuration change

A central configuration pushes both the application and its high availability.

Error-prone human coordination must simultaneously push the same change to both service nodes and network devices.

Code push

Software developers define services. Sysadmins deploy configuration files to a central location. Network engineers only assign IP addresses

Software developers define services. Sysadmins and network engineers figure out which part of the service rests on which hardware. (See configuration change.) Network engineers assign IP addresses attempt to guess whether services, network availability, or temporary resource constraints will define failover conditions. AKA Hope-as-Strategy

Preparing the system

  • Edit /etc/sysctl.conf

/etc/sysctl.conf
1net.ipv4.ip_nonlocal_bind=1
2net.ipv4.ip_forward=1
  • Make the sysctl changes permanent and install the RMPs

1# Cement the changes from the previous step
2sysctl -p
3# Install the packages
4yum install -y httpd keepalived haproxy
5# Enable the services we will use
6chkconfig keepalived on
7chkconfig haproxy on
8chkconfig httpd on

HA Proxy configuration

  • The HA Proxy configuration should be the same on each node.

/etc/haproxy/haproxy.cfg
 1global
 2    log 127.0.0.1   local0
 3    log 127.0.0.1   local1 notice
 4    maxconn 4096
 5    user haproxy
 6    group haproxy
 7    daemon
 8    #debug
 9    #quiet
10
11defaults
12    log     global
13    mode    http
14    option  httplog
15    option  dontlognull
16    retries 3
17    option redispatch
18    maxconn 2000
19    timeout connect     5000
20    timeout client      50000
21    timeout server      50000
22
23listen stats 192.168.56.200:8989
24    mode http
25    stats enable
26    stats uri /stats
27    stats realm HAProxy\ Statistics
28    stats auth admin:admin
29
30listen cluster37 0.0.0.0:80
31    mode http
32    balance roundrobin
33    option httpclose
34    option forwardfor
35    cookie SERVERNAME insert indirect nocache
36    server fred 192.168.56.201:8080 check
37    server ethyl 192.168.56.202:8080 check

Keepalive Daemon configuration

  • On each machine in the “cluster,” we configure keepalived.

  • Assume the shared IP is 192.168.56.200 and our two nodes are 192.168.56.201 and 192.168.56.202

Master /etc/keepalived/keepalived.conf
 1global_defs {
 2    # Keepalived process identifier
 3    lvs_id landlord_fred
 4}
 5# Script used to check if HAProxy is running
 6vrrp_script check_haproxy {
 7    script "killall -0 haproxy"
 8    interval 2
 9    weight 2
10}
11# Virtual interface
12# The priority specifies the order in which the assigned interface to take over in a failover
13vrrp_instance router37 {
14    state MASTER
15    interface eth1
16    virtual_router_id 37
17    priority 101
18    # The virtual ip address shared between the two loadbalancers
19    virtual_ipaddress {
20        192.168.56.200
21    }
22    track_script {
23        check_haproxy
24    }
25}
Slave /etckeepalived/keepalived.conf
 1global_defs {
 2    # Keepalived process identifier
 3    lvs_id landlord_ethyl
 4}
 5# Script used to check if HAProxy is running
 6vrrp_script check_haproxy {
 7    script "killall -0 haproxy"
 8    interval 2
 9    weight 2
10}
11# Virtual interface
12# The priority specifies the order in which the assigned interface to take over in a failover
13vrrp_instance router37 {
14    state SLAVE
15    interface eth1
16    virtual_router_id 37
17    priority 100
18    # The virtual ip address shared between the two loadbalancers
19    virtual_ipaddress {
20        192.168.56.200
21    }
22    track_script {
23        check_haproxy
24    }
25}