Using the haproxy charm

The haproxy charm in the charmstore https://jujucharms.com/haproxy/ is deceptively powerful. I recently had a use case which I thought it would not handle. It turns out, it does.

The details are all in the services config value. In my case, I am replacing the apache charm https://jujucharms.com/apache2/ using balancer and reverseproxy relations.

The apache charm has vhost_https_template and vhost_http_template which gets pasted in as apache httpd config. The haproxy charm services config value has service_options as yaml which works much the same.

In my case, port 80 redirects to 443, so I start with this in yaml:

- service_name: haproxy_service
  service_host: "0.0.0.0"
  service_port: 80
  server_options: maxconn 100 cookie S{i} check
  service_options:
      - 'redirect scheme https code 301 if !{ ssl_fc }'

You’ll notice this is almost identical to the default value for the services config:

- service_name: haproxy_service 
  service_host: "0.0.0.0" 
  service_port: 80 
  service_options: [balance leastconn, cookie SRVNAME insert] 
  server_options: maxconn 100 cookie S{i} check

I only changed the service_options entry with what would redirect haproxy in a frontend config.

This is where the magic of this haproxy charm happens. The haproxy charm knows which config values are for a frontend haproxy section and also for a backend haproxy section. The charm automatically puts the value in the right place.

The next thing which wasn’t obvious to me from reading the haproxy charm readme is that the Juju application related using reverseproxy relation becomes a backend section and its values will be merged from the services config.

e.g.

$ juju add-relation my-app haproxy:reverseproxy
$ juju add-relation kibana haproxy:reverseproxy

I can use defaults, or I can make some tweaks to the my-app and kibana applications.

For my use case, I was using apache httpd config RewriteRule ^/?KIBANA/(.*)$ balancer://kibana/$1 [P,L]

The equivalent in haproxy config looks like this:

    acl path_kibana path -m beg  /KIBANA/
    use_backend kibana if path_kibana

and in the kibana backend:

   reqirep  ^([^\ :]*)\ /KIBANA/(.*)     \1\ /\2

The haproxy charm allows all of this to be configured using services config. The related application is automatically set as a service name and your config must match it via service_name yaml.

I use juju2’s juju config command to set the config directly from a yaml file. If you are using juju1 you’ll need to use juju set command. juju config haproxy services=@haproxy-config-services.yaml

- service_name: my-app
  service_host: "0.0.0.0"
  service_port: 443
  crts: [DEFAULT]
  service_options:
      - balance leastconn
      - reqadd X-Forwarded-Proto:\ https
      - acl path_kibana path -m beg  /KIBANA/
      - use_backend kibana if path_kibana
  server_options: maxconn 100 cookie S{i} check
- service_name: kibana
  service_options:
      - balance leastconn
      - reqirep  ^([^\ :]*)\ /KIBANA/(.*)     \1\ /\2
      - rspirep ^Location:\ https?://[^/]+/(.*) Location:\ /KIBANA/\1
      - rspirep ^(Set-Cookie:.*)\ Path=(.*) \1\ Path=/KIBANA/\2
  server_options: maxconn 100 cookie S{i} check

The implication here is that there is another application, “my-app” which is also related to haproxy. This config tells haproxy to use my-app as the default application, but if the url starts with /KIBANA/, to use the kibana backend instead of the “my-app” backend. For completeness, I am including the equivalant of apache’s ProxyPassReverse and ProxyPassReverseCookiePath. These are the rspirep… Location and rspirep…Set-Cookie lines in the config respectively.

Leave a Reply

Your email address will not be published. Required fields are marked *