Rolling Deployment of Java/Tomcat application using Capistrano

331 views
Skip to first unread message

Jonas Gorauskas

unread,
Jun 4, 2014, 9:20:53 PM6/4/14
to capis...@googlegroups.com
I am using Capistrano 2.15.5 to deploy Java web applications to Tomcat application servers. For the time being, we are tied and committed to this version of Capistrano and we cannot upgrade to v3. The deploy task will call on the other tasks to get executed in parallel on all servers in the role. This works as expected and has been working well for a while. 

Now we need to implement a Rolling Deployment when the tasks are executed on one server at a time. So, based on the code below, we would execute shutdown_tomcat, download_bits, deploy_bits, start_tomcat on prdapp01 first and then prdapp02 and so on... So that only one server in the cluster is offline at any given time, thus maximizing our up-time. 

What is the best way to implement this Rolling Deployment strategy with Capistrano? 

This is what a paired-down version of my Capistrano script looks like currently:

    task :production do
      role
:app,   "deployuser@prdapp01", "deployuser@prdapp02", "deployuser@prdapp03"
     
# ...
   
end
   
    task
:deploy do
      shutdown_tomcat
      download_bits
      deploy_bits
      start_tomcat
   
end
   
    task
:shutdown_tomcat, :roles => :app do
      run
"sudo /sbin/service tomcat stop || exit 1;"
   
end
   
    task
:download_bits, :roles => :app do
      run
<<-EOS
       
[ -f /tmp/app_download ] && rm -rf /tmp/app_download;
        mkdir
-p /tmp/app_download;
        cd
/tmp/app_download;
        wget
-q https://internal.server/path/to/application.war || exit 1;
      EOS
   
end
   
    task
:deploy_bits, :roles => :app do
      run
<<-EOS
        cd
/tmp/app_download;
        unzip
-q -o /tmp/app_download/application.war -d /usr/local/tomcat/webapps/ || exit 1;
       
exit 0;
      EOS
   
end
   
    task
:start_tomcat, :roles => :app do
      run
"sudo /sbin/service tomcat start || exit 1;"
   
end


Then I run ...

    $ cap -f deploy.cap production deploy

Haani Niyaz

unread,
Jun 6, 2014, 12:10:16 AM6/6/14
to capis...@googlegroups.com
Hi Jonas,

I've written some Capistrano tasks to address deploying to Tomcat too. I understand Capistrano 3 is not an option for you but in Capistrano 3 there is an option to run all commands sequentially on one server at a time. Not sure if this is present in Capistrano 2.

Just comparing flows, I download the file on all servers first. This way even if the download fails, the service will still be up.
I also do a backup before deploying and then a restart.

You can see my code here:

Hope you find it useful.

Jonas Gorauskas

unread,
Jun 8, 2014, 3:00:30 PM6/8/14
to capis...@googlegroups.com
Haani,

I agree with you that the download can happen in parallel first before bringing Tomcat down and it independent of the other tasks, so I made some changes to the flow.

I believe that I came up with a reasonable solution to the Rolling Deployment strategy I described above. First, I pass in to Capistrano an argument telling it what deployment type I want to perform: Full or Rolling. Then I follow this process:
  1. Capture the server list from the appropriate role into another list.
  2. Iterate over the new list.
  3. Reset the original role to the value of the current server.
  4. Apply all the tasks to that server alone
  5. Leather, rinse, repeat...
Here are the relevant changes to the deploy task:

task :deploy do
  deploy_type
= fetch(:deploytype).downcase
 
case deploy_type
   
when 'full' then
      download_bits
      shutdown_tomcat
      deploy_bits
      start_tomcat
   
when 'rolling' then
      download_bits
      server_list
= roles[:app].servers    # get the original servers
      server_list
.each do |current_server| # apply other tasks in series
        roles
[:app].servers = []           # reset original server list
        role
:app, "#{current_server}"     # otherwise you'll append to it
        shutdown_tomcat
        deploy_bits
        start_tomcat
     
end                                  # each loop
   
else
      puts
'Invalid deployment type'
     
exit
 
end                                      # case deploy_type
end                                        # task :deploy


Then you can invoke this with the following command:

$ cap -f deploy.cap -s deploytype=rolling production deploy

Feedback is appreciated!
Reply all
Reply to author
Forward
0 new messages