dominicsayers/foreplay

View on GitHub
README.md

Summary

Maintainability
Test Coverage
## Foreplay

[![Gem version](https://badge.fury.io/rb/foreplay.svg)](https://rubygems.org/gems/foreplay)
[![Gem downloads](https://img.shields.io/gem/dt/foreplay.svg)](https://rubygems.org/gems/foreplay)
[![Build Status](https://travis-ci.org/dominicsayers/foreplay.svg?branch=master)](https://travis-ci.org/dominicsayers/foreplay)
[![Code Climate](https://codeclimate.com/github/dominicsayers/foreplay/badges/gpa.svg)](https://codeclimate.com/github/dominicsayers/foreplay)
[![Test Coverage](https://codeclimate.com/github/dominicsayers/foreplay/badges/coverage.svg)](https://codeclimate.com/github/dominicsayers/foreplay/coverage)
[![Dependency Status](https://gemnasium.com/badges/github.com/dominicsayers/foreplay.svg)](https://gemnasium.com/github.com/dominicsayers/foreplay)
[![Security](https://hakiri.io/github/dominicsayers/foreplay/master.svg)](https://hakiri.io/github/dominicsayers/foreplay/master)

Deploying Rails projects to Ubuntu using Foreman

I noticed with surprise on [RubyGems](https://rubygems.org/gems/foreplay) that my little gem had been downloaded a few times, so clearly people are trying to use it. Thanks for trying it, people.

There's now a CLI for the gem so you can use it as follows. To check what it's going to do:

    foreplay check production

...and if you're brave enough to try it for real:

    foreplay deploy production

...after you've set it up by creating a `config/foreplay.yml` file.

## Installation

Add this line to your application's Gemfile:

    gem 'foreplay'

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install foreplay

### How it works

Foreplay does this:

1.  Opens an SSH connection to the deloyment target
2.  Grabs a copy of your code from the repository
3.  Builds a `.env` file, a `.foreman` file and a `database.yml` file
4.  Does a `bundle install`
5.  Uses `foreman` to create an Upstart service (`foreman export`) for your app
6.  Launches the app
7.  Directs incoming traffic on port 80 to your app
8.  If there's a previous instance of the app running, Foreplay shuts it down gracefully after it has switched `iptables` to the new instance

There should be little or no downtime. If the app is b0rked then you can easily switch back to the previous instance: the Upstart service is still configured.

### foreplay.yml

Here's my actual foreplay.yml that I use to deploy my app Xendata:

```YAML
---
defaults:
  repository: git@github.com:Xenapto/xendata.git
  branch: master
  user: xenapto
  keyfile: ~/.ssh/id_circleci_github
  path: apps/%a
production:
  defaults:
    database:
      adapter: postgresql
      encoding: utf8
      database: xendata
      pool: 10
      host: sandham.xenapto.net
      reconnect: true
      timeout: 5000
      username: kjh123kj1h23
      password: ,mn23-1m412-not-really
    resque: redis://kjjh3425mnb:bn34=-23f2@redis.xenapto.net:6379
  web:
    servers: [sandham.xenapto.net]
    database:
      host: localhost
    foreman:
      concurrency: 'web=1,worker_immediate=2,worker_longjobs=1,scheduler=1,resque_web=1,new_relic_resque=1'
  auxiliary:
    config: ['stop_first'] # It runs out of memory unless I stop the service before asset precompile
    servers: [bradman.xenapto.net,edrich.xenapto.net:10022]
    foreman:
      concurrency: 'worker_regular=8'
  largeserver:
    servers: [simpson.xenapto.net]
    foreman:
      concurrency: 'worker_longjobs=1,worker_regular=24'
```

A quick walk-though of this configuration:

1.  I'm deploying the `master` branch of the Github project `git@github.com:Xenapto/xendata.git`
1.  I'm making an SSH connection to my production servers with the username `xenapto` and the keyfile in `~/.ssh/id_circleci_github` which lives on the machine I'm deploying from
2.  I'm deploying to the directory `~/apps/xendata` (`%a` is expanded to the name of the app)
3.  In this config file I'm defining the `production` environment. I could also define a `staging` section if I wanted to.
3.  On each server I'm creating a `database.yml` file with the contents of the `database` section of this config
4.  I'm creating a `resque.yml` file from the contents of the `resque` section
5.  I'm deploying three different types of server. The roles are `web`, `auxiliary` and `largeserver`. These names are completely arbitrary. I can deploy all or one of these roles.
6.  Each role contains a list of servers and any overrides to the default settings
7.  For instance the `web` role is deployed to `sandham.xenapto.net`. For that server the database is on the same machine (`localhost`). The Foreman `concurrency` setting defines which workers from my Procfile are launched on that server.
8.  Note that in the `auxiliary` role I am deploying to two servers. On the second (`edrich.xenapto.net`) I'm using port 10022 for SSH instead of the default.
9.  Precompiling assets uses a lot of memory. On these servers I get Out Of Memory errors unless I shut down my app first.

General format:

```YAML
defaults:       # global defaults for all environments
  name:         # app name (if omitted then Rails.application.class.parent_name.underscore is used)
  servers: [server1, server2, server3] # which servers to deploy the app on
  user:         # The username to connect with (must have SSH permissions)
  password:     # The password to use to connect (not necessary if you've set up SSH keys)
  keyfile:      # or a file containing a private key that allows the named user access to the server
  key:          # ...or a private key that allows the named user access to the server
  path:         # absolute path to deploy the app on each server. %s will substitute to the app name
  config:       # Configuration parameters to change the behaviour of Foreplay
  database:     # the database.yml elements to write to the config folder
  env:          # contents of the .env file
    key: value  # will go into the .env file as key=value
  foreman:      # contents of the .foreman file
    key: value  # will go into the .foreman file as key: value
production:     # deployment configuration for the production environment
  defaults:     # defaults for all roles in this environment (structure same as global defaults)
  role1:        # settings for the a particular role (e.g. web, worker, etc.)
```

### Environment

Settings for the `.env` files and `.foreman` files in specific sections will add to the defaults specified earlier. `.env` files will get a `RAILS_ENV=environment` entry (where `environment` is as specified in `foreplay.yml`). You can override this by adding a different `RAILS_ENV` setting to this configuration here.

The first instance of the first entry in `Procfile` that is instantiated by your Foreman concurrency settings will
be started on port 50100 or 51100 and the external port 80 will be mapped to this port by `iptables`. You cannot
configure the ports yourself. As an example, if your `Procfile` has a `web` entry on the first line and at
least one `web` instance is configured in the `.foreman` concurrency setting then the first instance of your `web`
process will be available to the outside world on port 80.

### Path

You can use `%u` in the path. This will be substituted with the `user` value. You can use `%a` in the path. This will be substituted with the app's `name`

Example:

    user: fred
    name: myapp
    path: /home/%u/apps/%a

### Dependencies

```ruby
gem 'foreman'
gem 'net-ssh-shell'
```

You can constrain this to whatever groups you use for initiating deployments, e.g.

```ruby
group :development, :test do
  gem 'foreman'
  gem 'net-ssh-shell'
end
```

## Contributing

1.  Fork it
1.  Create your feature branch (`git checkout -b my-new-feature`)
1.  Commit your changes (`git commit -am 'Add some feature'`)
1.  Push to the branch (`git push origin my-new-feature`)
1.  Create new Pull Request

## Acknowledgements

1.  Thanks to Ryan Bigg for the guide to making your first gem https://github.com/radar/guides/blob/master/gem-development.md