You are currently browsing the category archive for the ‘Amazon Web Services’ category.

My post on a Cloud Foundry AMI was with a simple Ruby application. In a comment Andrew Spyker asked about a node.js application. I’ve done Javascript but not node.js, so thought I’d give it a try. I followed my earlier instructions to get the CF Micro instance started and then starting with the ‘Use the Server’ I did something different.

Using the webserver example here, I created two files in a new directory. The first file, example.js, contained the following:

var http = require('http');
var port = parseInt(process.env.PORT,10);
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(port, '0.0.0.0');
console.log('Server running at http://0.0.0.0:' + port.toString() + '/');

The second file, package.json, contained the following:

{
  "name": "http-server",
  "version": "0.0.1",
  "author": "James Foster <github@jgfoster.net>",
  "description": "webserver demo from http://nodejs.org/",
  "dependencies" : [ ],
  "engines": {
    "node": ">=0.10"
  }
}

(Bear in mind that this is my first node.js application, and I just spent a hour or so poking around on the web to get this far.)

From the command line I’m able to run it by entering the following:

export PORT=1337; node example.js

With this I can open a web browser on http://localhost:1337/ and see the greeting.

To push my trivial application to my EC2 instance, I did the following:

cf target http://api.<my-ip>.xip.io
cf login --password mySecret admin
# okay to ignore CFoundry::InvalidRelation error in next command
# (see https://github.com/cloudfoundry/cf/issues/9)
cf create-space development 
cf target --space development
cf map-domain --space development <my-ip>.xip.io
cf push --command "node example.js"

The interaction included giving the application a name (“hello”), accepting the defaults, and saving the configuration:

Name> hello
Instances> 1
1: 128M
2: 256M
3: 512M
4: 1G
Memory Limit> 256M
Creating hello... OK
1: hello
2: none
Subdomain> hello
1: 54.200.62.218.xip.io
2: none
Domain> 54.200.62.218.xip.io
Binding hello.54.200.62.218.xip.io to hello... OK
Create services for application?> n
Save configuration?> y
Saving to manifest.yml... OK
Uploading hello... OK
Preparing to start hello... OK
-----> Downloaded app package (4.0K)
-----> Resolving engine versions
 Using Node.js version: 0.10.17
 Using npm version: 1.2.30
-----> Fetching Node.js binaries
-----> Vendoring node into slug
-----> Installing dependencies with npm
 npm WARN package.json http-server@0.0.1 No repository field.
 npm WARN package.json http-server@0.0.1 No readme data.
 npm WARN package.json http-server@0.0.1 No repository field.
 npm WARN package.json http-server@0.0.1 No readme data.
 Dependencies installed
-----> Building runtime environment
-----> Uploading droplet (15M)
Checking status of app 'hello'...
 0 of 1 instances running (1 starting)
 0 of 1 instances running (1 starting)
 1 of 1 instances running (1 running)
Push successful! App 'hello' available at http://hello.54.200.62.218.xip.io

When I went to the URL provided, I saw the greeting.

I recently made an Amazon Machine Image (AMI) available (described here) and described how to use it. In this post I will describe the process of taking an existing AWS Instance with Cloud Foundry Micro (described here) and making it suitable for a public AMI. The primary challenge is that we need to create a boot disk with Cloud Foundry already installed but configured so that it can use a new password, domain, and local IP address. We also need to remove any identifying information before shutting down the VM and taking a snapshot.

I first use the following script (named setup.sh) to install Cloud Foundry using the cf_nise_installer. Note that I am setting a well-known IP, domain, and password; these will be changed later during the boot process. I am also deleting some files that might exist from a previous install attempt. Finally, I am creating a list of files that might contain data that must be modified during the boot process.

#!/bin/bash
#
set -x
cd ~
sudo rm -rf \
  cf_nise_installer \
  /var/vcap/data/sys/log/* \
  domain password files ipv4 \
  2>/dev/null
#
export INSTALLER_URL="https://github.com/yudai/cf_nise_installer.git"
export INSTALLER_BRANCH='master'
export CF_RELEASE_BRANCH='release-candidate'
export NISE_IP_ADDRESS='127.0.0.1'
export NISE_DOMAIN='cloud.mycloud.local'
export NISE_PASSWORD='swordfish'
bash < <(curl -s -k -B https://raw.github.com/yudai/cf_nise_installer/${INSTALLER_BRANCH:-master}/local/bootstrap.sh)
#
list="$(sudo find /vol-a/var/vcap -type f \( -iname '*.yml' -o -iname 'postgres_ctl' \) )"
echo $list > ~/files

We next create a startup script, /home/ubuntu/rc.local, and modify /etc/rc.local to call our new script (if you delete the authorized_keys and fail to add them back, then you can’t log in to your instance!).

#!/bin/bash
# should be called from /etc/rc.local
echo "Starting /home/ubuntu/rc.local script to start Cloud Foundry"
#
# See if 'debug: true' is in user-data
#
X=$( curl http://instance-data/latest/user-data 2>/dev/null | grep 'debug:' )
X=$( echo $X | cut -d : -f 2 | tr -d ' ' )
if [ "$X" == "true" ]; then
  set -x
fi
#
# Make sure that we have some keys (should be deleted before snapshot)
#
cd /home/ubuntu
if [ ! -f .ssh/authorized_keys ]; then
  curl http://instance-data/latest/meta-data/public-keys/0/openssh-key \
    2>/dev/null > .ssh/authorized_keys
  chown ubuntu:ubuntu .ssh/authorized_keys
  chmod 600 .ssh/authorized_keys
  echo "Updated authorized_keys"
fi
#
# See if 'autoStart: false' is in user-data; if so, done!
#
X=$( curl http://instance-data/latest/user-data 2>/dev/null | grep 'autoStart:' )
X=$( echo $X | cut -d : -f 2 | tr -d ' ' )
if [ "$X" == "false" ]; then
  echo "user-data includes 'autoStart: false' so we will quit"
  exit 0
fi
#
# Each instance should have a different password (but once set it does not change!)
#
if [ -e password ]; then
  # If we have been through this code before, then use the previous password
  OLD_PASSWORD=$( < password )
  NEW_PASSWORD=$OLD_PASSWORD
else
  # This is the first time through, so we replace the default password
  OLD_PASSWORD='swordfish'
  # User may specify a 'password:' line in the user data
  X=$( curl http://instance-data/latest/user-data 2>/dev/null | grep 'password:' )
  NEW_PASSWORD=$( echo $X | cut -d : -f 2 | tr -d ' ' )
  if [ "$NEW_PASSWORD" == "" ]; then
    # No user-provided one and no previously created one, so assign a random one
    # another script is $( openssl rand -base64 6 )
    NEW_PASSWORD=$( < /dev/urandom tr -dc A-Za-z0-9 | head -c8 )
  fi
  # Save new password so it can be reused next time
  echo "$NEW_PASSWORD" > password
fi
#
# See if 'domain:' is in user-data
#
X=$( curl http://instance-data/latest/user-data 2>/dev/null | grep 'domain:' )
if [ "$X" != "" ]; then
  NEW_DOMAIN=$( echo $X | cut -d : -f 2 | tr -d ' ' )
else
  # get the public-hostname
  until [ "$NEW_DOMAIN" != "" ]; do
    X=$( curl http://instance-data/latest/meta-data/ 2>/dev/null | grep public-hostname )
    if [ "$X" != "" ]; then
      X=$( curl http://instance-data/latest/meta-data/public-hostname 2>/dev/null )
      if [ "$X" != "" ]; then
        NEW_DOMAIN=$( echo $X | cut -d "." -f 1 | cut -d "-" -f 2- | sed "s/\-/\./g" )
        NEW_DOMAIN="$NEW_DOMAIN.xip.io"
      fi
    fi
    if [ "$NEW_DOMAIN" == "" ]; then
      echo "`date`: public-hostname not yet available from meta-data"
      sleep 1
    fi
  done
fi
if [ -f domain ]; then
  OLD_DOMAIN=$( cat domain )
else
  OLD_DOMAIN='cloud.mycloud.local'
fi
echo $NEW_DOMAIN > domain
#
# Each new instance will have a unique private IP address
#
NEW_IPV4=$( curl http://instance-data/latest/meta-data/local-ipv4 2>/dev/null )
if [ -f ipv4 ]; then
  OLD_IPV4=$( cat ipv4 )
else
  OLD_IPV4="127.0.0.1"
fi
echo $NEW_IPV4 > ipv4
#
# Find all the files that need to be edited
# (takes several seconds, so cache it for faster start next time)
#
if [ -f files ]; then
  list=$( cat files )
else
  list="$(find /var/vcap -type f \( -iname '*.yml' -o -iname 'postgres_ctl' \) )"
  echo $list > files
fi
for f in $list
do
  if [ "$OLD_PASSWORD" != "$NEW_PASSWORD" ]; then
    if grep -q $OLD_PASSWORD $f; then
      sed -i "s/$OLD_PASSWORD/$NEW_PASSWORD/g" $f
      echo "Updated password in $f"
    fi
  fi
  if grep -q $OLD_DOMAIN $f; then
    sed -i "s/$OLD_DOMAIN/$NEW_DOMAIN/g" $f
    echo "Updated domain in $f"
  fi
  if grep -q "$OLD_IPV4" $f; then
    sed -i "s/$OLD_IPV4/$NEW_IPV4/g" $f
    echo "Updated IP in $f"
  fi
done
#
# start Cloud Foundry
(cd /home/ubuntu/cf_nise_installer; ./local/start_processes.sh)

Finally, I create a cleanup.sh script to remove identifying information and sanitize the system before shutdown. Note that once authorized_keys are removed from the .ssh directory you can’t log in unless during the boot process new authorized_keys are installed.

#!/bin/bash
#
sudo rm -f /root/.ssh/* /home/*/.ssh/* /var/vcap/monit/monit.log
(cd /var/log; sudo rm -f *.log dmesg* debug messages syslog)
rm -f ~/.viminfo ~/.bash_history
echo '' | sudo tee /var/log/lastlog
history -c
# sudo shutdown now

After running this script I use the AWS tools to stop the instance (this means that the command history is empty). At this point I can use the AWS tools to create an AMI. When the AMI starts it executes /etc/rc.local which calls my new script, /home/ubuntu/rc.local. This script sets the local IPv4 value, along with the domain and password (using configured values if provided in the user-data). When the appropriate configuration info has been updated, then we start Cloud Foundry. Each new instance has its own IP, domain, and password, making it secure and unique.

If there is something wrong with the boot volume (such as missing authorized_keys so you can’t log in), then you need to start another EC2 instance (a micro is fine), attach the volume, do the fix-up, and release it.

# mount a new volume (in case some surgery is needed)
#
sudo mkdir -p 000 /vol-a
sudo mount /dev/sdf /vol-a
#
# ... do whatever fix-up is needed and unmount the volume
#
sudo umount -d /vol-a
sudo rmdir /vol-a

I have used /etc/rc.local to hook into the boot process. An alternate may be to use crontab with the ‘@reboot’ option.

If you have an instance-store (a disk that exists only while the instance is running), then you might want to set up some swap space on it. The following script will create a 4 GB file to be used as swap space.

# http://serverfault.com/questions/218750/why-dont-ec2-ubuntu-images-have-swap
sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=4096 &&
sudo chmod 600 /mnt/swapfile &&
sudo mkswap /mnt/swapfile &&
echo /mnt/swapfile none swap defaults 0 0 | sudo tee -a /etc/fstab &&
sudo swapon -a
cat /proc/swaps

That summarizes how I created a Cloud Foundry Micro public AMI.

Video of ESUG 2013 Presentation

My presentation “Smalltalk in the Cloud” was recorded and can be found at the link. Unfortunately, the audio was not very strong.

Update: Slides are here.

Update: A video of this post is available here.

After creating a Cloud Foundry Micro on an Amazon EC2 instance (described here), I decided to make it available as a public AMI so that others could try it out (especially since the Micro is not available unless you built it yourself using Altoros vagrant or Nise BOSH).

To use this you need to sign up for an Amazon AWS account.

Start a Cloud Foundry Instance

  • Once you have an account, launch ami-98c956a8 (currently available in us-west-2; add a comment if you want it available elsewhere).
  • Confirm that the manifest reads “366179850620/Cloud Foundry Micro” and click Continue.
  • Change the instance type to m1.small and click Continue.
  • In the ‘User Data:’ field you may enter some optional customizations (each on its own line) and click Continue.
    • password: mySecret (AMI rules prohibit default passwords so if you don’t provide your own we will generate a random one);
    • domain: cloud.example.com (if you don’t provide a domain, we will assign <public-IP>.xip.io as the domain); and
    • debug: true (adds some debugging information to /var/log/boot.log).
  • Review the ‘Storage Device Configuration’ (an 8 GB root volume and an ephemeral instance store) and click Continue.
  • You may give a ‘Name’ tag to your EC2 instance, say CF Demo, and click Continue.
  • You may select or create a key pair to be used to log in to your server (optional, but useful), and click Continue.
  • Select or create a Security Group with at least HTTP access and click Continue.
    • ICMP – Echo Request (optional, to allow your server to respond to ping);
    • TCP – SSH (optional, to allow you to log on to your server using a private key); and
    • TCP – HTTP (required, to interact with Cloud Foundry and the applications you push to the server).
  • Review the configuration information and click Launch.
  • Click  View your instances on the Instances page to discover the public IP address.

Identify the Domain

To use the server you need to know its domain name.

  • If you provided a domain in the User Data above, then you need to create a record set in your domain name server to point your domain and all subdomains (using the ‘*’ wildcard match) to the indicated address; or
  • If you did not provide a domain, then your domain is <public-IP>.xip.io (a DNS that maps all requests to the given IP).

Log on to the Server (Optional)

  • Identify the path to the private key associated with the key pair you selected or created when you created the EC2 instance.
  • From a command shell (on Mac, Linux, or Unix), or an SSH client on Windows (such as PuTTY), connect to the server. E.g.,

ssh -i /path/to/my/private/key.pem ubuntu@domain

  • Once connected you can explore the server.
sudo /var/vcap/bosh/bin/monit summary # check Cloud Foundry status (all running except cloud_controller_jobs)
tail /var/log/boot.log # check here if things don't seem to start properly
cat ~/domain # show the configured domain (from User Data or public IP)
cat ~/password # show the configured password (from User Data or random generation)
cd /var/vcap/data/sys; sudo chmod +rx log; cd log; ll # list of log file directories
  • You can execute a single command the server using ssh:
ssh -i /path/to/my/private/key.pem ubuntu@hostname_or_domain cat password

Use the Server

To use the server you can refer to the cf command line reference. For example, on your local machine create and set up the environment:

mkdir ~/cloud ~/cloud/ruby; cd ~/cloud/ruby
sudo gem install bundle sinatra cf

Create three files using your favorite text editor:

Gemfile:

source 'https://rubygems.org'
 ruby '1.9.3'
 gem 'sinatra'

env.rb:

require 'rubygems'
require 'sinatra'
configure do
    disable :protection
end
get '/' do
    host = ENV['VCAP_APP_HOST']
    port = ENV['VCAP_APP_PORT']
    "<h1>Hello World!</h1><h2> I am in the Cloud! via: #{host}:#{port}</h2>"
end
get '/env' do
    res = ''
    ENV.each do |k, v|
        res << "#{k}: #{v}<br/>"
    end
    res
end

config.ru:

require ‘./env.rb’
run Sinatra::Application

To create a ‘Gemfile.lock’ from the ‘Gemfile’ run the following command:

bundle

I can test the application by running the following command:

ruby env.rb

When it tells me that Sinatra has taken the stage I enter http://localhost:4567/ and http://localhost:4567/env in a web browser.

Then I can use ‘cf’ to set my target, login, do some configuration, and push my application to the cloud (replacing <my-ip> and mySecret with your server’s public IP and password):

cf target http://api.<my-ip>.xip.io
cf login --password mySecret admin
# okay to ignore CFoundry::InvalidRelation error in next command
# (see https://github.com/cloudfoundry/cf/issues/9)
cf create-space development 
cf target --space development
cf map-domain --space development <my-ip>.xip.io
cf push

If the push is successful, it will show the URL at which you can see the application. When you care done you can stop and/or terminate your EC2 instance.

Recently we went through the process of installing a micro Cloud Foundry on a local virtual machine. We are now interested in doing the same on an Amazon EC2 instance. As far as I have been able to find, the existing instructions for using AWS set up a system with many VMs. In this post we look at a “micro” (or single-machine) Cloud Foundry setup.

To do this you need to sign up for an Amazon AWS account. Next, you need to decide where to build your Cloud Foundry instance. Amazon has data centers in eight regions, and you can pick based on geography (close to you has less network latency) and price (some are more expensive). I am close to the US (West) Oregon Region (us-west-2) and it is among the least expensive.

Next you select a base operating system for your machine. Cloud Foundry recommends 64-bit Ubuntu 10.04 LTS, so go to Ubuntu’s Amazon EC2 AMI Locator and enter ’64 lucid ebs’ in the search area (since we are going to make changes to the setup we want to be on a persistent store, hence the EBS selection). When the search list is narrowed down to one for each region, click on the link for the region you want.

Ubuntu Amazon EC2 AMI Locator

This takes us to the EC2 Management Console (perhaps with a login) where you can review information about the selected AMI and click the Continue button.

Screen Shot 2013-08-27 at 10.29.30 AM

For the Instance Details, change the Instance Type from ‘T1 Micro’ to ‘M1 Small’ or ‘M1 Medium’ and click Continue.

Screen Shot 2013-08-27 at 10.33.15 AM

Next, give ‘CF Micro’ as ‘User Data’ and click Continue.

Screen Shot 2013-08-27 at 11.57.38 AM

Do not make any changes to the Storage Device Configuration; simply click Continue.

Screen Shot 2013-08-27 at 12.01.43 PM

For the Tags, give ‘CF Micro’ as the Name and click Continue.

Screen Shot 2013-08-27 at 12.02.20 PM

To interact securely with the instance you need a key pair. The Wizard prompts for a name and you may enter anything (such as ‘cfMicro’) and then click ‘Create and Download your Key Pair’.

Screen Shot 2013-08-27 at 12.04.26 PM

The default Security Group does not allow any outside access to the instance. Create a new Security Group, named ‘CF Micro’ with a description of ‘ping, ssh, http’, add the appropriate rules, and click Continue.

Screen Shot 2013-08-27 at 12.10.57 PM

Next, click the Launch button to start the instance.

Screen Shot 2013-08-27 at 12.14.20 PM

When informed that the instance is starting, click Close.

Screen Shot 2013-08-27 at 12.16.28 PM

This takes us to the list of Instances on the Management Console.

When we built a micro Cloud Foundry on a local virtual machine, the IP address was assigned by Fusion and was the same from “inside” and “outside” the machine. When running an EC2 instance on AWS, the machine is behind a firewall and on an internal (private) network. While our virtual machine can be reached via a public IP address, the machine actually has a different IP address.

Optional: If we stop and start the machine the default behavior is that we are likely to get a different public address. In order to have a stable IP address for our instance, we can to allocate an Elastic IP and associated it with the running instance. (If you are only doing this once and will throw away the system, then you can skip this step.) From the EC2 Management Console, click Elastic IPs in the navigation pane on the left and then click the Allocation New Address button (instructions here).

Screen Shot 2013-08-28 at 2.57.14 PM

Optional (continued): Select the new address and click the Associate Address button. In the dialog box select the running instance and click the Yes, Associate button.

Screen Shot 2013-08-28 at 2.59.39 PM

Whether you have a stable IP or not, you now are almost ready to log on to our new server. Click on Instances in the navigation pane on the left and select the CF Micro instance, right click, and select the ‘Connect’ menu command.

Screen Shot 2013-08-27 at 12.17.59 PM

This gives us a window with instructions on how to connect to the instance. I prefer to use SSH from Terminal.app on my MacBook Pro, so look at the instructions for the standalone SSH client.

Screen Shot 2013-08-27 at 12.22.48 PM

Before you can use the command line provided (highlighted above) you need to do a little bit of setup on our local machine. Create a working directory and copy the private key downloaded earlier. Then connect to the new server (your IP address will be different).

mkdir ~/cloud/cfMicro
cd ~/cloud/cfMicro
mv ~/Downloads/cfMicro.pem .
chmod 400 cfMicro.pem 
ssh -i cfMicro.pem ubuntu@54.213.201.105

Once logged in to the server, you can install Cloud Foundry using Iwasaki Yudai’s cf_nise_installer.

export IPV4=`wget -qO- http://instance-data/latest/meta-data/public-ipv4`
export NISE_DOMAIN=$IPV4.xip.io
export CF_RELEASE_BRANCH=release-candidate
bash < <(curl -s -k -B https://raw.github.com/yudai/cf_nise_installer/${INSTALLER_BRANCH:-master}/local/bootstrap.sh)

When this finishes, you should restart your server.

sudo shutdown -r now

After a minute or so, log in to the server again using the ssh command above and start your Cloud Foundry.

(cd ~/cf_nise_installer; ./local/start_processes.sh)

Once the server is started, you can logout from the server (or open a second session on your client) and create a new application to push to your cloud. I suggest that you follow my earlier example with the following manifest.yml (change the domain to reference your server’s IP address):

---
applications:
- name: env
  memory: 256M
  instances: 1
  host: env
  domain: 54.213.204.16.xip.io
  path: .
  command: 'ruby env.rb'

Then use the Cloud Foundry command line tools to configure things and push your application (use your own IP address instead of the one shown here!).

cf target http://api.54.213.204.16.xip.io
cf login --password c1oudc0w admin
# okay to ignore CFoundry::InvalidRelation error in next command
# (see https://github.com/cloudfoundry/cf/issues/9)
cf create-space development 
cf target --space development
cf map-domain --space development 54.213.204.16.xip.io
cf push

When this finishes, you should be able to open a web browser on something like (use your own IP address) http://env.54.213.204.16.xip.io/env and see the application. Congratulations!

 

Categories