github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/acceptancetests/repository/trusty/haproxy/README.md (about)

     1  # Overview
     2  
     3  This charm deploys a reverse proxy in front of other servies. You can use this to load balance existing deployments.
     4  
     5  # Usage
     6  
     7      juju deploy haproxy
     8      juju deploy my-web-app
     9      juju add-relation my-web-app:website haproxy:reverseproxy
    10      juju add-unit my-web-app
    11      ...
    12  
    13  ## Reverse Proxy Relation
    14  
    15  The reverse proxy relation is used to distribute connections from one frontend
    16  port to many backend services (typically different Juju _units_).  You can use
    17  haproxy just like this, but typically in a production service you would
    18  frontend this service with apache2 to handle the SSL negotiation, etc.  See
    19  the "Website Relation" section for more information about that.
    20  
    21  When your charm hooks into reverseproxy you have two general approaches
    22  which can be used to notify haproxy about what services you are running.
    23  1) Single-service proxying or 2) Multi-service or relation-driven proxying.
    24  
    25  1. Single-Service Proxying
    26  
    27  In this case, your website relation will join underneath a single `listen`
    28  stanza in haproxy.  This stanza will have one `service` entry for each unit
    29  connecting. By convention, this is typically called "website".  The
    30  following is an example of a relation-joined or changed hook:
    31  
    32      #!/bin/bash
    33      # hooks/website-relation-joined
    34  
    35      relation-set "hostname=$(unit-get private-address)"
    36      relation-set "port=80"
    37  
    38      # Set an optional service name, allowing more config-based
    39      # customization
    40      relation-set "service_name=my_web_app"
    41  
    42  If you set the `service_name` relation setting, the configuration `services`
    43  yaml mapping will be consulted to lookup 3 other options based on service
    44  name.
    45  
    46    * `{service_name}_servers` - sets the `server` line in the listen stanza
    47      explicitly.
    48    * `{service_name}_server_options` - Will append to the charm-generated
    49      server line for for each joining unit in the reverseproxy relation.
    50    * `{service_name}_service_options` - expected to be a list of strings.  Will
    51      set each item as an option under the listen stanza.
    52  
    53  
    54  2. Relation-Driven Proxying 
    55  
    56  In this relation style, your charm should specify these relation settings
    57  directly as relation variables when joining reverseproxy.  Your charm's
    58  website-relation-changed hook would look something like this:
    59  
    60      #!/bin/bash
    61      # hooks/website-relation-changed
    62  
    63      host=$(unit-get private-address)
    64      port=80
    65  
    66      relation-set "services=
    67      - { service_name: my_web_app,
    68          service_host: 0.0.0.0,
    69          service_port: 80,
    70          service_options: [mode http, balance leastconn],
    71          servers: [[my_web_app_1, $host, $port, option httpchk GET / HTTP/1.0],
    72                    [... optionally more servers here ...]]}
    73      - { ... optionally more services here ... }
    74      "
    75  
    76  Once set, haproxy will union multiple `servers` stanzas from any units
    77  joining with the same `service_name` under one backend stanza, which will be
    78  the default backend for the service (requests against the given service_port on
    79  the haproxy unit will be forwarded to that backend). Note that `service-options`
    80  and `server_options` will be overwritten, so ensure they are set uniformly on
    81  all services with the same name.
    82  
    83  If you need additional backends, possibly handling ACL-filtered requests, you
    84  can add a 'backends' entry to a service stanza. For example in order to redirect
    85  to a different backend all requests to URLs starting with '/foo', you could have:
    86  
    87      relation-set "services=
    88      - { service_name: my_web_app,
    89          service_host: 0.0.0.0,
    90          service_port: 80,
    91          service_options: [mode http, acl foo path_beg -i /foo, use_backend foo if foo],
    92          servers: [[my_web_app_1, $host, $port, option httpchk GET / HTTP/1.0],
    93                    [... optionally more servers here ...]]
    94          backends:
    95          - { backend_name: foo,
    96              servers: [[my_web_app2, $host, $port2, option httpchk GET / HTTP/1.0],
    97                        [... optionally more servers here ...]]}}
    98  
    99  
   100  In all cases if your service needs to know the public IP(s) of the haproxy unit(s)
   101  it relates to, or the value of the default SSL certificate set on or generated by
   102  the haproxy service, you can look for the 'public-address' and 'ssl_cert' keys
   103  on your relation, which are set by the haproxy service as soon as it joins the
   104  reverseproxy relation.
   105  
   106  
   107  ## Website Relation
   108  
   109  The website relation is the other side of haproxy.  It can communicate with
   110  charms written like apache2 that can act as a front-end for haproxy to take of
   111  things like ssl encryption.  When joining a service like apache2 on its
   112  reverseproxy relation, haproxy's website relation will set an `all_services`
   113  varaible that conforms to the spec layed out in the apache2 charm.
   114  
   115  These settings can then be used when crafting your vhost template to make sure
   116  traffic goes to the correct haproxy listener which will in turn forward the
   117  traffic to the correct backend server/port
   118  
   119  ## SSL Termination
   120  
   121  You can turn on SSL termination by using the `ssl_cert`/`ssl_key` service configuration
   122  options and then using the `crts` key in the services yaml, e.g.:
   123  
   124      #!/bin/bash
   125      # hooks/website-relation-changed
   126  
   127      host=$(unit-get private-address)
   128      port=80
   129  
   130      relation-set "services=
   131      - { service_name: my_web_app,
   132          service_options: [mode http, balance leastconn],
   133          crts: [DEFAULT]
   134          servers: [[my_web_app_1, $host, $port, option httpchk GET / HTTP/1.0],
   135                    [... optionally more servers here ...]]}
   136      - { ... optionally more services here ... }
   137      "
   138  
   139  where the DEFAULT keyword means use the certificate set with `ssl_cert`/`ssl_key` (or
   140  alternatively you can inline different base64-encode certificates).
   141  
   142  Note that in order to use SSL termination you need haproxy 1.5 or later, which
   143  is not available in stock trusty, but you can get it from trusty-backports setting
   144  the `source` configuration option to `backports` or to whatever PPA/archive you
   145  wish to use.
   146  
   147  ## Development
   148  
   149  The following steps are needed for testing and development of the charm,
   150  but **not** for deployment:
   151  
   152      sudo apt-get install software-properties-common
   153      sudo add-apt-repository ppa:cjohnston/flake8
   154      sudo apt-get update
   155      sudo apt-get install python-mock python-flake8 python-nose python-nosexcover python-testtools charm-tools
   156  
   157  To run the tests:
   158  
   159      make build
   160  
   161  ... will run the unit tests, run flake8 over the source to warn about
   162  formatting issues and output a code coverage summary of the 'hooks.py' module.
   163  
   164  
   165  ## Known Limitations and Issues
   166  
   167  - Expand Single-Service section as I have not tested that mode fully.
   168  - Trigger website-relation-changed when the reverse-proxy relation changes
   169  
   170  
   171  # Configuration
   172  
   173  Many of the haproxy settings can be altered via the standard juju configuration
   174  settings.  Please see the config.yaml file as each is fairly clearly documented.
   175  
   176  ## statsd
   177  
   178  This charm supports sending metrics to statsd.
   179  
   180  This is done by setting config values (metrics_target being the primary one)
   181  to a host/port of a (UDP) statsd server.
   182  
   183  This could instead be done using a relation, but it is common to have
   184  one statsd server that serves multiple environments. Once juju supports
   185  cross-environment relations then that will be the best way to handle 
   186  this configuration, as it will work in either scenario.
   187  
   188  ## peering\_mode and the indirection layer
   189  
   190  If you are going to spawn multiple haproxy units, you should pay special
   191  attention to the peering\_mode configuration option.
   192  
   193  ### active-passive mode
   194  
   195  The peering\_mode option defaults to "active-passive" and in this mode, all
   196  haproxy units ("peers") will proxy traffic to the first working peer (i.e. that
   197  passes a basic layer4 check). What this means is that extra peers are working
   198  as "hot spares", and so adding units doesn't add global bandwidth to the
   199  haproxy layer.
   200  
   201  In order to achieve this, the charm configures a new service in haproxy that
   202  will simply forward the traffic to the first working peer. The haproxy service
   203  that actually load-balances between the backends is renamed, and its port
   204  number is increased by one.
   205  
   206  For example, if you have 3 working haproxy units haproxy/0, haproxy/1 and
   207  haproxy/2 configured to listen on port 80, in active-passive mode, and
   208  haproxy/2 gets a request, the request is routed through the following path :
   209  
   210  haproxy/2:80 ==> haproxy/0:81 ==> \[backends\]
   211  
   212  In the same fashion, if haproxy/1 receives a request, it's routed in the following way :
   213  
   214  haproxy/1:80 ==> haproxy/0:81 ==> \[backends\]
   215  
   216  If haproxy/0 was to go down, then all the requests would be forwarded to the
   217  next working peer, i.e. haproxy/1. In this case, a request received by
   218  haproxy/2 would be routed as follows :
   219  
   220  haproxy/2:80 ==> haproxy/1:81 ==> \[backends\]
   221  
   222  This mode allows a strict control of the maximum number of connections the
   223  backends will receive, and guarantees you'll have enough bandwidth to the
   224  backends should an haproxy unit die, at the cost of having less overall
   225  bandwidth to the backends.
   226  
   227  ### active-active mode
   228  
   229  If the peering\_mode option is set to "active-active", then any haproxy unit
   230  will be independent from each other and will simply load-balance the traffic to
   231  the backends. In this case, the indirection layer described above is not
   232  created in this case.
   233  
   234  This mode allows increasing the bandwidth to the backends by adding additional
   235  units, at the cost of having less control over the number of connections that
   236  they will receive.
   237  
   238  # HAProxy Project Information
   239  
   240  - [HAProxy Homepage](http://haproxy.1wt.eu/)
   241  - [HAProxy mailing list](http://haproxy.1wt.eu/#tact)