Configuring HAProxy is very straightforward, which may explain its popularity within the open source community (learn more about HAProxy at http://www.haproxy.org/). Let's look at an example HAProxy configuration file that would support the above architecture (haproxy.cfg is typically found at /etc/haproxy). We'll start with the two global and default sections, which you likely can leave "as-is" for most scenarios:
global
maxconn 4096
user haproxy
group haproxy
daemon
log 127.0.0.1 local0 debug
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
option http-server-close
option forwardfor
maxconn 2000
timeout connect 5s
timeout client 15min
timeout server 15min
frontend public
bind *:80
# Define hosts
acl host_subdomain1 hdr(host) -i subdomain1.myhost.com
acl host_subdomain2 hdr(host) -i subdomain2.myhost.com
## figure out which one to use
use_backend subdomain1 if host_subdomain1
use_backend subdomain2 if host_subdomain2
The "bind" directive is used to indicate which port HAProxy will be listening (in this case, port 80). The "acl" directives define the criteria for how to route the inbound traffic. The first parameter that follows the directive is simply an internal name for future referencing, with the remainder of the parameters defining the methods used for matching some element of the inbound request that is being used as the basis for routing (the HAProxy docs provide more details). In this case, the first "acl" directive is simply stating that if the inbound request has a hostname of "subdomain1.myhost.com", then assign the match to acl identifier called "host_subdomain1". Basically, we are simply defining the criteria for matching the inbound request.
The next part of this section is the "use_backend" directive, which assigns the matching "acl" rule to a specific host for processing. So, in the first example, it's saying that if the "acl" match name for the inbound request is assigned to "host_subdomain1", then use the backend identified by "subdomain1". Those "backends" are defined in the section that follows:
backend subdomain1
option httpclose
option forwardfor
cookie JSESSIONID prefix
server subdomain-1 someawshost.amazonaws.com:8080 check
backend subdomain2
option httpclose
option forwardfor
cookie JSESSIONID prefix
server subdomain-2 localhost:8080 check
In the first "backend" directive, we are defining the server backend used for processing that was identified through the assigned name "subdomain1" (these are just arbitrary names - just be sure to match what you have in the frontend directive). The "server" directive (which uses an arbitrary name assignment as the first parameter) is then used to identify which host/port to forward the request, which in this case is the host someawshost.amazonaws.com:8080 (the check parameter is actually only relevant when using load balancing). You can have multiple server directives defined if you are using load balancing/clustering.
There are a multitude of configuration options available for HAProxy, but this simple example illustrates how easily you can use it as a reverse proxy, redirecting inbound traffic to its final destination.
APIs & Microservices
One of the challenges in devising a API solution that runs using microservices is how to "carve" up the various service calls so that they can hosted in separate Docker containers. Using the subdomain based approach (i.e., subdomain.myhost.com) I described earlier for standard web applications isn't really practical when you want to convey a uniform API to your development community. However, it can still be accomplished in much the same fashion using URL path-based reverse proxy rules. For example, consider these two fictitious API calls:
GET api.mycompany.com/contracts/{contractId}
GET api.mycompany.com/customers/{customerId}
In the first example, this is a RESTful GET call that retrieves a given contract JSON object based upon a contractId that was specified. A similar pattern is used in example 2 for fetching a customer record. As you can see, the first path element is really the "domain" associated with the web service call. This could be used as a basis for separating those domains calls into their own separate microservice Docker container(s). Once nice benefit to doing so is that one of those domains may typically experience more traffic than the other. By splitting them into separate services, you can perform targeted scaling.
If you enjoyed this article, please link to it accordingly.
No comments:
Post a Comment