Prashant Srinivasan's Weblog
Typo in OpenSolaris RoR AMI on Amazon EC2
We released a new OpenSolaris Ruby on Rails image on Amazon EC2 last week. This version is different from the previous one on OpenSolaris because not only does this AMI include the usual goodies, updates etc., this is also a Typo appliance.
Want some marketing key words?
Since I'm not really a marketing guy, I just can't put words out there without feeling a primordial urge to explain myself.
Optimized
The stack (Ruby, extensions, gems, web servers) is compiled with the latest compilers, optimal compiler flags based on performance studies, and Nginx is configured to have its temporary file store on a tmpfs mount.
Secure
Nothing runs as root, even for an instruction. If you're wondering how Nginx can listen at port 80 without being started as root, the answer is easy - Solaris privileges: root is not a user, rather it's a role. So you can confer privileges (like listening on port 80) which are usually associated with root to other users (like the nginx user). So this obviates the necessity for applications(like Nginx or Apache) to run as root even for the brief set of instructions needed to bind to a port. The less instructions you run as root, the less your exposure.
Pre-configured, pre-installed, and very "appliancy".
The image contains a production ready configuration of Typo, which is the popular Rails blog engine(usage details are here[1]). Its architecture: there are two Nginx workers. Nginx listens at port 80 and is a fast web server which reduces the possibility of application bottlenecks. In all likelihood, one cannot fully stress two Nginx workers in an EC2 instance (or even one, in my opinion) In addition, Nginx has a low memory footprint, so the benign effect of having two instances outweighs the overhead).
The Rails servers are powered by Thin. There are two instances that Nginx communicates with. Rails is a CPU-intensive/low concurrency application that benefits from multiple instances. The Thins are sized in anticipation of a single CPU so it may be wise to increase the number of Thin instances depending on the number of CPUs in the image.
MySQL is the database. The three tiers are pre-configured. Nginx communicates with Thin, and Thin communicates with the database. Typo has its schema populated into the MySQL database, Rails and its dependencies are already installed. The native gems, like MySQL and Thin have been compiled and configured correctly . . . and it "just works". To use it start the instance, and log on to http://instance to begin setting up your web log.
SMF services exist for the Nginx, Thin, and MySQL components, and the dependencies between these services are also expressed in the manifests.
The catalog entry has information on where the service manifests are stored, and where the components are installed for one who wants further control.
Extremely monitorable
The AMI has DTrace support. DTrace provides for a low overhead profiling with fine-grained control over which code is being profiled. Please refer to Reference 2 below[2].
What's not in the AMI: one needs a backup mechanism for their weblog entries, this is something that the AMI does not provide at this time.
And that's it, please check out the AMI, and hope it is useful either in itself, as a starting point for an tiered architecture that can benefit from an SMF integration, and/or as a base Ruby on Rails AMI that can be used for building a private stack.
References:
[1] http://blogs.sun.com/ec2/entry/ruby_on_rails_2_ami
[2] http://blogs.sun.com/prashant/entry/dtrace_support2
Posted at 03:21PM Jun 19, 2009 by prashant in Ruby | Comments[1]
SMF manifest for Nginx
Here's an SMF manifest for Nginx. You'll need to tweak it for your location of Nginx. You can create an nginx user and group to run the web server as(highly recommended), or tweak my script to run as root(please don't do this).
The steps?
Posted at 10:45AM Apr 29, 2009 by prashant in Ruby | Comments[1]
Git is in OpenSolaris-dev.
I just noticed that git is available in the OpenSolaris dev repository. No more compiling from source. Very cool. All the volunteer work done to spruce up FOSS support in the OSOL community is paying off.
You can either get it by running "pkg image-update" on your OpenSolaris instance, or wait for the Ruby on Rails EC2 image which I'm creating right now, to get a preconfigured set of tools, gems, web servers etc.,
Posted at 11:14AM Apr 27, 2009 by prashant in Ruby |
Sun Web Stack is now called Sun GlassFish Web Stack.
Nothing else changes - the software that one usually expects to find here, like Ruby, Apache, PHP, MySQL, Memcached and more are still there, and will continue to be. actively developed on and enhanced.
http://opensolaris.org/os/project/webstack/
http://www.sun.com/software/webstack/
Posted at 11:43AM Feb 10, 2009 by prashant in Ruby |
How does one update Ruby in OpenSolaris?
The package manager UI(or it's command line alternative "pkg" ) in OpenSolaris is the right way to update installed programs. However, in 2008.11 (and onward) one may find that the package manager does not show that updates are available. This is because of how the package repositories for OpenSolaris are structured.
The package manager by default uses the package repository at http://pkg.opensolaris.org/release. This repository is frozen(this is not completely true, since we're allowed to put security patches into the release repository after it's been 'frozen', but new features and other bug fixes are not allowed here) at the time of an OpenSolaris release and is updated only when the next official release occurs. The 6 + builds that occur before the next freeze(for the next release) are not reflected in this repository.
It's a pity - with OpenSolaris, you don't have to worry about build instabilities, crashes etc., given all the good quality assurance that's built into the system - so one might(and probably would) prefer to stay on the latest build without giving up on stability.
But have no fear, there is an option for such users - the dev repository. Please set http://pkg.opensolaris.org/dev as one of your repositories(in the package manger tool). In fact, I also set it to be my primary repository. This way you can stay up to date, without waiting for the next official release; the dev repository is updated with every build. So expect an update every two weeks(approximately).
To update the installed Ruby, click on the "Update all" button - this will download a wad of packages to update your system with. The update takes a while, but this happens in the background while you continue working. For other programs you may be able to get away with updating only the concerned package.
Posted at 03:21PM Feb 04, 2009 by prashant in Ruby | Comments[5]
The curious case of the Ruby T-Square operator.
The "||=" operator is interesting, both in what it does as much as in how it's widely used in Ruby land. The operator does not do what you would usually expect. i.e.,
a ||= expr
is not the same as
a = a || expr
The evaluation happens to be
a or a = expr
and the difference is important in at least one use case [0]
As a little DTrace script will verify, this operator is not implemented is a method(or anywhere in Ruby land) and is intrinsic to the VM. The reason is performance, and the fact that the entire expression does not have to be evaluated to yield a result when you're 'OR'ing:
"Ruby's Boolean operators are built into the language and are not based on methods: classes, for example, cannot define their own && method. Ruby defines special true and false values but does not have a Boolean type. method. The reason for this is that Boolean operators can be applied to any value and must behave consistently for any kind of operand."
. . .
"Another reason that Ruby's Boolean operators are a core part of
the language rather than redefinable methods is that the binary
operators are "short-circuiting." If the value of the operation is
completely determined by the lefthand operand, then the righthand
operand is ignored and is never even evaluated."
(From The Ruby Programming Language, 1st Edition by David Flanagan and Yukihiro Matsumoto.)
Assuming that variable "a" was not defined prior, then a = a || expr works a little differently than it would if the rvalue was assigned to an lvalue other than a. That is, if a is not defined, then expr is assigned to a. If however, a was not present in the lvalue and the rvalue looked the same(say the code looked like b = a || expr ), then the rvalue will fail to evaluate since "a" is not defined.
>> b = a || "Foo"
NameError: undefined local variable or method `a' for main:Object
from (irb):2:in `binding'
from C:/Program Files/NetBeans 6.5/ruby2/jruby-1.1.4/lib/ruby/1.8/irb.rb:150:in `eval_input'
. . .
>> a = a || "Foo"
=> "Foo"
>>
This special operator finds popular usage not in logical "OR"s, but in variable assignment. It's a Rubyism to instantiate instance variables in methods other than constructors.
From mongrel_rails (version 1.1.5):
def config_keys @config_keys ||= %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors timeout throttle user group prefix) end
This deferred assignment is useful in general if your constructor contains many complex instantiations that need not be performed during object construction. But in this case, it's just a Rubyism. It's called lazy initialization[1]. There's also an easy patch to make that work transparently if you really like it[2].
There is another occasion to use this Rubyism: when your method accepts a parameter with a default value that needs to be populated with a method call. From Rails 1.1:
def install(method=nil, options = {}) method ||= rails_env.best_install_method?
It'd clutter the method definition line to have rails_env.best_install_method? all in the same place, and hence is a good way to produce readable code.
This Rubyism is not usable when nil or false are legal values for the variable being thus assigned.
"For the purposes of all Boolean operators, the values false and nil are considered false. And every other value, including true, 0, NaN, "", [], and {}, is considered true."
(From The Ruby Programming Language, 1st Edition by David Flanagan and Yukihiro Matsumoto.)
There's another interesting usage, to avoid the "nasty nils", as pointed out in the below comment by Mark Wilden, and here[4]
If you're too young to know what a T-Square is, see [3]
[0] http://dablog.rubypal.com/2008/3/25/a-short-circuit-edge-case
[1] http://blog.jayfields.com/2007/07/ruby-lazily-initialized-attributes.html
[2] http://pillowfactory.org/2008/08/28/lazy_attr_accessor
[3] http://en.wikipedia.org/wiki/T-square
[4] http://hassox.blogspot.com/2007/10/ruby-default-argument-gotcha.html

Posted at 11:56AM Dec 14, 2008 by prashant in Ruby | Comments[3]
Ruby, concurrency, and --enable-pthread
Ruby is a green threaded application. ie., irrespective of how many threads are running in the Ruby VM, there is only one operating system thread that the Ruby interpreter is scheduled on. Needless to say, this seriously limits the concurrency achievable with one Ruby VM - and quite naturally drives deployments to operate herds of web servers for their application, front ended by load balancers(like Pen, Pound etc.,) and deployed in an automated fashion(with the likes of Capistrano) - JRuby is way better at threading, though not as good as one would expect(see comments below for a clarification).
So why use --enable-pthread to configure/build Ruby?
Probably because building Ruby with support for a threaded extension(like Tcl/Tk on Solaris, which is compiled with support for pthreads) gives out a dire warning that compiling a threaded extension with a non-pthreaded Ruby will cause frequent crashes? Now that could potentially happen if the threaded extension changed the interpreters context and left it in an inconsistent state, but in practice that turns out not to be true(write a threaded extension and run it through a Ruby interpreter that is not pthread enabled to verify).
Using --enable-pthread has performance ramifications. In Ruby 1.8.6(which is still the sweet spot version in terms of security / stability) removing this option gives about a 2x boost in performance - this is due to the elimination of many getcontext and setcontext calls in the interpreter - these high overhead system calls are not used in the non-pthread enabled code, and avoiding pthreads does not impose a concurrency issue since Ruby is green threaded. further, using --enable-pthread increases the size of Ruby's stack to the point that the compiler has to use a more complicated algorithm(as opposed to simply decrementing the stack pointer) for alloca, further increasing the interpreters overhead.
It's a good practice to avoid using --enable-pthread for Ruby 1.8.6.
Posted at 03:42PM Nov 03, 2008 by prashant in Ruby | Comments[3]
Correct way to install the MySQL gem -
A note by Mandy on the right way to install the MySQL gem on OpenSolaris -
http://blogs.sun.com/mandy/entry/segmentation_fault_when_running_rails
Posted at 12:41PM Oct 08, 2008 by prashant in Ruby |
Rails2/Ruby/OpenSolaris on the Cloud
We just put up a Rails2 image running OpenSolaris on EC2.
This is the real deal, Rails2, Ruby with DTrace support, Thin, Git, Mongrel, Mongrel cluster, sample SMF scripts and configured for a sample Rails application, OpenSolaris with ZFS on the root disk, MySQL, and Postgres.
OpenSolaris includes the Image Packaging System(IPS) which makes it trivial to add more software to the system.(for example, need AMP? just utter "pkg install amp-dev" )
It's available here
Posted at 06:43PM Aug 22, 2008 by prashant in Ruby |
Where's my Ruby?
OpenSolaris is different from Solaris Expresses(ie., the SXCE and SXDEs) in that most of the software that comprises this release is not distributed in the CD media(or image that can be downloaded from opensolaris.com). There's just one CD, and rest of the packages that comprise OpenSolaris are hosted on pkg.opensolaris.org, and available through the "/usr/bin/pkg" command. Ruby is not on the base CD image, but to have a usable version of Ruby on OpenSolaris 2008.05 is very easy, just issue the following commands.
-bash-3.2#pkg install SUNWruby18
DOWNLOAD PKGS FILES XFER (MB)
Completed 1/1 1546/1546 8.50/8.50
PHASE ACTIONS
Install Phase 1844/1844
-bash-3.2# which ruby
/usr/bin/ruby
-bash-3.2#pkg install SUNWgcc
DOWNLOAD PKGS FILES XFER (MB)
Completed 4/4 2035/2035 88.13/88.13
PHASE ACTIONS
Install Phase 2457/2457
-bash-3.2#cd /usr/ruby/1.8/lib/ruby/1.8/i386-solaris2.11
-bash-3.2#mv rbconfig.rb rbconfig.rb.orig
-bash-3.2#wget http://blogs.sun.com/prashant/resource/gcc/rbconfig.rb
Posted at 02:24PM Jun 02, 2008 by prashant in Ruby | Comments[14]
How to install Mongrel, Mysql, and Postgres gems in OpenSolaris Ruby -
Rubygems is the preferred package manager for Ruby, and Ruby packages are called gems. While most of the gems out there are written in Ruby, there are a few that have C or C++ code in them for performance(like Fastthread for Mongrel), or Features(like the MySQL connector for Ruby).
Such gems need a C/C++ compiler to successfully be installed. Apart from that, they also need an appropriately configured rbconfig.rb file in the Ruby installation.
The rbconfig.rb file is typically present in lib/ruby/site_ruby/1.8/rubygems.rb - on OpenSolaris, it is present as /usr/ruby/1.8/lib/ruby/site_ruby/1.8/rubygems.rb
The rbconfig.rb file contains, among other things, information about which compiler(ie., the location) was used to build Ruby, compiler flags, linker flags, path to the C++ compiler, location of ginstall etc.,
This is strictly not necessary for C applications, ie., you can install a native gem compiled with CompilerB on a version of Ruby that was compiled with CompilerA, but the advantage of having the build time compiler flags captured is that you can optimize the extensions just as the Ruby binary was optimized(and the folks who built Ruby for you hopefully have done some reasearch in this area).
On OpenSolaris, there's a bug by which some paths in the rbconfig.rb file actually point to a compiler location that was used by the build infrastructure, and this location is normally not present on the system.
This is a bug that is being fixed, and the right paths should work out of the box. Until that fix shows up in the repository, here's how to make sure your Ruby installation works well.
Posted at 03:12PM May 19, 2008 by prashant in Ruby | Comments[3]
Ruby 1.9 very first impression
Program: ruby -e 'require "benchmark"; p Benchmark.realtime { a=0;1000000.times {a = a + 1}; p a} '
Posted at 10:23PM May 11, 2008 by prashant in Ruby | Comments[1]
DTrace support for Ruby
DTrace probe support is now into OpenSolaris - as of build 87. Siva, an engineer in my little Ruby team checked it into the free software gate last Tuesday night.
Pretty cool! DTrace is a great way to profile Ruby programs without introducing much overhead into the program you're tracing. This is a real boon considering that overhead is a big problem with Ruby profilers today. Also, you don't need to make _any_ changes to your Ruby programs in order to narrow your profile to a set of functions. DTrace rocks. Below is an example of how to use DTrace to profile Ruby programs. The program traces through method invocations in different classes, and indents nested invocations accordingly. Here's the program:
[root@dn10 dtrace]$cat rb_funcalls.d
#!/usr/sbin/dtrace -Zqs
/*
* How do you invoke this script?
* Use dtrace -xbufsize=40m -Zqs rb_funcall.d -c "ruby my_arg1 my_arg2"
* Where "ruby" is DTrace enabled.
*/
/*
* Thread local variable to
* store the indentation level
* during/after a function call.
*/
self int indent;
dtrace:::BEGIN
{
printf("Starting to trace\n");
}
ruby*:::function-entry
{
/*
* To ensure that we capture function
* entries and returnes made by a thread
* in order.
*/
self->thread = 1;
}
ruby*:::function-entry
/self->thread/
{
self->indent += 2;
printf("%*s", self->indent, " ");
printf("=> %s->%s\n", copyinstr(arg0), copyinstr(arg1));
}
ruby*:::function-return
/self->thread/
{
printf("%*s", self->indent, " ");
printf("<= %s->%s\n", copyinstr(arg0), copyinstr(arg1));
self->indent -= 2;
}
dtrace:::END
{
printf("Finished tracing\n");
}
[root@dn10 dtrace]$
There are a couple of ways to invoke this - either start a process through the DTrace command line, or attach to an existing Ruby process. We'll do the latter since our program is rather simple.
[root@dn10 dtrace]$cat /tmp/hello.rb
message = String.new("Hello World")
puts message
[root@dn10 dtrace]$
On uttering the appropriate DTrace mantra, the following ensues:
[root@dn10 dtrace]$dtrace -xbufsize=40m -Zqs rb_funcalls.d -c "ruby /tmp/hello.rb"
Starting to trace
Hello World
=> Class->new
=> String->initialize
<= String->initialize
<= Class->new
=> Object->puts
=> IO->write
<= IO->write
=> IO->write
<= IO->write
<= Object->puts
Finished tracing
A few comments about the invocation and the scripts are in order here. You don't need a very high value of xbufsize for a small data set, it's just illustrative. You really need the -Z option because it ensures that DTrace doesn't bail out with complaints that it couldn't find the ruby*:::function-return(and other) probes. DTrace would otherwise do that when dealing with application level probes.
#!/usr/sbin/dtrace -Zqs
ruby*:::function-entry
{
printf("Class name: %s, Method name %s, Source file %s, Line number %d\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
}
To invoke the above script, just utter "dtrace -xbufsize=40m -Zqs rb_fn_info.d", and assuming that you have a ruby process already running in your system - you'll begin to see data like the below.(else use the -c option to launch a command. Or use ruby$1 and a command line argument for the pid that you'd like to latch on to)[snip!] Class name: Proc, Method name call, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/ruby-lex.rb, Line number 207 Class name: IRB::Context, Method name prompting?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 115 Class name: IRB::Context, Method name verbose?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/context.rb, Line number 156 Class name: NilClass, Method name nil?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/context.rb, Line number 144 Class name: Object, Method name kind_of?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/context.rb, Line number 145 Class name: IO, Method name tty?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/context.rb, Line number 156 Class name: Object, Method name kind_of?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/context.rb, Line number 156 Class name: Object, Method name kind_of?, Source file /usr/ruby/1.8/lib/ruby/1.8/irb/context.rb, Line number 157 Class name: IRB::Irb, Method name prompt, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 116 Class name: Object, Method name dup, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 266 Class name: String, Method name initialize_copy, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 266 Class name: String, Method name gsub!, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 267 Class name: Object, Method name ===, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 269 Class name: String, Method name ==, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 269 Class name: Object, Method name ===, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 269 Class name: String, Method name ==, Source file /usr/ruby/1.8/lib/ruby/1.8/irb.rb, Line number 269 [snip!]And that's all it takes to get dangerous. You can of course do a lot of fancy profiling with all these(and I'll save that for a different entry, since I've got to get to bed in time for an early morning phone call
Posted at 11:17PM Mar 26, 2008 by prashant in Ruby |
Doing The Right Thing, or Why Rails is not in OpenSolaris
If you haven't been watching the discussion on webstack-discuss@opensolaris.org, the news is that Rails will not be integrated with OpenSolaris or Nevada.
ie., an installation of SXCE/SXDE/Nevada/Indiana will have Ruby installed, but Rails will not be available by default.
The recommended way to install Rails and other gems is by using the gem command - using the famous utterance "gem install rails --include-dependencies".
And that's it.
I think this is the right position for us to take. I think it's quite a brave one, and one that shows that we are truly working as a community now, at least in my corner of the universe.
Think about it . . . with other OSes drawing users away from Ruby's native packaging, in order to "do it their way" - what would the natural response from an operating system vendor be? To say "Let it be, a good solution already exists in Rubygems", or "hey, we want XYZ library delivered through our mechanism, irrespective of what the problems are"?
Rubygems is the chosen package manager for Ruby, and abiding by that decision helps Ruby users on Solaris - and people get that. We got a a lot of comments from our user community, as well as from the good folk at Twitter about Solaris packaging for Ruby gems in general - and nobody at Sun, after having heard the story, ever said, "you know, Debian has it, BSD has it, so we should do it too".
And that's a brave step to take.
Thanks to all the folk that provided comments, please keep them coming, and I'm proud of the folk at Sun, for working sincerely with the community.
Posted at 12:07AM Mar 19, 2008 by prashant in Ruby | Comments[2]
SXDE 1/08: First Solars Release that includes Ruby
SXDE 1/8(or the more wordy Solaris Express Developer Edition 1/8) was released two weeks ago, and this is the first Solaris release to have the Ruby programming language available as an operating system feature. Expect to find it as /usr/ruby/1.8/bin, and linked from /usr/bin/ruby. We used the /usr/ruby/1.8 location to be friendly to different Ruby versions. ie., when version 1.9 comes out, expect to find it in /usr/ruby/1.9/bin. Rubygems is also included in this release. So that should make it quite simple to be up-and-running with Ruby development . . . did I hear you ask about an IDE?
Posted at 01:51PM Feb 15, 2008 by prashant in Ruby |
Today's Page Hits: 3
| « October 2009 | ||||||
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
| Today | ||||||