Stager automates the process of staging web applications by coordinating the creation and destruction of nginx vhosts with the creation and destruction of docker containers.
##Installing
###Prerequisites
#install docker
curl -s https://get.docker.io/ubuntu/ | sudo sh #on ubuntu 12.04, see https://www.docker.io/gettingstarted/#h_installation for other platforms
#install build-essential
sudo apt-get install build-essential
#install ruby
sudo apt-get install ruby1.9.1-dev # tested version
#install bundler
sudo gem install bundler
#install nginx
sudo apt-get install nginx
###Quick Setup
# Add the user that will run Stager to the docker group, (you may need to log out and log back in after doing this)
sudo usermod -a -G docker user_to_add
# Give the docker group rw permissions to the nginx virtual hosts folder
sudo chown -R root:docker /etc/nginx/sites-enabled && sudo chmod -R 775 /etc/nginx/sites-enabled
# Expose Stager using default nginx vhost
HOST_NAME=stager_host.com sudo erb /path/to/stager/default_vhost.erb > /etc/nginx/sites-available/default
# Allow docker group to restart nginx without sudo password
# http://stackoverflow.com/questions/3011067/restart-nginx-without-sudo
echo %docker ALL=NOPASSWD: /path/to/stager/request_handlers/reload_nginx >> /etc/sudoers
# Start stager
cd /path/to/stager
bundle install
thin start
##Basic Use
Stager exposes three http endpoints, all of which expect POST requests.
####/launch
curl -d 'image_name=image_to_use&container_name=name_to_give_container' http://stager-instance.com/launch
#returns
http://name-to-give-container.stager-instance.com
Stager reads the image_to_use section of your config.yml images config to determine parameters to use when launching the container. See Describing Images below
All post params are defined as environment variables in the launched container, and any number of additional arbitrary parameters may be passed.
####/kill
curl -d 'image_name=image_to_use&container_name=name_to_give_container' http://stager-instance.com/kill
#returns
ok
####/event_receiver
This endpoint provides an extendable RPC interface for any configured event_listeners to utilize (see Customization - event_listeners section below)
Return values can be provided by any event listener that matches a request to this endpoint.
##Configuration
###Environment Vars
The following environment variables can be defined to customize the behavior of your Stager instance:
MIN_PORT = 3200
MAX_PORT = 3500
Stager uses a FIFO approach to keep ports mapped from host machine to container within the min/max defined in the environment, so you can cap simultaneous containers this way, eg: MAX_PORT=3204 would cap at 5 simultaneous containers.
###Describing Images
# config.yml
images:
name_of_image: #this must correspond with an actual docker image on the server
port: 80
# (required) The port on which your staged application will listen. This port will be mapped from any containers
# launched from this image to an allocated port on the host machine
command: 'bash /container_bootstrap_script.sh'
# (required) The command which all containers created from this image will run when launched.
# This should perform any bootstrap steps required by your staged application.
container_create_params:
# (optional) additional docker params available on container creation.
# See http://docs.docker.io/en/latest/reference/api/docker_remote_api_v1.10/#create-a-container
Volumes: '/shared/config': {}
container_start_params:
# (optional) additional docker params available on container start.
# See http://docs.docker.io/en/latest/reference/api/docker_remote_api_v1.10/#start-a-container
Binds: ['/shared/config:/shared/config:ro']
Requests to /launch or /kill will expect the image_name parameter to have a corresponding config block under images in your config.yml
###Routing Strategies
Stager uses a RoutingStrategy to decide how the containers it manages are exposed to the world. There are default strategies for nginx and haproxy.
####Nginx
routing_strategy: 'NginxRoutingStrategy'
nginx:
# Required if different from default
target_dir: '/path/to/vhost'
template_path: '/path/to/nginx.conf.erb'
####HAProxy
To use HAProxy instead of nginx, you can optionally specify the input haproxy template to use as well as where to output the config to.
routing_strategy: 'HaproxyRoutingStrategy'
haproxy:
# Defaults to /etc/haproxy/haproxy.cfg
target_path: '/path/to/haproxy.cfg'
# Internal default
template_path: '/path/to/haproxy.cfg.erb'
###Authentication
Stager requires use of an authentication strategy when posting to the /launch or /kill endpoints. Currently two authentication strategies are available, Basic Auth and Github Auth. Both require additional parameters be sent with each request, which can be handled for you by using the Stager cli gem
####Basic Authentication
Configuration
# config.yml
authentication_strategy: 'BasicAuthentication'
users:
- user_name:password
- another_user_name:another_password
Authenticating
# curl
curl --user user_name:password -d 'image_name=name_of_image&container_name=name_for_container'
# Stager cli
stager configure auth_strategy=Basic username=user_name #run only once
stager launch name_of_image name_for_container #will prompt for pw
####Github Authentication
Configuration
# config.yml
images:
name_of_image: # should match repo which user must have access to in order to launch/kill
repo_owner: user_or_org_which_owns_repo
authentication_strategy: 'GithubAuthentication'
# if you are using the Stager cli
event_listeners: 'GithubAuthorization'
github:
client_id: 'client_id_for_your_github_app'
client_secret: 'client_secret_for_your_github_app'
Authenticating
# curl
curl -d 'image_name=name_of_image&container_name=name_for_container&github_token=github_oauth_token_with_repo_scope'
# Stager cli
stager configure auth_strategy=Github #run only once
stager launch name_of_image name_for_container #will prompt for github creds once, then save oauth token for all future requests
If neither authentication strategy suits your use-case, new ones can easily be added. See adding authentication strategies for Stager, and Stager cli
##Customization
At this point you'll have a fully operational Stager, but you can easily customize the behavior of your instance and integrate with other systems using simple plugins referred to as request handlers.
###Request Handler Documentation
Every request handler contains a standardized comment documenting its purpose and usage. The comment contains the following elements:
- @description - Describes the purpose of the request handler
- @type - One or more of the types listed below where the request handler is appropriate to use, authentication_strategy, post_launch_handlers, pre_kill_handlers, or event_listeners
- @dependencies - Any other request handlers that this request handler requires to function properly
- @config - Required updates to config.yml that this request handler requires to function properly
There are five types of request handlers supported by Stager, each of which are invoked at different times in the lifecycle of a request.
###authentication_strategy
This request handler is invoked before processing a launch or kill request, as well as any event_listeners which explicitly specify that they should authenticate.
# config.yml
authentication_strategy: 'BasicAuthentication'
Included authentication strategies:
###container_rotation_strategy
This request handler is optional, and when configured is consulted when Stager needs to make room to process a launch request based on the configured port range (all ports are allocated,) and simply decides which container will be killed to make room for the new one being launched.
# config.yml
container_rotation_strategy: 'EvergreenExceptionsContainerRotationStrategy'
Included container rotation strategies:
When no container_rotation_strategy is defined, basic FIFO is used.
###post_launch_handlers
These request handlers are invoked immediately after a container is launched
# config.yml
post_launch_handlers:
- 'AddGithubPullReqeustCommentOnLaunch'
- 'PostToSlackOnLaunch'
Included post launch handlers:
###pre_kill_handlers
These request handlers are invoked immediately before a container is killed
# config.yml
pre_kill_handlers:
- 'RemoveGithubPullRequestCommentsOnKill'
Included pre kill handlers:
###event_listeners
These request handlers are invoked when requests are made to the /event_receiver endpoint. The first event listener to volunteer for handling a request to /event_receiver will handle the request, and only one will fire per request.
# config.yml
event_listeners:
- 'LaunchOnGithubPullRequestOpened'
- 'KillOnGithubPullRequestClosed'
Included event listeners:
###Creating Request Handlers
Request handlers are simple class definitions with a maximum of two methods required, making them very easy to create. Depending on the type of request handler you are creating, different instance variables are made available to those methods for interacting with the current state of the app.
For details on the required methods and exposed instance variables for the various types of request handlers, see the request handlers readme
##Contributing
- Fork the repo
- Create a branch
- Make your changes
- Include standardized comment if adding request handlers
- Spaces not tabs, 2 space indents
- Update docs to reflect changes
- Open a pull request