www.nico.schottelius.org/blog/great-rails-hosting-a-symlink-for-an-app.mdwn

120 lines
3.9 KiB
Text
Raw Permalink Normal View History

[[!meta title="Great Rails Hosting: A symlink for an app"]]
## Introduction
As [[!ungleich]] focusses on educated customers,
we meet pretty cool infrastructures from time to time.
In some sense I count [[!localch]]
as a customer: they supported me with one day off
per week so I was able to found [[!ungleich]]
and acquire first customers.
This article is dedicated to [[!localch]] and describes
a very elegant solution for Ruby on Rails hosting.
## Overview
The setup consists of the following services, glued
together in an elegant way:
* [[!nginx]]
* [[!unicorn]]
* [[!bind]]
* [[!capistrano]]
* Symlinks
## Nginx
The great trick of the setup is that nginx is used to forward requests
to a unix socket that depends on the **hostname**, which is
exposed as **$host** by nginx. The following
configuration snippet contains the important parts:
server {
listen 80;
location @error_page {
root /var/nginx/$host/current/public;
internal;
[...]
location ~ "^/assets/.*-[a-z0-9]{32}.\w+" {
root /var/nginx/$host/current/public;
[...]
location ~ ^/assets/ {
root /var/nginx/$host/current/public;
[...]
root /var/nginx/$host/current/public;
location @unicorn {
proxy_pass http://unix:/var/nginx/$host/unicorn.sock;
# Forward original host name to be seen in unicorn
proxy_set_header Host $host;
# Server name and address like being available in PHP
proxy_set_header SERVER_NAME $server_name;
proxy_set_header SERVER_ADDR $server_addr;
# The real client IP address - header has ben setup by Zeus
proxy_set_header X-Real-IP $http_x_cluster_client_ip;
# Needed second header for rails - See SYS-1587
proxy_set_header X_FORWARDED_FOR $http_x_cluster_client_ip;
As you can see, all paths are dependent on the actual hostname
as setup by nginx.
## Application Deployment
Applications are deployed under their project name below
**/var/nginx**
(like **ws-locomotive.dev-deploy** or **ws-locomotive.master**).
As you can see from the naming, developers can deploy one application
from different branches easily (dev-deploy and master branches is this
case).
Developers can use [[!capistrano]] to deploy their applications
and don't need to interact (reload/restart) with nginx, as it is
already configured to accept any hostname.
## Name Server Configuration
As you can imagine, it would be quite cumbersome for developers to
reach a host named **ws-locomotive.dev-deploy**.
That is why a wildcard domain is configured to point
to the host running nginx:
*.play.intra.local.ch. CNAME rails-dev-vm-snr01.intra.local.ch.
## Give the application a name
A new hostname can be assigned to an application simply by symlinking
it to the application:
% cd /var/nginx
% ln -s ws-locomotive.dev-deploy my-fancy-name.play.intra.local.ch
This way, developers can use **any name** below
play.intra.local.ch for their application. Some applications
actually behave differently depending on the name they are accessed
with:
info.ws-locomotive.master.play.intra.local.ch -> ws-locomotive.master
hp.ws-locomotive.master.play.intra.local.ch -> ws-locomotive.master
## Conclusions
The setup is pretty elegant, because it allows developers to
create new development environments without interacting with any
sysadmin to configure nginx, bind or whatsoever.
There is a security drawback though:
An attacker could try to use hostnames like
**../../../../etc/** and request the file **passwd**.
That is the reasion why this service is not exposed
to the outside world directly, but all external requests
are filtered (whitelisting) by a load balancer in front
of the rails hosts.
[[!tag net unix foss ungleich localch rails]]