Sunday, October 31, 2010

My _real_ blog

blog.sadogursky.com

Wednesday, December 24, 2008

Featured @ SlideShare

Yo! The presentation was selected as "featured" on SlideShare and now it appears on the main screen!

Sunday, December 7, 2008

Wednesday, October 8, 2008

The Run

Let's roll!
  • Start one instance of Server AMI
  • The Terracotta server starts automatically
  • Associate its instance ID with the Elastic IP, referred in tc-config.xml in the Client AMI
  • Start one instance Client AMI
  • The Shared Queue example runs
  • Copy the client's public DNS and access the UI in browser (http://the-client-public-dns:1990/webapp/)
  • Add less than 100 jobs to the queue
  • All the jobs processed by the existing single client
  • Add more than 100 jobs to the queue
  • See in the ElasticFox how another client instance is getting up and running
  • Check the UI to see how two clients now handling the jobs!
That's all :-)

Stage 4 - The Client AMI

The last stage - create the client AMI. Since I already had Server AMI with JDK and Terracotta installed, I only needed to complete couple of other things:
  • I instansiated server
  • Remotely copied the src and lib directories of the demo (the new code and the new dependencies), the run.sh (with the new classpath) and the build.xml (with the new compile classpath)
  • Logged in to the server and run build.
  • Added run.sh to the init.d (for the autostart of the client once the machine is up)
  • Created AMI, uploaded it to S3 and registered with EC2 (see Stage 2).
  • Added ports 1990-2000 (10 clients) to the whitelist of my custom "Terracotta" security group (already done in Stage 3).

Tuesday, September 30, 2008

Stage 3 - The Code

Now it's time to add the auto-start code.
I tried to change the existing code as little as possible (that's the idea - keep the changes small).
Ophir pointed me to great EC2 API for Java - Typica.
It has a little dependencies, so I added them to the demo, and changed the build appropriately.
The most simple and less intrusive addition I can think of is listener on the "doAddWork" operation in HttpHandler.
As it being invoked, the only new class, Ec2Instantiator is notified. There I can encapsulate all the EC2 work without intruding the example.

So, what the code does?

First of all, it checks if new server is needed - if after the addition more than 100 jobs will be in the queue, fire new server. It's stupid check, but very simple, isn't it?
If that's the case, load different EC2 properties from properties file. Those properties include:
  • AWS Access Id and Secret Key to perform any operations
  • Owner Id to find my images
  • Client Image Location to distinguish it from Server Image
  • Key Name to start the server with it
  • Custom Security Group additional security group (in addition to default) to run the server with it. In my case it's the same Terracotta security group, which opens port 9510 for the server, and ports 1190-2000 for 10 clients
Once the properties are loaded, the code gets all images by owner. From those, it picks one, which matches client image location, and launches one instance of it.

That's it! As simple as it gets! Here is the code:

@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"})

15
public class Ec2Instantiator implements QueueGrowListener {
16
17
private String accessId;
18
private String accessKey;
19
private String ownerId;

20
private String clientImageLocation;
21
private String keyName;
22
private String customSecurityGroup;
23
private int triggerSize;


24
25
public Ec2Instantiator(Properties appProps) {
26
this.accessId = appProps.getProperty("aws.accessId");
27
this.accessKey = appProps.getProperty("aws.secretKey");

28 ownerId = appProps.getProperty(
"aws.ownerId");
29 clientImageLocation = appProps.getProperty(
"aws.clientImageLocation");
30 keyName = appProps.getProperty(
"aws.keyName");
31 customSecurityGroup = appProps.getProperty(
"aws.customSecurityGroup");

32 triggerSize = Integer.parseInt(appProps.getProperty(
"queue.triggerSize"));
33 }

34
35
public void queueGrown(int oldSize, int newSize) {

36
37
//All this happnes only if too many jobs added to the queue
38
if (newSize > triggerSize) {
39 System.out.println(
"Queue overloaded! Will fire up another client to help");

40
41
//Start new instance asyncronicaly
42
new Thread(new Runnable() {
43
public void run() {
44
try {

45
46
//connect to EC2
47 Jec2 ec2 =
new Jec2(accessId, accessKey);
48
49
//get all my images

50 List<ImageDescription> myImages = ec2.describeImagesByOwner(asList(ownerId));
51 ImageDescription clientImage =
null;
52
for (ImageDescription image : myImages) {
53

54
//find the Terracotta client one
55
if (image.getImageLocation().equals(clientImageLocation)) {
56 clientImage = image;
57 }
58 }

59
if (clientImage == null) {
60
throw new IllegalStateException("Can't find image manifest in " + clientImageLocation);
61 }
62

63 System.out.println(
"Found JavaEdge Client image: "+clientImage.getImageId());
64
65
//launch configuration - which machine, which security groups
66 LaunchConfiguration launchConfiguration =
new LaunchConfiguration(clientImage.getImageId());
67 launchConfiguration.setKeyName(keyName);

68
69
//small instance
70 launchConfiguration.setInstanceType(InstanceType.DEFAULT);
71
72
//both default and terracotta security groups
73 launchConfiguration.setSecurityGroup(asList(
"default", customSecurityGroup));

74
75
//RUN!
76 System.out.println(
"Starting instance.");
77 ReservationDescription reservationDescription = ec2.runInstances(launchConfiguration);
78 ReservationDescription.Instance instance = reservationDescription.getInstances().get(
0);//i run one, so there is one

79
80
//check the state
81 System.out.println(
"Instance state: " + instance.getState());
82
while (!instance.isRunning()) {
83 paintActivity();

84 instance = ec2.describeInstances(asList(instance.getInstanceId())).get(
0).getInstances().get(0);//refresh
85 }
86 System.out.println(
"Instance is up. Check the UI to see the new client in action.");

87 }
catch (EC2Exception e) {
88 System.err.println(
"Error during client instance instantiation");
89 e.printStackTrace();
90 }
91

92 }
93 }).start();
94 }
95 }

Sunday, September 28, 2008

Stage 2 - Create Server AMI

OK, now to the real thing - create the Amazon Machine Images (AMIs) for the server and the client. I started with the server.
Using Elasticfox I choose one of the Ubuntu Hardy basic images, run it, logged in.
Two things to install on the server machine:
  • Java
  • Terracotta server
Java is easy - apptitude install sun-java6-jdk, and this is it. Accept the EULA, export JAVA_HOME, done.

Terracotta was more complicated. I didn't find debian distro for it, nor public download link on the site, so I scp-ed the tar.gz archive from my computer. Unzip it, and it runs!

Next step was to create AMI from the current setup, upload it to Amazon S3 storage and to register it in EC2. Step-by-step instructions are here.

Time to check the server. Terracotta server runs by default on port 9510. I created new permissions group in which this port is open (using Elasticfox, of course), and next I chose my own (!) image from the available images, and booted an instance with two permissions groups - default (for SSH etc.) and my custom one. Hooray, it is already preconfigured and has everything I need! The only thing I have to improve is to add the terracotta server startup to init.d.

Now, the problem arose. In the tc-config.xml file I have to specify the server's IP, but the IP I get from EC2 changes every time I boot new instance.
Elastic IP to the rescue! I can register IPs for my AMI user, and assign them to running instances. This is exactly what I need. I registered elastic IP using Elasticfox, and assigned it to my running server instance.

The sharedqueue sample runs smoothly. I am sceptical, so I want to check if it really use the server on EC2. I checked the logs on the server, and it is! The server got connection from my local IP!

P.S. Useful links for EC2 config and run:
  • EC2 Getting Started Guide - command line utilities, most of their operations can be done in Elasticfox, but gives great picture of what's going on.
  • Firefox EC2 UI screencast - shows old version of Elasticfox in action. The basics are the same, so it is useful.

Saturday, September 27, 2008

Stage 1 - Download & Run Terracotta

That's easy one (almost).
I went to www.terracotta.com and followed the download links.
Out of the blue I had a very stupid problem - I clicked on download link and... nothing happened. The server returned empty page.
The link looks something like this - http://www.terracotta.org/elqNow/elqRedir.htm?ref=http%3A%2F%2Fs3.amazonaws.com%3A80%2FTCreleases%2Fterracotta-generic-2.6.4.tar.gz%3FSignature%3Dkp0qhO7y9BHgKMsIhioUTXPWN18%253D%26Expires%3D1222556569%26AWSAccessKeyId%3D1ASD4K6SWEHW65J0HV82
Unescaping it give me this one - http://www.terracotta.org/elqNow/elqRedir.htm?ref=http://s3.amazonaws.com:80/TCreleases/terracotta-generic-2.6.4.tar.gz?Signature=kp0qhO7y9BHgKMsIhioUTXPWN18%3D&Expires=1222556569&AWSAccessKeyId=1ASD4K6SWEHW65J0HV82
So I took the s3 part only, and the download begun.

I am really impressed with the shared queue example. It is simple and powerful, and give me all I need.

Thursday, September 25, 2008

The Idea

This is what I want to do:
  1. Install Terracotta and the examples
  2. Customize Shared Work queue example by adding a queue monitor, which will fire up new instances of Workers, once the queue is overloaded
  3. Prepare AMIs of Terracotta server and client
  4. Upload them to Amazon
  5. During the demo startup the server from IntelliJ plugin
  6. Let the master startup workers programatically
Environment:
  • Ubuntu 8.04
  • OpenJDK 6
  • IntelliJ IDEA
  • FireFox 3 with ElasticFox plugin
Let's roll!