Tag: DZone Cloud Zone

Docker: Ready! Steady! Deploy!

Docker: Ready! Steady! Deploy!

How often have you had to set up a server environment to deploy your application? Probably more often than you would like.

At best, you had a script performing all the operations automatically. At worst, you had to:

  • Install the D database of the x.x.x version;
  • Install the N web server of the x.x version;
  • and so on…

Over time, environmental management configured in this way becomes very resource-intensive. Any minor change in configuration means at least that:

  • every member of a development team should be aware of this change;
  • this change should be safely added to the production environment.

It is challenging to track changes and manage them without special tools. One way or another, problems with the configuration of environment dependencies arise. As development continues, it becomes more and more challenging to find and fix bottlenecks.

I have described what is called vendor lock-in. This phenomenon becomes quite a challenge in the development of applications, in particular, server-type ones.

In this article, we will consider one of the possible solutions — Docker. You will learn how to create, deploy, and run an application based on this tool.

ÐаÑÑинки по запÑоÑÑ Docker

Disclaimer: This is not a review of Docker. This is the first entry point for developers who plan to deploy Node.JS applications using Docker containers.

While developing one of our projects, my team faced a lack of detailed articles on the topic, and this greatly complicated our work. This post was written to simplify the pathways of colleague developers who will follow our footsteps.

What is Docker?

Simply put, Docker is an abstraction over LXC containers. This means that processes launched using Docker will see only themselves and their descendants. Such processes are called Docker containers.

Images ( docker image ) are used for creating abstractions based on such containers. It is also possible to configure and create containers based on Docker images.

There are thousands of ready-made Docker images with pre-installed databases, web servers, and other important elements. Another advantage of Docker is that it is a very economical (in terms of memory consumption) tool since it uses only the resources it needs.

Let’s Get Closer

We will not dwell on the installation. Over the past few releases, the process has been simplified to a few clicks/commands.

In this article, we will analyze the deployment of a Docker application using the example of a server-side Node.js app. Here is its primitive, source code:

// index

const http = require('http');

const server = http.createServer(function(req, res) {

res.write('hello world from Docker');

res.end();

});

server.listen(3000, function() {

console.log('server in docker container is started on port : 3000');

});

We have at least two ways to package the application in a Docker container:

  • create and run a container using an existing image and the command-line-interface tool;
  • create your own image based on a ready sample.

The second method is used more often.

To get started, download the official node.js image:

docker pull node

The “docker pull” command downloads a Docker image. After that, you can use the “docker run” command. This way you will create and run the container based on the downloaded image.

docker run -it -d –rm -v “$ PWD”: / app -w = / app -p 80: 3000 node node index.js

This command will launch the index.js file, map a 3000 port to an 80 port, and display the ID of the created container. Much better! But you will not go far with CLI only. Let’s create a Dockerfile for a server.

FROM node

WORKDIR /app

RUN cp . /app

CMD ["node", "index.js"]

This Dockerfile describes the parent image of the current version, as well as the directory for starting container commands and the command for copying files from the directory where the image assembly is being launched. The last line indicates which command will run in the created container.

Next, we need to build an image that we will deploy based on this Dockerfile: docker build -t username / helloworld-with-docker: 0.1.0.   This command creates a new image, marks it with username helloworld-with-docker   and creates a 0.1.0 tag.

Our container is ready. We can run it with the docker run  command. The vendor lock-in problem is solved. The launch of the application is no longer dependent on the environment. The code is delivered along with the Docker image. These two criteria allow us to deploy the application to any place where we can run Docker.

Deploy

The first part is not as tricky as the remaining steps.

After we have completed all the instructions above, the deployment process itself becomes a matter of technics and your development environment. We will consider two options for deploying Docker:

  • Manual deployment of Docker image;
  • Deployment using Travis-CI.

In each case, we will consider delivering the image to an independent environment, for example, a staging server.

Manual Deployment

This option should be chosen if you do not have a continuous integration environment. First, you need to upload the Docker image to a place accessible to the staging server. In our case, it is a DockerHub. It provides each user with a free private image repository and an unlimited number of public repositories.

Log in to access the DockerHub:

docker login -e [email protected] -u username -p userpass

Upload the image:

docker push username/helloworld-with-docker:0.1.0

Next, go to the staging server (Docker must be already preinstalled on it). To deploy the application on the server, we need to execute only one command:  docker run -d --rm -p 80: 3000 username / helloworld-with-docker: 0.1.0 . And that’s all!

Check the local register of images. If you don’t find your image, enter username helloworld-with-docker  to check the DockerHub registry. An image with the name you indicate must be in the register since we have already uploaded it there. Docker will download it, create a container on its basis, and launch your application in it.

From this moment, every time you need to update the version of your application, you can make a push with a new tag and just restart the container on the server.

P.S. This method is not recommended if Travis-CI is available.

Deployment Using Travis-CI

First, we should add DockerHub data to Travis-CI. They will be stored in environment variables.

travis encrypt [email protected]

travis encrypt DOCKER_USER=username

travis encrypt DOCKER_PASS=password

Then we should add the received keys to the .travis.yml file. We should also add a comment to each key in order to distinguish between them in the future.

env:

  global:

    - secure: "UkF2CHX0lUZ...VI/LE=" # DOCKER_EMAIL

    - secure: "Z3fdBNPt5hR...VI/LE=" # DOCKER_USER

    - secure: "F4XbD6WybHC...VI/LE=" # DOCKER_PASS

Next, we need to login and upload the image:

after_success:

  - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS

  - docker build -f Dockerfile -t username/hello-world-with-travis.

  - docker tag username/hello-world-with-travis 0.1.0

  - docker push username/hello-world-with-travis

Also, image delivery can be launched from Travis-CI in various ways:

  • manually;
  • via SSH connection;
  • online deploy services (Deploy Bot, deployhq);
  • AWS CLI;
  • Kubernetes;
  • Docker deployment tools.

Conclusion

We have considered two ways (automatic and via Travis-CI) of preparation and deployment of Docker using an example of a simple node.js server. The knowledge gained should greatly simplify your work. Thanks for your time!

from DZone Cloud Zone

Fault Tolerance And Redundancy For Cloud Computing

Fault Tolerance And Redundancy For Cloud Computing

Cloud computing is now the foundation of many business solutions for a number of significant business-critical reasons, one of which is the fact that cloud computing—and the cloud ecosystem as a whole—is a lot more reliable (and easier to maintain) than on-premise hardware implementations. Clusters of servers that form today’s best cloud environments are designed from the ground up to offer higher availability and reliability.

AWS, for example, when leveraged to its fullest capabilities, offers high availability. You also have the option to set up redundancies and take the reliability of your cloud environment to a whole other level. It is worth noting, however, that hardware failure is still a risk to mitigate, even with Amazon’s robust AWS ecosystem. This is where fault tolerance and redundancy are needed.

Fault Tolerance vs. Redundancy

Before we can discuss how a cloud environment can be made more reliable, we need to first take a closer look at the two approaches to do so: increasing fault tolerance and redundancy. The two are often seen as interchangeable or completely similar, but fault tolerance and redundancy actually address different things.

Redundancy is more for components or hardware. Adding multiple EC2 instances so that one can continue serving your microservice when the other powers down due to a hardware issue is an example of using redundancy to increase your solution’s availability. Servers, disks, and other components are made with multiple redundancies for better reliability as a whole.

Fault tolerance, on the other hand, focuses more on systems. The cloud computing networks, your S3 storage buckets, and even Amazon’s own services such as Elastic Load Balancing (ELB) are made to be fault-tolerant, but that doesn’t mean all AWS services and components are the same. You still need to treat services like EC2 seriously to increase availability.

AWS Isn’t Perfect

One big advantage of using AWS when it comes to availability and fault tolerance is the fact that you can leverage Amazon’s experience in handling disasters and recovering from them. Just like other cloud service providers, Amazon deals with outages caused by power problems, natural disasters, and human error all the time. They’ve gotten good at it, too.

Amazon’s 99.5% SLA is about as good as it gets. You can still expect several hours of downtime every year, but that’s not a bad standard at all. In fact, Amazon is leading the market with better redundancy and multiple layers of protection to maintain availability.

Nevertheless, it is worth noting that AWS isn’t perfect. Relying entirely on the robustness of AWS and its services isn’t the way to create a reliable and robust cloud ecosystem. You still need to configure the different instances correctly and use services to strengthen your system from the core. Fortunately, there are a number of things you can do.

Improving Reliability

Fault tolerance in cloud computing is a lot like the balance of your car. You can still drive the car—with limitations, of course—if one of the tires is punctured. A fault-tolerant system can do the same thing. Even when one or some of its components stop working due to an error, the system can still deliver its functions or services to a certain extent.

Designing a system for maximum fault tolerance isn’t always easy, but AWS offers a number of tools that can be used to boost reliability, starting with AMI. When you begin setting up the system by creating an AMI that works, you have the option to start a new instance using the same AMI should another fail.

Another way to add fault tolerance is by using EBS, or Elastic Block Storage. Problems associated with your drives running out of storage space can be effectively mitigated using EBS. The use of EBS also allows you to attach different EC2 instances, meaning you can switch from a failing EC2 instance to another without switching storage.

Since all configurations and data are stored in the same EBS, you are basically keeping the system running, despite replacing the EC2 instance used by the system. You can take this a step further by introducing Elastic IP address, which allows for multiple EC2 instances to use the same IP address; this eliminates the need for DNS zone updates or reconfiguration of your load-balancing node.

A System That Scales

One more thing to note about fault tolerance and redundancy in AWS: you have plenty of auto-scale options to utilize. Many AWS services can now be scaled up (or down) automatically. The others support scaling when triggered.

Combined with services like EBS, you can create a fault-tolerant system rather easily in AWS. Adding multiple redundancies to further support the system will result in a capable and immensely reliable system.

With the market being as competitive as it is today, offering highly available services to users becomes an important competitive advantage. Downtime is unacceptable when your competitors are always available. Increasing fault tolerance and adding redundancies are how you stay ahead of the market. For more on optimizing your cloud ecosystem on AWS, read our article Optimizing DevOps and the AWS Well-Architected Framework.

This post was originally published here.

from DZone Cloud Zone

Calling Lambda Functions Through AWS API Gateway

Calling Lambda Functions Through AWS API Gateway

In recent times, most people are moving towards FaaS (Functions-as-a-Service). This article shows how to write a Lambda service in AWS and to call it through the AWS API gateway.

How to Write a Lambda Function Using Java

  • Create a Java Maven project.
  • Edit the Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>lk.sidath.cloud</groupId>
    <artifactId>cloud-lambda</artifactId>
    <version>1.0</version>

    <dependencies>
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-events</artifactId>
            <version>2.2.5</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  • Create a class named “APIRequestHandler.java.”
package lk.sidath.cloud;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class APIRequestHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) {

        APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent = new APIGatewayProxyResponseEvent();
        try {
            String requestString = apiGatewayProxyRequestEvent.getBody();
            JSONParser parser = new JSONParser();
            JSONObject requestJsonObject = (JSONObject) parser.parse(requestString);
            String requestMessage = null;
            String responseMessage = null;
            if (requestJsonObject != null) {
                if (requestJsonObject.get("requestMessage") != null) {
                    requestMessage = requestJsonObject.get("requestMessage").toString();
                }
            }
            Map<String, String> responseBody = new HashMap<String, String>();
            responseBody.put("responseMessage", requestMessage);
            responseMessage = new JSONObject(responseBody).toJSONString();
            generateResponse(apiGatewayProxyResponseEvent, responseMessage);

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return apiGatewayProxyResponseEvent;
    }

    private void generateResponse(APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent, String requestMessage) {
        apiGatewayProxyResponseEvent.setHeaders(Collections.singletonMap("timeStamp", String.valueOf(System.currentTimeMillis())));
        apiGatewayProxyResponseEvent.setStatusCode(200);
        apiGatewayProxyResponseEvent.setBody(requestMessage);
    }
}

How to Create a Lambda Function in AWS

Sign in to the AWS account and go to the Lambda service under “Services.”

According to the above image, we can start creating the function from scratch. There are several runtime environments and I chose Java 8 for this example.

We need to have permission for the lambda function. For that, I have created a new role with the basic lambda permission.

In order to create a lambda function, you need to upload the build jar.

  • Go to the project home and build the Maven project using  mvn clean install .
  • Upload the jar located inside the target folder.
  • Fill the handler info as with lk.sidath.cloud.APIRequestHandler::handleRequest.
  • Save the function.

Now the lambda function is ready to be invoked via the API Gateway. The best way to secure the lambda function is by calling those services via a gateway. So we use the AWS API gateway to secure the lambda function and it also gives API management.

How to Configure API Gateway

Drag and drop the API Gateway from the left panel. After that, you can configure the API Gateway.

In this configuration, we can define the API as a new API and can provide the security with the API key. After creating a new API you can see the API details with the API key.

Start the serverless function using the AWS API gateway.


The above image shows how testing is done and its integration. This is the default integration of the AWS environment.

The test client, which is provided by AWS, is very good and we can control all input parameters for our function. We can select the method, pass the query params, change the headers, change the request body and so on. They also provide the request and response logs and details related to the response such as HTTP status, response time, response body and headers.

We can call this function using the curl or postman from our local pc.

https://o6e.execute-api.us-west-5.amazonaws.com/default/FirstLambdaFunction \ -H 'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \ -H 'x-api-key: *****************************' \ -d '{ "requestMessage": "Sidath"}'

from DZone Cloud Zone

AWS Step Functions 101

AWS Step Functions 101

We’ll begin with a simple explanation of what are Step Functions. AWS Step Functions service is the most recent service released by none other than Amazon Web Services. The primary goal of Step Functions is to solve problems that often arise by the orchestration of complex flows via Lambda functions.

In most cases, there are several processes made out of different tasks. To be able to run the whole process serverless, you need to create Lambda function for every task, and after you’ve created them, you need to run them via your orchestrator.

You need to write a code that will orchestrate these functions, and successfully writing code like that is not an easy task at all because of optimization and debugging of this code.

AWS Step Functions service will make your life easier by removing the need for all this with its simple design, and it’ll also implement a complex flow for our tasks or functions.

In short, AWS Step Functions will make it easy to manage and coordinate the distributed application’s components along with microservices all thanks to the utilization of the visual workflow.

Why Should You Use Orchestration for Your Application Design?

Consider this — breaking your application into many pieces (service components) allows your system to keep on going even though a single element has failed. Each of these components can scale independently, and there’s no need to redeploy the entire system after every change because every component can be updated on its own.

Scheduling, managing execution dependencies, and concurrency within the logical flow of the application are all involved in the coordination of service components. In applications like this, the developers are able to use service orchestration to handle failures as well.

How Does Step Functions Work?

By using Step Functions, you’re actually defining state machines that describe your workflow as a series of steps, their relationship as well as their outputs and inputs. State machines consist of a number of states, and every state represents an individual step of a workflow diagram. States can pass parameters, make choices, perform work, manage timeouts, terminate your workflow with success or failure, and initiate parallel execution as well. The visual console will automatically graph every state following the execution order.

All of the above makes it very easy to design multi-step apps because the console highlights the status of each step in real-time, and it provides a history of every execution in details.

Connecting Step Function to Other AWS Services

By utilizing service tasks, you can connect and coordinate the workflow you’ve created via Step Functions with other AWS services. What else can you do? Here are some of the examples:

  • You can run Amazon Fargate task or Amazon ECS;

  • You can submit an AWS batch job, but you need to wait for it to complete;

  • You’re able to publish a message on SNS Topic;

  • You can create an Amazon SageMaker job, which can train a machine learning model;

  • You can invoke AWS Lambda function;

  • You can send a message to Amazon SQS queue;

  • You can also place a new item in the DynamoDB table or obtain an existing item from the DynamoDB table.

Step Functions — Monitoring & Logging

How does the monitoring work with Step Functions? Step Functions will send metrics to AWS CloudTrail and Amazon CloudWatch to monitor applications. CloudTrail will capture all API calls for Step Functions as events, while the calls from Step Functions and from code calls are all included to the Step Functions APIs.

CloudWatch can set alarms, collect, and track all the track metrics, and it will automatically react to any changes that occur in Step Functions. Step Functions support CloudWatch Events’ managed rules for every integrated service inside your workflow. Step Functions will create and manage CloudWatch Events rules within your Amazon Web Services account.

The Most Frequent Step Functions’ Use Cases

You can use Step Functions to create an end-to-end workflow which will allow you to manage jobs with interdependencies. The whole idea of Step Functions is to help you solve any business process or computational problem which can be divided into a series of steps. Here are some of the examples:

  • Implement a difficult sign-on authentication and user registration processes for web applications;

  • Creating event-driven apps that can automatically respond to infrastructure changes and building tools for constant deployment and integration. These are crucial for IT automation and DevOps;

  • Processing of data: Unified reports are made by consolidating data from multiple databases. It can reduce and refine large data sets into a more comfortable file format, or even coordinate machine learning workflow and multi-step analytics.

Let’s Wrap Step Functions Up

Primarily, Step Functions can help us achieve higher performance rates by allowing us to break down our application into service components and manipulate them all independently.

Amazon Step Function service is relatively new, and like everything else from AWS, it’ll only get better as time goes by, and it’ll surely become more useful. Discover Step Functions service for yourself. If you have experience with AWS Step Functions, share it with our readers and us in the comment section below. Feel free to start a discussion!

from DZone Cloud Zone

Serverless Fraud Detection Using Amazon Lambda, Node.js, and Hazelcast Cloud

Serverless Fraud Detection Using Amazon Lambda, Node.js, and Hazelcast Cloud

Recently, an interesting paper was published by UC Berkeley with their review of serverless computing and quite reasonable predictions:

“…Just as the 2009 paper identified challenges for the cloud and predicted they would be addressed and that cloud use would accelerate, we predict these issues are solvable and that serverless computing will grow to dominate the future of cloud computing.”

So, why should the industry go serverless at all? A simple answer is that we, as software engineers, are eager to be effective and want to focus on the result. Serverless computing is working towards that:

  • Your cloud provider bill gets lower since you pay only for what you use
  • You get more elastic scalability due to the more compact computation units (Functions)
  • You do not have to write much of the infrastructure code, which makes the overall system far less complex
  • In the end, it is cloud-native, i.e. things like discovery, fault tolerance, and auto-scaling come out of the box

Sounds like something worth adopting, right?

In this tutorial, we will build the complete serverless solution using Amazon Lambda, Node.js, and Hazelcast Cloud – a managed service for fast and scalable in-memory computing.

Problem Overview

Every time your credit or debit card initiates a transaction, the underlying payment system applies various checks to verify transaction validity. There are simple checks, such as verifying available funds. However, others can be far more advanced, such as looking around the history of card payments to identify a personalized pattern and validate the given transaction against it.

For this demo, we are going to use a simplified approach. We will be checking the validity of a limited subset of transactions performed at the airports, and the responsible bank, which in our case is the Bank of Hazelcast. You can also find this problem mentioned initially by my colleague Neil Stevenson in his blog post.

So, let’s go over the entire algorithm step by step.

  1. Consider two transactions, A and B, which take place in different airports: Image title
  2. What we do is we compare two dimensions:
  • The time between transactions A and B
  • Distance between given airports (we use their coordinates to calculate that)

By comparing them, we determine whether the person could move from one location to another within a given time frame. More specifically, if transaction A is performed in London and transaction B in Paris, and time between them is not less than two hours, then we consider this a valid scenario:Image title

3. The opposite example below will be identified as suspicious because Sydney is more than two hours away:

Image title

Serverless Solution Design

Now, we will explore the high-level architecture of our serverless solution:

Image title

As you can see, it’s composed of the following components:

  1. Amazon S3 handles uploads of the mostly static dataset with information about airports.
  2. Lambda Function “AirportsImportFn” is triggered by the upload into S3 buckets. Once triggered, it imports the dataset into the Hazelcast Cloud cluster so that it can be queried with the lowest latency.
  3. Amazon API Gateway and Lambda Function “ValidateFn” to serve the HTTP traffic. This Lambda function written in Node.js implements the actual fraud detection logic to validate the received requests. It communicates with the Hazelcast Cloud cluster to manage its state.
  4. Hazelcast Cloud is a managed service for Hazelcast IMDG — an open-source, in-memory data grid for fast, scalable data processing. Minimal latency, auto-scaling, and developer-oriented experience — this is why we chose it for our solution. 

We call it the Complete Serverless Solution since it employs both kinds of the Serverless Components – Function-as-a-Service (FaaS) provided by Amazon Lambda and Backend-as-a-Service (BaaS), a Hazelcast IMDG cluster managed by Hazelcast Cloud. The whole stack is co-located in the same AWS region to ensure the shortest network paths across the components.

Node.js Implementation

As mentioned at the beginning, software engineers want to apply an effective solution to the problem. We must be flexible in choosing an appropriate tool while avoiding limitations on programming language, technology, framework, etc. This is how we do it in 2019, right? And this is why we choose Node.js for the Lambda function implementation, one of the modern serverless runtimes supported by the major FaaS providers. 

We all know talk is cheap, so let’s explore the source code.

Lambda Function “AirportsImportFn”

const hazelcast = require('./hazelcast');
const aws = require('aws-sdk');

let sharedS3Client = null;

let getS3Client = () => {
   if (!sharedS3Client) {
       console.log("Creating S3 client...")
       sharedS3Client = new aws.S3();
   }
   return sharedS3Client;
};

exports.handle = async (event, context, callback) => {
   console.log('Got event: ' + JSON.stringify(event));
   context.callbackWaitsForEmptyEventLoop = false;
   let hazelcastClient = await hazelcast.getClient();
   let map = await hazelcastClient.getMap('airports');
   if (await map.isEmpty() && event.Records.length > 0) {
       let srcBucket = event.Records[0].s3.bucket.name;
       console.log('Handling upload into bucket \'' + srcBucket + '\'...');
       let srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
       let s3Client = getS3Client();
       let object = await s3Client.getObject({Bucket: srcBucket, Key: srcKey}).promise();
       let airports = JSON.parse(object.Body);
       await map.putAll(airports.map(airport => ([airport.code, airport])));
       console.log('Imported data about ' + airports.length + ' airports');
       return callback(null, true);
   }
   return callback(null, false);
};

So, this is what we implemented:

  • Defined a global variable to reuse the S3 client instance between the function invocations (later, we will discuss why it’s important)
  • Exported function “handle” implements the actual business logic of our Lambda function. It processes the incoming S3 event, reads the JSON contents of the uploaded object, deserializes it into an array, and then re-maps it to key-value pairs before storing in the Hazelcast map
  • Finally, we call an Amazon Lambda callback to return the result

Hazelcast Map (IMap) is a distributed hash map. Through the Node.js client, you can perform operations like reading and writing from/to a Hazelcast Map with the well-known get and put methods. For details, see the Map section in the Hazelcast IMDG Reference Manual.

Lambda Function “ValidateFn”

const hazelcast = require('./hazelcast');
const haversine = require('haversine');
const moment = require('moment');

exports.handle = async (request, context, callback) => {
   console.log('Got request: ' + JSON.stringify(request));
   context.callbackWaitsForEmptyEventLoop = false;
   let userId = request.userId;
   let requestTimestampMillis = moment(request.transactionTimestamp).utc().valueOf();
   let hazelcastClient = await hazelcast.getClient();
   let airports = await hazelcastClient.getMap('airports');
   if (await airports.isEmpty()) {
       return callback('Airports data is not initialized', null);
   }
   let users = await hazelcastClient.getMap('users');
   let user = await users.get(userId);
   if (!user) {
       await users.set(userId, {
           userId: userId,
           lastCardUsePlace: request.airportCode,
           lastCardUseTimestamp: requestTimestampMillis
       });
       return callback(null, {valid: true, message: 'User data saved for future validations'});
   }
   let [lastAirport, nextAirport] = await Promise.all([airports.get(user.lastCardUsePlace),
       airports.get(request.airportCode)]);
   if (lastAirport.code === nextAirport.code) {
       return callback(null, {valid: true, message: 'Transaction performed from the same location'});
   }
   let speed = getSpeed(lastAirport, user.lastCardUseTimestamp, nextAirport, request.transactionTimestamp);
   let valid = speed <= 13000; // 800 km/hr == ~13000 m/min let message = valid ? 'Transaction is OK' : 'Transaction is suspicious'; // Update user data user.lastCardUsePlace = request.airportCode; user.lastCardUseTimestamp = requestTimestampMillis; await users.set(userId, user); return callback(null, {valid: valid, message: message}); }; let getSpeed = (lastAirport, lastUseTimestamp, nextAirport, requestTimestamp) => {
   // Time
   let minutes = moment(requestTimestamp).diff(lastUseTimestamp, 'minutes');
   // Distance
   let meters = haversine(nextAirport, lastAirport, {unit: 'meter'});
   // Speed
   return meters / minutes;
};

Let’s look at what we need to do step by step:

  1. First, we set this:
    context.callbackWaitsForEmptyEventLoop = false;

This is the Amazon Lambda-specific setting to prevent waiting until the Node.js runtime event loop is empty. Here, you can find more info about the given setting.

2. Then our validation logic checks whether we already have any data for a user associated with the incoming request. If it’s a new user, we save it for the future validations and return a corresponding result:

{valid: true, message: 'User data saved for future validations'}

3. If there is available data about the previous transaction, we proceed by retrieving info about the current and prior airports:

let [lastAirport, nextAirport] = await Promise.all([airports.get(user.lastCardUsePlace),
   airports.get(request.airportCode)]);

And skip the validation if the airports are the same:

{valid: true, message: 'Transaction performed from the same location'}

4. After that, we use the haversine formula to calculate a “user speed” between two transactions. If it’s bigger than an average plane’s speed, we conclude that the transaction is suspicious: 

let valid = speed <= 13000; // 800 km/hr == ~13000 m/min
let message = valid ? 'Transaction is OK' : 'Transaction is suspicious';

At the end of our algorithm, we store the data from the request for the future validations:

await users.set(userId, user);

Hazelcast Client Module

To better organize our codebase, we’ve extracted the Node.js Hazelcast Client setup into a separate module within hazelcast.js:

const Client = require('hazelcast-client').Client;
const ClientConfig = require('hazelcast-client').Config.ClientConfig;

let sharedHazelcastClient = null;

let createClientConfig = () => {
   let cfg = new ClientConfig();
   cfg.groupConfig.name = process.env.GROUP;
   cfg.groupConfig.password = process.env.PASSWORD;
   cfg.networkConfig.cloudConfig.enabled = true;
   cfg.networkConfig.cloudConfig.discoveryToken = process.env.DISCOVERY_TOKEN;
   cfg.properties['hazelcast.client.cloud.url'] = 'https://coordinator.hazelcast.cloud';
   cfg.properties['hazelcast.client.statistics.enabled'] = true;
   cfg.properties['hazelcast.client.statistics.period.seconds'] = 1;
   cfg.properties['hazelcast.client.heartbeat.timeout'] = 3000000;
   return cfg;
};

module.exports.getClient = async () => {
   if (!sharedHazelcastClient) {
       console.log('Creating Hazelcast client...');
       sharedHazelcastClient = await Client.newHazelcastClient(createClientConfig());
   }
   return sharedHazelcastClient;
};

The main idea here is to re-use the Hazelcast Client instance between invocations. This is an optimization that is still available even though you deal with the ephemeral and stateless Function containers. Learn more about the Amazon Lambda function lifecycle in this corresponding article from AWS Compute Blog. Also, something worth mentioning is that Hazelcast settings are configured via environment variables — this is one of the suggested ways to configure the Lambda Function instance. Later, we will see how to set them.

Deployment

And the final spurt — we need to deploy the whole solution to your AWS account. Here are the steps:

1. We will start by creating our Lambda functions and the first candidate will be “ImportAirportsFn” — the function triggered by upload into the S3 bucket. A minimal deployment package for Amazon Lambda Node.js environment consists of the actual JS file with function handler and the required libraries for your code. Both should be zipped and passed as an argument to AWS CLI create-function command:

$ zip -r import.zip import.js hazelcast.js node_modules
$ aws lambda create-function --function-name --role lambda_role_arn --zip-file fileb://import.zip --handler import.handle --description "Imports Airports from S3 into Hazelcast Cloud" --runtime nodejs8.10 --region us-east-1 --timeout 30 --memory-size 256 --publish --profile aws_profile

Let’s quickly review the arguments:

  •  ImportAirportsFn – a logical function name
  •  lambda_role_arn – beforehand, you should create a Lambda Execution Role with the given policy attached; it gives basic permission to upload the logs and to read data from S3
  •  aws_profile – here, we use AWS Profile-based access to work with AWS CLI
  •  us-east-1 – the region that we’re going to use for our deployment
  •  import.handle – this is for Amazon Lambda to lookup the function handler; it’s set in the format  js_file_name.exported_function_name

After this, we should run just two more commands to make our function triggered by the uploads into S3 bucket:

$ aws lambda add-permission --function-name ImportAirportsFn --action lambda:InvokeFunction --principal s3.amazonaws.com --source-arn s3_bucket_arn --statement-id ncherkas-js-demo-lambda-permission --region us-east-1 --profile aws_profile

Where s3_bucket_arn is the ARN of the S3 bucket, it has a simple format like “ arn:aws:s3:::your_bucket_name” and ” ncherkas-js-demo-lambda-permission.” This is a logical id for the permission instance.

$ aws s3api put-bucket-notification-configuration --bucket your_bucket_name --notification-configuration file://s3_notification.json --profile aws_profile --region us-east-1

For this last one, you can use a sample notification config here. Just replace “LambdaFunctionArn” inside of it with a valid ARN of the Lambda Function.

Please note that the S3 bucket should be created beforehand. I’d suggest placing it in the same AWS region i.e. us-east-1.

2. Now, let’s set up the Hazelcast Cloud cluster so that we can start using it for the low-latency state management required by our Lambda Functions. Go https://cloud.hazelcast.com and create your account by clicking “Launch For Free.” Once you’ve entered a console, proceed by creating a new Hazelcast cluster:

Image title

Before pressing “+ Create Cluster,” click on “+Add” button next to the “Custom Map Configuration” setting to specify an expiration time for the user data we’re going to store. We do this for the user data since we don’t need to save it for a period of more than a few days, this is an excellent chance to optimize our storage!

Image title

After this, press “+Create Cluster,” and let’s have a brief overview of the options we specified for our Hazelcast Cluster:

  • Cloud Provider – a cloud provider that you want to use, which is AWS in the case of our tutorial
  • Region – AWS region that you’re going to use; let’s set the same region that we use for our Lambda Functions, i.e. us-east-1.
  • Cluster Type – a bunch of options available here; let’s leverage trial 50 $ and go with a Small type
  • Enable Auto-Scaling – when it’s ON, our cluster will automatically increase its capacity as the amount of stored data increases
  • Enable Persistence – backed with the Hazelcast Hot Restart Persistence; this avoids losing our data between the Hazelcast Cluster restarts
  • Enable Encryption – this is what you’d like to enable if you process the real payment transactions. Under the hood, it leverages the Hazelcast Security Suite
  • Enable IP Whitelist – this is, again, what you’d want to switch ON when you set up a production environment

Now that we have our Hazelcast Cluster up and running, what’s next? How do we connect to it? This is quite easy to do — go and click “Configure Client” to see what’s available:

Image title

Here, you have two options. You can download a ready code sample that is working with your cluster. Or, as we will do in the case of Lambda Function, you can configure your client manually. As you remember from the source code, we configure our client using the environment variables, but how do they propagate to our Lambda Function? Here is a command that we need to run:

$ aws lambda update-function-configuration --function-name ImportAirportsFn --environment Variables="{GROUP=<cluster_group_name>,PASSWORD=<password>,DISCOVERY_TOKEN=<token>" --profile aws_profile --region us-east-1

In this command, we take  cluster_group_name,  password, and  token from the “Configure Client” dialog that we were exploring above.

Now, let’s have some fun and test our Lambda Function “ ImportAirportsFn”. To do this, upload the given sample file with the airport’s data into S3 bucket that we created earlier in this tutorial. Once complete, go to Amazon Cloud Watch Console, click “Logs” and open a stream called “/aws/lambda/ImportAirportsFn”. Inside of the stream, the recent logs will go on the top. Let’s explore them:

Image title

As you can see, our dataset with the airport’s data was successfully copied into the corresponding Hazelcast map. Now, we can query it to validate the payment transactions; this is what we want to have at the end — the serverless fraud detection. Let’s move on and complete the last deployment step.

3. Creating Lambda Function “ValidateFn” and setting up the Amazon API Gateway.

Run the AWS CLI command to create a new function:

$ zip -r validate.zip validate.js hazelcast.js node_modules
$ aws lambda create-function --function-name ValidateFn --role lambda_role_arn --zip-file fileb://validate.zip --handler validate.handle --description "Validates User Transactions" --runtime nodejs8.10 --environment Variables="{GROUP=<cluster_group_name>,PASSWORD=<password>,DISCOVERY_TOKEN=<token>}" --region us-east-1 --timeout 30 --memory-size 256 --publish --profile aws_profile

As you can see, we establish the Hazelcast Cluster settings right away since our cluster is already up and running.

Before proceeding with the API gateway, we can quickly test our function within the AWS console:

1) Click “Select a test event” and configure a test JSON payload:

Image title

2) Click “Test” and explore the results:

Image title

Congrats! You’re now very close to getting the complete solution up and running!

And the last, yet crucial step — setting up the API gateway endpoint.

Go to Amazon API gateway and create a new API:

Image title

Then, create a new API Resource:

Image title

And add a POST mapping for our API:

Image title

Choose OK when prompted with Add Permission to Lambda Function.

After this, we need to configure a request body mapping. Go to “Models” under the newly created API and create a new model:

Image title

Then click on POST method and press “Method Request” to apply this mapping model:

Image title

We are ready now to deploy and test the Validate API. To do this, go Actions – Deploy API and create a new stage:

Image title

After it’s deployed, you will get the Invoke URL that exposes our newly created API. Let’s use this URL and do final testing (here, I use HTTPie, which is a good alternative to cURL):

$ http POST <invoke_url>/validate userId:=12345 airportCode=FRA transactionTimestamp=2019-03-18T17:55:40Z

{
    "message": "Transaction performed from the same location",
    "valid": true
}

Now, let’s try to emulate a suspicious transaction. The previous request was performed at Mon Mar 18 2019, 13:51:40 in Frankfurt, so let’s send another request for the transaction performed in New York, a few minutes later:

$ http POST <invoke_url>/validate userId:=12345 airportCode=EWR transactionTimestamp=2019-03-18T18:02:10Z

{
    "message": "Transaction is suspicious",
    "valid": false
}

We get “valid”: false since it’s unrealistic for the person to move from Frankfurt to New York in that short period. And one more example, a valid transaction:

$ http POST <invoke_url>/validate userId:=12345 airportCode=LCY transactionTimestamp=2019-03-19T02:20:30Z

{
    "message": "Transaction is OK",
    "valid": true
}

Here, we have “valid”: true because the given transaction was performed from London later at night, which seems reasonable. 

Also, it makes sense to inspect the performance of our solution. Below, you can see a graph of the average latency after running a simple 10-minute test, continuously sending validate requests to the API Gateway URL:

Image title

As you can see, the average duration of ValidateFn function was mostly less than 10 ms. during this test. This confirms the choice we’ve made by solving our problem with the in-memory computing solution. Note that in the case of Amazon Lambda, the less your latency is, the less you pay for the usage, so using the In-Memory Computing solution helps to reduce the processing costs.

Impressive! At this point, Serverless Fraud Detection is up and running! Thanks for joining me on this journey!

Now, for a summary…

Conclusion

This tutorial has shown that serverless computing is not just hype but is something worth learning and adopting. With reasonable efforts, you get a scalable, fault-tolerant, and performant solution with in-memory computing enhancing all of these attributes. Check out our community site to learn about more cool things you can do with the Hazelcast Node.js Client and Hazelcast Cloud.

from DZone Cloud Zone

End-to-End Tests: Managing Containers in Kubernetes

End-to-End Tests: Managing Containers in Kubernetes

Our infrastructure is more complex than ever, and there is greater pressure to deliver quality features to customers on time. To meet these needs, automated end-to-end tests play an important role in our continuous integration and delivery process. Let’s look at how we can execute these tests in a container within a Kubernetes cluster on Google Cloud Kubernetes Engine.

As software applications transition towards a microservices architecture and platforms become more cloud-native, these shifts have changed how development teams build and test software. Each component of the application is packaged in its own container.

To scale and manage these containers, organizations are turning to orchestration platforms like Kubernetes. Kubernetes is a well-known open-source orchestration engine for automating deployment, scaling, and management of containerized applications at scale, whether they run in a private, public, or hybrid cloud.

With increased complexity in the infrastructure and the need for timely delivery of quality features to customers, automated end-to-end tests play an important role in our continuous integration and delivery process. Let’s look at how we can execute these tests in a container within a Kubernetes cluster on Google Cloud Kubernetes Engine.

Building the Testing Container Image

We start by building our tests, which are written in TestNG using Selenium WebDriver, into a container image. The image includes all the test files, libraries, drivers, and a properties file, as well as the Shell script to start the tests.

Below, you’ll find some sample code that should give you a sense of how you can structure and configure your testing, including snippets from the following files:

Dockerfile

FROM centos:7.3.1611
RUN yum install -y \
java-1.8.0-openjdk \
java-1.8.0-openjdk-devel
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk/

# Set local to UTF8
RUN localedef -i en_US -f UTF-8 en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
ENV LC_ALL en_US.UTF-8

# Install automation tests
COPY ./build/libs ${test_dir}/bin
COPY ./build/lib ${test_dir}/lib
COPY ./testfiles ${test_dir}/testfiles
COPY ./build/resources ${test_dir}/resources
COPY ./build/drivers ${test_dir}/drivers
RUN chmod -R 755 ${test_dir}/drivers
COPY ./*.properties ${test_dir}/

WORKDIR /opt/automation
USER root

CMD [ "./run-suite.sh", "TestSuite" ]

run-suite.sh

#!/bin/bash

# run test suite
fullList=""
function listOfSuites() {
for i in $(echo $1 | sed "s/,/ /g")
do
# call your procedure/other scripts here below
echo "$i"
fullPath=`find . -type f -name "$i.xml"`
#echo "fullList=$fullList"
#echo "fullPath=$fullPath"
fullList="$fullList$fullPath "
done
}
listOfSuites $1
echo $fullList

java -Dlog4j.configuration=resources/main/log4j.properties -cp "./lib/*:./bin/*:." \
org.testng.TestNG $fullList

java -cp "./lib/*:./bin/*:." com.gcp.UploadTestData

build.gradle

buildscript {
repositories { maven { url "${nexus}" } }
dependencies {
classpath 'com.bmuschko:gradle-docker-plugin:3.6.2'
}
}

apply plugin: com.bmuschko.gradle.docker.DockerRemoteApiPlugin

import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage

def gcpLocation = 'gcr.io'
def gcpProject = 'automation'
def dockerRegistryAndProject = "${gcpLocation}/${gcpProject}"

// Provide a lazily resolved $project.version to get the execution time value, which may include -SNAPSHOT
def projectVersionRuntime = "${-> project.version}-${buildNumber}"
def projectVersionConfigurationTime = "${project.version}-${buildNumber}"
def projectRpm = "${projectName}-${projectVersionConfigurationTime}.${arch}.rpm"

// We want to use the branch name as part of the GCR tag. However we don't want the raw branch name,

// so we strip out symbols and non alpha-numerics. We also strip out git branch text that contains
// remotes/origin or origin/, since we don't care about that.
def sanitize = { input ->
return input.replaceAll("[^A-Za-z0-9.]", "_").toLowerCase().replaceAll("remotes_origin_", "").replaceAll("origin_", "");
}

def gitbranchNameRev = 'git name-rev --name-only HEAD'.execute().text.trim()
def gcpGitbranch = System.env.GIT_BRANCH ?: (project.hasProperty('gitbranch')) ? "${gitbranch}" : "${gitbranchNameRev}"
def gitbranchTag = sanitize(gcpGitbranch)

def projectVersionRuntimeTag = sanitize("${-> project.version}")
def dockerTag = "${dockerRegistryAndProject}/${projectName}:${projectVersionRuntimeTag}-${buildNumber}-${gitbranchTag}-${githash}"
def buildType = System.env.BUILD_NUMBER ? "JENKINS" : "LOCAL"

//Create gcpBuildVersion.properties file containing build information. This is for the build environment to pass onto
// other upstream callers that are unable to figure out this information on their own.
task versionProp() {
onlyIf { true }
doLast {
new File("$project.buildDir/gcpBuildVersion.properties").text = """APPLICATION=${projectName}
VERSION=${-> project.version}
BUILD=${buildNumber}
BRANCH=${gcpGitbranch}
>GIT_HASH=${githash}
TAG_FULL=${dockerTag}
TAG=${projectVersionRuntimeTag}-${buildNumber}-${gitbranchTag}-${githash}
TIMESTAMP=${new Date().format('yyyy-MM-dd HH:mm:ss')}
BUILD_TYPE=${buildType}

"""
}
}

// make sure the below version file generation is always run after build
build.finalizedBy versionProp
task dockerPrune(type: Exec) {
description 'Run docker system prune --force'
group 'Docker'

commandLine 'docker', 'system', 'prune', '--force'
}

task buildDockerImage(type: DockerBuildImage) {
description 'Build docker image locally'
group 'Docker'
dependsOn buildRpm
inputDir project.buildDir

buildArgs = [
'rpm': "${projectRpm}",
'version': "${projectVersionConfigurationTime}"
]

doFirst {
// Copy the Dockerfile to the build directory so we can limit the context provided to the docker daemon
copy {
from 'Dockerfile'
into "${project.buildDir}"
}

copy {
from 'docker'
into "${project.buildDir}/docker"
include "**/*jar"
}

println "Using the following build args: ${buildArgs}"

// This block will get the execution time value of $project.version, which may include -SNAPSHOT</span
tag "${dockerTag}"
}
}

task publishContainerGcp(type: Exec) {
description 'Publish docker image to GCP container registry'
group 'Google Cloud Platform'
dependsOn buildDockerImage

commandLine 'docker', 'push', "${dockerTag}"
}

Selenium Grid Infrastructure Setup

Our end-to-end tests use Selenium WebDriver to execute browser-related tests, and we have a scalable container-based Zalenium Selenium grid deployed in a Kubernetes cluster (you can see setup details here).

You can configure the grid URL and browser in the test_common.properties file included in the test container image:

targetUrl=http://www.mywebsite.com
# webDriver settings
webdriver_gridURL=http://${SELENIUM_GRID}/wd/hub
webdriver_browserType=${BROWSER_TYPE}

Deploying Test Containers in Kubernetes Cluster

Now that we have the container image built and pushed to the Google Cloud Platform, let’s deploy the container in Kubernetes. Here’s a snippet of the template for the manifest file to execute tests as a Kubernetes job:

apiVersion: batch/v1
kind: Job
metadata:

name: run-suite-${JENKINS_JOB_INFO}-${TEST_SUITE_LOWER}
labels:
jobgroup: runtest
spec:
template:
metadata:
name: runtest
namespace : automation
labels:
jobgroup: runtest
spec:
containers:
- name: testcontainer
image: gcr.io/automation/${IMAGE_NAME}:${IMAGE_TAG}
command: ["./run-suite.sh", "${TEST_SUITE}"]
env:
- name: SELENIUM_GRID
value: "${SELENIUM_GRID}"
- name: BROWSER_TYPE
value: "${BROWSER_TYPE}"
- name: TARGET_URL
value: "${TARGET_URL}"
- name: GCP_CREDENTIALS
value : "${GCP_CREDENTIALS}"
- name: GCP_BUCKET_NAME
value: "${GCP_BUCKET_NAME}"
- name : BUCKET_FOLDER
value : "${BUCKET-FOLDER}"
restartPolicy: Never

And, here’s the command to create the job:

kubectl apply -f ./manifest.yaml --namespace automation

Publishing Test Results

Once test execution is complete, you can upload test results to your Google Cloud Storage bucket using a code snippet similar to this:

public static void main(String... args) throws Exception {

String gcp_credentials = readEnvVariable(GCP_CREDENTIALS);
String gcp_bucket = readEnvVariable(GCP_BUCKET_NAME);
String bucket_folder_name = readEnvVariable(BUCKET_FOLDER);

// authentication on gcloud
authExplicit(gcp_credentials);

// define source folder and destination folder
String source_folder = String.format("%s/%s", System.getProperty("user.dir"), TEST_RESULTS_FOLDER_NAME);
String destination_folder = "";
if (!bucket_folder_name.isEmpty()) {
destination_folder = bucket_folder_name;
} else {
>String timeStamp = new
SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());
>destination_folder = TEST_RESULTS_FOLDER_NAME + "-" + timeStamp;
>System.out.println("destination folder: " + destination_folder);
>}
// get gcp bucket for automation-data
Bucket myBucket = null;
if (!gcp_bucket.isEmpty()) {
myBucket = storage.get(gcp_bucket);
}
// upload files
List<File> files = new ArrayList<File>();
GcpBucket qaBucket = new GcpBucket(myBucket, TEST_RESULTS_FOLDER_NAME);
if (qaBucket.exists()) {
qaBucket.createBlobFromDirectory(destination_folder, source_folder, files);
System.out.println(files.size() + "files are uploaded to " + destination_folder);
}
}
}

Next Steps

We’re looking into publishing test results in a centralized result database fronted by an API service. This allows users to easily post test result data for test result monitoring and analytics, which I will cover in a future blog post about building a centralized test results dashboard. Until then, I hope this post has helped you put all the pieces together for executing automated end-to-end testing using Kubernetes!

from DZone Cloud Zone

Auto-Scaling Clusters With Hazelcast Cloud

Auto-Scaling Clusters With Hazelcast Cloud

As cloud technologies evolve, applications require less human intervention and maintenance. “Serverless” is a term that implies that users should have nothing to do with servers. The most exciting claim of serverless functions is that they scale automatically as the user base and load grows. Moreover, when there is no user activity, there will be no server or resource usage. But, serverless functions are not always the best solution. Many use cases require servers, such as web applications, databases, queues, etc. When you need servers, cloud instances, and VMs in your architecture, then auto-scaling technologies will be beneficial. Auto-scaling enables a cluster to scale up and down depending on the load and usage rate of resources. You benefit from auto-scaling in two areas:

  • Better availability and service quality: Auto-scaling prevents outages and performance drops in case of a demand spike by increasing the resources and capacity of clusters.
  • Low cost: When the demand is low, auto-scaling decreases the size of the cluster. That means you do not pay more than you need when the demand is low.

In this instance, Hazelcast Cloud can act as a “Backend-as-a-Service” (BaaS). Under this approach, Hazelcast will maintain the clusters for you to provide a simplified experience without dealing with servers. Since auto-scaling is a critical component of Hazelcast Cloud, it can be used to scale your cluster without any human intervention.

Auto-Scaling Algorithm

Implementing auto-scaling for Hazelcast is straightforward. To grow a Hazelcast cluster, you need to add one more Hazelcast instance to the same network with the same credentials. The new node discovers and joins the cluster automatically. To shrink a cluster, terminate or shut down a node, and it will detect the dead node resulting in the partitions adjusting accordingly. Because multiple replicas of each partition are retained, no data is lost.

As scaling up and down with Hazelcast is quite easy, the only challenging part is to decide when to scale the cluster. The most popular use cases of Hazelcast are caching and data stores. For these use cases, memory is the primary resource that needs to be scaled, so we need auto-scaled, cloud-based Hazelcast clusters. Although the default metric used by Kubernetes’ horizontal autoscaler is CPU, we could customize it to check memory utilization instead of CPU. Unfortunately, this is not so simple. Hazelcast Cloud keeps user data in High-Density Memory Store (off-heap memory). Hazelcast reserves the off-heap memory beforehand, so the operating system can’t report what percentage of the cluster is free. Only Hazelcast knows how much data is being kept. To help, Hazelcast wrote an auto-scaling microservice that checks the actual memory utilization of the Hazelcast cluster and decides whether to scale. We call this microservice an “autoscaler.” As a simple algorithm, the Hazelcast autoscaler chooses to scale up (add one more node) when memory utilization is over 80 percent. When memory utilization drops below 40 percent, the autoscaler scales down the cluster by shutting down one node. To mitigate the side effects of repartitioning, the autoscaler sleeps for 1 minute after a scaling event. This means multiple-scale operations can take place with one-minute intervals between each.

The above algorithm looks too simple, but we prefer it over more complicated algorithms because we do not expect rapid increases in memory utilization. The 40-80 percent rule works for most of the use cases and cluster types of Hazelcast Cloud. In the future, as more sophisticated use cases and requirements are experienced, Hazelcast will improve its auto-scaling algorithm, including potentially allowing users to adjust when and how to scale.

Auto-Scaling in Action

Let’s see how auto-scaling works step by step.

Step 1: Hazelcast Cloud Account — Hazelcast Cloud has a free tier. You can register for free without a credit card and start using Hazelcast clusters in for free with some restrictions. For example, the memory of a free cluster is fixed, so auto-scaling is not possible in the free tier. But don’t worry! To help you try auto-scaling, you can use the following code to get a $30 credit for free: auto_scaling_blog_30.

If you don’t have one already, create a Hazelcast Cloud account. After confirming your email and login, go to Account >> Credits page and enter the promo code ” auto_scaling_blog_30″ as below:

Step 2: Creating a Cluster — Now that you have $30 in credits, you have access to the full functionality of Hazelcast Cloud. To start, create a cluster with 4GB memory without enabling auto-scaling, as shown below:

Step 3: Insert Data — Now, you should have an active cluster with 4GB memory capacity, but empty. To insert data, click on “Configure Client” and follow the procedure for your preferred language. You will need to download the zip file and run the specified command in the same folder. The example below ran the sample Java client, which produced output similar to this:

As you see from the logs above, Hazelcast Cloud created a 4-node, 4GB cluster. Run the client to start inserting entries to a map, and you will see some statistics in the dashboard, similar to the metrics below:

Step 4: Enable Auto-Scaling — We began to add data, but still, the data size is quite small. I waited a few minutes because the entries that the client inserts are too small; yet, the cluster is almost empty. So, 4GB is more than we need. Let’s enable auto-scaling by clicking on “Manage Memory,” and we should see Hazelcast Cloud scale down.

After clicking on “Update,” your cluster should scale down to 2GB step by step. First, it scales down to 3GB. In one minute, it rechecks the memory and scales down to 2GB. The minimum size of a small type cluster is 2GB; that’s why it does not attempt to scale down further.

Step 5: Insert More Data — We have tried scaling down. Now, let’s try scaling up. Our client example is very lazy, so we need to edit its code to put in larger objects at an accelerated pace. Here’s the updated code:

Random random = new Random();
       int THREAD_COUNT = 10;
       int VALUE_SIZE = 1_000_000;
       ExecutorService es = Executors.newFixedThreadPool(THREAD_COUNT);
       for (int i = 0; i < THREAD_COUNT; i++) {
           es.submit(new Runnable() {
               public void run() {
                   while (true) {
                       int randomKey = (int) random.nextInt(1000_000_000);                                           
                       map.put("key" + randomKey, new String(new byte[VALUE_SIZE]));
                       // map.get("key" + random.nextInt(100_000));
                       if(randomKey % 10 == 0 ) {
                           System.out.println("map size:" + map.size());
                       }
                   }
               }
           });               
       }

Here are the changes I have made to increase insertion rate:

  1. Converted the insertion code to multithreaded (10 threads)
  2. Increased the value size to 1MB.
  3. Commented on the get operation.
  4. Increased the range of keys to 1 million.

Then I started two clients. After waiting for a few minutes, you will see something similar to this:

As you can see, the cluster scaled up to 3GB when the data size exceeded 1.6GB.

If you are anxious to see results quicker, you can create more clients or increase the number of threads for the insertion rate. The best way to maximize the throughput is to run the clients inside the same region with the Hazelcast cluster. When you run the client from your laptop, the network latency between your laptop and AWS instances becomes the bottleneck.

What’s Next

We have explained and experimented with the auto-scaling capability. This is the initial version, so we are working on improving auto-scaling via the following methods:

  • Support metrics other than memory, such as CPU and requests per second for triggering auto-scaling
  • Support user-defined metrics to trigger auto-scaling
  • Allow users to define the percentages to trigger auto-scaling

from DZone Cloud Zone

Managed Service Providers Vs. AWS Next-Generation Managed Service Providers

Managed Service Providers Vs. AWS Next-Generation Managed Service Providers

This post was originally published here.

It is not surprising to see businesses relying more and more on IT and IT infrastructure these days. The resources and benefits offered by good IT infrastructure can transform businesses, increase business agility, and enhance their ability to adapt to market changes. Without robust IT infrastructure, it is difficult for organizations to keep up with the challenges of today’s market.

In the light of the varied and highly fluid technology options and issues around — such as ever-increasing hardware maintenance costs and rising security threats — many companies are moving towards managed IT services and away from in-house Infrastructure maintenance.

Of course, migrating to a more robust IT infrastructure is both clear and utterly complex at the same time. It is admittedly more simple now that there are definite paths to take — either to optimize a regular MSP or to migrate to the cloud — and methods to implement along the way. At the same time, you have more options to choose from and different IT infrastructure configurations to opt for which starts to increase the complexity. Plus, the technical challenges will make the migration more complicated.

Managed IT service providers act as the bridge between businesses and cloud implementation. Without having to set up an in-house IT team, businesses can fully benefit from cloud computing in a relatively short amount of time. The challenge now is choosing the right managed service provider to work with.

Traditional MSPs and AWS Managed Service Provider Partner Program

When it comes to top managed service providers or MSPs, you have a lot to choose from. Top names are now taking a more proactive approach to IT migration. These companies integrate migration-related services to their lineups.

At the same time, Amazon has introduced its own AWS Managed Service Provider Partner Program, complete with similar integration and migration services to help businesses utilize the AWS ecosystem without hassle. The two may seem comparable, but there are basic differences between traditional MSPs and cloud-based MSPs like AWS Next-Generation Managed Service Providers.

Before we get to those differences, however, we need to review the basic benefits of working with cloud-based MSPs, starting with the fact that you don’t have to be an expert in cloud infrastructure — or have a team of IT specialists to handle the migration — to move business operations to the cloud. This lowers the entry barrier substantially. For more information on working with managed service providers, check out this article here.

That simplicity leads to better migration of on-premise solutions to an environment with better accessibility. While traditional MSPs still rely heavily on centralized data centers, the process of moving on-premise solutions to boost its accessibility remains the same. Once the migration process is completed, team members can access business solutions from remote terminals.

The Case for AWS Next-Generation Managed Service Providers

Traditional MSPs have their benefits, but cloud managed services are the way forward. In this case, AWS Next-Generation Managed Service Providers have some clear advantages over traditional MSPs, starting with simplicity. While moving to AWS on your own is a hassle, having an entire team of AWS experts assisting you with everything from server setup to limited refactoring makes the whole thing simpler.

That last part — limited refactoring as a service — comes with its own advantages. Rather than moving older solutions to the cloud as big blocks, AWS Managed Services allows you to begin adjusting your solutions to fully take advantage of cloud computing from the moment you decide to move to the cloud. No more headaches to deal with along the way.

Researched conducted by Forrester Consulting on behalf of AWS returned an in-depth Total Economic Impact (TEI) study that demonstrated the extensive return on investment companies realized through working with APN Partners. AWS MSP Partners have a much deeper understanding of their client’s environments, and as such, they are able to predict things before issues even arise. AWS Next-Generation Managed Service Providers can be even more proactive than traditional MSPs too and are capable of thoroughly optimizing AWS services and customer experiences further up the stack too. Forrester’s interviews demonstrated that successful next-generation MSPs provided an ideal blend of professional and managed services as well as resale and business support services, complimentary third-party application sales, and value-add IP sales too.

There is also the fact that the entire AWS ecosystem is designed to be highly available from the ground up. Server issues are a thing of the past with Amazon Web Services. Your cloud environment can have multiple redundancies, a comprehensive backup solution, and other high-availability features.

Speed is another advantage to gain when you use AWS Managed Services. I’m not just talking about the speed at which the migration process gets completed either. AWS allows you to have instances closer to your users, which means users can enjoy a faster, more fluid business tools and solutions that run completely in the cloud.

Even changes and future updates are easier to handle. With the Infrastructure as a Service (IaaS) approach of AWS, everything is only a runtime away. Need to migrate to a different region? There is an automated process for that. Do you want to add a spurt of processing power for a very short period? Need to add additional EC2 instances for new business tools? Simply deploy one in your cloud environment. Need to add new functionality with new technology that you aren’t familiar with yet? A cloud MSP can easily procure it for you from AWS Services or the AWS Marketplace and leverage it for you in a very short time. The right cloud MSP will then teach you how to use new services and tech too.

The Time to Move

There is no better time to migrate to the cloud than right now, and there are two important reasons for that. Today’s market challenges are best solved with the power of cloud computing at your fingertips, which makes moving business operations to the cloud a great investment to make.

The second reason is the fact that moving to the cloud is now easy with AWS Next-Generation Managed Service Providers and other cloud MSPs taking the headaches out of the migration process. The boost in agility and the long list of benefits you can get from cloud computing make today the best time to migrate. Oh, and have we mentioned how affordable cloud-managed services are today?

from DZone Cloud Zone

Beginner’s Guide to Building an Online Retail Web Shop Workshop (Technical Rules)

Beginner’s Guide to Building an Online Retail Web Shop Workshop (Technical Rules)

With the release of Red Hat Decision Manager 7.3, I’ve started updating my free online workshop, a beginners guide to building an online retail web shop.

The previous article covered creating guided rules for your online retail web shop. This update is the for the sixth lab in this workshop, with more to follow. Learn how to create guided rules with Red Hat Decision Manager.

Below, you’ll find the embedded lab slides with all the instructions you need.

Create Technical Rules

In lab 6, get hands-on creating technical rules in Red Hat Decision Manager:

Image title

Next up in this workshop, create a guided decision table.

For more beginner’s guide workshops, please explore here.

from DZone Cloud Zone

Using ELBs and EC2 Auto-Scaling to Support AWS Workloads

Using ELBs and EC2 Auto-Scaling to Support AWS Workloads

Combining Elastic Load Balancers with EC2 Auto Scaling helps to manage and control your AWS workloads. This combination supports the demands put upon your infrastructure, while minimizing performance degradation. With this in mind, engineers and solution architects should have a deep understanding of how to implement these features.

In this article, we’ll cover the basics about Elastic Load Balancers and EC2 Auto Scaling.

Elastic Load Balancers

The main function of an Elastic Load Balancer, commonly referred to as an ELB, is to help manage and control the flow of inbound requests to a group of targets by distributing these requests evenly across the targeted resource group. These targets could be a fleet of EC2 instances, AWS Lambda functions, a range of IP addresses, or even containers. The targets defined within the ELB could be situated across different availability zones (AZs) for additional resilience or all placed within a single AZ.

Let’s look at this from a typical scenario. For example, let’s suppose you just created a new application currently residing on a single EC2 instance within your environment which is being accessed by a number of users. At this stage, your architecture can be logically summarized as shown below.

AWS EC2 architecture

If you are familiar with architectural design and best practices, then you would realize that using a single instance approach isn’t ideal; although, it would certainly work and provide a service to your users. However, this infrastructure layout brings some challenges. For example, the one instance where your application is located can fail – perhaps from a hardware or a software fault. If that happens, your application will be down and unavailable to your users.

Also, if you experience a sudden spike in traffic, your instance may not be able to handle the additional load based on its performance limitations. To strengthen your infrastructure and help remediate these challenges, such as the unpredictable traffic spikes and high availability, you should introduce an Elastic Load Balancer and additional instances running your application into the design as shown below.

AWS Architecture with ELB

As you can see in this design, the AWS Elastic Load Balancer acts as the point for receiving incoming traffic from users and evenly distributes the traffic across a greater number of instances. By default, the ELB is highly available since it is an AWS managed service, which works to ensure resilience so we don’t have to. Although it might seem the ELB is a single point of failure, the ELB is in fact comprised of multiple instances managed by AWS. Also, in this scenario we now have three instances running our application.

Now let me revisit the challenges we discussed previously. If any of these three instances fail, the ELB will automatically detect the failure based on defined metrics and divert any traffic to the remaining two healthy instances. Also, if you experienced a surge in traffic, then the additional instances running your application would help you with the additional load.

One of the many advantages of using an ELB is the fact that it is managed by AWS and it is, by definition, elastic. This means that it will automatically scale to meet your incoming traffic as the incoming traffic scales both up and down. If you are a system administrator or a DevOps engineer running your own load balancer by yourself, then you would need to worry about scaling your load balancer and enforcing high availability. With an AWS ELB, you can create your load balancer and enable dynamic scaling with just a few clicks.

Depending on your traffic distribution requirements, there are three AWS Elastic Load Balancers available:

  1. First, the Application Load Balancer: This provides a flexible feature set for your web applications running the HTTP or HTTPS protocols. The application load balancer operates at the request level. It also provides advanced routing, TLS termination, and visibility features targeted at application architectures, allowing you to route traffic to different ports on the same EC2 instance.
  2. Next, there is the Network Load Balancer: This is used for ultra-high performance for your application, while maintaining very low latencies at the same time. It operates at the connection level, routing traffic to targets within your VPC. It’s also capable of handling millions of requests per second.
  3. Finally, the Classic Load Balancer: This is primarily used for applications that were built in the existing EC2 classic environment and operates at both the connection and request level.

Now let me now talk a little about the components of an AWS Elastic Load Balancer and some of the principles behind them.

Listeners: For every load balancer, regardless of the type used, you must configure at least one listener. The listener defines how your inbound connections are routed to your target groups based on ports and protocols set as conditions. The configurations of the listener itself differ slightly depending on which ELB you have selected.

Target Groups: A target group is simply a group of your resources that you want your ELB to route requests to, such as a fleet of EC2 instances. You can configure the ELB with a number of different target groups, each associated with a different listener configuration and associated rules. This enables you to route traffic to different resources based upon the type of request.

Rules: Rules are associated to each listener that you have configured within your ELB, and they help define how an incoming request gets routed to which target group.

ELB rules

As you can see, your ELB can contain one or more listeners, each listener can contain one or more rules, each rule can contain more than one condition, and all conditions in the rule equal a single action. An example rule could look as follows, where the IF statement resembles the conditions and the THEN statement acts as the action if all the conditions are met.

ELB if statements

Depending on which listener the request was responded to by the ELB, a rule based upon a priority listing would be associated containing these conditions and actions. If the request comes from within the 10.0.1.0/24 network range (condition 1) and was trying to carry out a HTTP PUT request (condition 2) then the request would be sent to the target group entitled ‘Group1’ (Action).

Health Checks: The ELB associates a health check that is performed against the resources defined within the target group. These health checks allow the ELB to contact each target using a specific protocol to receive a response. If no response is received within set thresholds, then the ELB will mark the target as unhealthy and stop sending traffic to the target.

Internal or Internet-Facing ELBs: There are two different schemes that can be used for your Elastic Load Balancers, either internal or internet-facing.

  • Internet-Facing: As the name implies, the nodes of an ELB that are defined as internet-facing are accessible via the internet and so have a public DNS name that can be resolved to its public IP address, in addition to an internal IP address as well. This allows the ELB to serve incoming requests from the internet before distributing and routing the traffic to your target groups, which in this instance could be a fleet of web servers receiving HTTP or HTTPs requests. When your internet-facing ELB communicates with its target group, it will only use the internal IP address meaning that your target group do not need public IP addresses.
  • Internal ELB: An internal ELB only has an internal IP address. This means that it can only serve requests that originate from within your VPC itself. For example, you might have an internal load balancer sitting between your web servers in the public subnet and your application servers in a private subnet.

ELB Nodes: During the creation process of your ELBs, you are required to define which availability zone you’d like your ELB to operate within. For each AZ selected, an ELB node will be placed within that AZ. As a result, you need to ensure that you have an ELB node associated to an AZ for which you want to route traffic to. Without the AZ associated, the ELB will not be able to route traffic to any targets within the AZ even if they are defined within the target group. This is because the nodes are used by the ELB to distribute traffic to your target groups.

Cross-Zone Load Balancing: Depending on which ELB option you select, you may have the option of enabling and implementing cross-zone load balancing within your environment.

Let’s presume you have two availability zones activated for your ELB with each associated load balancer receiving equal amount of traffic. One AZ has six targets and the other has four, as shown below. When cross-load load balancing is disabled, each ELB in its associated AZ will distribute its traffic with the targets within that AZ only. As we can see from the image, this results in an uneven distribution of traffic for each target across the availability zones.

With cross-zone load balancing enabled, regardless of how many targets are in an associated AZ, the ELBs will distribute all incoming traffic evenly between all targets, ensuring each target across the AZs have an even distribution.

EC2 Auto Scaling

So what exactly is EC2 Auto Scaling? Put simply, auto scaling is a mechanism that automatically allows you to increase or decrease your EC2 resources to meet the demand based off custom-defined metrics and thresholds.

Let’s look at an example of how EC2 Auto Scaling can be used in practice. Let’s say you had a single EC2 instance acting as a web server receiving requests from the public users across the internet. As the requests increase (the demand), so does the load on the instance increase and additional processing power will be required to process the additional requests; therefore, the CPU utilization would also increase. To avoid running out of CPU resource on your instance – which would lead to poor performance experienced by your end users – you would need to deploy another EC2 instance to load balance the demand and process the increased requests.

With auto scaling, you can configure a metric to automatically launch a second instance when the CPU utilization gets to 75% of the first instance. By load balancing traffic evenly, it would reduce the demand put upon each instance and reduce the chance of the first web server failing or slowing due to high CPU usage. Similarly, when the demand on your web server reduces, so would your CPU utilization, so you could also set a metric to scale back. In this example, you could configure auto scaling to automatically terminate one of your EC2 instances when the CPU utilization dropped to 20% as it would no longer be required due to the decreased demand. Scaling your resources back helps you to optimize the cost of your EC2 fleet as you only pay for resources when they are running.

Through these customizable and defined metrics, you can increase (scale out) and decrease (scale in) the size of your EC2 fleet automatically with ease. This has many advantages, and here are some of the key points:

  • Automation – As this provides automatic provisioning based off of custom defined thresholds. Your infrastructure can elastically provision the required resources to prevent your operations team from manually deploying and removing resources to meet demands put upon your infrastructure.
  • Greater customer satisfaction – If you are always able to provision enough capacity within your environment when the demand increases, then it’s unlikely your end users will experience performance issues which will help with user retention.
  • Cost reduction – With the ability to automatically reduce the amount of resources you have when the demand drops, you will stop paying for those resources. You only pay for an EC2 resource when it’s up and running, which is based on a per second basis.

When you couple EC2 Auto Scaling with Elastic Load Balancer, you get a real sense of how beneficial building a scalable and flexible architecture for your resources can be.

from DZone Cloud Zone