Обзор
Elastic Load Balancing позволяет распределять трафик между несколькими инстансами EC2 и зонами доступности AWS, что повышает отказоустойчивость приложения.
С недавних пор — AWS предоставляет два типа балансировщиков — Application Load Balancer и Classic Load Balancer.
Вкратце — классический ELB работал на 4-ом и 7-ом уровнях OSI, Application Load Balancer — на 7-ом и позволяет строить более гибкие правила за счёт работы на уровне приложений.
Создание ELB
Запуск EC2
Задача — создать два EC2, поднять Application Load Balancer и настроить балансировку нагрузки по health-проверке инстансов.
Запускать будем из имеющего AMI, который собран Packer-ом.
$ aws ec2 describe-images --image-ids ami-369bd545 --output text IMAGES x86_64 2016-10-21T15:58:49.000Z xen ami-1398d660 947191746595/aws-api-test-ami machine aws-api-test-ami 947191746595 False /dev/sda1 ebs simple available hvm BLOCKDEVICEMAPPINGS /dev/sda1 EBS True False snap-0df2dbd2d5f9711b7 8 gp2
Нам требуется запустить два EC2 в двух разных подсетях, в двух availability zones.
Создаём подсеть A в VPC, в зоне eu-west-1a:
$ aws ec2 create-subnet --vpc-id vpc-43402c27 --cidr-block 10.0.1.0/24 --availability-zone eu-west-1a
{
"Subnet": {
"VpcId": "vpc-43402c27",
"CidrBlock": "10.0.1.0/24",
"State": "pending",
"AvailabilityZone": "eu-west-1a",
"SubnetId": "subnet-422f5134",
"AvailableIpAddressCount": 251
}
}
И подсеть B в зоне eu-west-1b:
$ aws ec2 create-subnet --vpc-id vpc-43402c27 --cidr-block 10.0.2.0/24 --availability-zone eu-west-1b
{
"Subnet": {
"VpcId": "vpc-43402c27",
"CidrBlock": "10.0.2.0/24",
"State": "pending",
"AvailabilityZone": "eu-west-1b",
"SubnetId": "subnet-c7c2409f",
"AvailableIpAddressCount": 251
}
}
(не забываем добавить Internet Gateway и настроить Route Tables, тут пропущено, см тут>>>)
Запускаем два инстанса — по одному в каждой подсети:
$ aws ec2 run-instances --image-id ami-369bd545 --key-name aws-test --count 1 --instance-type t2.medium --user-data file://api_start.sh --security-group-ids sg-9225eef4 --subnet-id subnet-422f5134 $ aws ec2 run-instances --image-id ami-369bd545 --key-name aws-test --count 1 --instance-type t2.medium --user-data file://api_start.sh --security-group-ids sg-9225eef4 --subnet-id subnet-c7c2409f
Теперь у нас имеется два инстанса, которые мы включим в ELB:
$ aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId]' --filters Name=instance-state-name,Values=running --output text i-0fb346245a14ff8bc i-0aac76eab30ec53a9
Скрипт api_start.sh, который передаётся в userdata инстанса — просто запускает Docker контейнеры с сервисами приложения при старте нового инстанса (потребуется позже для Auto Scaling):
#!/usr/bin/env bash
sleep 30
SPRING_PROFILE="dev"
DOCKER_REGISTRY="project.jfrog.io"
DOCKER_IMAGE_TAG="aws-test-1.0"
DOCKER_IMAGE_OAUTH="project-oauth2-authserver:${DOCKER_IMAGE_TAG}"
DOCKER_IMAGE_PROFILE="project-profile:${DOCKER_IMAGE_TAG}"
DOCKER_IMAGE_CONFIGURATION="project-configuration:${DOCKER_IMAGE_TAG}"
DOCKER_IMAGE_PRODUCERS="project-producers:${DOCKER_IMAGE_TAG}"
DOCKER_IMAGE_GATEWAY="project-api-gateway:${DOCKER_IMAGE_TAG}"
docker run -d -p 9999:9999 -e SPRING_PROFILES_ACTIVE=${SPRING_PROFILE} --restart unless-stopped ${DOCKER_REGISTRY}/${DOCKER_IMAGE_OAUTH}
docker run -d -p 8083:8083 -e SPRING_PROFILES_ACTIVE=${SPRING_PROFILE} --restart unless-stopped ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PROFILE}
docker run -d -p 8084:8084 -e SPRING_PROFILES_ACTIVE=${SPRING_PROFILE} --restart unless-stopped ${DOCKER_REGISTRY}/${DOCKER_IMAGE_CONFIGURATION}
docker run -d -p 8090:8090 -e SPRING_PROFILES_ACTIVE=${SPRING_PROFILE} --restart unless-stopped ${DOCKER_REGISTRY}/${DOCKER_IMAGE_PRODUCERS}
docker run -d -p 8080:8080 -e SPRING_PROFILES_ACTIVE=${SPRING_PROFILE} --restart unless-stopped ${DOCKER_REGISTRY}/${DOCKER_IMAGE_GATEWAY}
Создание Application Load Balancer
За создание ELB отвечают две одинаковые опции AWS CLI — create-load-balancer. Только для классического ELB — она вызывается из aws.elb, а для Application Load Balancer — из aws.elbv2.
С помощью create-load-balancer создаём:
$ aws elbv2 create-load-balancer --name api-test --subnets subnet-422f5134 subnet-c7c2409f --security-groups sg-9225eef4 --scheme internet-facing
{
"LoadBalancers": [
{
"VpcId": "vpc-43402c27",
"LoadBalancerArn": "arn:aws:elasticloadbalancing:eu-west-1:947191746595:loadbalancer/app/api-test/0af98b76b2b99307",
"State": {
"Code": "provisioning"
},
"DNSName": "api-test-607244287.eu-west-1.elb.amazonaws.com",
"SecurityGroups": [
"sg-9225eef4"
],
"LoadBalancerName": "api-test",
"CreatedTime": "2016-10-24T14:33:21.160Z",
"Scheme": "internet-facing",
"Type": "application",
"CanonicalHostedZoneId": "Z32O12XQLNTSW2",
"AvailabilityZones": [
{
"SubnetId": "subnet-422f5134",
"ZoneName": "eu-west-1a"
},
{
"SubnetId": "subnet-c7c2409f",
"ZoneName": "eu-west-1b"
}
]
}
]
}
--scheme internet-facing можно не укзывать, т.к. internet-facing используется по умолчанию.
Добавление Target Group
Для того, что бы ELB распределял трафик между несколькими EC2 инстансами — их необходимо объединить в target group.
Создаём с помощью create-target-group:
$ aws elbv2 create-target-group --name api-test --protocol HTTP --port 8080 --vpc-id vpc-43402c27
{
"TargetGroups": [
{
"HealthCheckPath": "/",
"HealthCheckIntervalSeconds": 30,
"VpcId": "vpc-43402c27",
"Protocol": "HTTP",
"HealthCheckTimeoutSeconds": 5,
"HealthCheckProtocol": "HTTP",
"UnhealthyThresholdCount": 2,
"HealthyThresholdCount": 5,
"TargetGroupArn": "arn:aws:elasticloadbalancing:eu-west-1:947191746595:targetgroup/api-test/3f0ff2ce8d667243",
"Matcher": {
"HttpCode": "200"
},
"HealthCheckPort": "traffic-port",
"Port": 8080,
"TargetGroupName": "api-test"
}
]
}
Добавляем к этой группе оба созданных инстанса, указав порт 8080:
$ aws elbv2 register-targets --target-group-arn arn:aws:elasticloadbalancing:eu-west-1:947191746595:targetgroup/api-test/3f0ff2ce8d667243 --targets Id=i-0fb346245a14ff8bc,Port=8080 Id=i-0aac76eab30ec53a9,Port=8080
Добавление Listener
Следующим — с помощью create-listener добавляем listener, который будет слушать указанный порт, и распределять обращения к этом порту по созданной ранее target group.
$ aws elbv2 create-listener --load-balancer-arn arn:aws:elasticloadbalancing:eu-west-1:947191746595:loadbalancer/app/api-test/0af98b76b2b99307 --protocol HTTP --port 8080 --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:eu-west-1:947191746595:targetgroup/api-test/3f0ff2ce8d667243
{
"Listeners": [
{
"Protocol": "HTTP",
"DefaultActions": [
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:eu-west-1:947191746595:targetgroup/api-test/3f0ff2ce8d667243",
"Type": "forward"
}
],
"LoadBalancerArn": "arn:aws:elasticloadbalancing:eu-west-1:947191746595:loadbalancer/app/api-test/0af98b76b2b99307",
"Port": 8080,
"ListenerArn": "arn:aws:elasticloadbalancing:eu-west-1:947191746595:listener/app/api-test/0af98b76b2b99307/c5ab6c413ae29a1b"
}
]
}
Вот так это всё выглядит в панели управления:
И проверяем:
$ curl api-test-607244287.eu-west-1.elb.amazonaws.com/health
{"description":"Spring Cloud Eureka Discovery Client","status":"UP"}
Готово.

