Skip to content

Instantly share code, notes, and snippets.

@faermanj
Created March 18, 2019 08:27
Show Gist options
  • Save faermanj/2a1676979712ae3818d03e1e2db315be to your computer and use it in GitHub Desktop.
Save faermanj/2a1676979712ae3818d03e1e2db315be to your computer and use it in GitHub Desktop.
Multi-region serverless backend
#!/usr/bin/env bash
set -e
# Script for creating a multi-region active-active deployment
# usgin the AWS CLI based on the post from @adhorn:
# https://medium.com/@adhorn/multi-region-serverless-backend-reloaded-1b887bc615c0
RID="$RANDOM"
PREFIX="globalapp"
REGIONS="us-east-1 us-west-2 eu-west-1"
TABLE_NAME="${PREFIX}table${RID}"
PK="item_id"
LB_NAME="${PREFIX}alb${RID}"
ROLE_NAME="${PREFIX}fnrole${RID}"
FN_NAME="${PREFIX}fnget${RID}"
AXEL_NAME="${PREFIX}axel${RID}"
ALB_GROUP_NAME="${PREFIX}axelgrp${RID}"
echo "[RID=$RID] Global Application Deployment Script \o/"
echo "[RID=$RID] Prepare local resources"
rm -rf globalapp
mkdir globalapp
pushd globalapp
echo "[RID=$RID] Create lambda package"
echo 'simplejson==3.16.0' > requirements.txt
wget http://julio.cloud/pub/get.py
docker run -v $PWD:/var/task -it lambci/lambda:build-python3.6 /bin/bash -c "pip install -r requirements.txt -t vendor"
zip -r function.zip .
echo "[RID=$RID] Creating IAM Role for Lambda Functions"
wget http://julio.cloud/pub/trust-lambda.json
LAMBDA_ROLE=$(aws iam create-role \
--role-name $ROLE_NAME \
--assume-role-policy-document file://trust-lambda.json \
--query Role.Arn \
--output text)
echo $LAMBDA_ROLE
sleep 5
aws iam attach-role-policy \
--role-name "$ROLE_NAME" \
--policy-arn 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
aws iam attach-role-policy \
--role-name "$ROLE_NAME" \
--policy-arn 'arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess'
echo "[RID=$RID] Creating Global Accelerator [$AXEL_NAME]"
AXEL_ARN=$(aws globalaccelerator create-accelerator \
--region us-west-2 \
--name $AXEL_NAME \
--enabled \
--idempotency-token "globalappxceltoken" \
--query "Accelerator.AcceleratorArn" \
--output text)
echo "[AXEL] AXEL_ARN=$AXEL_ARN"
AXEL_LIST_ARN=$(aws globalaccelerator create-listener \
--region us-west-2 \
--accelerator-arn $AXEL_ARN \
--port-ranges FromPort=80,ToPort=80 \
--protocol TCP \
--idempotency-token "globalappxceltoken" \
--query "Listener.ListenerArn" \
--output text)
echo "[AXEL] AXEL_LIST_ARN=$AXEL_LIST_ARN"
echo "Creating per-region resources"
for REGION in $REGIONS; do
echo "[$REGION] Creating resources"
echo "[$REGION] Creating Network Infrastructre"
echo "[$REGION] [VPC] Creating VPC"
VPC_ID=$(aws ec2 create-vpc \
--cidr-block "10.0.0.0/16" \
--query "Vpc.VpcId" \
--region "$REGION" \
--output text)
echo "[$REGION] [VPC] $VPC_ID"
AZ_A=$(aws ec2 describe-availability-zones \
--region $REGION \
--query "AvailabilityZones[0].ZoneName" \
--output text)
AZ_B=$(aws ec2 describe-availability-zones \
--region $REGION \
--query "AvailabilityZones[1].ZoneName" \
--output text)
echo "[$REGION] [VPC] AZs=$AZ_A,$AZ_B"
SUBNET_A=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.10.0/24 \
--availability-zone $AZ_A \
--region $REGION \
--query "Subnet.SubnetId" \
--output text)
SUBNET_B=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.20.0/24 \
--availability-zone $AZ_B \
--region $REGION \
--query "Subnet.SubnetId" \
--output text)
echo "[$REGION] [VPC] Subnets $SUBNET_A,$SUBNET_B"
IGW=$(aws ec2 create-internet-gateway \
--region $REGION \
--query "InternetGateway.InternetGatewayId" \
--output text)
aws ec2 attach-internet-gateway \
--region $REGION \
--vpc-id $VPC_ID \
--internet-gateway-id $IGW
echo "[$REGION] [VPC] IGW $IGW"
ROUTES=$(aws ec2 create-route-table --vpc-id $VPC_ID \
--region $REGION \
--query "RouteTable.RouteTableId" \
--output text)
echo "[$REGION] [VPC] ROUTES=$ROUTES"
aws ec2 create-route \
--region $REGION \
--route-table-id $ROUTES \
--destination-cidr-block "0.0.0.0/0" \
--gateway-id $IGW
echo "[$REGION] [VPC] Associating routing tables"
aws ec2 associate-route-table \
--region $REGION \
--subnet-id $SUBNET_A \
--route-table-id $ROUTES
aws ec2 associate-route-table \
--region $REGION \
--subnet-id $SUBNET_B \
--route-table-id $ROUTES
echo "[$REGION] [VPC] Setting public-ip-on-launch"
aws ec2 modify-subnet-attribute \
--region $REGION \
--subnet-id $SUBNET_A \
--map-public-ip-on-launch
aws ec2 modify-subnet-attribute \
--region $REGION \
--subnet-id $SUBNET_B \
--map-public-ip-on-launch
GROUP_ID=$(aws ec2 create-security-group \
--region $REGION \
--group-name globalappsg --description "Global app security group" \
--vpc-id $VPC_ID \
--query "GroupId" \
--output text)
echo "[$REGION] [VPC] Security Group $GROUP_ID"
aws ec2 authorize-security-group-ingress \
--region $REGION \
--group-id $GROUP_ID \
--protocol tcp \
--port 80 \
--cidr "0.0.0.0/0"
echo "[$REGION] [DDB] Creating DynamoDB Table"
aws dynamodb create-table \
--table-name $TABLE_NAME \
--attribute-definitions AttributeName=$PK,AttributeType=S \
--key-schema AttributeName=$PK,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
--region $REGION
echo "[$REGION] [LAMDA] Creating Lambda Function"
LAMBDA_ARN=$(aws lambda create-function \
--function-name "$FN_NAME" \
--zip-file "fileb://function.zip" \
--role "$LAMBDA_ROLE" \
--handler get.get_item \
--runtime python3.6 \
--timeout 30 \
--memory-size 128 \
--query FunctionArn \
--environment "Variables={tablename=GlobalApp,STATUS=200}" \
--output text \
--region $REGION)
echo "[$REGION] [ALB] Creating Load Balancer"
LB=$(aws elbv2 create-load-balancer \
--name $LB_NAME \
--region $REGION \
--subnets $SUBNET_A $SUBNET_B \
--security-groups $GROUP_ID \
--query "LoadBalancers[0].LoadBalancerArn" \
--output text)
TARGETS=$(aws elbv2 create-target-group \
--name "$ALB_GROUP_NAME" \
--region $REGION \
--target-type lambda \
--health-check-enabled \
--health-check-path "/health" \
--health-check-interval-seconds 15 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 3 \
--unhealthy-threshold-count 3 \
--query "TargetGroups[0].TargetGroupArn" \
--output text)
echo "[$REGION] [LAMBDA] Add Permission"
aws lambda add-permission \
--region $REGION \
--function-name "$FN_NAME" \
--statement-id alb-lambda \
--action "lambda:InvokeFunction" \
--source-arn "$TARGETS" \
--principal elasticloadbalancing.amazonaws.com
echo "[$REGION] [ALB] Register Target"
aws elbv2 register-targets \
--region $REGION \
--target-group-arn "$TARGETS" \
--targets "Id=$LAMBDA_ARN"
echo "[$REGION] [ALB] Create Listener"
aws elbv2 create-listener \
--region $REGION \
--load-balancer-arn $LB \
--protocol HTTP \
--port 80 \
--default-actions Type=forward,TargetGroupArn="$TARGETS"
ENDPOINT=$(aws elbv2 describe-load-balancers \
--region $REGION \
--load-balancer-arns $LB \
--query "LoadBalancers[0].DNSName" \
--output text)
echo "[$REGION] [ALB] $ENDPOINT"
echo "[$REGION] [ALB] Waiting $LB"
aws elbv2 wait load-balancer-available \
--region $REGION \
--load-balancer-arns $LB
echo "[$REGION] [AXEL] Creating Endpoint Group on Listener [$AXEL_LIST_ARN]"
aws globalaccelerator create-endpoint-group \
--region us-west-2 \
--idempotency-token "globalappxceltoken" \
--listener-arn "$AXEL_LIST_ARN" \
--endpoint-group-region $REGION \
--endpoint-configurations EndpointId=$LB,Weight=10
echo "[$REGION] [AXEL] $ENDPOINT"
echo "[$REGION] done"
done
echo "[DDB] Creating global table"
aws dynamodb create-global-table \
--global-table-name $TABLE_NAME \
--replication-group RegionName=us-east-1 RegionName=us-west-2 RegionName=eu-west-1 \
--region us-east-1
echo "[$RID] Done and global!"
echo "Try This:"
echo aws dynamodb put-item \
--table-name $TABLE_NAME \
--item \'{\"item_id\": {\"S\":\"toy\"}}\' \
--region us-east-1
echo aws dynamodb get-item \
--table-name $TABLE_NAME \
--key \'{\"item_id\": {\"S\":\"toy\"}}\' \
--region eu-west-1
IP_A=$(aws globalaccelerator describe-accelerator \
--region us-west-2 \
--accelerator-arn $AXEL_ARN \
--query "Accelerator.IpSets[].IpAddresses[0]" \
--output text)
echo "Endpoint for Global Accelerator [$AXEL_NAME]"
echo curl "http://$IP_A/health"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment