How do I change root volume size of AWS Batch at runtime - amazon-ec2

I have an application which makes requests to AWS to start batch jobs. The jobs vary, and therefore resource requirements change for each job.
It is clear how to change CPUs and memory, however I cannot figure out how to specify root volume size, or if it is even possible
Here is an example of the code I am running:
import boto3
client = boto3.client('batch')
JOB_QUEUE = "job-queue"
JOB_DEFINITION="job-definition"
container_overrides = {
'vcpus': 1,
'memory': 1024,
'command': ['echo', 'Hello World'],
# 'volume_size': 50 # this is not valid
'environment': [ # this just creates env variables
{
'name': 'volume_size',
'value': '50'
}
]
}
response = client.submit_job(
jobName="volume-size-test",
jobQueue=JOB_QUEUE,
jobDefinition=JOB_DEFINITION,
containerOverrides=container_overrides)
My question is similar to this However I am specifically asking if this is possible at runtime. I can change the launch template however that doesn't solve the issue of being able to specify required resources when making the request. Unless the solution is to create multiple launch templates and then select that at run time, though that seems unnecessarily complicated

You can use AWS Elastic File System for this. EFS volumes can be mounted to the containers created for your job definition. EFS doesn't require you to provide a specific volume size because it automatically grows and shrinks depending on the usage.
You need to specify an Amazon EFS file system in your job definition through the efsVolumeConfiguration property
{
"containerProperties": [
{
"image": "amazonlinux:2",
"command": [
"ls",
"-la",
"/mount/efs"
],
"mountPoints": [
{
"sourceVolume": "myEfsVolume",
"containerPath": "/mount/efs",
"readOnly": true
}
],
"volumes": [
{
"name": "myEfsVolume",
"efsVolumeConfiguration": {
"fileSystemId": "fs-12345678",
"rootDirectory": "/path/to/my/data",
"transitEncryption": "ENABLED",
"transitEncryptionPort": integer,
"authorizationConfig": {
"accessPointId": "fsap-1234567890abcdef1",
"iam": "ENABLED"
}
}
}
]
}
]
}
Reference: https://docs.aws.amazon.com/batch/latest/userguide/efs-volumes.html

Related

How to execute a lambda function which copies objects from one S3 bucket to another via Step functions?

I was able to perform the task to copy data from the source bucket to a destination bucket using lambda function, however, I got an error while executing the lambda function in Step functions. Below are the steps I followed from the scratch.
Region chosen is ap-south-1
Created 2 buckets. Source bucket: start.bucket & Destination bucket: final.bucket
Created a Lambda function with the following information:
Author from scratch
Function name: CopyCopy
Runtime: Python 3.8
Had created a lambda IAM role: LambdaCopy and gave the necessary policies(S3 full access and Step functions full access) and attached it to the function.
Added a trigger and chose:
S3
Bucket: start.bucket
Event type: All object create events
I found a python code in GeeksforGeeks and applied in the code section.
import json
import boto3
s3_client=boto3.client('s3')
# lambda function to copy file from 1 s3 to another s3
def lambda_handler(event, context):
#specify source bucket
source_bucket_name=event['Records'][0]['s3']['bucket']['name']
#get object that has been uploaded
file_name=event['Records'][0]['s3']['object']['key']
#specify destination bucket
destination_bucket_name='final.bucket'
#specify from where file needs to be copied
copy_object={'Bucket':source_bucket_name,'Key':file_name}
#write copy statement
s3_client.copy_object(CopySource=copy_object,Bucket=destination_bucket_name,Key=file_name)
return {
'statusCode': 3000,
'body': json.dumps('File has been Successfully Copied')
}
- I deployed the code and it worked. Uploaded a csv file in start.bucket and it was copied to final.bucket.
Then, I created a State machine in Step functions with the following information:
Design your workflow visually
Type: Standard
Dragged the AWS Lambda between the Start and End state.
Changed its name to LambdaCopy
Integration type: Optimized
Under API Parameters, Function name(I chose the Lambda function that I had created): CopyCopy:$LATEST
Next State: End
Next and then again Next
State machine name: StepLambdaCopy
IAM Role: Create a new role (Later gave it S3 full access, Lambdafullaccess and Step function fullaccess too).
It showed error when I tried to execute it.
I know I am missing out on something. I would really appreciate the help.
Step functions now allows you to utilize the S3 Copy SDK directly completely bypassing the need for Lambda and boto3. Take a look here for more information.
So in your case you would need a simple task that looks like this:
{
"Comment": "A description of my state machine",
"StartAt": "CopyObject",
"States": {
"CopyObject": {
"Type": "Task",
"End": true,
"Parameters": {
"ServerSideEncryption": "AES256",
"Bucket.$": "$.destination_bucket",
"CopySource.$": "$.source_path",
"Key.$": "$.key"
},
"Resource": "arn:aws:states:::aws-sdk:s3:copyObject"
}
}
}
Then your input state will need to feed in the parameters you would normally use to copy a file with the copy command. Source Path, Destination Bucket, and Object Key exactly the same as the boto3 command.
Note: Your state machine IAM role will need direct S3 permissions and will need to be in the same region as the buckets.
It's always confusing what exactly you have to pass as parameters. Here is a template I use to copy the output of an Athena query. You can adapt it to your needs:
"athena_score": {
"Type": "Task",
"Resource": "arn:aws:states:::athena:startQueryExecution.sync",
"Parameters": {
"QueryExecutionContext": {
"Catalog": "${AthenaCatalog}",
"Database": "${AthenaDatabase}"
},
"QueryString": "SELECT ...",
"WorkGroup": "${AthenaWorkGroup}",
"ResultConfiguration": {
"OutputLocation": "s3://${BucketName}/${OutputPath}"
}
},
"TimeoutSeconds": 300,
"ResultPath": "$.responseBody",
"Next": "copy_csv"
},
"copy_csv": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:s3:copyObject",
"Parameters": {
"Bucket": "${BucketName}",
"CopySource.$": "States.Format('/${BucketName}/${OutputPath}/{}.csv', $.responseBody.QueryExecution.QueryExecutionId)",
"Key": "${OutputPath}/latest.csv"
},
"ResultPath": "$.responseBody.CopyObject",
"Ent": "true"
}

Generating number of thumbnails depending on video size using AWS MediaConvert

After reading this article I get the sense that AWS media convert job template cannot be re-used to generate thumbnails of arbitrary video size. The article assumes that we know the size/duration of video uploaded upfront hence the number of thumbnails we desire.
What I am looking for is to generate a random number of thumbnails based of video size (e.g large number of thumbnails for large video and small number of thumbnails for small video). I approached this solution using lambda trigger and ffmpeg lambda layer but lambda function timeouts (15 minutes max) for videos larger than 150MB (since it takes time to read the video from the s3 bucket).
What are my options to process large number of video , generate a variable number of thumbnails, merge those thumbs to generate a sprite?
I tried lambda trigger with ffmpeg/ffprob to generate sprite, but that has timeout issue. Now I have setup a cloud watch event rule to trigger lambda function on mediaconvert job status change( completed)
and merge the thumbs to generate sprite, which seems much lighter but I need an arbitrary number of thumbs.
You could switch from using ffprobe to MediaInfo [1]. This can get you the duration and file size of an input file. From there you can use the calculations from the article and use MediaConvert to create your sprite images. After the job completes you can use the COMPLETE CloudWatch Event to trigger a postprocessing workflow that can create the sprite manifest. By default the COMPLETE details section will include the output path to the last JPEG frame capture from your MediaConvert output [2].
As a pre-processing step you can gather the duration of the file as well as the size. If you only require the size of the file, then you can use the S3 SDK to get the content-length of the file (calls HEAD to the object) and calculate from there.
s3 = boto3.client('s3')
response = s3.head_object(Bucket='bucket', Key='keyname')
size = response['ContentLength']
print(size)
You can also use parameter overrides in job templates. For example you can create a job that reference a template but you can specify settings that override those settings with you call the CreateJob API.
The following is for presets however the same concept applies to Job Templates.
Using the following HLS System Presets
System-Avc_16x9_720p_29_97fps_3500kbps
System-Avc_16x9_360p_29_97fps_1200kbps
System-Avc_16x9_270p_14_99fps_400kbps
JSON Payload changes the Audio Selector Source Name for Output 2 (System-Avc_16x9_360p_29_97fps_1200kbps) and Output 3 (System-Avc_16x9_270p_14_99fps_400kbps)
{
"Queue": "arn:aws:mediaconvert:us-west-2:111122223333:queues/Default",
"UserMetadata": {},
"Role": "arn:aws:iam::111122223333:role/EMFRoleSPNames",
"Settings": {
"OutputGroups": [
{
"Name": "Apple HLS",
"Outputs": [
{
"NameModifier": "_1",
"Preset": "System-Avc_16x9_720p_29_97fps_3500kbps"
},
{
"NameModifier": "_2",
"Preset": "System-Avc_16x9_360p_29_97fps_1200kbps",
"AudioDescriptions": [
{
"AudioSourceName": "Audio Selector 2"
}
]
},
{
"NameModifier": "_3",
"Preset": "System-Avc_16x9_270p_14_99fps_400kbps",
"AudioDescriptions": [
{
"AudioSourceName": "Audio Selector 2"
}
]
}
],
"OutputGroupSettings": {
"Type": "HLS_GROUP_SETTINGS",
"HlsGroupSettings": {
"ManifestDurationFormat": "INTEGER",
"SegmentLength": 10,
"TimedMetadataId3Period": 10,
"CaptionLanguageSetting": "OMIT",
"Destination": "s3://myawsbucket/out/master",
"TimedMetadataId3Frame": "PRIV",
"CodecSpecification": "RFC_4281",
"OutputSelection": "MANIFESTS_AND_SEGMENTS",
"ProgramDateTimePeriod": 600,
"MinSegmentLength": 0,
"DirectoryStructure": "SINGLE_DIRECTORY",
"ProgramDateTime": "EXCLUDE",
"SegmentControl": "SEGMENTED_FILES",
"ManifestCompression": "NONE",
"ClientCache": "ENABLED",
"StreamInfResolution": "INCLUDE"
}
}
}
],
"Inputs": [
{
"AudioSelectors": {
"Audio Selector 1": {
"Offset": 0,
"DefaultSelection": "DEFAULT",
"ProgramSelection": 1,
"SelectorType": "TRACK",
"Tracks": [
1
]
},
"Audio Selector 2": {
"Offset": 0,
"DefaultSelection": "NOT_DEFAULT",
"ProgramSelection": 1,
"SelectorType": "TRACK",
"Tracks": [
2
]
}
},
"VideoSelector": {
"ColorSpace": "FOLLOW"
},
"FilterEnable": "AUTO",
"PsiControl": "USE_PSI",
"FilterStrength": 0,
"DeblockFilter": "DISABLED",
"DenoiseFilter": "DISABLED",
"TimecodeSource": "EMBEDDED",
"FileInput": "s3://myawsbucket/input/test.mp4"
}
],
"TimecodeConfig": {
"Source": "EMBEDDED"
}
}
}
== Resources ==
[1] https://aws.amazon.com/blogs/media/running-mediainfo-as-an-aws-lambda-function/
[2] https://docs.aws.amazon.com/mediaconvert/latest/ug/file-group-with-frame-capture-output.html

ECS provisions multiple servers but never runs the task

I have an ECS cluster where the capacity provider is an auto-scaling group of ec2 servers with a Target Tracking scaling policy and Managed Scaling turned on.
The min capacity of the cluster is 0, the max is 100. The instance types it's employing are c5.12xlarge.
I have a task that uses 4 x vCPUs and 4 GiB memory. When I run a single instance of that task on that cluster, ECS very slowly auto scales the group to > 1 servers (usually 2 to begin with, and then eventually adds a third one - I've tried multiple times), but never actually runs the task and stays in a state of PROVISIONING for ages and ages before I get annoyed and stop the task.
Here is a redacted copy of my task description:
{
"family": "my-task",
"taskRoleArn": "arn:aws:iam::999999999999:role/My-IAM-Role",
"executionRoleArn": "arn:aws:iam::999999999999:role/ecsTaskExecutionRole",
"cpu": "4 vCPU",
"memory": 4096,
"containerDefinitions": [
{
"name": "my-task",
"image": "999999999999.dkr.ecr.us-east-1.amazonaws.com/my-container:latest",
"essential": true,
"portMappings": [
{
"containerPort": 12012,
"hostPort": 12012,
"protocol": "tcp"
}
],
"mountPoints": [
{
"sourceVolume": "myEfsVolume",
"containerPath": "/mnt/efs",
"readOnly": false
}
]
}
],
"volumes": [
{
"name": "myEfsVolume",
"efsVolumeConfiguration": {
"fileSystemId": "fs-1234567",
"transitEncryption": "ENABLED",
"authorizationConfig": {
"iam": "ENABLED"
}
}
}
],
"requiresCompatibilities": [
"EC2"
],
"tags": [
...
]
}
My questions are:
Why, if I'm running a single task that would easily run on once instance, is it scaling the group to at least 2 servers?
Why does it never just deploy and run my task?
Where can I look to see what the hell is going on with it (logs, etc)?
So it turns out that, even if you set an ASG to be the capacity provider for an ECS cluster, if you haven't set the User Data up in the launch configuration for that ASG to have something like the following:
#!/bin/bash
echo ECS_CLUSTER=my-cluster-name >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config;
then it will never make a single instance available to your cluster. ECS will respond by continuing to increase the desired capacity of the ASG.
Personally I feel like this is something that ECS should ensure happens without your knowledge. Maybe there's a good reason why not.

Schedule AWS-Lambda with Java and CloudWatch Triggers

I am new to AWS and AWS-Lambdas. I have to create a lambda function to run a cron job in every 10 minutes. I am planning to add a Cloudwatch trigger to trigger the same in every 10 minutes but without any event. I looked up on the internet and found that some event needs to be there to get it running.
I need to get some clarity and leads on below 2 points:
Can I schedule a job using AWS-Lambda with cloudwatch triggering the same in span of 10 minutes without any events.
How do one need to make it interact with MySQL databases that have been hosted on AWS.
I have my application built on SpringBoot running on multiple instances with a shared database (single source of truth). I have devised everything stated above using Spring's in-built scheduler and proper synchronisation on DB level using locks but because of the distributed nature of instances, I have been advised to do the same using lambdas.
You need to pass ScheduledEvent object to the handleRequest() of the lambda.
handleRequest(ScheduledEvent event, Contex context)
Configure a cron job that runs every 10 minutes in your cloudwatch template (if using cloudformation). This will make sure to trigger your lambda after every 10 min.
Make sure to add below mentioned dependency to your pom.
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>2.2.5</version>
</dependency>
Method 2:
You can specify something like this in your cloudformation template. This will not require any argument to be passed to your handler(), incase you do not require any event related information. This will automatically trigger your lambda as per your cron job.
"ScheduledRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "ScheduledRule",
"ScheduleExpression": {
"Fn::Join": [
"",
[
"cron(",
{
"Ref": "ScheduleCronExpression"
},
")"
]
]
},
"State": "ENABLED",
"Targets": [
{
"Arn": {
"Fn::GetAtt": [
"LAMBDANAME",
"Arn"
]
},
"Id": "TargetFunctionV1"
}
]
}
},
"PermissionForEventsToInvokeLambdaFunction": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Ref": "NAME"
},
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": {
"Fn::GetAtt": [
"ScheduledRule",
"Arn"
]
}
}
}
}
If you want to run a cronjob from cloudwatch event is the only option.
If you don't want to use cloudwatch events then go ahead with EC2 instance. But EC2 will cost you more than the cloudwatch event.
Note: Cloudwatch events rule steup is just like defining cronjob in crontab in any linux system, nothing much. In linux serevr you will define everything as a RAW one but here its just an UI based one.

Jelastic API environment create trigger data

The jelastic api environment.Trigger.AddTrigger takes "data" as parameter, but i cannot find what are all the different possible variables that i can use. Jelastic API docs just says "data : string , information about trigger". Is this "data" documented on somewhere else?
There are some JPS javascript/java examples that i have found, that are pointing me to the right direction, but more information would be nice to have.
https://github.com/jelastic-jps/magento-cluster/blob/master/scripts/addTriggers.js
https://docs.cloudscripting.com/0.99/examples/horizontal-scaling/
https://github.com/jelastic-jps/basic-examples/blob/master/automatic-horizontal-scaling/scripts/enableAutoScaling.js
The environment.Trigger.AddTrigger method requires a set of the parameters:
name - name of a notification trigger
nodeGroup - target node group (you can apply trigger to any node
group within the chosen environment)
period - load period for nodes
condition - rules for monitoring resources
type - comparison sign, the available values are GREATER and LESS
value - percentage of a resource that is monitored
resourceType - types of resources that are monitored by a trigger,
namely CPU, Memory (RAM), Network, Disk I/O, and Disk IOPS
valueType - measurement value. Here, PERCENTAGES is the only possible
measurement value. The available range is from 0 up to 100.
actions - object to describe a trigger action
type - trigger action, the available values are NOTIFY, ADD_NODE, and
REMOVE_NODE
customData:
notify - alert notification sent to a user via email
The following code shows how a new trigger can be created:
{
"type": "update",
"name": "AddTrigger",
"onInstall": {
"environment.trigger.AddTrigger": {
"data": {
"name": "new alert",
"nodeGroup": "sqldb",
"period": "10",
"condition": {
"type": "GREATER",
"value": "55",
"resourceType": "MEM",
"valueType": "PERCENTAGES"
},
"actions": [
{
"type": "NOTIFY",
"customData": {
"notify": false
}
}
]
}
}
}
}
More information about events and other CloudScripting language features you can find here.

Resources