Last active
July 9, 2019 07:46
-
-
Save broglep-work/0ceed52c509f75a6c26afc43b807a0eb to your computer and use it in GitHub Desktop.
PoC for Workaround proposed in https://github.com/aws/amazon-ecs-agent/issues/1351
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: 2010-09-09 | |
Description: 'Registers ecs tasks to a second target group' | |
Parameters: | |
ClusterArn: | |
Description: 'ANR of cluster the service resides in' | |
Type: String | |
ServiceName: | |
Description: 'Name of the service to register' | |
Type: String | |
ContainerName: | |
Description: 'Name of the container to register' | |
Type: String | |
VpcId: | |
Description: 'VPC ID' | |
Type: String | |
Subnets: | |
Description: 'Subnets for ALB (comma separated)' | |
Type: String | |
Outputs: | |
LambdaFunction: | |
Value: !GetAtt LambdaFunction.Arn | |
Resources: | |
ALB: | |
Type: AWS::ElasticLoadBalancingV2::LoadBalancer | |
Properties: | |
Scheme: 'internet-facing' | |
SecurityGroups: | |
- !Ref ALBPublicSecurityGroup | |
Subnets: !Split [ ",", !Ref Subnets ] | |
Type: 'application' | |
ALBPublicSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: "Public security group" | |
VpcId: !Ref VpcId | |
SecurityGroupIngress: | |
- IpProtocol: "tcp" | |
FromPort: "80" | |
ToPort: "80" | |
CidrIp: "0.0.0.0/0" | |
ALBSslListener: | |
Type: AWS::ElasticLoadBalancingV2::Listener | |
Properties: | |
DefaultActions: | |
- Type: 'forward' | |
TargetGroupArn: !Ref ALBTargetGroup | |
LoadBalancerArn: !Ref ALB | |
Port: '80' | |
Protocol: 'HTTP' | |
ALBTargetGroup: | |
Type: AWS::ElasticLoadBalancingV2::TargetGroup | |
Properties: | |
HealthCheckIntervalSeconds: '5' | |
HealthCheckPath: '/is-alive' | |
HealthCheckProtocol: 'HTTP' | |
HealthCheckTimeoutSeconds: '3' | |
HealthyThresholdCount: '2' | |
Port: '80' | |
Protocol: 'HTTP' | |
TargetGroupAttributes: | |
- Key: 'deregistration_delay.timeout_seconds' | |
Value: '60' | |
UnhealthyThresholdCount: '3' | |
VpcId: !Ref VpcId | |
ECSTaskStateChangeEventRule: | |
Type: AWS::Events::Rule | |
Properties: | |
Description: !Sub "Invoke Lambda Function for ECS Task Stage Changes of ${ServiceName}" | |
State: ENABLED | |
EventPattern: | |
source: | |
- "aws.ecs" | |
detail-type: | |
- "ECS Task State Change" | |
detail: | |
clusterArn: | |
- !Ref ClusterArn | |
group: | |
- !Sub "service:${ServiceName}" | |
Targets: | |
- | |
Arn: !GetAtt LambdaFunction.Arn | |
Id: "lambda" | |
LambdaInvokePermission: | |
Type: AWS::Lambda::Permission | |
Properties: | |
Action: 'lambda:InvokeFunction' | |
FunctionName: !Sub ${LambdaFunction.Arn} | |
Principal: 'events.amazonaws.com' | |
SourceArn: !Sub ${ECSTaskStateChangeEventRule.Arn} | |
LambdaFunction: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: !Sub 'Lambda function for registering and de-registering target of ${ServiceName}' | |
Environment: | |
Variables: | |
SERVICE: !Ref ServiceName | |
CONTAINER: !Ref ContainerName | |
TARGET_GROUP: !Ref ALBTargetGroup | |
Handler: 'index.lambda_handler' | |
MemorySize: 128 | |
Role: !GetAtt LambdaExecutionRole.Arn | |
Runtime: python3.6 | |
Timeout: 10 | |
Code: | |
ZipFile: | | |
import os | |
import json | |
import boto3 | |
SERVICE = os.environ['SERVICE'] | |
CONTAINER = os.environ['CONTAINER'] | |
TARGET_GROUP = os.environ['TARGET_GROUP'] | |
CONTAINER_PORT = None | |
elbv2 = boto3.client('elbv2') | |
ecs = boto3.client('ecs') | |
def lambda_handler(event, context): | |
if event["source"] != "aws.ecs": | |
raise ValueError("Function only supports input from events with a source type of: aws.ecs") | |
if event["detail-type"] != "ECS Task State Change": | |
raise ValueError("Function only supports input from events with a detail-type of: ECS Task State Change") | |
lastStatus = event['detail']['lastStatus'] | |
desiredStatus = event['detail']['desiredStatus'] | |
print("Last Status: %s" % lastStatus) | |
print("Desired Status: %s" % desiredStatus) | |
res = ecs.describe_container_instances(cluster=event['detail']['clusterArn'],containerInstances=[event['detail']['containerInstanceArn']]) | |
ec2InstanceId = res['containerInstances'][0]['ec2InstanceId'] | |
# Optimize: Cache this lookup | |
res = ecs.describe_services(cluster=event['detail']['clusterArn'],services=[SERVICE]) | |
service = next(service for service in res['services'] if service['serviceName'] == SERVICE) | |
loadBalancer = next(loadBalancer for loadBalancer in service['loadBalancers'] if loadBalancer['containerName'] == CONTAINER) | |
CONTAINER_PORT = loadBalancer['containerPort'] | |
if lastStatus == 'PENDING': | |
# In Pending state we do not know host port yet | |
return | |
container = next(container for container in event['detail']['containers'] if container['name'] == CONTAINER) | |
binding = next(binding for binding in container['networkBindings'] if binding['containerPort'] == CONTAINER_PORT) | |
hostPort = binding['hostPort'] | |
target = {'Id': ec2InstanceId,'Port': hostPort} | |
print("Target: %s" % target) | |
if lastStatus == 'RUNNING' and desiredStatus == 'RUNNING': | |
print('Registering Target') | |
elbv2.register_targets(TargetGroupArn=TARGET_GROUP, Targets=[target]) | |
if lastStatus == 'RUNNING' and desiredStatus == 'STOPPED': | |
print('Deregistering Target') | |
elbv2.deregister_targets(TargetGroupArn=TARGET_GROUP,Targets=[target]) | |
LambdaExecutionRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- | |
Effect: "Allow" | |
Principal: | |
Service: | |
- "lambda.amazonaws.com" | |
Action: | |
- "sts:AssumeRole" | |
ManagedPolicyArns: | |
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" | |
Policies: | |
- | |
PolicyName: "DescribeECSAndElasticLoadBalancing" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- | |
Effect: "Allow" | |
Action: | |
- "ecs:DescribeServices" | |
- "ecs:DescribeContainerInstances" | |
Resource: "*" | |
- | |
PolicyName: "UpdateTargetGroup" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- | |
Effect: "Allow" | |
Action: | |
- "elasticloadbalancing:RegisterTargets" | |
- "elasticloadbalancing:DeregisterTargets" | |
Resource: !Ref ALBTargetGroup | |
Path: "/service-role/" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@broglep-koubachi
I understood this lambda function need three environment variables.
SERVICE is the ECS service name in that ECS cluster
TARGET_GROUP is the new created target group in this template
But what
CONTAINER
name I should provide? Any sample for me?### Update
I knew what need be used.
By the way, if you need test it with a sample events, check here:
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_cwe_events.html