Skip to content

Instantly share code, notes, and snippets.

@twolfson
Last active May 20, 2016 03:59
Show Gist options
  • Save twolfson/a26174b833b66d2ad05688dceee755c8 to your computer and use it in GitHub Desktop.
Save twolfson/a26174b833b66d2ad05688dceee755c8 to your computer and use it in GitHub Desktop.
Proof of concept to explore NGINX round robin redirect + logging

gist-nginx-round-robin

We want to get an easier to use system than bit.ly which doesn't support updating URL redirects. Our current idea is to use NGINX with its basic logging.

However, we also want to see if round robin redirects work (a la A/B testing) as we are using it for donations.

Here's our setup:

# Clone our repository
git clone https://gist.github.com/a26174b833b66d2ad05688dceee755c8.git gist-nginx-round-robin
cd gist-nginx-round-robin

# Launch a container via Docker with a bash shell
# DEV: We could replace this with Vagrant but this is lighter
./run.sh

# In this Docker container, run our bootstrap script
/docker/bootstrap.sh

# Request from our server
curl --include http://localhost:80/
# ...
# Location: http://paypal.me/twolfson
# or
# Location: http://flattr.com/twolfson

# View NGINX logs
cat /var/log/nginx/round-robin.access.log
# 127.0.0.1 - - [19/May/2016:09:31:03 +0000] "GET / HTTP/1.1" 301 193 "-" "curl/7.35.0" "-" "paypal"
# 127.0.0.1 - - [19/May/2016:09:31:04 +0000] "GET / HTTP/1.1" 301 193 "-" "curl/7.35.0" "-" "flattr"
# 127.0.0.1 - - [19/May/2016:09:31:04 +0000] "GET / HTTP/1.1" 301 193 "-" "curl/7.35.0" "-" "flattr"
# 127.0.0.1 - - [19/May/2016:09:31:05 +0000] "GET / HTTP/1.1" 301 193 "-" "curl/7.35.0" "-" "paypal"

To tear down the Docker container, stop and remove it via:

./rm.sh
#!/usr/bin/env bash
# Exit on first error
set -e
# If apt hasn't been updated, then update it
if ! test -f ~/.apt-get-updated; then
apt-get update
touch ~/.apt-get-updated
fi
# Install development dependencies
if ! which curl &> /dev/null; then
apt-get install -y curl wget man nano
fi
# If NGINX isn't installed, then install NGINX
if ! test -x /etc/init.d/nginx; then
apt-get install -y nginx
rm /etc/nginx/sites-enabled/default
/etc/init.d/nginx reload
fi
# If our NGINX file contents are out of sync, then align them and reload NGINX
src_file="/docker/round-robin.conf"
target_file="/etc/nginx/conf.d/round-robin.conf"
if ! test -f target_file || test "$(cat "$src_file")" != "$(cat "$target_file")"; then
cp "$src_file" "$target_file"
/etc/init.d/nginx reload
fi
#!/usr/bin/env bash
# Exit on first error
set -e
# If a docker instance exists, then stop and remove it
container_name="gist-nginx-round-robin"
if docker ps --all | grep "$container_name" &> /dev/null; then
docker stop "$container_name" &> /dev/null
docker rm "$container_name" &> /dev/null
fi
# Determine client bucket for redirects
# http://nginx.org/en/docs/http/ngx_http_split_clients_module.html
# DEV: This is included in core
# d942041b2016770e41da69689389bfe117ed4fa8
# DEV: Use date as a random variable
# https://www.viget.com/articles/split-test-traffic-distribution-with-nginx
split_clients "${remote_addr}${http_user_agent}${date_gmt}" $variant {
50% flattr;
# TODO: Add dollar amount variance (e.g. `paypal-1`)
50% paypal;
}
server {
# Listen on port 80
listen 80;
server_name localhost 127.0.0.1;
# TODO: Require repo query parameter?
# Declare domains based on keys
# DEV: We use `set + if` over `map` as this can handle query parameters (e.g. `$arg_repo`)
if ($variant = paypal) {
set $redirect_url https://www.paypal.me/twolfson/5;
}
if ($variant = flattr) {
# TODO: Ditch flattr, it's really annoying
set $redirect_url https://flattr.com/thing/2553273/twolfsonsexy-bash-prompt-on-GitHub;
}
# Log our variant
log_format variant '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$variant" "$arg_repo"';
access_log /var/log/nginx/round-robin.access.log variant;
error_log /var/log/nginx/round-robin.error.log;
# Redirect to our domain
location / {
return 302 $redirect_url;
}
}
#!/usr/bin/env bash
# Exit on first error
set -e
# If a docker instance already exists, then verify it's running and connect to it
container_name="gist-nginx-round-robin"
if docker ps --all | grep "$container_name" &> /dev/null; then
docker start "$container_name" &> /dev/null
docker exec --interactive --tty "$container_name" /bin/bash
# Otherwise, start it up
else
docker run --name "$container_name" --interactive --tty --volume "$PWD:/docker" ubuntu:14.04 /bin/bash
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment