Thursday, September 22, 2016

Playing Haproxy in Layer 7 : Redirect Traffics to Different Backends Based on HTTP Reqeust Header

haproxy

Playing Haproxy in Layer 7 : Redirect Traffics to Different Backends Based on HTTP Reqeust Header

Haproxy 1.6 installation

Following the steps to install haproxy 1.6.

sudo add-apt-repository ppa:vbernat/haproxy-1.6
sudo apt-get update
sudo apt-get dist-upgrade
apt-get install haproxy

Simple testing

Simply test the haproxy we installed to make sure the haproxy is right.

adding to /etc/haproxy/haproxy.cfg

global
  log         127.0.0.1 syslog
  maxconn     1000
  user        haproxy
  group       haproxy
  daemon

defaults
  log  global
  mode  http
  option  httplog
  option  dontlognull
  option  http-server-close
  option  forwardfor except 127.0.0.0/8
  option  redispatch
  option  contstats
  retries  3
  timeout  http-request 10s
  timeout  queue 1m
  timeout  connect 10s
  timeout  client 1m
  timeout  server 1m
  timeout  check 10s

listen  http_proxy
  bind        127.0.0.1:80
  balance     roundrobin
  server      server1 127.0.0.1:6000 maxconn 100

Runing the python SimpleHTTPServer as a Haproxy Backend Server.

root@haproxy:~# python -m SimpleHTTPServer 6000
Serving HTTP on 0.0.0.0 port 6000 ...

Using Curl to test it. You should get the following result and the result is right. Now we can go to next step.

root@haproxy:~# curl 127.0.0.1:80
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
<li><a href=".bash_history">.bash_history</a>
<li><a href=".bashrc">.bashrc</a>
<li><a href=".cache/">.cache/</a>
<li><a href=".profile">.profile</a>
<li><a href=".ssh/">.ssh/</a>
<li><a href=".viminfo">.viminfo</a>
<li><a href="haproxy.cfg">haproxy.cfg</a>
<li><a href="sources.list">sources.list</a>
</ul>
<hr>
</body>

Redirect by Layer 7 header

Now we are going to redirect traffic according to reqeust header information.

Simple Cases to capture header

An simple test to make sure the haproxy knowing the request header.

Chaning the /etc/haproxy/haproxy.cfg

global
 # variables memory consumption, in bytes
 tune.vars.global-max-size 1048576
 tune.vars.reqres-max-size     512
 tune.vars.sess-max-size      2048
 tune.vars.txn-max-size        256
 log         127.0.0.1 syslog
  maxconn     1000
  user        haproxy
  group       haproxy
  daemon

defaults
 mode http

frontend f_myapp
 bind 127.0.0.1:80
 http-request set-var(txn.host) req.hdr(Host)
 http-request set-var(txn.ua) req.hdr(User-Agent)
 default_backend b_myapp

backend b_myapp
 http-response set-header Your-Host %[var(txn.host)]
 http-response set-header Your-User-Agent %[var(txn.ua)]
 server s1 127.0.0.1:6000 check

Test it. And you will see the response informaiton contains the request header information (host, and User-Agent).

curl -H "host":"hhh" -H "User-Agent":"hhh" http://127.0.0.1:80 -sv
.
.
> Accept: */*
> host:hhh
> User-Agent:hhh
.
.

Try this and you will NOT see the response header contains the reqest header.

curl http://127.0.0.1:80 -sv

Redirect the traffic by Header

This topic is the most important.
According to the following config setting, we can redirect the traffic to different backends based on the request header.

/etc/haproxy/haproxy.cfg

global
 # variables memory consumption, in bytes
 tune.vars.global-max-size 1048576
 tune.vars.reqres-max-size     512
 tune.vars.sess-max-size      2048
 tune.vars.txn-max-size        256
 log         127.0.0.1 syslog
  maxconn     1000
  user        haproxy
  group       haproxy
  daemon

defaults
 mode http

frontend f_myapp
 bind 127.0.0.1:80

use_backend bk_app1 if { hdr(Host) -i app1.domain1.com app1.domain2.com region2 }
 default_backend b_myapp

backend bk_app1
 http-response set-header Your-Host %[var(txn.host)]
 http-response set-header Your-User-Agent %[var(txn.ua)]
 server s1 127.0.0.1:7000 check

backend b_myapp
 http-response set-header Your-Host %[var(txn.host)]
 http-response set-header Your-User-Agent %[var(txn.ua)]
 server s1 127.0.0.1:6000 check
root@haproxy:~# python -m SimpleHTTPServer 7000
Serving HTTP on 0.0.0.0 port 7000 ...

When hdr(Host) satisfies the condition of app1.domain1.com app1.domain2.com region2, the traffic will be redirect to bk_app1 that is the port 7000 running by SimpleHTTPServer.
When hdr(Host) NOT satisfies the condition, it will be redirect to default_backend b_myapp.
For simply, we shutdown the port 6000 backend server.

You can test app1.domain1.com, app1.domain2.com, and region2 as the reqeust header.

root@haproxy:~# curl -H "Host:"region2" " http://127.0.0.1:80 -sv
* Rebuilt URL to: http://127.0.0.1:80/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Accept: */*
> Host:region2
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
.
.

Try region3 and it should return 503 since we didn't launch port 6000 for detection the error.

root@haproxy:~# curl -H "Host:"region3" " http://127.0.0.1:80 -sv
* Rebuilt URL to: http://127.0.0.1:80/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Accept: */*
> Host:region3
>
* HTTP 1.0, assume close after body
< HTTP/1.0 503 Service Unavailable
< Cache-Control: no-cache
< Connection: close
< Content-Type: text/html
<
<html><body><h1>503 Service Unavailable</h1>
No server is available to handle this request.
</body></html>
* Closing connection 0

Some Warning

When we restart the haproxy, we found some warning as followed.
But it not affect our experience, so we ignore it.

root@haproxy:~# service haproxy restart
 * Restarting haproxy haproxy                                                                                                                                                        [WARNING] 265/160929 (3164) : config : missing timeouts for frontend 'f_myapp'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[WARNING] 265/160929 (3164) : config : missing timeouts for backend 'bk_app1'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[WARNING] 265/160929 (3164) : config : missing timeouts for backend 'b_myapp'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[WARNING] 265/160929 (3165) : config : missing timeouts for frontend 'f_myapp'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[WARNING] 265/160929 (3165) : config : missing timeouts for backend 'bk_app1'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[WARNING] 265/160929 (3165) : config : missing timeouts for backend 'b_myapp'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.

No comments:

Post a Comment