Migrating to RackCloud

As a final exercise, we’re going to migrate our servers out into the cloud. For this, I’ve chosen Rackspace RackCloud because a) it’s what I know worked at the moment, and b) even after a day of creating and destroying instances in the clouse, it cost me nothing. :-)

  1. Create a Rackspace Account
  2. Configure Knife for Rackspace
  3. Boostrap a Cloud Instances
  4. Convert Deployment Process
  5. Commit Our Work

Create a Rackspace Account

Signing up for a cloud account is pretty easy. Point your browser to the pricing page and click on the cart icon in the middle of the page.

Fill out the forms and login. It really is as easy as that! They will ask you for a billing credit card. Don’t be alarmed! At $0.022 cents an hour, I’ve yet to be charged anything after a full day of createing and destroying instances for this tutorial.






Configure Knife for Rackspace

Now that we have a rackspace account, we need to configure our knife.rb file to be able to talk to the Rackspace API.

First, login to the RackCloud interface and get your API key.

Select “API Keys” from the drop down account menu in the upper right corner.

Now, at the end of your .chef/knife.rb, add the following code:

1 knife[:rackspace_username] =  "youraccountname"
2 knife[:rackspace_api_key] =   "yourapikey"   # looks likes this -> b936bd3468844f0ec1ddefa53e625fee4"
3 
4 require 'excon'
5 Excon.defaults[:read_timeout] = 500

From the trenches: Due to my crappy DSL connection, I ended up with a lot of Excon timeouts errors while waiting for instancs to spin up. The last two lines extend that timeout period and make things stable for me.

Now, in your Gemfile, add the knife-rackspace gem, which allows the knife command line utility to talk to the Rackspace API, and run bundle install.

1 source :rubygems
2 
3 gem 'vagrant'
4 gem 'veewee'
5 gem 'chef'
6 gem 'knife-github-cookbooks'
7 gem 'rails'
8 gem 'knife-rackspace'
 1 $ bundle install
 2 Fetching gem metadata from http://rubygems.org/.....
 3 Using rake (0.9.2.2) 
 4 Using Platform (0.4.0) 
 5 Using i18n (0.6.1) 
 6 Using multi_json (1.3.6) 
 7 Using activesupport (3.2.8) 
 8 Using builder (3.0.3) 
 9 Using activemodel (3.2.8) 
10 Using erubis (2.7.0) 
11 Using journey (1.0.4) 
12 Using rack (1.4.1) 
13 Using rack-cache (1.2) 
14 Using rack-test (0.6.2) 
15 Using hike (1.2.1) 
16 Using tilt (1.3.3) 
17 Using sprockets (2.1.3) 
18 Using actionpack (3.2.8) 
19 Using mime-types (1.19) 
20 Using polyglot (0.3.3) 
21 Using treetop (1.4.10) 
22 Using mail (2.4.4) 
23 Using actionmailer (3.2.8) 
24 Using arel (3.0.2) 
25 Using tzinfo (0.3.33) 
26 Using activerecord (3.2.8) 
27 Using activeresource (3.2.8) 
28 Using archive-tar-minitar (0.5.2) 
29 Using bundler (1.1.5) 
30 Using bunny (0.7.9) 
31 Using highline (1.6.15) 
32 Using json (1.5.4) 
33 Using mixlib-log (1.4.1) 
34 Using mixlib-authentication (1.3.0) 
35 Using mixlib-cli (1.2.2) 
36 Using mixlib-config (1.1.2) 
37 Using mixlib-shellout (1.1.0) 
38 Using moneta (0.6.0) 
39 Using net-ssh (2.2.2) 
40 Using net-ssh-gateway (1.1.0) 
41 Using net-ssh-multi (1.1) 
42 Using ipaddress (0.8.0) 
43 Using systemu (2.5.2) 
44 Using yajl-ruby (1.1.0) 
45 Using ohai (6.14.0) 
46 Using rest-client (1.6.7) 
47 Using uuidtools (2.1.3) 
48 Using chef (10.14.2) 
49 Using ffi (1.0.11) 
50 Using childprocess (0.3.5) 
51 Using configuration (1.3.2) 
52 Using diff-lcs (1.1.3) 
53 Using gherkin (2.11.2) 
54 Using cucumber (1.2.1) 
55 Installing excon (0.16.4) 
56 Installing formatador (0.2.3) 
57 Using net-scp (1.0.4) 
58 Installing nokogiri (1.5.5) with native extensions 
59 Installing ruby-hmac (0.4.0) 
60 Installing fog (1.6.0) 
61 Using launchy (0.4.0) 
62 Using knife-github-cookbooks (0.1.7) 
63 Installing knife-rackspace (0.5.16) 
64 Using log4r (1.1.10) 
65 Using open4 (1.3.0) 
66 Using popen4 (0.1.2) 
67 Using progressbar (0.11.0) 
68 Using rack-ssl (1.3.2) 
69 Using rdoc (3.12) 
70 Using thor (0.14.6) 
71 Using railties (3.2.8) 
72 Using rails (3.2.8) 
73 Using rspec-core (2.11.1) 
74 Using rspec-expectations (2.11.3) 
75 Using rspec-mocks (2.11.3) 
76 Using rspec (2.11.0) 
77 Using vagrant (1.0.5) 
78 Using virtualbox (0.9.2) 
79 Using veewee (0.2.3) 
80 Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

Let’s test out our configuration by listing out what images and flavors RackCloud supports:

 1 $ knife rackspace image list
 2 ID   Name                                                     
 3 100  Arch 2012.08                                             
 4 114  CentOS 5.6                                               
 5 121  CentOS 5.8                                               
 6 118  CentOS 6.0                                               
 7 122  CentOS 6.2                                               
 8 127  CentOS 6.3                                               
 9 104  Debian 6 (Squeeze)                                       
10 120  Fedora 16                                                
11 126  Fedora 17                                                
12 107  FreeBSD 9.0                                              
13 108  Gentoo 12.3                                              
14 110  Red Hat Enterprise Linux 5.5                             
15 111  Red Hat Enterprise Linux 6                               
16 112  Ubuntu 10.04 LTS                                         
17 115  Ubuntu 11.04                                             
18 119  Ubuntu 11.10                                             
19 125  Ubuntu 12.04 LTS                                         
20 85   Windows Server 2008 R2 x64                               
21 86   Windows Server 2008 R2 x64 + SQL Server 2008 R2 Standard 
22 89   Windows Server 2008 R2 x64 + SQL Server 2008 R2 Web      
23 91   Windows Server 2008 R2 x64 + SQL Server 2012 Standard    
24 92   Windows Server 2008 R2 x64 + SQL Server 2012 Web         
25 24   Windows Server 2008 SP2 x64                              
26 57   Windows Server 2008 SP2 x64 + SQL Server 2008 R2 Standard
27 31   Windows Server 2008 SP2 x86                              
28 56   Windows Server 2008 SP2 x86 + SQL Server 2008 R2 Standard
29 109  openSUSE 12                                              
30 
31 $ knife rackspace flavor list
32 ID  Name           Architecture  RAM    Disk   
33 1   256 server     64-bit        256    10 GB  
34 2   512 server     64-bit        512    20 GB  
35 3   1GB server     64-bit        1024   40 GB  
36 4   2GB server     64-bit        2048   80 GB  
37 5   4GB server     64-bit        4096   160 GB 
38 6   8GB server     64-bit        8192   320 GB 
39 7   15.5GB server  64-bit        15872  620 GB 
40 8   30GB server    64-bit        30720  1200 GB

And we’re connected to the cloud!

Boostrap a Cloud Instance

Now that we can talk to Rackspace, it’s time to bootstrap a cloud image. This process is much like creating Vagrant instances. Once we’ve asked the cloud provider to create a server, knife will hand it a “bootstrap” file. This file serves the exact same purpose as the postinstall.sh file we used with Vagrant/Veewee when creating a box. The only difference is that we don’t need vagrant specific user or ssh key in the cloud, nor do we need to install the VirtualBox Guest Additions.

In fact, let’s create a bootstrap directory for knife and copy over the postinstall.sh from our box definition:

1 $ mkdir .chef/bootstrap
2 $ cp definitions/MyServer/postinstall.sh .chef/bootstrap/centos-5.6.erb

Now open .chef/bootstrap/centos-5.6.erb in your editor and remove the lines related to creating a vagrant user and installing the guest additions. You should end up with a file that looks like this:

#http://chrisadams.me.uk/2010/05/10/setting-up-a-centos-base-box-for-development-and-testing-with-vagrant/

date > /etc/vagrant_box_build_time

fail()
{
  echo "FATAL: $*"
  exit 1
}

#kernel source is needed for vbox additions
rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
rpm -i http://vault.centos.org/5.6/os/x86_64/CentOS/kernel-devel-2.6.18-238.el5.x86_64.rpm
yum -y install gcc bzip2 make
#yum -y update
#yum -y upgrade

yum -y install gcc-c++ zlib-devel openssl-devel readline-devel sqlite-devel libyaml-devel
yum -y erase gtk2 libX11 hicolor-icon-theme avahi freetype bitstream-vera-fonts
yum -y clean all

#Installing ruby
cd /tmp
wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.gz || fail "Could not download Ruby source"
tar xzvf ruby-1.9.3-p194.tar.gz
cd ruby-1.9.3-p194
./configure
make && make install
cd /tmp
rm -rf /tmp/ruby-1.9.3-p194
rm /tmp/ruby-1.9.3-p194.tar.gz
ln -s /usr/local/bin/ruby /usr/bin/ruby # Create a sym link for the same path
ln -s /usr/local/bin/gem /usr/bin/gem # Create a sym link for the same path

#Installing chef & Puppet
echo "Installing chef and puppet"
/usr/local/bin/gem install chef --no-ri --no-rdoc || fail "Could not install chef"
/usr/local/bin/gem install puppet --no-ri --no-rdoc || fail "Could not install puppet"

#Installing shadow password support for chef
echo "Installing ruby shadow password support for chef"
/usr/local/bin/gem install ruby-shadow --no-ri --no-rdoc || fail "Could not install ruby-shadow"

sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers
sed -i "s/^\(.*env_keep = \"\)/\1PATH /" /etc/sudoers

# Disabling SELinux in config since kickstart ignores the flag
echo "Disabling SELinux"
sed -i "s/^SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config

#poweroff -h

exit

We’re not quite ready yet though. One of the things vagrant does for us automatically is configuring and running the chef client. With a custom boostrap file for knife and cloud instances, we need to ensure those same steps are performed in the bootstrap file.

At the end of centos-5.6.erb, add the following code right before the “exit” line:

 1 # Configure Chef
 2 echo "Configuring Chef Client"
 3 mkdir -p /etc/chef
 4 
 5 cat << 'EOP' >> /tmp/validation.pem
 6 <%= validation_key %>
 7 EOP
 8 awk NF /tmp/validation.pem > /etc/chef/validation.pem
 9 rm /tmp/validation.pem
10 
11 cat << 'EOP' >> /etc/chef/client.rb
12 <%= config_content %>
13 EOP
14 
15 cat << 'EOP' >> /etc/chef/first-boot.json
16 <%= first_boot.to_json %>
17 EOP
18 
19 <%= start_chef %>

From the top: This code creates the chef directory, saves the validation key, configures the client, adds the initial run list, then kicks off the Chef Client run.

Now it’s time to ask RackCloud to make us a server! We’ll do that using the knife rackspace server create command.

When you run this command, you’re going to see a lot of output. I mean a LOT. This will be the output from every command the server is running, including the yum updates, install commands, compiling ruby, etc.

Also, towards the top, you will see your temporary root password float by. Take note of it. You’ll need it. But don’t worry if you miss it. You can reset the root password in the RackCloud Control Panel on their website.

1 $ knife rackspace server create -S rackspace-db -N rackspace-db -I 114 -f 1 -d centos-5.6 -r 'role[db]' -j '{"mysql":{"server_root_password":"abcd1234"},"myapp":{"database":{"password":"myapp_1234"}}}'

From the left: The create command has a heap of options. In our case, we ask it to name the server rackspace-db (-S), name the chef node rackspace-db (-N), use image #114/CenstOS 5.6 (-I), use flavor #1/256MB server (-f) and use our custom centos5.6.erb boostrap file (-d), set the chef run list to role[db] (-r), and supply the attributes (in JSON format) for mysql server root password and out app database password (-j)

Yes, that’s a lot of command options to remember. In future posts, we’ll look at making those more manageable, namely a set of Rake tasks for developers to use without having to remember all of those options every time. More over, passing in passwords is not only awkword, but means your storing them in your task. We’ll cover how to store passwords in encrypted data bags later on.

$ knife rackspace server create -S rackspace-db -N rackspace-db -I 114 -f 1 -d centos-5.6 -r 'role[db]' -j '{"mysql":{"server_root_password":"abcd1234"},"myapp":{"database":{"password":"myapp_1234"}}}'
Instance ID: 21162952
Host ID: c33bb243be8ca28e0bd42f0583000a30
Name: rackspace-db
Flavor: 256 server
Image: CentOS 5.6
Metadata: {}

Waiting server........................................................................
Public DNS Name: 198-101-233-114.static.cloud-ips.com
Public IP Address: 198.101.233.114
Private IP Address: 10.180.147.191
Password: uhDWm72J1rackspace-db

Waiting for sshddone
Bootstrapping Chef on 198.101.233.114
198.101.233.114 Retrieving http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
198.101.233.114 warning: 
198.101.233.114 /var/tmp/rpm-xfer.4j5AaH: Header V3 DSA signature: NOKEY, key ID 217521f6
198.101.233.114 Preparing...                
198.101.233.114 ####################                        (100%)
...
198.101.233.114 2012-09-28 19:16:06 (1.23 MB/s) - `ruby-1.9.3-p194.tar.gz' saved [12432239/12432239]
198.101.233.114 
198.101.233.114 ruby-1.9.3-p194/
198.101.233.114 ruby-1.9.3-p194/doc/
198.101.233.114 ruby-1.9.3-p194/defs/
198.101.233.114 ruby-1.9.3-p194/goruby.c
198.101.233.114 ruby-1.9.3-p194/complex.c
198.101.233.114 ruby-1.9.3-p194/regparse.c
198.101.233.114 ruby-1.9.3-p194/README.EXT
...
198.101.233.114 Configuring Chef Client
198.101.233.114 [2012-09-28T19:23:19+00:00] INFO: *** Chef 10.14.4 ***
198.101.233.114 [2012-09-28T19:23:21+00:00] INFO: Client key /etc/chef/client.pem is not present - registering
198.101.233.114 [2012-09-28T19:23:23+00:00] INFO: Setting the run_list to ["role[db]"] from JSON
198.101.233.114 [2012-09-28T19:23:23+00:00] INFO: Run List is [role[db]]
198.101.233.114 [2012-09-28T19:23:23+00:00] INFO: Run List expands to [mysql::server, myapp::db]
198.101.233.114 [2012-09-28T19:23:23+00:00] INFO: Starting Chef Run for rackspace-db
198.101.233.114 [2012-09-28T19:23:23+00:00] INFO: Running start handlers
198.101.233.114 [2012-09-28T19:23:23+00:00] INFO: Start handlers complete.
198.101.233.114 [2012-09-28T19:23:24+00:00] INFO: Loading cookbooks [apache2, build-essential, database, iptables, myapp, mysql, openssh, openssl, passenger_apache2]
198.101.233.114 [2012-09-28T19:23:24+00:00] INFO: Storing updated cookbooks/mysql/recipes/client.rb in the cache.
198.101.233.114 [2012-09-28T19:23:25+00:00] INFO: Storing updated cookbooks/mysql/recipes/default.rb in the cache.
198.101.233.114 [2012-09-28T19:23:25+00:00] INFO: Storing updated cookbooks/mysql/recipes/ruby.rb in the cache.
...
198.101.233.114 [2012-09-28T19:24:56+00:00] INFO: Chef Run complete in 92.387689 seconds
198.101.233.114 [2012-09-28T19:24:56+00:00] INFO: Running report handlers
198.101.233.114 [2012-09-28T19:24:56+00:00] INFO: Report handlers complete

Instance ID: 21162952
Host ID: c33bb243be8ca28e0bd42f0583000a30
Name: rackspace-db
Flavor: 256 server
Image: CentOS 5.6
Metadata: {}
Public DNS Name: 198-101-233-114.static.cloud-ips.com
Public IP Address: 198.101.233.114
Private IP Address: 10.180.147.191
Password: uhDWm72J1rackspace-db
Environment: _default
Run List: role[db]

Let’s login to our new instance and check things out.

ssh root@198.101.233.114
root@198.101.233.114's password: uhDWm72J1rackspace-db
Last login: Fri Sep 28 19:13:49 2012 from adsl-76-253-135-65.dsl.akrnoh.sbcglobal.net

[root@rackspace-db ~]# mysql -u root -p
Enter password: abcd1234
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.0.95-log Source distribution

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use myapp;
Database changed
mysql> exit

[root@rackspace-db ~]# grep bind /etc/my.cnf
bind-address            = 10.180.147.191

[root@rackspace-db ~]# /sbin/service iptables status
Table: filter
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    FWR        all  --  0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         

Chain FWR (1 references)
num  target     prot opt source               destination         
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
4    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:3306 
5    ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           udp dpt:3306 
6    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:22 
7    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp flags:0x16/0x02 reject-with icmp-port-unreachable 
8    REJECT     udp  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-port-unreachable 

MySQL is running, root password is set, and MySQL has bound to the Rackspace Private IP address, which is what we want. (The web server and db server can talk to each other using the private internal IP addresses). The iptables firewall rules are in place as well.

Now in your OpsCode site, you should see the rackspace-db machine under the clients and nodes tabs as well as within the knife utility itself:

 1 $ knife client list
 2   claco-personal-validator
 3   rackspace-db
 4   vagrant-db
 5   vagrant-web
 6 
 7 $ knife node list
 8   rackspace-db
 9   vagrant-db
10   vagrant-web


Now, let’s create a cloud server for our web role in the same manner:

1 $ knife rackspace server create -S rackspace-web -N rackspace-web -I 114 -f 1 -d centos-5.6 -r 'role[web]' -j '{"myapp":{"database":{"host":"10.180.147.191","password":"myapp_1234"}}}'

From the left: In this case, we ask it to name the server rackspace-web (-S), name the chef node rackspace-web (-N), use image #114/CenstOS 5.6 (-I), use flavor #1/256MB server (-f) and use our custom centos5.6.erb boostrap file (-d), set the chef run list to role[web] (-r), and supply the attributes (in JSON format) for myapp database password and host (-j)

$ knife rackspace server create -S rackspace-web -N rackspace-web -I 114 -f 1 -d centos-5.6 -r 'role[web]' -j '{"myapp":{"database":{"host":"10.180.147.191","password":"myapp_1234"}}}'
Instance ID: 21163069
Host ID: 039a7acde829ad51d7f8405103db4ed6
Name: rackspace-web
Flavor: 256 server
Image: CentOS 5.6
Metadata: {}

Waiting server.........................................................
Public DNS Name: 198-61-206-121.static.cloud-ips.com
Public IP Address: 198.61.206.121
Private IP Address: 10.178.109.29
Password: g6XsP7uS7rackspace-web

Waiting for sshddone
Bootstrapping Chef on 198.61.206.121
198.61.206.121 Retrieving http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
198.61.206.121 warning: 
198.61.206.121 /var/tmp/rpm-xfer.ljhMwC: Header V3 DSA signature: NOKEY, key ID 217521f6
198.61.206.121 Preparing...                
198.61.206.121 ####################                        (100%)
...
198.61.206.121 Saving to: `ruby-1.9.3-p194.tar.gz'
198.61.206.121 
100%[======================================>] 12,432,239   723K/s   in 13s     
198.61.206.121 
198.61.206.121 2012-09-28 20:22:06 (906 KB/s) - `ruby-1.9.3-p194.tar.gz' saved [12432239/12432239]
198.61.206.121 
198.61.206.121 ruby-1.9.3-p194/
198.61.206.121 ruby-1.9.3-p194/doc/
198.61.206.121 ruby-1.9.3-p194/defs/
198.61.206.121 ruby-1.9.3-p194/goruby.c
198.61.206.121 ruby-1.9.3-p194/complex.c
198.61.206.121 ruby-1.9.3-p194/regparse.c
...
198.61.206.121 Configuring Chef Client
198.61.206.121 [2012-09-28T20:29:09+00:00] INFO: *** Chef 10.14.4 ***
198.61.206.121 [2012-09-28T20:29:11+00:00] INFO: Client key /etc/chef/client.pem is not present - registering
198.61.206.121 [2012-09-28T20:29:13+00:00] INFO: Setting the run_list to ["role[web]"] from JSON
198.61.206.121 [2012-09-28T20:29:14+00:00] INFO: Run List is [role[web]]
198.61.206.121 [2012-09-28T20:29:14+00:00] INFO: Run List expands to [apache2, passenger_apache2::mod_rails, mysql::client, myapp::web]
198.61.206.121 [2012-09-28T20:29:14+00:00] INFO: Starting Chef Run for rackspace-web
198.61.206.121 [2012-09-28T20:29:14+00:00] INFO: Running start handlers
198.61.206.121 [2012-09-28T20:29:14+00:00] INFO: Start handlers complete.
198.61.206.121 [2012-09-28T20:29:15+00:00] INFO: Loading cookbooks [apache2, build-essential, database, iptables, myapp, mysql, openssh, openssl, passenger_apache2]
...
198.61.206.121 [2012-09-28T20:35:11+00:00] INFO: Chef Run complete in 357.585831 seconds
198.61.206.121 [2012-09-28T20:35:11+00:00] INFO: Running report handlers
198.61.206.121 [2012-09-28T20:35:11+00:00] INFO: Report handlers complete

Instance ID: 21163069
Host ID: 039a7acde829ad51d7f8405103db4ed6
Name: rackspace-web
Flavor: 256 server
Image: CentOS 5.6
Metadata: {}
Public DNS Name: 198-61-206-121.static.cloud-ips.com
Public IP Address: 198.61.206.121
Private IP Address: 10.178.109.29
Password: g6XsP7uS7rackspace-web
Environment: _default
Run List: role[web]

Let’s login to the new server and chack things out:

 1 $ ssh root@198.61.206.121
 2 root@198.61.206.121's password: 
 3 Last login: Fri Sep 28 20:20:11 2012 from adsl-76-253-135-65.dsl.akrnoh.sbcglobal.net
 4 
 5 [root@rackspace-web ~]# cat /var/www/myapp/shared/database.yml
 6 development:
 7   adapter: mysql2
 8   encoding: utf8
 9   reconnect: true
10   database: myapp
11   pool: 5
12   username: myapp
13   password: myapp_1234
14   host: 10.180.147.191
15 
16 [root@rackspace-web ~]# mysql -u myapp -p -D myapp -h 10.180.147.191
17 Enter password: 
18 Welcome to the MySQL monitor.  Commands end with ; or \g.
19 Your MySQL connection id is 8
20 Server version: 5.0.95-log Source distribution
21 
22 Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
23 
24 Oracle is a registered trademark of Oracle Corporation and/or its
25 affiliates. Other names may be trademarks of their respective
26 owners.
27 
28 Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
29 
30 mysql> exit
31 Bye
32 
33 [root@rackspace-web ~]# cat /etc/httpd/sites-enabled/myapp.conf
34 <VirtualHost *:80>
35   ServerName 198.61.206.121
36   ServerAlias 198.61.206.121 
37   DocumentRoot /var/www/myapp/current/public
38 
39   RailsBaseURI /
40   RailsEnv development
41 
42   <Directory /var/www/myapp/current/public>
43     Options FollowSymLinks
44     AllowOverride None
45     Order allow,deny
46     Allow from all
47   </Directory>
48 
49   LogLevel info
50   ErrorLog /var/log/httpd/myapp-error.log
51   CustomLog /var/log/httpd/myapp-access.log combined
52 
53   RewriteEngine On
54   RewriteLog /var/log/httpd/myapp-rewrite.log
55   RewriteLogLevel 0
56   # Canonical host
57   RewriteCond %{HTTP_HOST}   !^198.61.206.121 [NC]
58   RewriteCond %{HTTP_HOST}   !^$
59   RewriteRule ^/(.*)$        http://198.61.206.121/$1 [L,R=301]
60 
61   RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
62   RewriteCond %{SCRIPT_FILENAME} !maintenance.html
63   RewriteRule ^.*$ /system/maintenance.html [L]
64 
65 </VirtualHost>

Our database.yml is setup correctly. We can login to the rackspace-db server, so the firewall works. Our application apache conf file is configured with the public ip address.

We should now have another client/node in knife and on the website.

 1 $ knife client list
 2   claco-personal-validator
 3   rackspace-db
 4   rackspace-web
 5   vagrant-db
 6   vagrant-web
 7 
 8 $ knife node list
 9   rackspace-db
10   rackspace-web
11   vagrant-db
12   vagrant-web


In your browser, go to the public ip address of the web server, in this case http://198.61.206.121/

Uh oh! That should be our System Maint. page, not the apache default. What happened? Good quesiton. I don’t know. :-) My guess is that it has something to do with apache binging to port 80, and the difference in the order that happens between machines with one network card (our Vagrant instances) and two network cards (Rackspace instances).

In either case, the solution is simple. Disabled the default site in apache. Now we could login and simple delete this file: /etc/httpd/sites-enabled/000-default. The proper way given we’re using Chef/Knife is to change the nodes apache.default_site_enabled property to false, then rereun the apache recipe.

We can do that using the knife node edit command:

1 $ knife node edit rackspace-web

Doing so will open the nodes attributes, in JSON format, in your editor:

 1 {
 2   "name": "rackspace-web",
 3   "chef_environment": "_default",
 4   "normal": {
 5     "myapp": {
 6       "database": {
 7         "host": "10.180.147.191",
 8         "password": "myapp_1234"
 9       }
10     },
11     "tags": [
12 
13     ],
14     "mysql": {
15       "conf_dir": "/etc",
16       "confd_dir": "/etc/mysql/conf.d",
17       "socket": "/var/lib/mysql/mysql.sock",
18       "pid_file": "/var/run/mysqld/mysqld.pid",
19       "old_passwords": 1,
20       "grants_path": "/etc/mysql_grants.sql",
21       "tunable": {
22         "innodb_adaptive_flushing": false
23       }
24     },
25     "platform?": "ubuntu",
26     "apache": {
27       "root_group": "root",
28       "package": "httpd",
29       "dir": "/etc/httpd",
30       "log_dir": "/var/log/httpd",
31       "error_log": "error.log",
32       "user": "apache",
33       "group": "apache",
34       "binary": "/usr/sbin/httpd",
35       "icondir": "/var/www/icons",
36       "cache_dir": "/var/cache/httpd",
37       "pid_file": "/var/run/httpd.pid",
38       "lib_dir": "/usr/lib64/httpd",
39       "libexecdir": "/usr/lib64/httpd/modules",
40       "default_site_enabled": false
41     }
42   },
43   "run_list": [
44     "role[web]"
45   ]
46 }

Find the default_site_enabled on line 40 and change it false. Then save and close your editor. Knife will then upload the changes to your node.

1 Saving updated normal on node rackspace-web

Now let’s run a remote comand using knife to trigger another run of the chec client:

$ knife ssh "name:rackspace-web" "chef-client" -V -x root -P g6XsP7uS7rackspace-web -a ipaddress
198.61.206.121 [2012-09-28T21:16:52+00:00] INFO: *** Chef 10.14.4 ***
198.61.206.121 [2012-09-28T21:16:54+00:00] INFO: Run List is [role[web]]
198.61.206.121 [2012-09-28T21:16:54+00:00] INFO: Run List expands to [apache2, passenger_apache2::mod_rails, mysql::client, myapp::web]
198.61.206.121 [2012-09-28T21:16:54+00:00] INFO: Starting Chef Run for rackspace-web
...

From the top: The knife ssh command search for a list of nodes, then runs the specified command. We search for nodes with the name rackspace-web, run chef-client, then pass in options to tell ssh what username/password to use, and what ip address attribute to connect to. As your toolkit matures, you’ll probably install a company ssh account/key rather than using the root credentials.

If you check your browser again, you’ll see… that this didn’t work. :-( That’s ok. I think it’s a bug in the cookbook itself. But we needed to see how to run remote commands anyways. In the future, this is how you would update recipes/attribues and kick off new provisioning with chef-client.

For now, let’s implement our original idea and just remote the file from sites-enabled:

$ knife ssh "name:rackspace-web" "rm /etc/httpd/sites-enabled/000-default" -V -x root -P g6XsP7uS7rackspace-web -a ipaddress
$ knife ssh "name:rackspace-web" "/sbin/service httpd restart" -V -x root -P g6XsP7uS7rackspace-web -a ipaddress
198.61.206.121 Stopping httpd: 
198.61.206.121                                             [
198.61.206.121   OK  
198.61.206.121 ]
198.61.206.121 
198.61.206.121 Starting httpd: 
198.61.206.121 httpd: Could not reliably determine the server's fully qualified domain name, using 198.61.206.121 for ServerName
198.61.206.121                                             [
198.61.206.121   OK  
198.61.206.121 ]
198.61.206.121 

Now, this time, you’ll see the system maint. page.

Convert Deployment Process

The last thing we need to do is deploy our app. Now that we have two possible destinations, vagrant, and rackcloud, let’s tweak our myapp/config/deploy.app file:

 1 require "bundler/capistrano"
 2 
 3 set :application, "myapp"
 4 set :deploy_to, "/var/www/myapp"
 5 set :deploy_via, :copy
 6 set :user, "myapp"
 7 
 8 set :rails_env, "development"
 9 set :use_sudo, false
10 
11 set :scm, :none
12 set :repository, "./"
13 
14 task :vagrant do
15   role :web, "10.10.10.10"                          # Your HTTP server, Apache/etc
16   role :app, "10.10.10.10"                          # This may be the same as your `Web` server
17   role :db,  "10.10.10.10", :primary => true # This is where Rails migrations will run
18 end
19 
20 task :rackspace do
21   role :web, ENV['address']                          # Your HTTP server, Apache/etc
22   role :app, ENV['address']                          # This may be the same as your `Web` server
23   role :db,  ENV['address'], :primary => true # This is where Rails migrations will run
24 end
25 
26 after "deploy:finalize_update", "deploy:copy_database_yml"
27 
28 namespace :deploy do
29   task :start do ; end
30   task :stop do ; end
31   task :restart, :roles => :app, :except => { :no_release => true } do
32     run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
33   end
34 
35   task :copy_database_yml do
36     run "cp #{shared_path}/database.yml #{release_path}/config/database.yml"
37   end
38 end

From the top: We’ve added a rackspace task to the deployment file and moved the vagrant settings into a vagrant task. Running this task in conjunction with the deploy commands simple overrides the default vagrant ip addresses with whatever address we send in ENV.

Now, let’s go for the gold and deploy out application to rackspace!

 1 $ cd myapp
 2 $ cap rackspace deploy:setup address=198.61.206.121
 3   * executing `rackspace'
 4   * executing `deploy:setup'
 5   * executing "mkdir -p /var/www/myapp /var/www/myapp/releases /var/www/myapp/shared /var/www/myapp/shared/system /var/www/myapp/shared/log /var/www/myapp/shared/pids"
 6     servers: ["198.61.206.121"]
 7 Password: myapp_1234
 8     [198.61.206.121] executing command
 9     command finished in 768ms
10   * executing "chmod g+w /var/www/myapp /var/www/myapp/releases /var/www/myapp/shared /var/www/myapp/shared/system /var/www/myapp/shared/log /var/www/myapp/shared/pids"
11     servers: ["198.61.206.121"]
12     [198.61.206.121] executing command
13     command finished in 240ms
 1 $ cap rackspace deploy:cold address=198.61.206.121
 2   * executing `rackspace'
 3   * executing `deploy:cold'
 4   * executing `deploy:update'
 5  ** transaction: start
 6   * executing `deploy:update_code'
 7   * getting (via checkout) revision  to /var/folders/nf/zwnznxhj35n_143061ppg9rr0000gn/T/20120928215119
 8     executing locally: cp -R ./ /var/folders/nf/zwnznxhj35n_143061ppg9rr0000gn/T/20120928215119
 9     command finished in 72ms
10   * Compressing /var/folders/nf/zwnznxhj35n_143061ppg9rr0000gn/T/20120928215119 to /var/folders/nf/zwnznxhj35n_143061ppg9rr0000gn/T/20120928215119.tar.gz
11     executing locally: tar czf 20120928215119.tar.gz 20120928215119
12     command finished in 114ms
13     servers: ["198.61.206.121"]
14 Password: 
15  ** sftp upload /var/folders/nf/zwnznxhj35n_143061ppg9rr0000gn/T/20120928215119.tar.gz -> /tmp/20120928215119.tar.gz
16     [198.61.206.121] /tmp/20120928215119.tar.gz
17     [198.61.206.121] done
18   * sftp upload complete
19   * executing "cd /var/www/myapp/releases && tar xzf /tmp/20120928215119.tar.gz && rm /tmp/20120928215119.tar.gz"
20     servers: ["198.61.206.121"]
21     [198.61.206.121] executing command
22     command finished in 374ms
23   * executing `deploy:finalize_update'
24     triggering before callbacks for `deploy:finalize_update'
25   * executing `bundle:install'
26   * executing "cd /var/www/myapp/releases/20120928215119 && bundle install --gemfile /var/www/myapp/releases/20120928215119/Gemfile --path /var/www/myapp/shared/bundle --deployment --quiet --without development test"
27     servers: ["198.61.206.121"]
28     [198.61.206.121] executing command
29     command finished in 33040ms
30   * executing "chmod -R g+w /var/www/myapp/releases/20120928215119"
31     servers: ["198.61.206.121"]
32     [198.61.206.121] executing command
33     command finished in 330ms
34   * executing "rm -rf /var/www/myapp/releases/20120928215119/public/system && mkdir -p /var/www/myapp/releases/20120928215119/public/"
35     servers: ["198.61.206.121"]
36     [198.61.206.121] executing command
37     command finished in 229ms
38   * executing "ln -s /var/www/myapp/shared/system /var/www/myapp/releases/20120928215119/public/system"
39     servers: ["198.61.206.121"]
40     [198.61.206.121] executing command
41     command finished in 228ms
42   * executing "rm -rf /var/www/myapp/releases/20120928215119/log"
43     servers: ["198.61.206.121"]
44     [198.61.206.121] executing command
45     command finished in 228ms
46   * executing "ln -s /var/www/myapp/shared/log /var/www/myapp/releases/20120928215119/log"
47     servers: ["198.61.206.121"]
48     [198.61.206.121] executing command
49     command finished in 227ms
50   * executing "rm -rf /var/www/myapp/releases/20120928215119/tmp/pids && mkdir -p /var/www/myapp/releases/20120928215119/tmp/"
51     servers: ["198.61.206.121"]
52     [198.61.206.121] executing command
53     command finished in 230ms
54   * executing "ln -s /var/www/myapp/shared/pids /var/www/myapp/releases/20120928215119/tmp/pids"
55     servers: ["198.61.206.121"]
56     [198.61.206.121] executing command
57     command finished in 235ms
58   * executing "find /var/www/myapp/releases/20120928215119/public/images /var/www/myapp/releases/20120928215119/public/stylesheets /var/www/myapp/releases/20120928215119/public/javascripts -exec touch -t 201209282152.06 {} ';'; true"
59     servers: ["198.61.206.121"]
60     [198.61.206.121] executing command
61 *** [err :: 198.61.206.121] find:
62 *** [err :: 198.61.206.121] /var/www/myapp/releases/20120928215119/public/images
63 *** [err :: 198.61.206.121] : No such file or directory
64 *** [err :: 198.61.206.121] 
65 *** [err :: 198.61.206.121] find:
66 *** [err :: 198.61.206.121] /var/www/myapp/releases/20120928215119/public/stylesheets
67 *** [err :: 198.61.206.121] : No such file or directory
68 *** [err :: 198.61.206.121] 
69 *** [err :: 198.61.206.121] find:
70 *** [err :: 198.61.206.121] /var/www/myapp/releases/20120928215119/public/javascripts
71 *** [err :: 198.61.206.121] : No such file or directory
72 *** [err :: 198.61.206.121] 
73     command finished in 335ms
74     triggering after callbacks for `deploy:finalize_update'
75   * executing `deploy:copy_database_yml'
76   * executing "cp /var/www/myapp/shared/database.yml /var/www/myapp/releases/20120928215119/config/database.yml"
77     servers: ["198.61.206.121"]
78     [198.61.206.121] executing command
79     command finished in 239ms
80   * executing `deploy:create_symlink'
81   * executing "rm -f /var/www/myapp/current && ln -s /var/www/myapp/releases/20120928215119 /var/www/myapp/current"
82     servers: ["198.61.206.121"]
83     [198.61.206.121] executing command
84     command finished in 229ms
85  ** transaction: commit
86   * executing `deploy:migrate'
87   * executing "cd /var/www/myapp/releases/20120928215119 && bundle exec rake RAILS_ENV=development  db:migrate"
88     servers: ["198.61.206.121"]
89     [198.61.206.121] executing command
90  ** [out :: 198.61.206.121] ==  CreatePeople: migrating ===================================================
91  ** [out :: 198.61.206.121] -- create_table(:people)
92  ** [out :: 198.61.206.121] -> 0.0091s
93  ** [out :: 198.61.206.121] ==  CreatePeople: migrated (0.0092s) ==========================================
94  ** [out :: 198.61.206.121] 
95     command finished in 6495ms
96   * executing `deploy:start'

Congratualtions! You’re app is in the cloud! Go to the website and check it out!

Don’t forget to delete your cloud instances when you’re done! Although I suspect at 2.2 cents an hour, you’re not going to go broken any time soon.

Whenever you’re ready to do that, pass in the node name and Chef will also delete the client/node from the OpsCode website:

$ knife rackspace server list
Instance ID  Public IP        Private IP      Flavor  Image  Name           State 
21163069     198.61.206.121   10.178.109.29   1       114    rackspace-web  active
21162952     198.101.233.114  10.180.147.191  1       114    rackspace-db   active

$ knife rackspace server delete 21163069 -N rackspace-web
Instance ID: 21163069
Host ID: 039a7acde829ad51d7f8405103db4ed6
Name: rackspace-web
Flavor: 256 server
Image: CentOS 5.6
Public IP Address: 198.61.206.121
Private IP Address: 10.178.109.29

Do you really want to delete this server? (Y/N) Y

Commit Our Work

Let’s commit all of our changes to git.

1 $ cd ~/devops_toolbox
2 $ git add .
3 $ git commit -m "Added cloud boostrap"
4 [master 3cda16c] Added cloud boostrap
5  5 files changed, 110 insertions(+), 3 deletions(-)
6  create mode 100644 .chef/bootstrap/centos-5.6.erb

To Continue…

  1. Introduction – Introduction
  2. Installing Prerequisites – XCode, CommandLineTools, Homebrew, RVM, Ruby, and VirtualBox
  3. Project Setup – Create the git repository and directory structure for Vagrant, Chef, etc.
  4. Vagrant/Veewee Installation – Install Vagrant/Vewee to create/control VirtualBox machines
  5. Define/Create a Vagrant Box – Define and Create a Vagrant Box for use i VirtualBox
  6. Provisioning Machines with Vagrant – Provision a cluster (Web/DB) of machines using Vagrant
  7. Configuring Machines Using Chef Solo – Configuring our new machine instances using Chef Solo
  8. Customizing Recipes for Our Application – Customize the recipes we have to prepare for our application deployment
  9. Create and Deploy a Rails Applications – Create a simple Rails application and deploy it to our Vagrant instances
  10. Migrate from Chef Solo to Hosted Chef – Migrate from using Chef Solo to hosted Chef at OpsCode
  11. Migrate Servers to RackCloud – Migrate your servers from VirtualBox to “The Cloud” using Rackspace.
See more posts about: devops toolbox | All Categories