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'.