Skip to content

Latest commit

 

History

History
519 lines (434 loc) · 16 KB

README.md

File metadata and controls

519 lines (434 loc) · 16 KB

haproxy

Table of Contents

  1. Overview
  2. Module Description - What the module does and why it is useful
  3. Setup - The basics of getting started with haproxy
  4. Usage - Configuration options and additional functionality
  5. Reference - An under-the-hood peek at what the module is doing and how
  6. Limitations - OS compatibility, etc.
  7. License
  8. Development - Guide for contributing to the module

Overview

The haproxy module lets you use Puppet to install, configure, and manage HAProxy.

Module Description

HAProxy is a daemon for load-balancing and proxying TCP- and HTTP-based services. This module lets you use Puppet to configure HAProxy servers and backend member servers.

Setup

Beginning with haproxy

The simplest HAProxy configuration consists of a server that listens on a port and balances against some other nodes:

node 'haproxy-server' {
  include ::haproxy
  haproxy::listen { 'puppet00':
    collect_exported => false,
    ipaddress        => $facts['networking']['ip'],
    ports            => [8140],
  }
  haproxy::balancermember { 'server00':
    listening_service => 'puppet00',
    server_names      => 'server00.example.com',
    ipaddresses       => '10.0.0.10',
    ports             => [8140],
    options           => 'check',
  }
  haproxy::balancermember { 'server01':
    listening_service => 'puppet00',
    server_names      => 'server01.example.com',
    ipaddresses       => '10.0.0.11',
    ports             => [8140],
    options           => 'check',
  }
}

Usage

Configure HAProxy options

The main haproxy class has many options for configuring your HAProxy server:

class { 'haproxy':
  global_options   => {
    'log'     => "${facts['networking']['ip']} local0",
    'chroot'  => '/var/lib/haproxy',
    'pidfile' => '/var/run/haproxy.pid',
    'maxconn' => '4000',
    'user'    => 'haproxy',
    'group'   => 'haproxy',
    'daemon'  => '',
    'stats'   => 'socket /var/lib/haproxy/stats',
  },
  defaults_options => {
    'log'     => 'global',
    'stats'   => 'enable',
    'option'  => [
      'redispatch',
    ],
    'retries' => '3',
    'timeout' => [
      'http-request 10s',
      'queue 1m',
      'connect 10s',
      'client 1m',
      'server 1m',
      'check 10s',
    ],
    'maxconn' => '8000',
  },
}

The above shown values are the module's defaults for platforms like Debian and RedHat (see haproxy::params for details). If you wish to override or add to any of these defaults set merge_options => true (see below) and set global_options and/or defaults_options to a hash containing just the option => value pairs you need changed or added. In case of duplicates your supplied values will "win" over the default values (this is especially noteworthy for arrays -- they cannot be merged easily). If you want to completely remove a parameter set it to the special value undef:

class { 'haproxy':
  global_options   => {
    'maxconn' => undef,
    'user'    => 'root',
    'group'   => 'root',
    'stats'   => [
      'socket /var/lib/haproxy/stats',
      'timeout 30s'
    ]
  },
  defaults_options => {
    'retries' => '5',
    'option'  => [
      'redispatch',
      'http-server-close',
      'logasap',
    ],
    'timeout' => [
      'http-request 7s',
      'connect 3s',
      'check 9s',
    ],
    'maxconn' => '15000',
  },
}

HAProxy and Software Collections

To use this module with a software collection such as rh-haproxy18 you will need to set a few extra parameters like so:

class { 'haproxy':
  package_name        => 'rh-haproxy18',
  config_dir          => '/etc/opt/rh/rh-haproxy18/haproxy',
  config_file         => '/etc/opt/rh/rh-haproxy18/haproxy/haproxy.cfg',
  config_validate_cmd => '/bin/scl enable rh-haproxy18 "haproxy -f % -c"',
  service_name        => 'rh-haproxy18-haproxy',
}

Configure HAProxy daemon listener

To export the resource for a balancermember and collect it on a single HAProxy load balancer server:

haproxy::listen { 'puppet00':
  ipaddress => $facts['networking']['ip'],
  ports     => [8140],
  mode      => 'tcp',
  options   => {
    'option'  => [
      'tcplog',
    ],
    'balance' => 'roundrobin',
  },
}

Configure multi-network daemon listener

If you need a more complex configuration for the listen block, use the $bind parameter:

haproxy::listen { 'puppet00':
  mode    => 'tcp',
  options => {
    'option'  => [
      'tcplog',
    ],
    'balance' => 'roundrobin',
  },
  bind    => {
    '10.0.0.1:443'             => ['ssl', 'crt', 'puppetlabs.com'],
    '168.12.12.12:80'          => [],
    '192.168.122.42:8000-8100' => ['ssl', 'crt', 'puppetlabs.com'],
    ':8443,:8444'              => ['ssl', 'crt', 'internal.puppetlabs.com']
  },
}

Note: $ports and $ipaddress cannot be used in combination with $bind.

Configure HAProxy load-balanced member nodes

First export the resource for a balancermember:

@@haproxy::balancermember { 'haproxy':
  listening_service => 'puppet00',
  ports             => '8140',
  server_names      => $facts['networking']['hostname'],
  ipaddresses       => $facts['networking']['ip'],
  options           => 'check',
}

Then collect the resource on a load balancer:

Haproxy::Balancermember <<| listening_service == 'puppet00' |>>

Then create the resource for multiple balancermembers at once:

haproxy::balancermember { 'haproxy':
  listening_service => 'puppet00',
  ports             => 8140,
  server_names      => ['server01', 'server02'],
  ipaddresses       => ['192.168.56.200', '192.168.56.201'],
  options           => 'check',
}

This example assumes a single-pass installation of HAProxy where you know the members in advance. Otherwise, you'd need a first pass to export the resources.

Configure a load balancer with exported resources

Install and configure an HAProxy service listening on port 8140 and balanced against all collected nodes:

node 'haproxy-server' {
  include ::haproxy
  haproxy::listen { 'puppet00':
    ipaddress => $facts['networking']['ip'],
    ports     => 8140,
  }
}

node /^server\d+/ {
  @@haproxy::balancermember { $facts['networking']['fqdn']:
    listening_service => 'puppet00',
    server_names      => $facts['networking']['hostname'],
    ipaddresses       => $facts['networking']['ip'],
    ports             => 8140,
    options           => 'check',
  }
}

The resulting HAProxy service uses storeconfigs to collect and realize balancermember servers, and automatically collects configurations from backend servers. The backend nodes export their HAProxy configurations to the Puppet Server, which then distributes them to the HAProxy server.

Set up a frontend service

This example routes traffic from port 8140 to all balancermembers added to a backend with the title 'puppet_backend00':

haproxy::frontend { 'puppet00':
  ipaddress     => $facts['networking']['ip'],
  ports         => 8140,
  mode          => 'tcp',
  bind_options  => 'accept-proxy',
  options       => {
    'default_backend' => 'puppet_backend00',
    'timeout client'  => '30s',
    'option'          => [
      'tcplog',
      'accept-invalid-http-request',
    ],
  },
}

If option order is important, pass an array of hashes to the options parameter:

haproxy::frontend { 'puppet00':
  ipaddress     => $facts['networking']['ip'],
  ports         => [8140],
  mode          => 'tcp',
  bind_options  => 'accept-proxy',
  options       => [
    { 'default_backend' => 'puppet_backend00' },
    { 'timeout client'  => '30s' },
    { 'option'          => [
        'tcplog',
        'accept-invalid-http-request',
      ],
    }
  ],
}

This adds the frontend options to the configuration block in the same order as they appear within your array.

Set up a backend service

haproxy::backend { 'puppet00':
  options => {
    'option'  => [
      'tcplog',
    ],
    'balance' => 'roundrobin',
  },
}

If option order is important, pass an array of hashes to the options parameter:

haproxy::backend { 'puppet00':
  options => [
    { 'option'  => [
        'tcplog',
      ]
    },
    { 'balance' => 'roundrobin' },
    { 'cookie'  => 'C00 insert' },
  ],
}

Set up a resolver

Note: This is only available on haproxy 1.6+

# Need to start with an init-addr parameter set to none and enable runtime DNS resolution.
class { 'haproxy':
...
  defaults_options => {
    'default-server' => 'init-addr none',
...
  },
}

# Declare the resolver
haproxy::resolver { 'puppet00':
  nameservers           => {
    'dns1' => '192.168.56.1:53',
    'dns2' => '192.168.56.2:53'
  },
  hold                  => {
    'nx'    => '30s',
    'valid' => '10s'
  },
  resolve_retries       => 3,
  timeout               => {
    'retry' => '1s'
  },
  accepted_payload_size => 512,
}

# Setup the balancermember to use the resolver for DNS resolution
haproxy::balancermember { 'haproxy':
  listening_service => 'puppet00',
  ports             => 8140,
  server_names      => ['server01', 'server02'],
  ipaddresses       => ['server01', 'server02'],
  options           => 'check resolvers puppet00 resolve-prefer ipv4',
}

Set up stick-tables for a frontend (or a backend)

haproxy::backend { 'backend01':
  options => [
    { 'stick-table' => 'type ip size 1 nopurge peers LB' },
    { 'stick'       => 'on dst' },
  ],
}

This adds the backend options to the configuration block in the same order as they appear within the array.

Configure multiple haproxy instances on one machine

This is an advanced feature typically only used at large sites.

It is possible to run multiple haproxy processes ("instances") on the same machine. This has the benefit that each is a distinct failure domain, each can be restarted independently, and each can run a different binary.

In this use case, instead of using Class['haproxy'], each process is started using haproxy::instance{'inst'} where inst is the name of the instance. It assumes there is a matching Service['inst'] that will be used to manage service. Different sites may have different requirements for how the Service[] is constructed. However, haproxy::instance_service exists as an example of one way to do this, and may be sufficient for most sites.

In this example, two instances are created. The first uses the standard class and uses haproxy::instance to add an additional instance called beta.

   include ::haproxy
   haproxy::listen { 'puppet00':
     instance         => 'haproxy',
     collect_exported => false,
     ipaddress        => $facts['networking']['ip'],
     ports            => 8800,
   }

   haproxy::instance { 'beta': }
   ->
   haproxy::instance_service { 'beta':
     haproxy_package     => 'custom_haproxy',
     haproxy_init_source => "puppet:///modules/${module_name}/haproxy-beta.init",
   }
   ->
   haproxy::listen { 'puppet00':
     instance         => 'beta',
     collect_exported => false,
     ipaddress        => $facts['networking']['ip'],
     ports            => 9900,
   }

In this example, two instances are created called group1 and group2. The second uses a custom package.

   haproxy::instance { 'group1': }
   ->
   haproxy::instance_service { 'group1':
     haproxy_init_source => "puppet:///modules/${module_name}/haproxy-group1.init",
   }
   ->
   haproxy::listen { 'group1-puppet00':
     section_name     => 'puppet00',
     instance         => 'group1',
     collect_exported => false,
     ipaddress        => $facts['networking']['ip'],
     ports            => 8800,
   }
   haproxy::instance { 'group2': }
   ->
   haproxy::instance_service { 'group2':
     haproxy_package     => 'custom_haproxy',
     haproxy_init_source => "puppet:///modules/${module_name}/haproxy-group2.init",
   }
   ->
   haproxy::listen { 'group2-puppet00':
     section_name     => 'puppet00',
     instance         => 'group2',
     collect_exported => false,
     ipaddress        => $facts['networking']['ip'],
     ports            => 9900,
   }

Manage a map file

haproxy::mapfile { 'domains-to-backends':
  ensure   => 'present',
  mappings => [
    { 'app01.example.com' => 'bk_app01' },
    { 'app02.example.com' => 'bk_app02' },
    { 'app03.example.com' => 'bk_app03' },
    { 'app04.example.com' => 'bk_app04' },
    'app05.example.com bk_app05',
    'app06.example.com bk_app06',
  ],
}

This creates a file /etc/haproxy/domains-to-backends.map containing the mappings specified in the mappings array.

The map file can then be used in a frontend to map Host: values to backends, implementing name-based virtual hosting:

frontend ft_allapps
  [...]
  use_backend %[req.hdr(host),lower,map(/etc/haproxy/domains-to-backends.map,bk_default)]

Or expressed using haproxy::frontend:

haproxy::frontend { 'ft_allapps':
  ipaddress => '0.0.0.0',
  ports     => ['80'],
  mode      => 'http',
  options   => {
    'use_backend' => '%[req.hdr(host),lower,map(/etc/haproxy/domains-to-backends.map,bk_default)]'
  }
}

Reference

For information on the classes and types, see the REFERENCE.md

Limitations

For an extensive list of supported operating systems, see metadata.json

License

This codebase is licensed under the Apache2.0 licensing, however due to the nature of the codebase the open source dependencies may also use a combination of AGPL, BSD-2, BSD-3, GPL2.0, LGPL, MIT and MPL Licensing.

Development

Acceptance tests for this module leverage puppet_litmus. To run the acceptance tests follow the instructions here. You can also find a tutorial and walkthrough of using Litmus and the PDK on YouTube.

If you run into an issue with this module, or if you would like to request a feature, please file a ticket. Every Monday the Puppet IA Content Team has office hours in the Puppet Community Slack, alternating between an EMEA friendly time (1300 UTC) and an Americas friendly time (0900 Pacific, 1700 UTC).

If you have problems getting this module up and running, please contact Support.

If you submit a change to this module, be sure to regenerate the reference documentation as follows:

puppet strings generate --format markdown --out REFERENCE.md