Обзор
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"}
Готово.