Terraform by HashiCorp enables you to safely and predictably create, change, and improve infrastructure. It is an open source tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
Terraform is a simple and reliable way to manage infrastructure in AWS, Google Cloud, Azure, Digital Ocean and more IaaS (providers, in terms of Terraform). The main idea of such tools is to create reproducible infrastructure and Terraform provides DSL to describe infrastructure and then apply to different environments. Previously we used a set of Python and bash scripts to describe what create in AWS, described different conditions which checks if some resource exists in AWS and create if it's not. Actually, Terraform is doing the same underhood. This is an introduction which covers simple use case to create Alluxio cluster I used in the previous post.
Terraform supports a number of different providers, but Terraform script must be written every time for new provider.
понеділок, 20 листопада 2017 р.
Applying Alluxio to warm up your data
Alluxio, formerly Tachyon, enables any application to interact with any data from any storage system at memory speed.states https://www.alluxio.org/. In this article I'd like to describe the general idea of using Alluxio and how it helped me. Alluxio is not one known to everyone, however it has a lot of features to propose and can be a game changer for your project. Alluxio already powers data processing at Barclays, Alibaba, Baidu, ZTE, Intel, etc. The current license is Apache 2.0 and source code can be reviewed here https://github.com/Alluxio/alluxio .
Alluxio provides virtual filesystem which create a layer between your application (i.e. computational framework) and real storage such as HDFS, S3, Google Cloud Storage, Azure Blob Storage and so on. Alluxio has several interfaces: Hadoop compatible FS, native key-value interface, NFS interface. From component point of view, Alluxio has single Master (plus Secondary Master which similar to SNN in Hadoop, i.e. doesn't process requests from clients), multiple Slaves and, obviously, Client.
My use case was inspired by layered storage in HDFS: it's when you can configure HDFS to save specific HDFS paths on Hot storage (let say in memory) or Warm (~ SSD) or Cold (~ HDD). However, cloud usage is growing every day and it's not so often to see hardware Hadoop cluster and the issue with a clouds (at the same time, a benefit): storage is isolated from computations, which makes impossible or hard to implement storage layers. And that's very good use case for Alluxio: deploy alluxio cluster to play the role of Hot storage where only high-frequency used data is located.
While saving data on S3, we'd like to partition them by year, month and day to increase access speed while executing access to data in known time range. However it's not often happen to access data according to uniform distribution, much often there is very specific patterns like:
- actively access last 3 months
- actively access last month and the same month of last year
Let's see the practical example of working with data stored on S3 using Apache Spark on EMR.
I used Terraform to create Alluxio cluster, having 3 r4.xlarge slaves and one m4.xlarge master. Also, we will need computational power to run Spark job, let's create AWS EMR cluster:
aws emr create-cluster --name 'Alluxio_EMR_test' \
--instance-type m4.2xlarge \
--instance-count 3 \
--ec2-attributes SubnetId=subnet-131cda0a,KeyName=my-key-name,InstanceProfile=EMR_EC2_DefaultRole \
--service-role EMR_DefaultRole \
--applications Name=Hadoop Name=spark \
--region us-west-2 \
--log-uri s3://alluxio-poc/emrlogs \
--enable-debugging \
--release-label emr-5.7.0 \
--emrfs Consistent=true
After that, Alluxio is ready to be started and out data is ready to be pulled in:
[ec2-user@ip-172-16-175-35 ~]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c876a0ffe4d alluxio "/entrypoint.sh wo..." 9 minutes ago Up 9 minutes cranky_brown
[ec2-user@ip-172-16-175-35 ~]$ docker exec -it 1c876a0ffe4d /bin/sh
/ # cd /opt/alluxio/bin
/opt/alluxio/bin # ./alluxio runTests
/opt/alluxio/bin # ./alluxio fs mkdir /mnt
Successfully created directory /mnt
# the following command cache S3 folder inside of Alluxio
opt/alluxio/bin # ./alluxio fs mount -readonly alluxio://localhost:19998/mnt/s3 s3a://alluxio-poc/data
Mounted s3a://alluxio-poc/data at alluxio://localhost:19998/mnt/s3
/opt/alluxio/bin #
/opt/alluxio/bin # ./alluxio fs ls /mnt/s3
-rwx------ pc-nord-account66pc-nord-account66410916576 09-22-2017 18:03:10:815 Not In Memory /mnt/s3/part-00084-2e9dafb0-2d7a-428e-b517-b6eb4d70f781.snappy.parquet
Then, back to EMR Master and start spark shell:
spark-shell --jars ~/alluxio-1.5.0/client/spark/alluxio-1.5.0-spark-client.jar
The following command starts spark context and register alluxio file sustem:
val hadoopConf = sc.hadoopConfiguration
hadoopConf.set("fs.alluxio.impl", "alluxio.hadoop.FileSystem")
val x = spark.read.parquet("alluxio://172.16.175.46:19998/mnt/s3")
// let's see how fast is' gonna be
x.select($"itemid", $"itemdescription", $"GlobalTransactionID", $"amount").orderBy(desc("amount")).show(20) // 4 sec
x.count() // 3 sec
// now let's compare with s3 dataset
val p = spark.read.parquet("s3a://alluxio-poc/data")
p.select($"itemid", $"itemdescription", $"GlobalTransactionID", $"amount").orderBy(desc("amount")).show(20) // 19 sec
p.count() // value 19 sec
To sum up, Alluxio provides great way to speed up data processing in update-based warehouse when you need access only to limited dataset. Potential use case: hot data that must be accessed and processed x10 times more often, but is only 10% of all dataset is an ideal candidate to be cached with Alluxio.
# Einführung in Alluxio (in English)
четвер, 31 серпня 2017 р.
Druid: fixed lambda
Druid is an excellent high-performance, column-oriented and distributed data storage. Used by IT giant to get answers in sub-seconds from TBs (or even PBs) datasets. Needless to say I felt in love since day one.
Several examples:
Netflix ingest up to 2 TB per hour with the ability to query data as its being ingested
eBay ingest over 100.000 events'sec and supports over 100 concurrent queries without impacting ingest rate and query latency
Main featured of Druid that helps to stand out of the crowd:
- Sub-seconds query
- Scalable to PBs
- Real-time strams
- Deploy anywhere (can work with Hadoop or without by processing data from S3)
I'm excited I had an opportunity to work with Druid a year ago. It's really cool, works super fast and delivers excellent result! The JSON-based query language wasn't super hard to learn, I managed even to calculate average using post action:) previous MR experience really helped.
One remark, I'd like to add there:
we developer and tested druid based system in us-east-1 region, everything was good, deployment was automated, so we moved to prod which, surprisingly, was selected to be in Frankfurt AWS region. We got pretty nasty error in Druid when deployment script finished his work there:
Caused by: io.druid.segment.loading.SegmentLoadingException: S3 fail!
Looks like the problem was in additional configuration required for non US-east region, unfortunately there isn’t documentation so I derived that from source code, looks like it works now:
On each historical node, please add the following file “/opt/druid/config/_common/jets3t.properties” with a content:
storage-service.request-signature-version=AWS4-HMAC-SHA256
s3service.s3-endpoint=s3.eu-central-1.amazonaws.com
1st line forces to use v4 auth
2nd line sets endpoint, default is us-east-1, but for Frankfurt it must be s3.eu-central-1.amazonaws.com
Anyway, Metamarket team - thank you for great product! Now going to test Caravela from AirBnb
Anyway, Metamarket team - thank you for great product! Now going to test Caravela from AirBnb
пʼятниця, 29 липня 2016 р.
Protecting Spark UI, part 2: servlet filter
In the previous post it was described how to configure simple NGINX instance to add basic auth to Spark job. In this part let see what Spark's suggest itself by implementing filter.
Filter is an special class which participate in Java servlet lifecycle and is called on each request (and even response). Using filter a resource can be protected by basic authentication from unauthorized access. According to documentation the filter must be implemented and then passed (full name) as a parameter. Let's pass valid username and password through environment variables, it must be good enough, as it equals to the approach used to pass AWS credentials for instance. Obviously, this env variable must be set on the instance where driver is supposed to be run. Another option is to pass them as arguments into filter using spark..params param1=value1 param2=value2 ...
Let's imagine our class in the package my.company.filters (and using several helpers, like commons-codec, commons-lang)
public class BasicAuthFilter implements Filter {
private String login;
private String pass;
// this method is called one time on Filter creation
public void init(FilterConfig config) {
this.login = System.getenv("SPARK_LOGIN");
this.pass = System.getenv("SPARK_PASS");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest hreq = (HttpServletRequest) req;
HttpServletResponse hres = (HttpServletResponse) res;
String auth = hreq.getHeader( "Authorization" );
if ( auth != null ) {
int index = auth.indexOf(' ');
if ( index > 0 ) {
String[] creds = StringUtils.split( new String( Base64(auth.substring(index)), Charset.UTF_8), ':' );
if ( creds.length == 2 && login.equals(creds[0]) && pass.equals( creds[1] ) ) {
// auth passed successfully
return;
}
}
}
hres.setHeader( "WWW-Authenticate", "Basic realm=\"ProtectedSpark\"" );
hres.sendError( HttpServletResponse.SC_UNAUTHORIZED );
}
}
Ok, next step is to build JAR (pack this filter into JAR). After that, we can run our job in secured manner: execute spark-submit and pass newly assembled jar with flag --jars and through configuration (*.conf file or --conf param) pass full class path: spark.ui.filters=my.company.filters.BasicAuthFilter
Filter is an special class which participate in Java servlet lifecycle and is called on each request (and even response). Using filter a resource can be protected by basic authentication from unauthorized access. According to documentation the filter must be implemented and then passed (full name) as a parameter. Let's pass valid username and password through environment variables, it must be good enough, as it equals to the approach used to pass AWS credentials for instance. Obviously, this env variable must be set on the instance where driver is supposed to be run. Another option is to pass them as arguments into filter using spark.
Let's imagine our class in the package my.company.filters (and using several helpers, like commons-codec, commons-lang)
public class BasicAuthFilter implements Filter {
private String login;
private String pass;
// this method is called one time on Filter creation
public void init(FilterConfig config) {
this.login = System.getenv("SPARK_LOGIN");
this.pass = System.getenv("SPARK_PASS");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest hreq = (HttpServletRequest) req;
HttpServletResponse hres = (HttpServletResponse) res;
String auth = hreq.getHeader( "Authorization" );
if ( auth != null ) {
int index = auth.indexOf(' ');
if ( index > 0 ) {
String[] creds = StringUtils.split( new String( Base64(auth.substring(index)), Charset.UTF_8), ':' );
if ( creds.length == 2 && login.equals(creds[0]) && pass.equals( creds[1] ) ) {
// auth passed successfully
return;
}
}
}
hres.setHeader( "WWW-Authenticate", "Basic realm=\"ProtectedSpark\"" );
hres.sendError( HttpServletResponse.SC_UNAUTHORIZED );
}
}
Ok, next step is to build JAR (pack this filter into JAR). After that, we can run our job in secured manner: execute spark-submit and pass newly assembled jar with flag --jars and through configuration (*.conf file or --conf param) pass full class path: spark.ui.filters=my.company.filters.BasicAuthFilter
Protecting Spark UI, part 1: nginx
Apache Spark WEB UI is a descent place to check cluster health and monitor job performance, starting point for almost every performance optimization. A guys from Databricks hardworking on improvements of UI from version to version.
But it still have one issue which I'm facing on every project and which must be resolver every time: I'm talking about publicity of this information, everyone how can reach the port (defaults, 8080 or 4040) can then access UI, and all information there (and there are a lot of stuff you want to keep private).
There are several solution to deal with it:
But it still have one issue which I'm facing on every project and which must be resolver every time: I'm talking about publicity of this information, everyone how can reach the port (defaults, 8080 or 4040) can then access UI, and all information there (and there are a lot of stuff you want to keep private).
There are several solution to deal with it:
- Close all ports and configure nginx to listen specific port and forward requests (of course w/ basic authentication)
- protect UI using Spark's built-in method: implementing own filter
In this post, let's start from How to protect Spark UI with NGINX?
The instruction below is suitable for protecting standalone spark Web UI when job is executed in client mode (so you can predict where driver is up and run).
Let's assume that there is a node with both spark and nginx installed (obviously they can be on different nodes).
First of all, close all spark related ports (and there are a lot of them): they must be still accessible in-network. In Amazon, it easy to do with security groups: just specify appropriate CIDR mask for each inbound rule, for instance 172.16.0.0/12. Next, open 2 ports not used by Spark, but which you're going to make accessible to get into spark master ui or spark driver ui: just for example let's assume it's 2020 and 2020.
Now the small part left: configure nginx to perform basic auth and forward requests to Spark UI. In this case nginx is in provate network, so request will be handled by Spark and UI actually presented to end user.
Before configuring nginx itself, the file to keep proper configuration must be created:
It's simple to do with htpasswd tool, can be installed by running sudo yum install -y httpd-tools
Then generate password and store it into a file (user name will be spark and passowrd entered in CLI):
sudo htpasswd -c /etc/nginx/.htpasswd spark
Last step is to create proper nginx configuration (the eample is only to forward all request on Spark Master 8080 to 2000):
vi /etc/nginx/nginx2001.conf
{
events {
worker_connections 1000;
}
server {
listen 2020;
| auth_basic "Private Beta"; auth_basic_user_file /etc/nginx/.htpasswd; |
location / {
proxy_pass http://localhost:8080;
}
}
}
Actually, that's it. After that we just need to start nginx
nginx -c /etc/nginx/nginx2001.conf
And point prowser to HOST:2020 to be asked enter credentials and only after that be redirected to Spark Master UI.
вівторок, 6 жовтня 2015 р.
Apache Zeppelin: impressions
A notebooks are getting more and more attraction from data analytics, data scientists and developers. Jupiter is a famous notebooks created by Python guys and widely adopted among different users. At the same time, the new notebook provider was recently born: Apache Zeppelin with main focus on integration with BigData technology stack.
In fact, Apache Zeppelin provides build-in integration with Apache Spark (and SparkSQL), Apache Flink, Hive, Ignite, Tajo (does someone outside South Korea is using that?), definitely markdown and html, and event AngularJS. It's good part about Zeppelin. Also, Ambari integration give a possibility to install Zeppelin in "a couple clicks" and get access through Amabari Views. And practically it works very well:
And now I'd like to focus on the what's wrong with Apache Zeppelin:
1) Security. Zeppelin 0.5 doesn't have security. Anybody can open any notebook, view and edit that. It doesn't work for enterprises, moreover it doesn't work even for RnD. I want to have protected notebooks, I want to have roles and groups, and give notebook only to specific group of people for specific set of actions.
2) Workspace. One-level list of notebooks, really? That's awful. Guys, add possibility to combine them in folders of folders and etc, it's really important. Also, only one way to backup notebooks, is to backups underlying folders from filesystem. Not very good, UI button is required at least.
3) Security 2. I've already written about notebooks security, but data on storage is also must be protected. Currently Zeppelin run everything as ZEPPELIN user, and I have to share data with ZEPPELIN users which is not what I want to do. So, it makes sense for each notebook to provide a setting "run as" to specify specific user for this research. Enterprises really value that.
Personally I also tried to make it works on Docker (more or less it works) and EMR (failed, and everybody failed as far as I know).
To sum up: Zeppelin is an interesting and promising product, but it has to much weakness to be seriously used and consider for production projects, specially for enterprises. So, in technology radar I can definitely put Zeppelin into the section "Be informed"
In fact, Apache Zeppelin provides build-in integration with Apache Spark (and SparkSQL), Apache Flink, Hive, Ignite, Tajo (does someone outside South Korea is using that?), definitely markdown and html, and event AngularJS. It's good part about Zeppelin. Also, Ambari integration give a possibility to install Zeppelin in "a couple clicks" and get access through Amabari Views. And practically it works very well:
1) Security. Zeppelin 0.5 doesn't have security. Anybody can open any notebook, view and edit that. It doesn't work for enterprises, moreover it doesn't work even for RnD. I want to have protected notebooks, I want to have roles and groups, and give notebook only to specific group of people for specific set of actions.
2) Workspace. One-level list of notebooks, really? That's awful. Guys, add possibility to combine them in folders of folders and etc, it's really important. Also, only one way to backup notebooks, is to backups underlying folders from filesystem. Not very good, UI button is required at least.
3) Security 2. I've already written about notebooks security, but data on storage is also must be protected. Currently Zeppelin run everything as ZEPPELIN user, and I have to share data with ZEPPELIN users which is not what I want to do. So, it makes sense for each notebook to provide a setting "run as" to specify specific user for this research. Enterprises really value that.
Personally I also tried to make it works on Docker (more or less it works) and EMR (failed, and everybody failed as far as I know).
To sum up: Zeppelin is an interesting and promising product, but it has to much weakness to be seriously used and consider for production projects, specially for enterprises. So, in technology radar I can definitely put Zeppelin into the section "Be informed"
понеділок, 13 липня 2015 р.
How to waste the whole day with Spark Streaming and HBase
The "funny" story how to waste the whole day debugging resolving simple case... tips and tricks :)
Spark Streaming application hangs out on action and nothing is changing during hours.
The long story is: custom Receiver accept events from external source and store them to RDD (actually, DStream) for future processing. When I run it I noticed that action hung out! And what was a really scare: the messages were read from source. After spending couple hours trying to find the issue with Reciever, I realized it works fine and finally found the issue ... ... in how I run the job!
I did it in local environment first and submit it to YARN:
...
--num-executors 2
...
It fact, it doesn't work for me because no one worker (spark executor) was able to start! So, just by increasing number of executors to 3, I was able to make everything working.
HBase related Spark Streaming application hangs out and nothing is changing during hours.
Again, the long story is then Spark Streaming application hangs out as soon as it touch HBase. I spent several hours (again) and I was really surprised when found the reason: HBase connection was broken. OMG! I haven't seen any errors or warning related to HBase connection in logs... what is the reason? In fact, HBase tried to establish connection again and again without throwing an error. Consider the following a piece of code (grey - my original part, when blue - an update that helped me to overcome the issue):
Configuration config = HBaseConfiguration.create();
config.set(HConstants.ZOOKEEPER_QUORUM, "host:port");
config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/hbase");
config.set("hbase.client.retries.number", Integer.toString(3));
config.set("zookeeper.session.timeout", Integer.toString(60000));
config.set("zookeeper.recovery.retry", Integer.toString(0));
It really helps because of the number of retries was limited. Default value is 35 and can definitely confuse.
Spark Streaming application hangs out on action and nothing is changing during hours.
The long story is: custom Receiver accept events from external source and store them to RDD (actually, DStream) for future processing. When I run it I noticed that action hung out! And what was a really scare: the messages were read from source. After spending couple hours trying to find the issue with Reciever, I realized it works fine and finally found the issue ...
I did it in local environment first and submit it to YARN:
...
--num-executors 2
...
It fact, it doesn't work for me because no one worker (spark executor) was able to start! So, just by increasing number of executors to 3, I was able to make everything working.
HBase related Spark Streaming application hangs out and nothing is changing during hours.
Again, the long story is then Spark Streaming application hangs out as soon as it touch HBase. I spent several hours (again) and I was really surprised when found the reason: HBase connection was broken. OMG! I haven't seen any errors or warning related to HBase connection in logs... what is the reason? In fact, HBase tried to establish connection again and again without throwing an error. Consider the following a piece of code (grey - my original part, when blue - an update that helped me to overcome the issue):
Configuration config = HBaseConfiguration.create();
config.set(HConstants.ZOOKEEPER_QUORUM, "host:port");
config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/hbase");
config.set("hbase.client.retries.number", Integer.toString(3));
config.set("zookeeper.session.timeout", Integer.toString(60000));
config.set("zookeeper.recovery.retry", Integer.toString(0));
It really helps because of the number of retries was limited. Default value is 35 and can definitely confuse.
вівторок, 9 червня 2015 р.
How to present XML in Hive flat table after XSLT transformation
Let's start from defining a task. Imaging that the dataset is a set of XML files and the requirement is to present some specific information from this file as simple flat structure. Let's illustrate:
Definetely, we can use SerDe for XML, but what if XML structure is not defined before hand and we want to give end-user a chance to control parsing process? One of possible solutions is to incorporate XSLT to transform XML to desired format.
пʼятниця, 30 січня 2015 р.
Demystify BloomFilter on Hadoop
I believe most of you have seen BloomFilter class. But how to correctly use it?
Accordint to Wikipedia, "A Bloom filter is a space-efficient probabilistic data structure, conceived by Burton Howard Bloom in 1970, that is used to test whether an element is a member of a set. False positive matches are possible, but false negatives are not, thus a Bloom filter has a 100% recall rate. In other words, a query returns either "possibly in set" or "definitely not in set"."
Also, I found this site wich give a very goo description of Bloom filter with perfect visualization, please check
As it is clear from Bloom filter definition, this datastructure can really help when we need to filter some records. Particularly, performing join: in this case we can transform small dataset into filter, and then apply filter on map stage in second MR, which perform a real join. In other words, we will have 2 MR when 1st is used for creating filter and 2nd is used to perform filtrtion on map and join on reduce.
Ok, first MepReduce contains 2 stages: mapper and reducer, because in result we should got exactly one Bloom filter object:
Your filter is prepared now, it can be desiarilized at any place and used for data filtration.
Accordint to Wikipedia, "A Bloom filter is a space-efficient probabilistic data structure, conceived by Burton Howard Bloom in 1970, that is used to test whether an element is a member of a set. False positive matches are possible, but false negatives are not, thus a Bloom filter has a 100% recall rate. In other words, a query returns either "possibly in set" or "definitely not in set"."
Also, I found this site wich give a very goo description of Bloom filter with perfect visualization, please check
As it is clear from Bloom filter definition, this datastructure can really help when we need to filter some records. Particularly, performing join: in this case we can transform small dataset into filter, and then apply filter on map stage in second MR, which perform a real join. In other words, we will have 2 MR when 1st is used for creating filter and 2nd is used to perform filtrtion on map and join on reduce.
Ok, first MepReduce contains 2 stages: mapper and reducer, because in result we should got exactly one Bloom filter object:
- initialize BloomFilter object as Mapper clas member: BloomFilter = new BloomFilter(10000, 10, hash.MURMUR_HASH)
- on each record, add it to filter: filter.add( new Key(str.getBytes()) );
- emmit data only in cleanup method, for example you can just write file withoutusing context at all
Your filter is prepared now, it can be desiarilized at any place and used for data filtration.
пʼятниця, 23 січня 2015 р.
Composite join with MapReduce
As everyone knows, map-side join is the most effective techniques to join datasets on Hadoop. However, at the same time it gives a possibility to join ONE BIG dataset and ONE OR MORE SAMLL datasets. This is the limitation, because sometimes you wish to join TWI HUGE datasets. Typically, this is the use case for reducer-side join, but it cause Cartesian product and obviously we would like to ommit so heavy operation.
And this is time for Composite join: map-side join on huge datasets. In fact, both datasets must meet several requirements in this case:
The mapper with have key-value pair of type Text, TupleWritable:
Bonus: you can use this powerful feature with Hive! Composite join in Hive: To do that, the following hive properties must be set:
hive.input.format=org.apache.hadoop.give.ql.io.BucketizedHiveInputFormat;
hive.optimize.bucketmapjoin=truel
hive.optimize.bucketmapjoin.sortedmerge=true;
Ofcourse, it requires all the keys to be sorted in both tables and then must be bucketized in the same number of buckets
And this is time for Composite join: map-side join on huge datasets. In fact, both datasets must meet several requirements in this case:
- The datasets are all sorted by the join key
- Each dataset has the same number of file (you can achive that by setting reducers number)
- File N in each dataset contains the same join key K
- Each file is not splitable
In this case you can perform map join to join block from dataset A versus block from dataset B. Hadoop API provides CompositeInputFormat to achive this requirement. Example of usage:
// in job configuration you have to set job.setInputFormatClass(CompositeInputFormat.class); // inner - reference to inner join (you can specify outer as well) // d1, d2 - Path to both datasets job.getConfiguration().set(CompositeInputFormat.JOIN_EXPR, CompositeInputFormat.compose("inner", KeyValueTextInputFormat.class, d1, d2)); job.setNumReduceTasks(0);
The mapper with have key-value pair of type Text, TupleWritable:
@Override public void map(Text key, TupleWritable value, Context ctx) { ... }
hive.input.format=org.apache.hadoop.give.ql.io.BucketizedHiveInputFormat;
hive.optimize.bucketmapjoin=truel
hive.optimize.bucketmapjoin.sortedmerge=true;
Ofcourse, it requires all the keys to be sorted in both tables and then must be bucketized in the same number of buckets
Kafka web console with Docker
My first Docker file aims to run Kafka Web Console (application for monitoring Apache Kafka):
Dockerfile might be buid with command:
docker build -t kafka/web-console:2.0 .
and run as:
docker run -i -t -p 9000:9000 kafka/web-console:2.0
At the end, Kafka Web Console will be available at host:9000 - zookeeper hosts must be added and Kafka brokers will be discovered aautomatically
FROM ubuntu:trusty RUN apt-get update; apt-get install -y unzip openjdk-7-jdk wget git docker.io RUN wget http://downloads.typesafe.com/play/2.2.6/play-2.2.6.zip RUN unzip play-2.2.6.zip -d /tmp RUN wget https://github.com/claudemamo/kafka-web-console/archive/master.zip RUN unzip master.zip -d /tmp WORKDIR /tmp/kafka-web-console-master CMD ../play-2.2.6/play "start -DapplyEvolutions.default=true"
Dockerfile might be buid with command:
docker build -t kafka/web-console:2.0 .
and run as:
docker run -i -t -p 9000:9000 kafka/web-console:2.0
At the end, Kafka Web Console will be available at host:9000 - zookeeper hosts must be added and Kafka brokers will be discovered aautomatically
вівторок, 11 листопада 2014 р.
Spark and Location Sensitive Hashing, part 2
This is a second part of topic about Locality Sensitive Hashing, and here is example of creating working example using Apache Spark.
Let's start from definition of task: there are two datasets - bank accounts and web-site visitors. In common, they have only name, but it's possible misspeling. Let's consider the following example:
Bank Accounts
Web-site Visitors
Let's start from definition of task: there are two datasets - bank accounts and web-site visitors. In common, they have only name, but it's possible misspeling. Let's consider the following example:
Bank Accounts
| Name | Tom Soyer | Andy Bin | Tom Wiscor | Tomas Soyér |
| Credit score | 10 | 20 | 30 | 40 |
Web-site Visitors
| Name | Tom Soyer | Andrew Bin | Tom Viscor | Thomas Soyer |
| 1@1 | 2@1 | 3@1 | 2@2 |
пʼятниця, 7 листопада 2014 р.
Spark and Location Sensitive Hashing, part 1
Location Sensitive Hashing is the name of special algorithm designed to address complexity of BigData processing.
Let's consider the follwoing example: assume we have two independent systems, one is web-application that gets user's profile from social network, second system is online payment system. Our idea is merge profiles from social network and payment system. Of course, the social network user might not be presented in payment system at all, cerate accounts in different time and definetely we don't have foreign key to match them exactly. There are two possible issues:
Let's consider the follwoing example: assume we have two independent systems, one is web-application that gets user's profile from social network, second system is online payment system. Our idea is merge profiles from social network and payment system. Of course, the social network user might not be presented in payment system at all, cerate accounts in different time and definetely we don't have foreign key to match them exactly. There are two possible issues:
- there are two huge data sets that must be merged
- an user's name might look different in social network and payment system
The naive approach is to compare social network user and payment system user names, calculate Hamming distance between them and pick up the most similar pair as successfuly matched. The biggest issue here is O(n2) complexity of this approach.
We want to minimize a number of comparison between two datasets. Hopefully, this issue was resolved by inventing Location Sensitive Hashing algorithm. Let's consider simple hashing:
f(str) → x
we can calculate hashing function f on string (user name from profile) s and get integer x; then we need to compare Hamming distances only for strings which have the same x. The issue here is to pick up very good hashing function, which is almost impossible. Hopefully, we are not limited by one function: we can apply several/tens/hundreds hashing functions - in this case we would have data duplication, because one string would be assigned to several buckets (hash value). It would increase the number of useles comparisons, but at the some moment we would have a bigger chance to get succesful comparison.
However, it wouldn't work good enough, because names might have misprintings and using special lettern in social profile when only traditional latin in payments system or vice versa. n-grams and minhashing might come in handy in this situation. The main idea is to get all possible n-grams for string and apply minhashing algorithm to them. In result, we aims to get a set of new hash codes based on n-grams and make comparison of string that was placed into the same buckets based on these hashcodes.
Step by step algorithm is next:
In next part: source code example and implementation over Apache Spark
However, it wouldn't work good enough, because names might have misprintings and using special lettern in social profile when only traditional latin in payments system or vice versa. n-grams and minhashing might come in handy in this situation. The main idea is to get all possible n-grams for string and apply minhashing algorithm to them. In result, we aims to get a set of new hash codes based on n-grams and make comparison of string that was placed into the same buckets based on these hashcodes.
Step by step algorithm is next:
- Define a collection of hash functions
- Calculate minhash function on n-gramm of profile by minhash algo
- Based on equals hashcodes get pairs of similar profiles from social and payment networks
- Calculate Hamming distance in pairs to select the most similar matching for each case
In next part: source code example and implementation over Apache Spark
пʼятниця, 10 жовтня 2014 р.
Tuning the MapReduce job
that's what I got yesterday while running my new shining MapReduce job.java.lang.OutOfMemoryError: GC overhead limit exceeded
OutOfMemory in java has different reasons: no more memory available, or GC was called to often (my case), no more free PermGem space, etc.
To get more information, about JVM internals we have to tune JVM runing. I'm using Hortonworks distribution, so I went to Ambari, MapReduce configuration tab and found mapreduce.reduce.java.opts This property is responsible for reducer's JVM configuration. Let's add GarbageCollector loggining
-verbose:gc -Xloggc:/tmp/@taskid@.gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
We set up to write GC log to local filesystem in folder tmp, file name - taskId + gc extension.
In general, the following properties are important for JVM tuning:
- mapred.child.java.opts - Provides JVM options to pass to map and reduce tasks. Usually includes the -Xmx option to specify the maximum heap size. May also specify -Xms to specify the start heap size.
- mapreduce.map.java.opts - Overrides mapred.child.java.opts for map tasks.
- mapreduce.reduce.java.opts - Overrides mapred.child.java.opts for reduce tasks.
It'a but diffiulty to read the log, but hopefulyl several UI tools exist on the market. i prefer the open sourced GCViewer, which is java application and doesn't require instalation. It supports wide range of JVM, moreove it has command line interface for generation reports - so automation for getting reports might be applied.
The open GC log gets the detail overview of memory state:
Legend:
- Green line that shows the length of all GCs
- Magenta area that shows the size of the tenured generation (not available without PrintGCDetails)
- Orange area that shows the size of the young generation (not available without PrintGCDetails)
- Blue line that shows used heap size
четвер, 9 жовтня 2014 р.
Unit test for Hive query
Sometimes the soul wants something really extraordinaly... for example, to write a unit test for Hive query :)
Let's how it is possible step be step. So, to write unit test for Hive:
First of all, the local hive instance must be run, and for that we need local metastor (I propose Apache Derby) and directories for temporary data, logs, etc. As all configuration will be read from system properties, I didn't find beter way then set up all of them programaticaly...
Be shure to create all mentioned directories before starting Hive, for example with google Guava:
FileUtils.forceMkdir(HIVE_BASE_DIR);
And after then register all of them in system environment:
After that, the local hive executor might be started:
In fact, we are ready in this moment. Now I propose to create a Hive table, load data into it and perform some queries. The best practice in Java world is to put all metadata/data for test in separate file, so I put them under resources directory in this example, and here is reading from resource text files:
Ok, now data in the table and Hive knows about them. Let's perform a query:
Even more, we can register custom function and test it!
And after that we can call fresh function:
Let's how it is possible step be step. So, to write unit test for Hive:
First of all, the local hive instance must be run, and for that we need local metastor (I propose Apache Derby) and directories for temporary data, logs, etc. As all configuration will be read from system properties, I didn't find beter way then set up all of them programaticaly...
Be shure to create all mentioned directories before starting Hive, for example with google Guava:
FileUtils.forceMkdir(HIVE_BASE_DIR);
And after then register all of them in system environment:
System.setProperty("javax.jdo.option.ConnectionURL", "jdbc:derby:;databaseName=" + HIVE_METADB_DIR.getAbsolutePath() + ";create=true"); System.setProperty("hive.metastore.warehouse.dir", HIVE_WAREHOUSE_DIR.getAbsolutePath()); System.setProperty("hive.exec.scratchdir", HIVE_SCRATCH_DIR.getAbsolutePath()); System.setProperty("hive.exec.local.scratchdir", HIVE_LOCAL_SCRATCH_DIR.getAbsolutePath()); System.setProperty("hive.metastore.metadb.dir", HIVE_METADB_DIR.getAbsolutePath()); System.setProperty("test.log.dir", HIVE_LOGS_DIR.getAbsolutePath()); System.setProperty("hive.querylog.location", HIVE_TMP_DIR.getAbsolutePath()); System.setProperty("hadoop.tmp.dir", HIVE_HADOOP_TMP_DIR.getAbsolutePath()); System.setProperty("derby.stream.error.file", HIVE_BASE_DIR.getAbsolutePath() + sep + "derby.log");
After that, the local hive executor might be started:
HiveInterface client = new HiveServer.HiveServerHandler();
In fact, we are ready in this moment. Now I propose to create a Hive table, load data into it and perform some queries. The best practice in Java world is to put all metadata/data for test in separate file, so I put them under resources directory in this example, and here is reading from resource text files:
client.execute(readResourceFile("/Example/table_ddl.hql")); client.execute("LOAD DATA LOCAL INPATH '" + this.getClass().getResource("Example/data.csv").getPath() + "' OVERWRITE INTO TABLE " + tableName);
Ok, now data in the table and Hive knows about them. Let's perform a query:
client.execute("select sum(revenue), avg(revenue) from " + tableName + " group by state");
Even more, we can register custom function and test it!
client.execute("ADD JAR " + HIVE_BASE_DIR.getAbsolutePath() + jar.getAbsoluteFile()); client.execute("CREATE TEMPORARY FUNCTION TempFun as 'org.my.example.MainFunClass'");
And after that we can call fresh function:
client.execute("select TempFun(revenue) from " + tableName); String revenueProcessed = client.fetchOne();
понеділок, 18 серпня 2014 р.
Writing in ElasticSearch directly from Hadoop MapReduce
ElasticSearch is a hot topic today. This is powerful open source search and analytics engine that makes data easy to explore. Several times I faced with data populating into ElasticSearch after Hadoop jobs completion. A couple years it was non trivial issue that requires using binary ElasticSearch client and publishing data manually. Hopefully, there is already support by EalsticSearch for Hadoop today.
Let's see how it might be done with a simplest case: we have to put JSON formatted data into ElasticSearch for further analysis. So, our purpose is to write Map-only job that will populate ElasticSearch with data from text file (already in JSON).
First of all, let configure Configuration object:
I guess, everything is clear here.
Very important is to set up correct output format, pay attention on register:
After that we will implement Mapper (it emits only value, without key - this behavior is required by ES output format class!):
Let's back to the second code snippet. There is EsOutputFormat, pay attention on register, because there is old deprecated API with ESOutputFormat class.It might be required to add exclusion to Maven file, to pull correct versions of jars and omit dependencies hell:
yarn
cascading</groupId>
cascading-hadoop
cascading
cascading-local
</exclusion>
org.apache.pig
pig
org.apache.hive
hive-service
Let's see how it might be done with a simplest case: we have to put JSON formatted data into ElasticSearch for further analysis. So, our purpose is to write Map-only job that will populate ElasticSearch with data from text file (already in JSON).
First of all, let configure Configuration object:
conf.setBoolean("mapred.map.tasks.speculative.execution", false); conf.setBoolean("mapred.reduce.tasks.speculative.execution", false); conf.set("es.resource", "emailIndex/email"); // intex/type conf.set("es.nodes", "192.168.12.04"); // host conf.set("es.port", "11000"); // port conf.set("es.input.json", "yes");
I guess, everything is clear here.
Very important is to set up correct output format, pay attention on register:
// Set input and output format classes job.setInputFormatClass(TextInputFormat.class); job.setOutputFormatClass(EsOutputFormat.class); // Specify the type of output keys and values job.setOutputKeyClass(NullWritable.class); job.setOutputValueClass(Text.class);
After that we will implement Mapper (it emits only value, without key - this behavior is required by ES output format class!):
public static class EmailToEsMapper extends org.apache.hadoop.mapreduce.Mapper<LongWritable, Text, NullWritable, Text> { private Text output = new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String email = value.toString(); output.set(email) context.write(NullWritable.get(), output); } }
Let's back to the second code snippet. There is EsOutputFormat, pay attention on register, because there is old deprecated API with ESOutputFormat class.It might be required to add exclusion to Maven file, to pull correct versions of jars and omit dependencies hell:
org.elasticsearch</groupId> <artifactId>elasticsearch-hadoop</artifactId> 1.3.0.M2
середа, 13 серпня 2014 р.
Geo Coordinates converting
I've made discovery working on the last task: could you imagine that there are many many many geographical coordinate systems in the world? I couldn't. I was pretty sure that there is only one: longitude and latitude.
Surprise! There are much more of them and they are widely popular. Some of them are used in particular domain, some of them are specific for some countries. For example, you can read more about Gauss–Krüger coordinate system.
Ok, it looks good. One time consuming issue - it's to include correct libraries with Maven, because this small piece of code has very wide dependencies and it took several hours to manage correct combination :)
So, maven dependencies:
Surprise! There are much more of them and they are widely popular. Some of them are used in particular domain, some of them are specific for some countries. For example, you can read more about Gauss–Krüger coordinate system.
import org.geotools.geometry.GeneralDirectPosition; import org.geotools.referencing.CRS; import org.opengis.geometry.DirectPosition; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; public strictfp double[] translate(String from, String to, double x, double y) throws FactoryException, NoSuchAuthorityCodeException, TransformException { CoordinateReferenceSystem sourceCRS = CRS.decode( from ); CoordinateReferenceSystem targetCRS = CRS.decode( to ); MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true); DirectPosition expPt = new GeneralDirectPosition(x, y); expPt = transform.transform(expPt, null); return expPt.getCoordinate(); }
Ok, it looks good. One time consuming issue - it's to include correct libraries with Maven, because this small piece of code has very wide dependencies and it took several hours to manage correct combination :)
So, maven dependencies:
<dependency> <groupId>org.geotools</groupId> <artifactId>gt-opengis</artifactId> <version>2.7.0.1</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-metadata</artifactId> <version>2.7.0.1</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-referencing</artifactId> <version>2.7.0.1</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-epsg-hsql</artifactId> <version>2.7.0.1</version> </dependency> <dependency> <groupId>javax.media</groupId> <artifactId>jai_core</artifactId> <version>1.1.3</version> </dependency>
Підписатися на:
Дописи (Atom)


