Skip to content

Instantly share code, notes, and snippets.

@harryworld
Created May 17, 2014 05:13
Show Gist options
  • Save harryworld/2f2a44b5303cd2c320df to your computer and use it in GitHub Desktop.
Save harryworld/2f2a44b5303cd2c320df to your computer and use it in GitHub Desktop.
Discourse Installation Guide fo GA Alumni Website (ga-alumni.com)
# -*- encoding : utf-8 -*-
# config/deploy.rb
require 'capistrano/ext/multistage'
require "rvm/capistrano"
require 'bundler/capistrano' #Using bundler with Capistrano
require 'cape'
set :stages, %w(staging production)
set :default_stage, "production"
Cape do
mirror_rake_tasks :dev
end

Reason for this Installation Guide

I wrote this guide for my students in WDI of General Assembly, so that they could build a community for all the graduates globally, namely GA Alumni. Thus, this guide is showing actual deployment steps in this website.

We decided to use Discourse as the community platform, due to several reasons:

  1. We learn Ruby on Rails in the class, Discourse is a big project powered by Ruby on Rails.
  2. We can learn how a Ruby on Rails project look like, which connects the students to the open source community.
  3. Students will be responsible in running this website, so this guide serve as a teaching material.
  4. Students will be fixing issues or working on new features for Discourse project, so that they could contribute back to the open source community.

There are some guides available on the Internet, look at reference section at the bottom. However, due to the many combinations of tools, I decided to write our own to ensure the students can go thru all the detailed steps. Also, I hope this will become a once and for all guide in our website.

Server Structure

GA Alumni is planned to serve the need for Hong Kong students first, and then promote it to Global. We will be using a cloud server, so that we can scale up easily according to need.

As a start, we will build a web server serving the static assets, and direct traffic to 4 rails instances, running as thin server. The deployment process is based on Capistrano.

The domain is registered in GoDaddy, and use its DNS service as well.

List of tools:

  • GoDaddy for Domain
  • rvm - ruby version manager
  • ruby 2.1.1
  • Thin Server for Rails
  • Redis
  • postgresql
  • Capistrano - Deployment

Preparation


Step-by-Step

  1. Setup SSH keys (optional)
  2. Register Cloud Service at DigitalOcean
  3. Register domain at GoDaddy
  4. Setup SSH for Github
  5. Setup Capistrano for Discourse
  6. Run Capistrano
  7. Setup Nginx

1. Create SSH keys (optional)

In your local machine, turn on terminal and type ssh-keygen, save it to the default location. It will create both the public and private keys. This helps in authenticating the cloud server securely, so that you need not to type password every time.

Assume you are saving the keys in default location, copy the public key into clipboard by typing pbcopy < ~/.ssh/id_rsa.pub, then paste it into DigitalOcean.

2. Register Cloud Service at DigitalOcean

Visit DigitalOcean to register for a Cloud Server.

Setup Billing method.

In DigitalOcean's SSH Keys section, paste the public key, and give it a name, e.g. My MacBook Air.

Create Droplet, pick the size, start with the basic one, and you could upgrade it easily later on. Set the Hostname to the domain name ga-alumni.com. Select the Region, the speed to visit the site is generally better if the region is near you. Select Image as Ubuntu 14.04 x64. Select SSH Keys previously created, otherwise it will send you the root access password by email. Only tick Enable VirtIO in the Settings.

3. Register domain at GoDaddy

Visit GoDaddy to register for your favourite domain, in my case http://ga-alumni.com

Visit GoDaddy DNS Manager to modify DNS record. Select Edit Zone under your domain.

Modify the A (Host) record. Points your domain to the IP Address provided by DigitalOcean. Click Save on top-right corner. Since it is .com domain, the update will effect almost immediately, otherwise wait upto several hours

4. Setup for Github

Fork your project, clone in to local, then checkout to the latest stable tag by typing git checkout tags/v0.9.9.4. We will now create a branch here by git checkout -b deploy, the new branch is named deploy.

Push the new branch to your repository by git push origin deploy. We need this to do the deployment.

5. Setup the cloud server

SSH into your server by ssh root@ga-alumni.com

Instead of using root user to setup the server, I highly recommend to create a user (e.g. ubuntu) for doing the job. We will use this user to run the application as well. root user should be used for the super administrator in urgent case.

root@www:~# adduser ubuntu

Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for harry
Enter the new value, or press ENTER for the default
	Full Name []:
	Room Number []:
	Work Phone []:
	Home Phone []:
	Other []:
Is the information correct? [Y/n] Y

We will turn ubuntu user to use the SSH key as well, so we need not type password everytime.

Exit the root access, SSH into your server using ssh ubuntu@ga-alumni.com, copy the same public key created in local machine, paste it in ~/.ssh/authorized_keys

Start installing the cloud server under ubuntu user:

sudo apt-get install build-essential openssl libreadline6 libreadline6-dev \
             curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev \
             sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev libgdbm-dev \
             ncurses-dev automake libtool bison subversion pkg-config libffi-dev

To install RVM, open terminal and type in this command:

curl -L https://get.rvm.io | bash -s stable

After it is done installing, load RVM. You may first need to exit out of your shell session and start up a new one.

source ~/.rvm/scripts/rvm

Check version for rvm

rvm -v
rvm 1.25.25 (stable) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]

Install ruby 2.1.1, it takes some time

rvm install 2.1.1

After ruby is installed, pick this as the default version

rvm use 2.1.1 --default

Check the ruby version by,

ruby -v
ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux]

Finally, we'll need bundler to install gems for rails project

gem install bundler --no-rdoc --no-ri

Install PostgreSQL, Redis, and other required libraries

Discourse requires PostgreSQL and Redis to run, so let’s install those as well:

sudo apt-get install postgresql-9.1 postgresql-contrib-9.1 redis-server \
                     libxml2-dev libxslt-dev libpq-dev make g++

Note that postgresql-contrib-9.1 will install the hstore extension for PostgreSQL, which Discourse requires. You’ll need to create a postgres role and Discourse’s production database:

# Create your postgres role
sudo -u postgres createuser discourse -s -P

# Create discourse's production database (I named mine discourse_production)
createdb -U discourse discourse_production

Install Nginx

Nginx is the web server, or sometimes Reverse Proxy server, which we will set before the Rails application.

sudo apt-get install nginx

This will install Nginx to folder /etc/nginx/. Then setup the Nginx to redirect traffic into Discourse, we need a config file, types sudo vi /etc/nginx/sites-enabled/discourse and save the config file.

We can test the correctness of Nginx config

sudo nginx -t

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Then restart the Nginx web server

sudo service nginx restart

Configure Capistrano

Until now, we have succesfully prepared the environment for running Discourse, it's time to deploy the application to the server.

The following part may contain some secrets that we don't want to share to the public, thus we will create a separate git branch. Type git checkout -b secret

Open the Gemfile, add these gems to the bottom

gem "rvm-capistrano"

group :development do
  gem "capistrano", "2.15.4"
  gem "capistrano-ext"
  gem 'capistrano-unicorn', :require => false
  gem "cape"
end

Then, create several files thin.yml, config/deploy.rb and config/deploy/production.rb

These files sets the environment on steps during the deployment process, once and for all. We will then try to run the following commands:

Deploy Discourse

# Set up the server's directory structure for Capistrano
cap deploy:setup

# Seed the database you created earlier
cap db:seed

# Do the first deploy!
cap deploy:restart

Congratulations! You should now have Discourse running on your cloud server. You'll want to follow the Quick Start Guide to setup the Admin Panel.

Setup SSH keys

In the cloud server ssh-keygen

Troubleshooting

  • Use master branch or tag?
  • Capistrano
  • 502 Bad Gateway
  • 403 Forbidden
  • Restart your cloud server
  • Postgresql Peer Authentication

Advanced Topic

Upgrade DigitalOcean Droplet

Pending topicshar

  • Costing
  • Backup

Create a swapfile

Pending

Change Server type

Pending topics

  • Thin to Unicorn
  • Multiple server instances

Email

Pending topics

  • Mailgun

References

Other information

# /etc/nginx/sites-enabled/discourse
upstream discourse {
server unix:///home/ubuntu/discourse/current/tmp/pids/thin.0.sock max_fails=1 fail_timeout=15s;
server unix:///home/ubuntu/discourse/current/tmp/pids/thin.1.sock max_fails=1 fail_timeout=15s;
server unix:///home/ubuntu/discourse/current/tmp/pids/thin.2.sock max_fails=1 fail_timeout=15s;
server unix:///home/ubuntu/discourse/current/tmp/pids/thin.3.sock max_fails=1 fail_timeout=15s;
}
server {
listen 80;
server_name ga-alumni.com;
root /home/ubuntu/discourse/current/public;
access_log /var/www/ga-alumni.com/log/access.log;
error_log /var/www/ga-alumni.com/log/error.log;
client_max_body_size 5M;
location ~* ^/assets/ {
expires 1y;
add_header Cache-Control public;
if ($request_filename ~* ^.*?\.(eot)|(ttf)|(woff)|(svg)|(otf)$){
add_header Access-Control-Allow-Origin *;
}
break;
}
try_files $uri/index.html $uri.html $uri @app;
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://discourse;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
}
# -*- encoding : utf-8 -*-
# config/deploy/production.rb
default_environment["PATH"] = "/opt/ruby/bin:/usr/local/bin:/usr/bin:/bin"
set :application, "discourse"
set :repository, "git@github.com/harryworld/discourse.git"
set :deploy_to, "/home/ubuntu/discourse"
set :branch, "deploy"
set :scm, :git
set :user, "ubuntu"
set :group, "ubuntu"
set :deploy_to, "/home/ubuntu/#{application}"
set :runner, "ubuntu"
set :deploy_via, :remote_cache
set :git_shallow_clone, 1
set :use_sudo, false
set :rvm_ruby_string, '2.1.1'
role :web, "ga-alumni.com" # Your HTTP server, Apache/etc
role :app, "ga-alumni.com" # This may be the same as your `Web` server
role :db, "ga-alumni.com" , :primary => true # This is where Rails migrations will run
set :deploy_env, "production"
set :rails_env, "production"
set :scm_verbose, true
set :use_sudo, false
namespace :deploy do
desc "Restart thin process"
task :restart, :roles => [:web], :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
run "cd #{deploy_to}/current; bundle exec thin start -C ~/discourse/thin.yml"
end
end
namespace :my_tasks do
task :symlink, :roles => [:web] do
run "mkdir -p #{deploy_to}/shared/log"
run "mkdir -p #{deploy_to}/shared/pids"
symlink_hash = {
"#{shared_path}/config/database.yml" => "#{release_path}/config/database.yml",
}
symlink_hash.each do |source, target|
run "ln -sf #{source} #{target}"
end
end
end
namespace :remote_rake do
desc "Run a task on remote servers, ex: cap staging rake:invoke task=cache:clear"
task :invoke do
run "cd #{deploy_to}/current; RAILS_ENV=#{rails_env} bundle exec rake #{ENV['task']}"
end
end
after "deploy:finalize_update", "my_tasks:symlink"
# ~/discourse/thin.yml
---
chdir: /home/ubuntu/discourse/current
environment: production
address: 0.0.0.0
port: 3000
timeout: 30
log: /home/ubuntu/discourse/current/log/thin.log
pid: /home/ubuntu/discourse/current/tmp/pids/thin.pid
socket: /home/ubuntu/discourse/current/tmp/pids/thin.sock
max_conns: 1024
max_persistent_conns: 100
require: []
wait: 30
servers: 4
daemonize: true
@harryworld
Copy link
Author

For postgresql, use this

sudo apt-get install postgresql-9.3 postgresql-contrib-9.3 redis-server \
                     libxml2-dev libxslt-dev libpq-dev make g++

@harryworld
Copy link
Author

Try sudo -u postgres psql to check whether postgresql is running

@harryworld
Copy link
Author

harryworld commented May 19, 2014

To solve Peer authentication failed for user "discourse"

Modify sudo vi /etc/postgresql/9.3/main/pg_hba.conf

There must be a line like this as the first non-comment line:

local   all             postgres                                peer

OR

local   all             postgres                                md5

Change peer or md5 to trust

After altering this file, don't forget to restart your PostgreSQL server. If you're on Linux, that would be sudo service postgresql restart.

http://stackoverflow.com/questions/17443379/psql-fatal-peer-authentication-failed-for-user-dev

@harryworld
Copy link
Author

  • In order to create a Swpfile, so that it will have enough memory to do the bundle install

If you’re running on a VPS service that automatically provisions a swap (such as Linode), you can skip this. On Digital Ocean, however, you’ll need to do this yourself. 1GB of RAM was enough for my first deployment but, when I tried to run a second, I wasn’t able to allocate enough memory to compile assets. Creating a swap, however, has fixed this issue for me. Let’s create a 512MB swapfile:

sudo dd if=/dev/zero of=/swapfile bs=1024 count=512k
sudo mkswap /swapfile
sudo swapon /swapfile

Then, you’ll need to edit /etc/fstab and paste in the following line:

/swapfile       none    swap    sw      0       0

Finally, prevent your swapfile from being readable:

sudo chown root:root /swapfile
sudo chmod 0600 /swapfile
  • In order to do db:migrate on server
cap remote_rake:invoke task=db:migrate
  • In order to do db:seed on server
cap remote_rake:invoke task=db:seed

@harryworld
Copy link
Author

@harryworld
Copy link
Author

harryworld commented Jan 9, 2015

Use ssh-copy-id to copy the public key (default: id_rsa.pub) from local computer to server at ~/.ssh/authourized_keys

Modify sudoers to allow ubuntu user to sudo without password

%sudo ALL=(ALL:ALL) ALL
to
%sudo ALL=(ALL:ALL) NOPASSWD:ALL

How To Edit the Sudoers File on Ubuntu and CentOS

@harryworld
Copy link
Author

Ruby 2.2.0
Postgresql 0.4
sudo apt-get install postgresql-9.4 postgresql-contrib-9.4 libxml2-dev libxslt-dev libpq-dev make g++

@harryworld
Copy link
Author

To install nodejs latest

sudo apt-get update
curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install nodejs

@harryworld
Copy link
Author

How to Install PostgreSQL 9.5 on Ubuntu 16.04 and 14.04 LTS
http://tecadmin.net/install-postgresql-server-on-ubuntu/#

@harryworld
Copy link
Author

Installing ElasticSearch v2 on Ubuntu
Install
Setup
Show error log

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment