The AWS application load balancer module allows us to add one or more front-end listeners and one or more back-end target groups which can be ec2 instances, private IP addresses, auto-scaling groups or even other load balancers.
Traffic can be routed based on the front-end host (aka host based routing), the request URI or the content. It can also be routed based on the back-end load, health or a strategy such as round robin delivery.
module load-balancer
{
source = "github.com/devops4me/terraform-aws-load-balancer"
in_vpc_id = "${ module.vpc-network.out_vpc_id }"
in_subnet_ids = "${ module.vpc-network.out_public_subnet_ids }"
in_security_group_id = "${ module.security-group.out_security_group_id }"
in_ip_addresses = "${ aws_instance.server.*.private_ip }"
in_ip_address_count = 3
in_front_end = [ "http" ]
in_back_end = [ "etcd" ]
in_ecosystem = "${ local.ecosystem_id }"
}
output dns_name{ value = "${ module.load-balancer.out_dns_name}" }
Input Variable | Type | Notes - Description |
---|---|---|
in_vpc_id | String | The ID of the VPC containing all the back-end targets, subnets and security groups to route to. |
in_security_group_id | String | The security group must be configured to permit the type of traffic the load balancer is routing. A 504 Gateway Time-out error from your browser means a missing security group rule is blocking the traffic. |
in_subnet_ids | List | Use public subnets for an externally accessible front-end even when the back-end targets are in private subnets. Use private subnets for internal load balancers. The IDs of the subnets that traffic will be routed to. Important - traffic will not be routed to two or more subnets in the same availability zone. |
in_is_internal | Boolean | If true the load balancer's DNS name is private - if false the DNS name will be externally addressable. |
in_ip_addresses | List | List of private or public IP addresses that the load balancer's back-end will route traffic to. If internal [ in_is_internal = true ], then only private IP addresses **inside private subnets*** can be specified. |
in_ssl_certificate_id | String | The ID of the SSL certificate living in the ACM (Amazon Certificate Manager) repository. |
in_front_end | List | List of front end listener configurations for this load balancer like web (for http port 80) and ssl (for https port 443). |
in_back_end | List | List of back end target configuration for this load balancer like etcd (for http port 2379), web (for http port 80) and ssl (for https port 443). |
in_access_logs_bucket | String | The name of the S3 bucket to which the load balancer will post access logs. |
in_ecosystem | String | the class name of the ecosystem being built here. |
-- | -- On the front end a load balancer listens to http and/or https traffic | -- whilst on the back-end, its tentacles latch onto target groups. |
---|---|---|
-- We vertically read the front-end and back-end configuration. | ||
-- | ||
-- in_front_end = [ "web", "etcd", "ssl" ] | ||
-- in_back_end = [ "rabbit", "etcd", "rmqssl" ] | ||
-- | ||
-- In this example (reading column-wise) | ||
-- | ||
-- 1> listen to http (port 80) traffic and send to rabbitmq (port 15672) | ||
-- 2> listen to etcd (port 2379) traffic and send to etcd (port 2379) | ||
-- 3> listen to HTTPS (port 443) traffic and send to rabbit (ssl) on 15671 | ||
-- |
Always use public subnets for internet facing load balancers and private subnets for internal load balancers.
For external load balancers with services in private subnets you use the vpc network module to create twin public and private subnets in each availability zone (usually 6). Give the load balancer the public subnets (in the same order) and create services in the private subnets.
Internal load balancers are not allowed to sit in public subnets and external load balancers are not allowed to sit in private subnets (but the services can).
Use public subnet ids even when the back-end targets are in private subnets if you want a public facing load-balancer front-end. From the browser the load-balancer will just hang if you have used private subnets because you can't connect to a service in private subnets from the outside world.
The vpc-network module will provide the correct infrastructure to ensure services in private subnets can connect, via a NAT gateway and routes, to the internet.
If you desire an internal load balancer then use private subnet IDs. Your load balancer will be available through a VPN or bastion host, and it will be accessible to services in any VPCs for the same account without the need for a peering connection.
Load balancers will error saying that traffic will not be routed to two or more subnets in the same availability zone if that is what has been provided.
A 504 Gateway Time-out error from your browser means that a security group is blocking your application load-balancer from initiating a connection using a given protocol on a given port.
Fix it by allowing all-traffic through then narrow the gap until you discover the missing security group rule.
Here are the most popular output variables exported from this VPC and subnet creating module.
Exported | Type | Example | Comment |
---|---|---|---|
out_vpc_id | String | vpc-1234567890 | the VPC id of the just-created VPC |
out_rtb_id | String | "rtb-2468013579" | ID of the VPC's default route table |
out_subnet_ids | List of Strings | [ "subnet-545123498798345", "subnet-83507325124987" ] | list of all private and public subnet ids |
out_private_subnet_ids | List of Strings | [ "subnet-545123498798345", "subnet-83507325124987" ] | list of private subnet ids |
out_public_subnet_ids | List of Strings | [ "subnet-945873408204034", "subnet-8940202943031" ] | list of public subnet ids |
This terraform module has runnable example integration tests. Read the instructions on how to clone the project and run the integration tests.
In order to create a load balancer you need security groups, subnets and you must specify whether it can be accessible on the internet, rather than just internally.
For internet accessible load balancers you must ensure that the VPC (parent of the subnets) has an internet gateway and a route to some kind of destination (the most open being 0.0.0.0/0).
Currently the target group is hardcoded to HTTPS at port 443.
The health check is hardcoded
There are two possible values for target type
Note that you can't specify targets for a target group using both instance IDs and IP addresses. If the target type is ip, specify IP addresses from the subnets of the virtual private cloud (VPC) for the target group, the RFC 1918 range (10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16), and the RFC 6598 range (100.64.0.0/10). Remember that you cannot specify publicly routable IP addresses.
When AWS creates instance (node) clusters for the likes of Redis, Postgres, Kubernetes and ElasticSearch, it places ENIs (elastic network interfaces) that expose private IP addresses that load balancers can hook onto.
This method enables SSL termination (using Certificate Manager SSL certificates) whilst connecting to clusters through a load balancer.
The relevant network interfaces are queried for and when returned, we loop over them creating target group attachments that bind the target group to their private intra VPC IP addresses.
The port to use for each attachment is currently hardcoded to 443.
Splat syntax is used to retrieve the list of IP addresses, which are hen passed over for one to one creation of target group attachments.
A load balancer listener keeps an ear out for incoming traffic conforming to the specified protocol and arriving at the said port.
AWS impose a limit of 50 listeners per load balancer.
Listeners mostly forward traffic on but they can also
For a listener to terminate SSL, you must provide the ARN of the SSL certificate which is usually kept in certificate manager. However you can also import externally sourced SSLs certificate directly into the load balancer.
The application load balancer can have many rules meaning we can route traffic to different places based on
This load balancer can implement the reverse proxy pattern by terminating SSL which is useful when backend services either cannot will not or should not work with SSL and/or manage SSL certificates within applications and containers.
As such the ID of the ssl certificate in AWS's certificate manager is typically provided to the load balancer's listener. These certificates are free and are automatically renewed which is highly attractive in contrast with the manual and costly nature of dealing with certificate authorities.
If you use nginx only for SSL termination and/or reverse proxying you should consider replacing it with an application load balancer. (Note that this also applies to the likes of Apache2 and WeBrick(Ruby).
A load balancer will perform better, is cheaper, simpler, more secure and inherently super scaleable. They scale to higher throughputs and loads without any performance degradation and they take the headaches of maintenance, upgrades, security and deployment off your shoulders.
AWS load balancers can write access logs into an S3 bucket further making the case to migrate away from traditional web servers.
That said, nginx is ideal for more complex workloads like url rewriting, email routing, caching and authentication.
The move to a serverless infrastructure is an undeniable upwards trend and replacing traditional webservers with load balancers achieves just that. Serverless comes into play when you use
Migrating towards load balancers and away from web servers is a step towards the serverless paradigm.
Load balancers can achieve port mapping between front-end listeners and back-end targets.
An example is the etcd3 cluster that maps the back-end etcd port 2379 to the front-end listener port 80.
module load-balancer
{
source = "github.com/devops4me/terraform-aws-load-balancer"
in_vpc_id = "${ module.vpc-network.out_vpc_id }"
in_subnet_ids = "${ module.vpc-network.out_subnet_ids }"
in_security_group_id = "${ module.security-group.out_security_group_id }"
in_ip_addresses = "${ aws_instance.node.*.private_ip }"
in_front_end = [ "web" ]
in_back_end = [ "etcd" ]
in_ecosystem = "${ local.ecosystem_id }"
}
The *in_front_end defintion web is saying that the ubiquitous port 80 should be mapped to etcd port 2379 as signaled by in_back_end**.
VPC peering allows services in the private subnets of different VPCs to talk to each other.
VPC peering encourages the hardcoding of one (or two) VPC IDs and a number of subnet IDs. It also adds complexity due to the routing and subnet associations that must be made.
Consider using internal load balancers instead of VPC peering. Services in private subnets of one VPC can talk to their peers in private subnets of another VPC through a load balancer without configuring VPC peering.
Often proxying machines called bastion hosts are setup in a public subnet simply to allow connectivity to services in sister private subnets. IP tables and port forwarding are typically used to effect the connectivity.
Consider replacing bastion hosts with a load balancer. An externally accessible load balancer can route to services in private subnets thus negating the need for clumsy EC2 instances that risk becoming single points of failure in your architecture.
The AWS application load balancer is a (network) layer 7 load balancer which opens up incoming packets. Its ability to look into the request data means it can
A layer 4 network load balancer does not open up the message thus making it faster, but the trade-off is it does not have the capabilities of a layer 7 load balancer.
We could write load balancer access logs to an S3 bucket every 5 minutes, 60 minutes or indeed never.
If the load balancer receives 5,000 requests per seconds how many file lines would result?
5,000 x 60 x 60
5,000 x 3,600
5,000 x 3,600
3,600,000 x 5
18,000,000
18 million lines
Now approximate the byte size of each line and then multiply out to determine roughly how big the file would be if produced
Bug reports and pull requests are welcome on GitHub at the https://github.com/devops4me/terraform-aws-vpc-network page. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
MIT License Copyright (c) 2006 - 2014
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.