Skip to content

Load balancing with WHM, NGINX, and mirrors


I have a project where I need to do a bit of load balancing to keep a website online for a very small window of time when it would receive an extremely high level of traffic.  I saw a dramatic increase in performance after simply installing NGINX, but I also wanted to be able to use mirrors and change the split of traffic during that peak time if we experienced any issues. NGINX does have a module for setting up upstream proxies to handle load balancing, but for this project redirects were simpler and more flexible.

This does only apply to virtual private servers or dedicated hosting – you won’t be able to run this kind of setup in a shared hosting environment.  Start by installing NGINX, below I have instructions on how to add NGINX if your site is on CPanel.

  1. cd /usr/local/src
  2. wget http://nginxcp.com/latest/nginxadmin.tar
  3. tar -xf nginxadmin.tar
  4. cd publicnginx
  5. ./nginxinstaller install

You will also need to delete the automatically generated vhost file for the domain/subdomain you want to change.  And you’ll need to re-delete it if your ever regenerate the vhosts.  You can do this by:

  1. cd /etc/nginx/vhosts/
  2. rm sub.domain.com

NGINX comes pre-installed with the split clients module.  This module is specifically designed for A/B testing, but it will also allow you to split up your traffic between different sites and allow you to adjust how much traffic each site receives.  If you’re using NGINX CP as I described above then you can either edit you NGIX config in WHM, or you can find the file at “/etc/nginx/nginx.conf” to edit from the shell.  In my example below I am also using the $http_referer variable to set-up a separate redirect specifically for direct traffic.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
http {
 
split_clients "${remote_addr}" $mirror {
	33%	"http://google.com";
	33%	"http://yahoo.com";
	*	"http://bing.com";
}
 
server {
	listen 0.0.0.0:80;
	listen [::]:80;
	server_name sub.domain.com www.sub.domain.com;
 
	location / {
		add_header X-Cache "Redirect load balancing";
 
		if ($http_referer = "") {
			set $mirror "http://duckduckgo.com";
		}
 
		return 302 $mirror;
	}
}
 
}

REFERENCES

http://www.nginxcp.com/installation-instruction/
http://nginx.org/en/docs/http/ngx_http_split_clients_module.html
https://www.digitalocean.com/community/tutorials/how-to-target-your-users-with-nginx-analytics-and-a-b-testing
http://serverfault.com/questions/597671/load-balancing-in-nginx-with-redirect-rather-than-proxy

Exporting data to CSV in rails console

I’ve had a similar post to this in the past where I exported an entire table to CSV, but what if you want to export only a subset of that data? Well, that’s what this post is for 🙂

  1. Start with a query that narrows things down as much as possible
  2. Then loop through those results to apply any additional filtering you need
  3. Finally generate the CSV file with a header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
date = Date.new(2016,12,15)
l = Table.where("created_at > ? AND column != 'variable'", date)
 
ActiveRecord::Base.logger = nil
results = []
l.each do |i|
 u = User.where(:email => i.email)
 if u.present?
   if Table.where(:email => u[0].email).blank?
     results << {:email=>i.email,:member=>'y'}
   end
 else
   results << {:email=>i.email,:member=>'n'}
 end
end;0
 
 
require 'csv'
 
file = "#{Rails.root}/public/data.csv"
 
CSV.open( file, 'w' ) do |writer|
 writer << results.first.map { |a,v| a }
 results.each do |s|
 writer << s.map { |a,v| v }
 end
end
 
 

Global Replace Content in RoR

I think it’s fairly typical to have to make a global replace on your database at some time or another – updating old links, old code, ect.  And today this was something I was tasked with!  It was pretty straight forward, and these two stackoverflow questions helped me do it.  To get this working you should just need to follow the commands below, while replacing the following:

  • TABLE – The database table you’re replacing content in
  • COLUMN – The column in that table you’re replacing content in
  • BAD – The old string you want to replace
  • GOOD – The new string you want to replace BAD with
1
2
3
4
5
6
7
8
9
10
rails c
 
ActiveRecord::Base.connection.execute(%q{
    update TABLE
    set COLUMN = replace(
        COLUMN,
        'BAD',
        'GOOD'
    )
})

Checking for a string

You could also use the following code to check whether or not the string exists in any entries in that model in the first place. Just swap out MODEL for the model, SEARCH for the string you want to check, and COLUMN for the column you want to search. It will output the total number of entries containing that string, and then loop through their ID numbers.

1
2
3
4
5
6
7
8
9
10
11
rails c
 
a = MODEL.where("COLUMN like ?", "%SEARCH%");0
 
puts a.length
 
puts " "
 
a.all.each do |a|
  puts a.id
end;0

Export table in rails console

This is just a small twist on the very nice snippet I found from this post.  I just changed it slightly, so you can get all the attributes automatically so you can easily swap out any model.  And it will also list the column names in the first row.

1
2
3
4
5
6
7
8
9
10
11
12
require 'csv'
 
file = "#{Rails.root}/public/data.csv"
 
table = User.all;0
 
CSV.open( file, 'w' ) do |writer|
  writer << table.first.attributes.map { |a,v| a }
  table.each do |s|
    writer << s.attributes.map { |a,v| v }
  end
end

simple-zoom

Just finished pushing a very simple zoom app in GitHub, I couldn’t find a nice script that would allow the image to take up the full size of it’s parent element when zoomed in, and would handle updating the image dynamically.  So I wrote one for myself.

Get the files on GitHub.

See a preview: