Disable scheduling on second instance of same project on AWS - laravel

I have 2 instances of the same deployment/project on AWS Elastic Beanstalk.
Both contain a Laravel project which contains scheduling code which runs various commands which can be found in the schedule method/function of the Kernel.php class within 'app/Console' - the problem I have is that if a command runs from one instance then it will also run the command from the second instance which is not what I want to happen.
What I would like to happen is that the commands get run from only one instance and not the other. How do I achieve this in the easiest way possible?
Is there a Laravel package which could help me achieve this?

From Laravel 5.6:
Laravel provides a onOneServer method which you can use if your applications share a single cache server. You could use something like ElastiCache to host Redis or Memcached and use it as your cache server for both of your application instances. Then you would be able to use the onOneServer method like this:
$schedule->command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
For older versions of Laravel:
You could use the jdavidbakr/multi-server-event package. Once you have it set up you should be able to use it like:
$schedule->command('inspire')
->daily()
->withoutOverlappingMultiServer();

I had the same issue to run some cronjobs (nothing related to Laravel) and I found a nice solution (don't remember where I found it)
What I do is check if the instance running the code is the first instance on the Auto Scaling Group, if it's the first then I execute the command otherwise just exit.
This is the way it's implemented:
#!/bin/bash
INSTANCE_ID=`curl http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null`
REGION=`curl -s http://169.254.169.254/latest/dynamic/instance-identity/document 2>/dev/null | jq -r .region`
# Find the Auto Scaling Group name from the Elastic Beanstalk environment
ASG=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" \
--region $REGION --output json | jq -r '.[][] | select(.Key=="aws:autoscaling:groupName") | .Value'`
# Find the first instance in the Auto Scaling Group
FIRST=`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG \
--region $REGION --output json | \
jq -r '.AutoScalingGroups[].Instances[] | select(.LifecycleState=="InService") | .InstanceId' | sort | head -1`
# If the instance ids are the same exit 0
[ "$FIRST" = "$INSTANCE_ID" ]
Try implementing those calls using PHP and it should work.

Related

How to get only the priority from the entire JSON object in Bash Script?

I am trying to get the highest priority used for the application gateway rules. I can get the rules using the below command:
az network application-gateway rule list -g $resource_group_name --gateway-name $app_gateway_name
How can we retrieve the priority from that data in a bash script?
try to use jq tool, it requires installation, but it allows to manipulate JSON structures and even modify it.
I see that 'az' command also support JMESpath. So you can filter exactly what you need.
--query JMESPath query string. See http://jmespath.org/ for more information and examples.
I didn't know what is output structure. So as example command can be look like this:
az network application-gateway rule list \
-g $resource_group_name \
--gateway-name $app_gateway_name \
--query '[].priority | sort(#) | [0]'

How to list public IPs of all compute instances in OCI?

I need to get the public IPs of all instances of my OCI tenant.
Here i saw a python scripts to do this from OCI Console Cloud Shell : https://medium.com/oracledevs/writing-python-scripts-to-run-from-the-oci-console-cloud-shell-a0be1091384c
But I want to create a bash script, that uses OCI CLI commands to fetch the required data.
How can I achieve this using OCI CLI commands?
OCI CLI structured-search and query feature can be used to fetch the OCID of instances, and instance command can be used fetch the instance details.
The output would be in json format by default.
You can use jq to filter needed data from the output json and create an array with it.
(OCI tool supports JMESPath queries)
Here is the snippet from bash script that uses OCI CLI commands to get public IPs of all compute instances in the compartment :
Pre-requisites: OCI CLI should be installed and configured properly to authenticate with the correct tenant and compartment
# Fetch the OCID of all the running instances in OCI and store to an array
instance_ocids=$(oci search resource structured-search --query-text "QUERY instance resources where lifeCycleState='RUNNING'" --query 'data.items[*].identifier' --raw-output | jq -r '.[]' )
# Iterate through the array to fetch details of each instance one by one
for val in ${instance_ocids[#]} ; do
echo $val
# Get name of the instance
instance_name=$(oci compute instance get --instance-id $val --raw-output --query 'data."display-name"')
echo $instance_name
# Get Public Ip of the instance
public_ip=$(oci compute instance list-vnics --instance-id $val --raw-output --query 'data[0]."public-ip"')
echo $public_ip
done
References :
https://blogs.oracle.com/cloud-infrastructure/post/exploring-the-search-and-query-features-of-oracle-cloud-infrastructure-command-line-interface
https://docs.oracle.com/en-us/iaas/tools/oci-cli/2.9.9/oci_cli_docs/cmdref/compute/instance.html

How to find out which ECS cluster is associated to an ALB

We run an ECS cluster behind an ELB (ALB, to be specific).
I have a process that allows me to find out which ECS cluster is associated with the ALB by querying the ALB and tracing the results back through the target group and then instances:
Here is the bash script:
ELB_NAME=$(aws route53 list-resource-record-sets --hosted-zone-id <Zone-ID> | jq -r --arg URL "$URL"'.ResourceRecordSets[]|select(.Name==$URL)|.AliasTarget.DNSName')
ELB_NAME=$(echo $ELB_NAME | cut -f 2- -d "." | rev | cut -f 2- -d "." | rev)
ELB_ARN=$(aws elbv2 describe-load-balancers | jq -r --arg ELB_NAME "$ELB_NAME" '.LoadBalancers[]|select((.DNSName|ascii_downcase)==$ELB_NAME)|.LoadBalancerArn')
TG_ARNS=$(aws elbv2 describe-target-groups | jq -r --arg ELB_ARN "$ELB_ARN" '.TargetGroups[]|select(.LoadBalancerArns[]==$ELB_ARN)|.TG_ARN=$(echo $TG_ARNS | cut -f 1 -d " ")
INSTANCE_ID=$(aws elbv2 describe-target-health --target-group-arn $TG_ARN | jq -r '.TargetHealthDescriptions[].Target.Id' | head -n 1)
CLUSTER=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID | jq -r '.Reservations[].Instances[].Tags[]|select(.Key=="aws:cloudformation:stack-name")|.Value' | cut -f 2 -d "-")
The problem I have is that when there are no running instances associated with the ECS cluster, I can no longer query them for the the tag that returns the Cloudformation stack name, the request for the targets from the target group is empty.
How can I use the AWS API so that I can determine which ECS cluster the ALB would target if it had running instances?
It's not really clear what you're asking for, or indeed the purpose you are trying to achieve, but the following should set you on the right track.
An ECS "cluster" is really just an Amazon service, when you create a cluster nothing is really provisioned. You can think of an empty cluster as a record or a placeholder in the ECS service.
In order to do anything with a cluster, it needs instances. When you boot an EC2 machine from a supported AMI, appropriate IAM role and the cluster name written to a config file, the instance will join the cluster. (If you create a cluster via the AWS console, a CloudFormation template is created that handles the provisioning and orchestration of these steps.) The ECS cluster management can then schedule tasks and services onto that instance as you have defined in the ECS service.
Without any instances, there can be no listening containers, therefore there can be no target groups in your ALB that route to anything. So it is not possible to get from the ELB to the cluster... as you have asked when there are no running instances.
You might find the following commands are a better way of determining whether or not you have a running cluster.
First, use the list-clusters command to show which clusters are available:
aws ecs list-clusters
{
"clusterArns": [
"arn:aws:ecs:eu-west-1:XXXXXXXXX:cluster/your_cluster"
]
}
Then use the output from that to show if there are any EC2 instances registered to the cluster:
aws ecs describe-clusters --clusters your_cluster
{
"clusters": [
{
"status": "ACTIVE",
"statistics": [],
"clusterName": "your_cluster",
"registeredContainerInstancesCount": 1,
"pendingTasksCount": 0,
"runningTasksCount": 0,
"activeServicesCount": 0,
"clusterArn": "arn:aws:ecs:eu-west-1:XXXXXXXXX:cluster/your_cluster"
}
],
"failures": []
}
Note the registeredContainerInstancesCount property shows the number of running instances. I assume you have your ECS services set to register tasks (containers) with the ALB, so when the count is greater than 0, this will be possible.
So, querying that property should tell you if your cluster is "on" or not:
if [[ $(aws ecs describe-clusters --clusters your_cluster | jq -r '.clusters[].registeredContainerInstancesCount') -gt 0 ]] ; then
echo "cluster is on"
else
echo "cluster is off"
fi

How do I Copy the same AMI to multiple regions simultaneously?

I am trying to find a way to perform a simultaneously copy of a AMI to all other regions.
I have search near and far but beside seeing on a blog post that it can be done, I haven't found a way using aws cli ...
https://aws.amazon.com/blogs/aws/ec2-ami-copy-between-regions/
Currently I have written a bash script to do so, but I would like to find a better, easier way to do so
I have 8 AMI's that need to be passed to all regions.
using an array-
declare -a DEST=('us-east-1' ...2....3)
aws copy-image --source-region $SRC --region ${DESTx[#]} --source-ami-id $ami
Do you guys have any other suggestion?
Thanks.
you can make a single line bash, specially useful if in future there are new regions:
aws ec2 describe-regions
--output text |\
cut -f 3 | \
xargs -I {} aws copy-image
--source-region $SRC
--region {}
--source-ami-id $ami
basically it goes like this:
aws ec2 describe-regions --output text returns the list of all available regions for ec2, its a 3 columns table ("REGIONS", endpoint, region-name)
cut -f 3 takes the 3rd column of the previous table (read as list)
keep the current region from previous argument (xargs) into {} so you can send it to the region parameter of the copy-image command

Adding a tag to an EC2 Snapshot using ec2-api-tools

I've created a snapshot using ec2-api-tools of a volume within my AWS EC2 account. Currently I have:
>> ec2addsnap vol-xxxxxxxx -d 'My-first-Snapshot'
SNAPSHOT snap-12345678 vol-xxxxxxxx pending 2013-01-30T17:09:35+0000 086018780037 8 My-first-Snapshot
What I want to do is add a --tag Name='Name Tag' to this newly created snapshot from the snap-12345678 id in the response.
This works>
>> ec2addtag snap-12345678 --tag Name='Name Tag'
How can I automate this? I've started writing a simple shell script - but I'm not sure how I would query the response from the initial ec2addsnap to grab the newly created snapshot id in order to apply ec2addtag? Cheers (Thought I was posting this in Serverfault - my apologies)
I managed to solve this via the use of awk. My Bash Script =
today=$(date +"%d-%m-%Y")
tagname=$2
ec2addsnap vol-$1 -d $2'-'$today;
ec2dsnap | grep $2'-'$today | awk -v tagname=$tagname '{print "Adding Tag too: " $2}; system("ec2addtag "$2" --tag Name=\""tagname"\"")';

Resources