Rails Capistrano Deployment - ruby-on-rails

for the first time I'm trying to deploy a rails app not to Heroku but to DigitalOcean, mainly because of the price. I'm completely new to Capistrano as well as VPS deployment and I'm totally lost. I've created the 1-click-Rails-droplet and followed this tutorial:
http://guides.beanstalkapp.com/deployments/deploy-with-capistrano.html
This are the settings in my deploy.rb file:
require 'capistrano/ext/multistage'
set :stages, ["staging", "production"]
set :default_stage, "staging"
set :application, "myAppName"
set :scm, :git
set :repository, "myGitRepository"
set :use_sudo, :false
set :deploy_via, :remote_cache
set :scm_passphrase, "myPassword"
set :user, "root"
role :web, "xx.xxx.x.xxx"
role :app, "xx.xxx.x.xxx"
The staging.rb file:
server "xx.xxx.x.xxx", :app, :web, :db, :primary => true
set :deploy_to, "/var/www/xx.xxx.x.xxx_staging"
And the production.rb file
server "xx.xxx.x.xxx", :app, :web, :db, :primary => true
set :deploy_to, "/var/www/xx.xxx.x.xxx"
Now, when I run
cap deploy:check
in the terminal I get the following:
[xx.xxx.x.xxx] executing command
xx.xxx.x.xxx: env:
xx.xxx.x.xxx: sh
xx.xxx.x.xxx: : No such file or directory
xx.xxx.x.xxx:
command finished in 88ms
* executing "test -w /var/www/xx.xxx.x.xxx_staging"
servers: ["xx.xxx.x.xxx"]
[xx.xxx.x.xxx] executing command
xx.xxx.x.xxx: env:
xx.xxx.x.xxx: sh
xx.xxx.x.xxx: : No such file or directory
xx.xxx.x.xxx:
command finished in 79ms
* executing "test -w /var/www/xx.xxx.x.xxx_staging/releases"
servers: ["xx.xxx.x.xxx"]
[xx.xxx.x.xxx] executing command
xx.xxx.x.xxx: env:
xx.xxx.x.xxx: sh
xx.xxx.x.xxx: : No such file or directory
xx.xxx.x.xxx:
command finished in 72ms
* executing "which git"
servers: ["xx.xxx.x.xxx"]
[xx.xxx.x.xxx] executing command
xx.xxx.x.xxx: env:
xx.xxx.x.xxx: sh
xx.xxx.x.xxx: : No such file or directory
xx.xxx.x.xxx:
command finished in 85ms
* executing "test -w /var/www/xx.xxx.x.xxx_staging/shared"
servers: ["xx.xxx.x.xxx"]
[xx.xxx.x.xxx] executing command
xx.xxx.x.xxx: env:
xx.xxx.x.xxx: sh
xx.xxx.x.xxx: : No such file or directory
xx.xxx.x.xxx:
command finished in 81ms
The following dependencies failed. Please check them and try again:
--> `/var/www/xx.xxx.x.xxx_staging/releases' does not exist. Please run `cap staging deploy:setup'. (xx.xxx.x.xxx)
--> You do not have permissions to write to `/var/www/xx.xxx.x.xxx_staging'. (xx.xxx.x.xxx)
--> You do not have permissions to write to `/var/www/3xx.xxx.x.xxx_staging/releases'. (xx.xxx.x.xxx)
--> `git' could not be found in the path (xx.xxx.x.xxx)
--> `/var/www/xx.xxx.x.xxx_staging/shared' is not writable (xx.xxx.x.xxx)
I've googled for a long time now but can't fix this. As I'm totally new to this and feel like there is a huge gap between learning to create a rails app and knowing all the things about deploying them (btw: what happened to ftp upload from earlier days?), I really hope someone knows how to fix this and could guide me into a direction to a not so steep learning curve for deployment. By the way: are there "easier" ways to do what I'm trying to do? Thank you very much!
Edit: One more question: Do I really need to deploy the app at github on the way to the server?

require "bundler/capistrano"
server "XXX.XX.XXX.XXX", :web, :app, :db, primary: true
This is just informing Capistrano which server you want to be accessing. For now since you have only one server it will be acting as a web, app and db server. The web server is your nginx + unicorn. App server is your rails application and db is your database server. The primary true indicates that this is your primary database server once you expand to more database servers. These are called roles you can define more but these are the main ones that are required. You can specify some tasks to work on some roles and not others, but that's all when you have more than 1 server.
set :application, "applicationName"
set :user, "deployer"
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false
set :scm, :git
set :repository, "GIT REPO URL"
set :branch, "master"
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
These just set up some variables for Capistrano to use. Your application name, the user that you will be deploying as (don't use root create another user), where you want to deploy, whether to use sudo when running commands (Ryan says sometimes he encounters permission errors if sudo is used). Then the source code management system as git / subversion, and a link to your repository and the branch with which you want to deploy to.
The next 2 options If I recall were to handle some things with the ssh keys.
So when it comes to thinking of how Capistrano works think of exactly how a Rakefile and Rake tasks work.
after "deploy", "deploy:cleanup"
if you see this kind of statement after "XXX", "YYY", it's just saying after task XXX is done do YYY. So in this case once task deploy is done, do deploy:cleanup.
namespace :deploy do
%w[start,stop,restart].each do |command|
desc "#{command} unicorn server"
task command, roles: :app, except: {no_release: true} do
run "/etc/init.d/unicorn_#{application} #{command}"
end
end
This task is just creating tasks to be able to stop start and restart the unicorn server. Therefore this task can be used in conjunction later as soon as the site is deployed to restart the unicorn server.
task :setup_config, roles: :app do
sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
run "mkdir -p #{shared_path}/config"
put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml"
puts "Now edit the config files in #{shared_path}."
end
after "deploy:setup", "deploy:setup_config"
task :symlink_config, roles: :app do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
end
after "deploy:finalize_update", "deploy:symlink_config"
So this is a very complex looking task, but it's actually not doing much. Since in the railscasts Ryan was not putting his database.yml file in git (with production values), he just created a template database.example.yml file which is being put in folder apps/APPNAME/shared/ you have to manually go and edit it. These shared configurations are being symlinked to the current folder.
In the other files nginx.conf unicorn.rb and unicorn_init.sh you'd want to edit the paths to point to correct path.
Additional Resources
Also take a look at the Railscasts Capistrano Recipes where he expands on the details of his recipes and makes them more DRY. The Capistrano Wiki also has a great resource called from the beginning. In addition Nginx & Unicorn covers more on those 2 things. I'd also suggest reading Github's blog post on Unicorn and this Nginx primer they were help me clear up some other doubts I had.
But most of all you don't worry if you get errors just try breaking it down 1 error at a time and good luck.

Related

WARN [SKIPPING] No Matching Host for /usr/bin/env touch /srv/yenta/releases/20140411230746/tmp/restart.txt

I am using capistrano 3. I don't have tmp/restart.txt file in my rails 4.0 app. When I deploy my app, I receive this error:
WARN [SKIPPING] No Matching Host for /usr/bin/env touch /srv/yenta/releases/20140411230746/tmp/restart.txt
In the deployed server, the app doesn't have tmp/restart.txt. I wonder whether I should create a task explicitly, or if I am missing any settings in capistrano. In capistrano 2, I'd create a task to explicitly touch tmp/restart.txt file instead.
Any help is appreciated!
This issue stems from not defining a particular role - the code is using the :app role without that role being defined, which led to a host not found problem.
deploy.rb
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
# Your restart mechanism here, for example:
execute :touch, current_path.join('tmp/restart.txt')
end
end
deploy/staging.rb
role :web, "qa4-yenta"
set :deploy_to, "/srv/yenta"
set :rails_env, "staging"
set :user, "yenta"
set :use_sudo, false
set :branch, "staging"
set :deploy_via, :remote_cache
the :app role needs to be changed to :web, or the :app role needs to be defined.
You're trying to touch in the releases folder - you need to do it in the current folder.
And yes, there's no tmp/restart.txt. I've been told you have to create it, but the version of Passenger we're using (4) seems to just restart the app anyways?
This error usually means that it couldn't find any matching hosts to perform this task.
What is your task like to restart passenger? What roles are you specifying for this task?
If you can provide your restart task code here that would help.
For reference, check out this blog on how to setup restart task that updates the current folder: http://robmclarty.com/blog/how-to-deploy-a-rails-4-app-with-git-and-capistrano
Look at step 4.

How to deploy licensed fonts that can't go into version-control with Capistrano?

I am using Capistrano to deploy my Rails application to a VPS (as shown in episode 335 on railscasts.com). I have some custom fonts I'm using for the application and don't want to check them into source-control for licensing reasons (See clarification at bottom).
What's the best way to go about this? Should I edit the deploy.rb file to sftp the assets/webfonts directory from my local computer? Maybe I just need to copy them manually to the my_app/shared/assets directory on the VPS, but I'd rather it be automated as part of the deploy task.
Here's my current deploy.rb file:
require "rvm/capistrano"
require "bundler/capistrano"
server "ip_address", :web, :app, :db, primary: true
set :application, "armory"
set :user, "username"
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false
set :scm, "git"
set :github_user, "gorrillamcd"
set :repository, "git#github.com:#{github_user}/Armory.git"
set :branch, "master"
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
after "deploy", "deploy:cleanup" # keep only the last 5 releases
namespace :deploy do
%w[start stop restart].each do |command|
desc "#{command} unicorn server"
task command, roles: :app, except: {no_release: true} do
run "/etc/init.d/unicorn_#{application} #{command}"
end
end
task :setup_config, roles: :app do
sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
run "mkdir -p #{shared_path}/config"
put File.read("config/database.base.yml"), "#{shared_path}/config/database.yml"
puts "Now edit the config files in #{shared_path}."
end
after "deploy:setup", "deploy:setup_config"
task :symlink_config, roles: :app do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
run "ln -nfs #{shared_path}/config/initializers/stripe.rb #{release_path}/config/initializers/stripe.rb"
end
after "deploy:finalize_update", "deploy:symlink_config"
desc "Make sure local git is in sync with remote."
task :check_revision, roles: :web do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
before "deploy", "deploy:check_revision"
end
Edit: Sorry for being so vague beforehand. I'm using Museo Slab from myfonts.com and a public repo on github (check my user profile if you want to see it). I read through the license and found this:
3. The Licensed Webfont(s) may be used on any Website owned or controlled by the Licensee
4. Agencies responsible for multiple clients’ Websites, for example web design agencies or hosting providers, may not share a single Webfont license across multiple clients’ Websites.
Those two lines of the license make me believe that checking the font into a public repository would be against the license, since it could be used on sites that are not mine without obtaining a new license for the font (even though they were free to begin with). I imagine that other people have had this problem before. So my question is, What's the normal/best way to handle deploy, with Capistrano, fonts (or any file for that matter) that can't be checked into source control?
I would pack the web fonts in an archive and put that archive somewhere separate from the repository. (Amazon S3 is a prime candidate.) Then, I'd have the release process retrieve and unpack this archive at deploy time.
This lets you put your entire app in your public repository -- the Capistrano scripts, the Rake tasks, etc. -- but leaves the actual font binaries out, with their location specified as a matter of configuration. This is consistent with 12-factor app principles, treating these webfonts as an external resource needed by but not contained within your application.
You could, for example, specify a WEBFONTS_URL environment variable, and add a fetch_webfonts Rake task. This task would automatically pull these resources into your application, and would do nothing in the case that WEBFONTS_URL is unspecified -- e.g. if someone else did a checkout of your repo without having these fonts. (You could even make the task do appropriate things with stylesheets, ensuring that the fresh-checkout users get a working app without 404ing on font files.) Rake allows you to add dependencies to existing tasks, so you can ensure this happens automatically as part of assets:precompile, which Capistrano very likely already invokes.
Additionally, this approach works equally well on development and production: you can take a fresh checkout of your own repo, specify the appropriate WEBFONTS_URL, and end up with a working set of fonts on your local machine. (The dotenv gem makes it easy to get development configuration into environment variables, keeping all your environments consistent.) Just be sure that you .gitignore the results so you don't accidentally make them public after all.
You could upload/synchronize the fonts directory using rsync. For example:
Add your IP/domain to a separate variable:
set :host, "ip_address"
server host, :web, :app, :db, primary: true
And add a task to run
`rsync -vz app/assets/webfonts #{user}##{host}:#{shared_path}/assets/webfonts`
Then symlink it into the current version of your application like is done already in the deploy script with database.yml and stripe.rb

Deploy Ruby on Rails project on EC2

I have setup everything and i am able to connect to the server via ssh on a free tier
now my project is how can i upload the server files and database?
I see that there are some gems in rails such as rubber that deploy the files to a server but how can i also integrate git so i can commit changes as well?
Code
For uploading the files, check out capistrano. Another popular tool is Vlad the deployer.
Here's a simple deploy.rb for a rails3 app (assuming passenger) that only needs one server right now (replace all the with actual values):
require 'bundler/capistrano'
require 'capistrano_colors'
set :application, "<APP_NAME>"
set :repository, "git#github.com:<USER>/<PROJECT>.git"
set :branch, "master"
set :deploy_to, "/home/<DEPLOY_USER>/<APP_NAME>"
set :keep_releases, 3
set :scm, :git
set :user, "<DEPLOY_USER>"
set :use_sudo, false
set :deploy_via, :remote_cache
default_run_options[:pty] = true
default_run_options[:shell] = '/bin/bash -l'
ssh_options[:forward_agent] = true
role :web, "<IP_OR_PUBLIC_DNS>"
role :app, "<IP_OR_PUBLIC_DNS>"
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
end
end
after "deploy", "deploy:cleanup"
Note: you should ssh-add the private keys for the deploy user as well as the key you use for github before running cap deploy. (e.g. ssh-add ~/.ssh/id_dsa)
Database
In terms of the database, you can run migrations as part of a deploy via cap deploy:migrations, or independently via cap deploy:migrate. Just be sure to list a server with role :db
role :db, "<IP_OR_PUBLIC_DNS>", :primary => true
(Note: this is the server that will be used to run the migrations, not the actual db. The db should be specified in your database.yml).
Setting up the database itself is beyond the scope of this answer, but you can either run a db directly on the instance, or take a look at Amazon's RDS, which is really simple to set up and more stable than a db running on a single instance.

Why are Moonshine/Capistrano having SFTP trouble during deploy:setup?

I'm having trouble setting up a new server using Moonshine and Capistrano. It seems to get started pretty well, installs a bunch of Ubuntu packages, compiles REE, installs some gems, but then it fails to upload a file via SFTP with this output:
* executing `moonshine:setup_directories'
* executing "mkdir /tmp/moonshine"
servers: ["myserver.tld"]
[myserver.tld] executing command
command finished
servers: ["myserver.tld"]
** sftp upload /Users/arussell/Sites/mysite/config/moonshine/production.yml -> /tmp/moonshine/production.yml
/Users/arussell/.rvm/gems/ree-1.8.7-2011.03/gems/capistrano-2.5.19/lib/capistrano/transfer.rb:196:in `normalize': undefined method `pos' for #<Pathname:0x10f3a6988> (NoMethodError)
from /Users/arussell/.rvm/gems/ree-1.8.7-2011.03/gems/capistrano-2.5.19/lib/capistrano/transfer.rb:104:in `prepare_transfers'
Googling that error isn't really turning much up, all I can figure out is that capistrano/transfer.rb is expecting something other than a PathName object on line 196, but I'm not sure what it's expecting, nor am I sure why it's being fed a PathName object.
Edit: Here is my deploy.rb:
set :stages, %w(staging production dev)
set :default_stage, "staging"
require 'capistrano/ext/multistage' rescue "YOU NEED TO INSTALL THE capistrano-ext GEM"
require 'fileutils'
if ENV['branch']
set :branch, ENV['branch']
end
set :deploy_via, :remote_cache
before "deploy:restart", "deploy:delete_cache"
namespace(:deploy) do
desc "delete cache"
task :delete_cache do
run "rm -rf /usr/local/shared/cache/"
end
task :null, :roles => :app do
run "date"
end
end
require './config/boot'
... and my deploy/production.rb:
server "myserver.tld", :app, :web, :db, :primary => true
set :rails_env, 'production'
Edit 2: I tried using SCP instead of SFTP, but that didn't go any better. I added this to my deploy/production.rb:
upload "local", "remote", :via => :scp
download "remote", "local", :via => :scp
and got this error instead while trying to deploy:
upload via scp failed on myserver.tld: SCP did not finish successfully () (SCP did not finish successfully ())
I can't figure that error out either. It appears the version of Capistrano your using is fairly outdated by a year plus. I would update the version your using to a more recent release, and try again.
If that still doesn't resolve your problem it would help if you provided sanitized copy of your deploy.rb configuration.
Turns out that this was due to Moonshine not being able to handle the presence of deploy-stage-specific files (eg config/moonshine/production.yml) during cap production deploy:setup.
Here's how I got around it:
Remove config/moonshine/production.yml (replace production with whatever your deploy stage is called)
Run cap production deploy:setup
Put config/moonshine/production.yml back
Then run cap production deploy

Capistrano and Git, Ruining my life. "Unable to resolve revision for [HEAD] on repository ..."

I searched all of the relevant Capistrano issues, but couldn't find something that even elucidated anything here for me.
git version 1.6.4.2
Capistrano v2.5.10
Basically, when I run my cap deploy.rb script, it connects to the server, starts executing the deploy:update task, then in the deploy:update_code task:
*** [deploy:update_code] rolling back
* executing "rm -rf /home/user_name/public_html/project_name/releases/20091223094358; true"
servers: ["project_name.com"]
It fails with the following error:
/Library/Ruby/Gems/1.8/gems/capistrano-2.5.10/lib/capistrano/recipes/deploy/scm/git.rb:231:in `query_revision': Unable to resolve revision for 'master' on repository 'ssh://git#slice_ip:path_to_git_repository'. (RuntimeError)
Here's my deploy script, I've tried including and omitting:
set :branch 'master'
I also just thought my path to the repository was off, but i've tried just about every permutation (absolute, not absolute, .git suffix, no suffix). There's definitely a bare git repository at the path i'm pointing to.
**I do have multiple projects being hosted on one slice. The other projects is also a rails project, but is running SVN. Capistrano deployments work fine.
Any pointers in the right direction or any ideas would help reduce the amount of drinking I am planning on doing if I can't figure this out. (Paths / IPs obfuscated, dont hack me bro!)
set :application, "project1"
set :user, "username"
set :repository, "ssh://git#67.24.9.133/home/git/project1.git"
set :branch, "master"
set :port, 696969
set :deploy_to, "/home/username/public_html/#{application}"
set :scm, :git
role :app, application
role :web, application
role :db, application, :primary => true
# deployment via remote client (workstation)
set :deploy_via, :copy
set :runner, user
# mod_rails
namespace :deploy do
desc "Restarting mod_rails with restart.txt"
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
end
[:start, :stop].each do |t|
desc "#{t} task is a no-op with mod_rails"
task t, :roles => :app do ; end
end
end
This was the most relevant post (extremely relevant even), but I couldn't really figure out what they were saying the fix is. I'm pretty new with git / capistrano configs.
https://capistrano.lighthouseapp.com/projects/8716/tickets/56-query_revision-unable-to-resolve-revision-for-head-on-repository
Ok I seemed to have fixed it.
Basically, since I have 2 separate repositories on the remote server, I think the "git" user was failing because I hadn't registered an ssh keypair for the git user. That explains why one of my deploy.rb scripts was working properly, while this one wasn't.
In the link I posted in the question, one of the commenters pointed out the issue:
https://capistrano.lighthouseapp.com/projects/8716/tickets/56-query%5Frevision-unable-to-resolve-revision-for-head-on-repository
Note this error is also displayed if
you are using multiple github keys per
http://capistrano.lighthouseapp....
and you do not have these keys and a
corresponding entry in your
.ssh/config on the workstation you're
running the deploy from. so the
ls-remote is run locally. is there a
way to reference the repository at
github.com for this request while the
remote deploy uses
git#github-project1:user/project1.git
Also, see the following link for more details, since the whole ssh issue would apply even if you're not using github.
http://github.com/guides/multiple-github-accounts
Both your workstation and your server must be able to reach the repository at the address specified, if not then you may have to set :local_repository to how you access it from your workstaion, and :repository to be how your servers should access it.
For me Capistrano deployments with Git only seem to work when setting set :copy_cache, true
I've only used capistrano with git once, but never used or seen the use of ssh:// in the repository definition.
Try using set :repository, "git#67.24.9.133/home/git/project1.git" instead
Make sure the branch you are deploying from exists.
set :branch, "upgrade-to-2013.4.3"
is not equal to
set :branch, "upgrade-to-2013.3.4"

Resources