- Fargate Design Patterns
- Compendium and Code Examples of AWS Fargate Patterns.
- Instructions for Running the Examples
- Examples
- Tom Thumb - A Video Thumbnail Generator Task
- Setup Instructions
- Create a repository in ECR
- Build the Docker Image
- Create the Log Group
- Create the ECS Cluster
- Generate the Task Definition
- Register the Task Definition
- Generate the parameters for running the task
- Manually run the task
- Create a Lambda Trigger
- Set the S3 bucket ARN
- Create Lambda
- Create a Log Group for Lambda
- Package the Lambda
- Deploy the Lambda
- Testing Tom-Thumb
- Bean-counter - A Coin Counter Service
- Setup Instructions
- Create a repository in ECR
- Build the Docker Image
- Create the Log Group
- Create the ECS Cluster
- Generate the Task Definition
- Register the Task Definition
- Generate the Service Definition
- Create the Bean-counter Service
- Testing the Bean-counter Service
- Set the Scaling Policy for the Service
- Test the Scaling Policy
- Application in Action
- Conclusion
Table of contents generated with markdown-toc
This is a companion project for my blog post on Fargate Patterns.
You can find the code examples, prerequisites and installation instructions for the blog post here.
All the examples require a few prerequisites to be performed. These range from creating appropriate roles in IAM for the Lambda and Fargate to invoke AWS services. For instance, the Lambda to invoke the Fargate task, the Fargate task to read files from the S3 bucket and write back responses to it. Additionally, the S3 bucket must be prepped to notify the Lambda function.
All these examples use AWS CLI to invoke various AWS services. To avoid the idiosyncrasies of personal development environments, an EC2 instance running Ubuntu 16.04 LTS was used to run the AWS CLI and deploy the AWS services.
As you run the various shell scripts indicated in the instructions, pay close attention to scripts requiring you to source shell scripts. This is done to carry over the environment variables generated in one script to subsequent ones.
To be able to run the aws cli commands in the following exercises, create a group in IAM with the following permissions as shown below and assign it to the IAM user you will be using to work on this exercise.
Start a brand new EC2 instance running Ubuntu 16.04 LTS as your development environment and in the terminal window clone the Git repository to your development environment
$ git clone https://github.com/skarlekar/fargate-patterns.git
Run the prereqs-ubuntu.sh script to install Docker, Python, pip, AWS CLI and other development enablers in your environment.
$ cd fargate-patterns/prerequisities
$ prereqs-ubuntu.sh
Log out of your shell and log back for the newly installed programs to take effect.
Verify that you are able to run AWS CLI in your newly minted environment
$ aws --version
aws-cli/1.16.144 Python/2.7.10 Darwin/18.5.0 botocore/1.12.134
Create AWS roles ecsTaskExecutionRole and task roles in IAM for Fargate to access other AWS services on your behalf
$ source ./create-roles.sh
Ensure the role was created by verifying the TASK_ROLE_ARN variable was set
$ echo $TASK_ROLE_ARN
Create VPC, Subnets and Security groups for running Fargate
$ source ./create-vpc-subnets.sh
Ensure the role was created by verifying the VPC, SUBNET1, SUBNET2 and SECURITYGROUP variables was set.
$ echo echo $VPC, $SUBNET1, $SUBNET2, $SECURITYGROUP
Create an application load balancer for the Bean-Counter service we will create later
$ source ./create-alb.sh
Ensure the ALB, Target Group and Listener were created successfully.
$ echo $ALB_ARN $TG_ARN $LISTENER_ARN
Do not close the terminal or the shell. You will need the environment variables for running the commands below in the examples.
The following code examples demonstrate these behavioral patterns.
Tom Thumb is a video thumbnail generator task. It is implemented following the Container-on-Demand pattern.
In typical usage, a user uploads a video file to an S3 bucket. A trigger is set on the S3 bucket to notify a Lambda function in the event of a file upload to the video folder in the bucket. The Lambda is deployed with a Python code to extract the name of the video file from the Lambda notification event and invoke a Fargate task. The Fargate task consists of one container that uses FFmpeg application to decode the video and freeze an image at a given position in the video. The frozen image is written to a pre-configured folder in an S3 bucket.
In the same shell that you used to run the prerequisites, run the following commands.
Create a repository in ECR for storing the Tom-Thumb container image
$ source ./create-tom-thumb-repository.sh
If the repository already exists, you will get an error message. This is expected. Make sure that the variable ECR_REPO_URI is set
$ echo $ECR_REPO_URI
Build a Docker image and push to ECR repository
$ ./push-to-ecr.sh
Ensure the latest image was pushed to the ECR Repository.
Create the tom-thumb log group
$ ./create-tom-thumb-log-group.sh
This will create a log group called /ecs/tom-thumb-service
Create the tom-thumb cluster in ECS
$ ./create-tom-thumb-cluster.sh
This will create an ECS cluster called tom-thumb-cluster.
Generate tom-thumb task definition from the template by passing an URL for a sample video and the duration in the video where you want the frame captured for the thumbnail.
$ ./generate-tom-thumb-task-definition.sh https://s3.amazonaws.com/your-bucket-name/raw/samplevideo.mp4 10
This will create a temp directory and write the register-tom-thumb-task-definition.json file. Inspect this file and notice that the task contains one container and it uses the my-ecs-tasks-role you created earlier to run the Fargate task.
Referring to the ECS documentation you can see that the execution role is the IAM role that executes ECS actions such as pulling the image and storing the application logs in cloudwatch. On the other hand, the TaskRole is the IAM role used by the task itself. For example, if your container wants to call other AWS services like S3, Lambda, etc, it uses the permissions from the TaskRole to perform those actions. You need the TaskRole to avoid storing the access keys in a config file on the container instance.
Register the tom-thumb task definition in ECS and verify it has been created in the Task Definition section of ECS.
$ ./register-tom-thumb-task.sh
Generate the parameters for running the task as follows. This will generate a file run-tom-thumb-task.json in the temp directory.
$ ./generate-run-tom-thumb-task.sh https://s3.amazonaws.com/your-bucket/raw/samplevideo.mp4 12 your-output-bucket
Notice that there is a section for overrides. You can make changes to this file if you want to change the parameters after the fact that the task has been registered.
Verify the task runs and generates the thumbnail as desired. $ ./run-tom-thumb-task.sh Go to the tom-thumb-cluster and verify that the task is running and the thumbnail was generated.
Create a Lambda to automatically trigger the Fargate Task when a video file lands in the desired bucket.
$ cd lambda
Identify a bucket that will notify the lambda when a video file is uploaded. Note down its ARN and set the S3_BUCKET_ARN variable. $ EXPORT S3_BUCKET_ARN=arn:aws:s3:::your-bucket-name
Create the policies and roles required for the lambda to invoke the Fargate task.
$ source ./create-lambda-role.sh
$ echo $LAMBDA_ROLE_ARN
This will create a new role called my-run-task-lambda-role. Verify that the role is created through the IAM section of the AWS console.
Create the log group required for the lambda to post logs to CloudWatch
$ ./create-task-runner-log-group.sh
Package the python code that has the function that will be triggered through the Lambda when a video file is uploaded. The following script will create a zip file with the Python code.
$ ./package-lambda.sh
Deploy the zip file with the Lambda function on AWS. If the function already exists, it will be updated. This script also adds the permission for the Lambda to be invoked when a file is uploaded to the S3 bucket mentioned in the S3_BUCKET_ARN variable.
$ ./create-lambda.sh
Verify the Lambda task-runner was created through the console and the following environment variables are set right for the following:
- SUBNET1
- SUBNET2
- SECURITYGROUP
Additionally, verify that the Lambda permission Note: An update to the function does not update the environment variables.
-
Create a folder called 'video', 'thumbnail' and 'raw' in the S3 bucket that you chose for this project. The Bucket ARN for this should match the S3_BUCKET_ARN variable you set earlier.
-
In the Console go to the Advanced Settings in the Properties tab of the S3 bucket and create a notification event to trigger the task-runner lambda that was created earlier when a file is dropped into a particular folder in your S3 bucket.
- Upload a video file in the 'video' folder of the bucket and verify a thumbnail is created in the 'thumbnail' folder. It will take around a minute for the process to complete depending upon the size of the video file.
Bean Counter is a coin counter service. It will analyze an image of coins and return the total value of the coins in the image. It works only on US Mint issued coined and does not recognize any denomination above a quarter dollar coin. It also assumes that the picture contains a quarter. The quarter is used to calibrate the size of the coins. It is implemented following the Scaling-Container pattern.
In typical usage, a user navigates to the URL of the ALB on the browser and enters the URL for the service along with the location of the image file containing the picture of the coins. The Bean-Counter service then invokes the Fargate Task and returns the response to the browser.
In the same shell that you used to run the prerequisites, run the following commands.
Create a repository in ECR for storing the Tom-Thumb container image
$ source ./create-bean-counter-repository.sh
If the repository already exists, you will get an error message. This is expected. Make sure that the variable ECR_REPO_URI is set
$ echo $ECR_REPO_URI
Build a Docker image and push to ECR repository
$ ./push-to-ecr.sh
Ensure the latest image was pushed to the ECR Repository.
Create the bean-counter log group
$ ./create-bean-counter-log-group.sh
This will create a log group called /ecs/bean-counter-service
Create the bean-counter cluster in ECS
$ ./create-bean-counter-cluster.sh
This will create an ECS cluster called tom-thumb-cluster.
Generate a bean-counter task definition from the template.
$ ./generate-bean-counter-task-definition.sh
This will create a temp directory and write the register-bean-counter-task-definition.json file. Inspect this file and notice that the task contains one container and it uses the my-ecs-tasks-role you created earlier to run the Fargate task.
Register the bean-counter task definition in ECS and verify it has been created in the Task Definition section of ECS.
$ ./register-bean-counter-task.sh
Generate a bean-counter service definition from the template.
$ ./generate-bean-counter-service-definition.sh
This will create a temp directory and write the create-bean-counter-service-definition.json file. Inspect this file and notice that it contains the target group for the service under the load balancers section. This ties the load balancer to the service. Also, notice the desiredCount variable set to 2.
Create the bean-counter service from the service definition file generated in the previous step. $ ./create-bean-counter-service.sh
Verify that the service has been created and two tasks are being provisioned for the service.
Retrieve the DNS name of the application load balancer. Cut & paste the DNS in the browser.
$ export DNS=$(aws elbv2 describe-load-balancers | jq '.LoadBalancers[] | if .LoadBalancerName == "My-Fargate-ALB" then .DNSName else null end' | grep -v null | sed "s/\"//g")
$ echo $DNS
My-Fargate-ALB-xxxxxxx.us-east-1.elb.amazonaws.com
Set a target scaling policy for the service such that the desired count of the service is set to 2 and can increase to 4 on demand. The auto-scaling-policy.json specifies that when the combined load on the service breaches 75% the service should scale-out. A cool-out period of 60 seconds is also specified so that the service doesn't thrash around.
$ ./set-scaling-policy.sh
Use Apache Bench to hit the server $100,000 times with 100 concurrent threads with a timeout of 120 seconds to see the service scale out. You will have to wait for the cooling period to see the scaling out. Scaling in will take 15 minutes after scale out. Verify this on the ECS console.
$ ./test-scaling.sh
Following is the output of running Apache Bench:
In the following picture, you can see that Fargate has scaled-out as a result of the load.
The following is the examples of the application in action
Each application is unique and solving different needs based on business requirements. If the task of infrastructure management is too onerous and/or if you only want to pay for your computing time, then Fargate may be the right choice for you.
On the other hand, if you need greater control of the network resources or have large container workloads that warrant maintaining a cluster of servers to run ECS or EKS, then stick with the latter.
Fargate can be used with any type of containerized application. However, this doesn’t mean that you will get the same benefit in every scenario. Fargate would be most beneficial for projects that need to reduce the time from ideation to realization such as proofs-of-concept and well-designed, decoupled, micro service-based architectures deployed in production environments.
Applications can consist of a mix of Fargate & Lambda to exploit the Serverless model.
Use Lambdas for small & tight services with low memory (<3GB) and small request-response cycles (<15 mins).
Use containers deployed on Fargate for:
- Existing legacy services that cannot be trivially refactored,
- Applications are written in languages not supported by Lambda,
- Need to use large libraries that cannot fit into a Lambda profile (Quantlib, Scikit, etc),
- Where you need more control over networking, compute horsepower or memory
- Use cases that require a long in-process runtime.
- When you require greater control of your EC2 instances to support networking, COTS applications that require broader customization options, then use ECS without Fargate.
- When you want fast request-response cycle time then Lambda may be a good choice. This is especially true if you are using large container images written with object-heavy languages such as Java/Scala that requires significant initiation time to start the JVM and bootstrap objects.
- By breaking down your application into smaller modules that fit into Lambdas and using Layers and Step Functions you can reap the benefits of Serverless architectures while paying only for your compute time.