From b65ccc6482c753ac98ced8e8b600fe7eb0e886e8 Mon Sep 17 00:00:00 2001 From: Xavier Date: Thu, 6 Apr 2017 21:34:26 +0200 Subject: [PATCH] Add Docker & Cloud66 Deploy options Closes #6 and #7 --- pathfinder.rb | 3 ++ recipes/base.rb | 8 +++ recipes/bower_rails.rb | 9 ++++ recipes/deploy.rb | 49 ++++++++++++++++++ recipes/deploy/cloud66/bower.sh | 1 + recipes/deploy/cloud66/cache_permissions.sh | 1 + recipes/deploy/cloud66/deploy_hooks.yml | 19 +++++++ recipes/deploy/docker/.dockerignore | 2 + recipes/deploy/docker/Dockerfile | 57 +++++++++++++++++++++ recipes/deploy/docker/README.md | 17 ++++++ recipes/deploy/docker/fix_permissions.sh | 3 ++ recipes/deploy/docker/nginx.conf | 30 +++++++++++ recipes/deploy/docker/rails-env.conf | 3 ++ recipes/simple_form.rb | 9 ++-- 14 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 recipes/deploy.rb create mode 100644 recipes/deploy/cloud66/bower.sh create mode 100644 recipes/deploy/cloud66/cache_permissions.sh create mode 100644 recipes/deploy/cloud66/deploy_hooks.yml create mode 100644 recipes/deploy/docker/.dockerignore create mode 100644 recipes/deploy/docker/Dockerfile create mode 100644 recipes/deploy/docker/README.md create mode 100644 recipes/deploy/docker/fix_permissions.sh create mode 100644 recipes/deploy/docker/nginx.conf create mode 100644 recipes/deploy/docker/rails-env.conf diff --git a/pathfinder.rb b/pathfinder.rb index 066f1ce..068d6fc 100644 --- a/pathfinder.rb +++ b/pathfinder.rb @@ -12,6 +12,7 @@ require_relative 'recipes/rollbar' require_relative 'recipes/sidekiq' require_relative 'recipes/simple_form' +require_relative 'recipes/deploy' require_relative 'recipes/utils' class Pathfinder @@ -38,6 +39,8 @@ def generate_initializers end def ask_for_recipes + deploy_answer = @template.ask('Deploy with:', limited_to: %w(docker cloud66 none)) + add_recipe(Recipes::Deploy.new(self, type: deploy_answer)) add_recipe(Recipes::Postgres.new(self)) add_recipe(Recipes::CarrierWave.new(self)) if @template.yes?('Do you want to use Carrierwave?') diff --git a/recipes/base.rb b/recipes/base.rb index 15fab83..fd9a00c 100644 --- a/recipes/base.rb +++ b/recipes/base.rb @@ -11,5 +11,13 @@ def gems def init_file end + + private + + def relative_file_content(path) + caller_path = caller_locations.first.path + puts caller_path + File.read(File.join(File.dirname(caller_path), path)) + end end end diff --git a/recipes/bower_rails.rb b/recipes/bower_rails.rb index 8d3f519..c559eef 100644 --- a/recipes/bower_rails.rb +++ b/recipes/bower_rails.rb @@ -31,6 +31,15 @@ def init_file } TEXT end + + @template.run 'rm config/initializers/bower_rails.rb' + @template.initializer 'bower_rails.rb' do <<~CODE + BowerRails.configure do |bower_rails| + bower_rails.install_before_precompile = true + bower_rails.resolve_before_precompile = true + end + CODE + end @template.rake 'bower:install' @template.rake 'bower:resolve' end diff --git a/recipes/deploy.rb b/recipes/deploy.rb new file mode 100644 index 0000000..1080dfc --- /dev/null +++ b/recipes/deploy.rb @@ -0,0 +1,49 @@ +module Recipes + class Deploy < Base + + def initialize(pathfinder, type: 'none') + if !%w(cloud66 docker none).include?(type) + fail 'none, docker or cloud66 are the only allowed options for deploy recipe' + end + super(pathfinder) + @type = type + end + + def init_file + case @type + when 'docker' then docker_config_files + when 'cloud66' then cloud66_config_files + end + end + + private + + def cloud66_config_files + @template.add_file '.cloud66/bower.sh', + relative_file_content('deploy/cloud66/bower.sh') + @template.add_file '.cloud66/cache_permissions.sh', + relative_file_content('deploy/cloud66/cache_permissions.sh') + @template.add_file '.cloud66/deploy_hooks.yml', + relative_file_content('deploy/cloud66/deploy_hooks.yml') + end + + def docker_config_files + @template.add_file 'docker/rails-env.conf', + relative_file_content('deploy/docker/rails-env.conf') + @template.add_file '.dockerignore', + relative_file_content('deploy/docker/.dockerignore') + @template.append_file 'README.md', + relative_file_content('deploy/docker/README.md') + add_file_and_replace_app_name('docker/fix_permissions.sh', + 'deploy/docker/fix_permissions.sh') + add_file_and_replace_app_name('Dockerfile', 'deploy/docker/Dockerfile') + add_file_and_replace_app_name('docker/nginx.conf', 'deploy/docker/nginx.conf') + end + + def add_file_and_replace_app_name(target_file, source_file) + @template.add_file target_file, relative_file_content(source_file) + @template.run "sed -i '' 's/app-name/#{@pathfinder.app_name}/g' #{target_file}" + end + + end +end diff --git a/recipes/deploy/cloud66/bower.sh b/recipes/deploy/cloud66/bower.sh new file mode 100644 index 0000000..83f1a41 --- /dev/null +++ b/recipes/deploy/cloud66/bower.sh @@ -0,0 +1 @@ +sudo npm install -g bower diff --git a/recipes/deploy/cloud66/cache_permissions.sh b/recipes/deploy/cloud66/cache_permissions.sh new file mode 100644 index 0000000..eb6357d --- /dev/null +++ b/recipes/deploy/cloud66/cache_permissions.sh @@ -0,0 +1 @@ +chown nginx:app_writers $STACK_BASE/shared/cache && chmod 775 $STACK_BASE/shared/cache && sudo chmod g+s $STACK_BASE/shared/cache diff --git a/recipes/deploy/cloud66/deploy_hooks.yml b/recipes/deploy/cloud66/deploy_hooks.yml new file mode 100644 index 0000000..25091e5 --- /dev/null +++ b/recipes/deploy/cloud66/deploy_hooks.yml @@ -0,0 +1,19 @@ +production: + first_thing: + - snippet: cloud66/node + target: rails + execute: true + - snippet: cloud66/newrelic + target: any + execute: true + - source: /.cloud66/bower.sh + destination: /tmp/bower.sh + target: rails + execute: true + after_rails: + - source: /.cloud66/cache_permissions.sh + destination: /tmp/cache_permissions.sh + target: rails + run_on: all_servers + execute: true + sudo: true diff --git a/recipes/deploy/docker/.dockerignore b/recipes/deploy/docker/.dockerignore new file mode 100644 index 0000000..d5ef56b --- /dev/null +++ b/recipes/deploy/docker/.dockerignore @@ -0,0 +1,2 @@ +public/uploads +vendor/bundle diff --git a/recipes/deploy/docker/Dockerfile b/recipes/deploy/docker/Dockerfile new file mode 100644 index 0000000..1b69c81 --- /dev/null +++ b/recipes/deploy/docker/Dockerfile @@ -0,0 +1,57 @@ +FROM phusion/passenger-ruby24 +MAINTAINER MarsBased "hola@marsbased.com" + +ENV HOME /home/app/app-name + +# Install software dependencies +RUN apt-get update +RUN apt-get install -y imagemagick + +# Use baseimage-docker's init system. +CMD ["/sbin/my_init"] + +# Expose Nginx HTTP service +EXPOSE 80 +EXPOSE 443 + +# Start Nginx / Passenger +RUN rm -f /etc/service/nginx/down + +# Remove the default site +RUN rm /etc/nginx/sites-enabled/default + +# Create app home dir +RUN mkdir -p $HOME +WORKDIR $HOME + +# Install bundle of gems +ADD Gemfile Gemfile +ADD Gemfile.lock Gemfile.lock +RUN bundle install --without development test + +# Add the nginx site and config +ADD docker/nginx.conf /etc/nginx/sites-enabled/app-name.conf +ADD docker/rails-env.conf /etc/nginx/main.d/rails-env.conf + +# Add the Rails app +ADD . /home/app/app-name + +RUN RAILS_ENV=production SECRET_KEY_BASE=NOT-IMPORTANT bin/rake assets:precompile + +# Add a tmp folder for pids +RUN mkdir -p tmp/pids + +# Define volumes + +VOLUME $HOME/public/uploads +VOLUME $HOME/log + +# Configure init scripts +RUN mkdir -p /etc/my_init.d +ADD docker/fix_permissions.sh /etc/my_init.d/fix_permissions.sh +RUN chmod +x /etc/my_init.d/fix_permissions.sh + +RUN chown -R app:app $HOME + +# Clean up APT and bundler when done. +RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*. diff --git a/recipes/deploy/docker/README.md b/recipes/deploy/docker/README.md new file mode 100644 index 0000000..746b5a7 --- /dev/null +++ b/recipes/deploy/docker/README.md @@ -0,0 +1,17 @@ + +## Docker + +Production deploys are managed with Docker. The Dockerfile makes use of some config files that can be found in the `docker` folder. + +- **fix_permissions.sh**: Makes the mountable volumes writable for the +application user. +- **rails-env.conf**: Makes env variables visible to Nginx. As the +application is running on Passenger through Nginx, every ENV variable +that needs to reach the application must be defined here. +- **nginx.conf**: Base nginx configuration. The file contains +instructions to tune the application performance. + +It's recommended to use Dockerhub (or a private docker repository) to store the application images and then use docker-compose to orchestrate the deployment. + +The docker-compose file is not included in the project and needs to be +configured on a project basis. diff --git a/recipes/deploy/docker/fix_permissions.sh b/recipes/deploy/docker/fix_permissions.sh new file mode 100644 index 0000000..0cc0534 --- /dev/null +++ b/recipes/deploy/docker/fix_permissions.sh @@ -0,0 +1,3 @@ +#!/bin/sh +chown -R app:app /home/app/app-name/public/uploads +chown -R app:app /home/app/app-name/log diff --git a/recipes/deploy/docker/nginx.conf b/recipes/deploy/docker/nginx.conf new file mode 100644 index 0000000..18c4e64 --- /dev/null +++ b/recipes/deploy/docker/nginx.conf @@ -0,0 +1,30 @@ +server { + listen 80 default_server; + root /home/app/app-name/public; + passenger_enabled on; + passenger_user app; + passenger_ruby /usr/bin/ruby2.4; + + ## Configure this with the same value of the passenger_max_pool_size + # passenger_min_instances 10; + + location ~ ^/assets/ { + expires 1y; + add_header Cache-Control public; + add_header ETag ""; + break; + } + + location ~ ^/uploads/ { + expires 24h; + add_header Cache-Control public; + add_header ETag ""; + break; + } +} + +## The number for max_pool_size should be (TOTAL_RAM * 0.75) / RAM_PER_PROCESS +# passenger_max_pool_size 10; + +## Configure this option with the app host url to preload app after deploy +# passenger_pre_start https://app-name.com; diff --git a/recipes/deploy/docker/rails-env.conf b/recipes/deploy/docker/rails-env.conf new file mode 100644 index 0000000..86cb9d2 --- /dev/null +++ b/recipes/deploy/docker/rails-env.conf @@ -0,0 +1,3 @@ +# List here all the ENV variables that need to be available in the application +# Only ENV variable names are expected, not their values. +# env APP_HOST; diff --git a/recipes/simple_form.rb b/recipes/simple_form.rb index 3a46b58..b2048ea 100644 --- a/recipes/simple_form.rb +++ b/recipes/simple_form.rb @@ -15,7 +15,8 @@ def init_file def run_generators case ask_framework_for_forms when 'marsman' - @template.initializer 'simple_form.rb', simple_form_template('marsman.rb') + @template.initializer 'simple_form.rb', + relative_file_content('simple_form/marsman.rb') when 'bootstrap' @template.generate 'simple_form:install --bootstrap' else @@ -25,7 +26,8 @@ def run_generators def add_sample_i18n @template.run 'rm config/locales/simple_form.en.yml' - @template.append_to_file 'config/locales/en.yml', simple_form_template('en.yml') + @template.append_to_file 'config/locales/en.yml', + relative_file_content('simple_form/en.yml') end def ask_framework_for_forms @@ -33,8 +35,5 @@ def ask_framework_for_forms limited_to: %w(marsman bootstrap default)) end - def simple_form_template(filename) - File.read(File.join(File.dirname(__FILE__), 'simple_form', filename)) - end end end