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)