diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1585cd9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +Dockerfile +db_data diff --git a/.env.example b/.env.example index d3b2c45..49a8759 100644 --- a/.env.example +++ b/.env.example @@ -1,19 +1,33 @@ -RACK_ENV= -RAILS_ENV= -APP_SECRET= +RACK_ENV=production +RAILS_ENV=production +APP_SECRET=randomstringhere -DEPLOY_PATH= +DEPLOY_PATH=/var/www -PUMA_WORKERS=1 +PUMA_WORKERS=5 PUMA_MIN_THREADS=1 PUMA_MAX_THREADS=16 PUMA_PORT=4000 PUMA_TIMEOUT=30 +# Docker adds mysql to hosts +MYSQL_HOST=mysql + +# This is used by both rails + mysql MYSQL_DATABASE=ensl -MYSQL_USERNAME= -MYSQL_PASSWORD= -MYSQL_CONNECTION_POOL=8 + +# Add to allow docker image to connect +MYSQL_ROOT_HOST=% + +# FIXME: Use root since normal user does not work. atm. +MYSQL_ROOT_PASSWORD=randomstringhere + +# These variables are for ensl +MYSQL_USERNAME=root +MYSQL_PASSWORD=randomstringhere + +# More MySQL vars +MYSQL_CONNECTION_POOL=32 NEW_RELIC_APP_NAME=ENSL NEW_RELIC_LICENSE_KEY= diff --git a/.gitignore b/.gitignore index c9d3f82..30f40f0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /log/* /tmp/* /spec/tmp/* +/env/production.sh +/env/development.sh .ruby-version .ruby-gemset .env @@ -12,6 +14,10 @@ *.rbc *.sassc +# Database and files +db_data/* +!db_data/.placeholder + # OS X .DS_Store @@ -20,6 +26,7 @@ /public/files/* /public/local /public/uploads +/public/assets # RubyMine /.idea diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..af6db94 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +FROM ruby:2.2 + +ENV RAILS_ENV production + +# Add 'web' user which will run the application +RUN adduser web --home /home/web --shell /bin/bash --disabled-password --gecos "" + +RUN apt-get update && apt-get -y upgrade +RUN apt-get -y install mysql-client libmysqlclient-dev memcached nodejs +RUN service memcached start + +# Separate Gemfile ADD so that `bundle install` can be cached more effectively + +ADD Gemfile /var/www/ +ADD Gemfile.lock /var/www/ +RUN chown -R web:web /var/www &&\ + mkdir -p /var/bundle &&\ + chown -R web:web /var/bundle + +RUN su -c "bundle config github.https true; cd /var/www && bundle install --path /var/bundle --jobs 4" -s /bin/bash -l web + +# Add application source +ADD . /var/www +RUN chown -R web:web /var/www + +WORKDIR /var/www +USER web + +RUN bundle config github.https true; cd /var/www && bundle install --path /var/bundle --jobs 4 +RUN bundle exec rake assets:precompile + +# This is a temporary solution to fix assets issue +RUN mv /var/www/public/assets /home/web/assets + +USER root +CMD ["/var/www/script/entry.sh"] diff --git a/Gemfile b/Gemfile index 978b2d0..0ce571a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source 'http://rubygems.org' -ruby '2.2.2' +ruby '2.2.10' +#ruby '2.2.2' gem 'dotenv-rails', '~> 0.10.0' gem 'rails', '~> 3.2.22' @@ -8,6 +9,7 @@ gem 'mysql2', '~> 0.3.17' gem 'dalli', '~> 2.7.0' gem 'puma', '~> 2.11.1' +gem 'i18n-js' gem 'exceptional', '~> 2.0.33' gem 'oj', '~> 2.5.5' gem 'faraday', '~> 0.9.0' @@ -27,7 +29,7 @@ gem 'test-unit', '~> 3.1.3' gem 'google-api-client', '~> 0.10.3' # Please install nodejs locally. -gem 'therubyracer', '~> 0.12.1' if RUBY_PLATFORM == 'x86_64-linux' +#gem 'therubyracer', '~> 0.12.1' if RUBY_PLATFORM == 'x86_64-linux' gem 'sprockets', '~> 2.2.1' gem 'coffee-rails', '~> 3.2.2' diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9fc489 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +REGISTRY ?= ensl +PROJECT ?= ensl.org +TAG ?= latest + +ifdef REGISTRY + IMAGE=$(REGISTRY)/$(PROJECT):$(TAG) +else + IMAGE=$(PROJECT):$(TAG) +endif + +all: + @echo "Available targets:" + @echo " * build - build a Docker image for $(IMAGE)" + @echo " * pull - pull $(IMAGE)" + @echo " * push - push $(IMAGE)" + @echo " * test - build and test $(IMAGE)" + +build: Dockerfile + docker build -t $(IMAGE) . + +pull: + docker pull $(IMAGE) || true + +push: + docker push $(IMAGE) + +test: build + fig run web ./env/test.sh ./test.sh + diff --git a/config/application.rb b/config/application.rb index aaa2b35..1ce42b6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -10,6 +10,7 @@ module Ensl # Additional assets config.assets.precompile += ["themes/*/theme.css", "themes/*/errors.css"] + config.assets.initialize_on_precompile = false # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += Dir["#{config.root}/app/services/**/", "#{config.root}/app/models/concerns/"] diff --git a/config/database.yml b/config/database.yml index b91e757..bb0011b 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,7 +1,7 @@ base: &db adapter: mysql2 encoding: utf8 - host: localhost + host: <%= ENV['MYSQL_HOST'] %> database: <%= ENV['MYSQL_DATABASE'] %> username: <%= ENV['MYSQL_USERNAME'] %> password: <%= ENV['MYSQL_PASSWORD'] %> diff --git a/config/environments/production.rb b/config/environments/production.rb index 6cdc6d3..692d211 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -37,7 +37,7 @@ Ensl::Application.configure do # config.logger = SyslogLogger.new # Use a different cache store in production - config.cache_store = :dalli_store + config.cache_store = :dalli_store, 'memcached:11211', 'localhost' # Enable serving of images, stylesheets, and JavaScripts from an asset server # config.action_controller.asset_host = "http://assets.example.com" @@ -64,4 +64,7 @@ Ensl::Application.configure do # Custom Session Store config to allow gathers.staging.ensl.org config.session_store :cookie_store, key: "_ENSL_session_key", expire_after: 30.days.to_i, domain: ".ensl.org" -end \ No newline at end of file + +# config.cache_store = :dalli_store, 'cache', 'cache-2.example.com:11211:2', +# { :namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compress => true } +end diff --git a/config/puma.rb b/config/puma.rb index d5b690d..ca304e2 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -2,24 +2,24 @@ require "dotenv" Dotenv.load base_path = (ENV['DEPLOY_PATH'] || Dir.pwd) -current_path = "#{base_path}/current" -shared_path = "#{base_path}/shared" -stderr_path = "#{shared_path}/log/puma.stderr.log" -stdout_path = "#{shared_path}/log/puma.stdout.log" +#current_path = "#{base_path}/current" +#shared_path = "#{base_path}/shared" +stderr_path = "#{base_path}/lpuma.stderr.log" +stdout_path = "#{base_path}/lpuma.stdout.log" tag 'ENSL' preload_app! -daemonize true -directory current_path -pidfile "#{shared_path}/tmp/pids/puma.pid" -state_path "#{shared_path}/tmp/pids/puma.state" +daemonize false +directory base_path +pidfile "#{base_path}/tmp/puma.pid" +state_path "#{base_path}/tmp/puma.state" stdout_redirect stdout_path, stderr_path environment ENV['RACK_ENV'] || 'production' rackup DefaultRackup -bind "unix://#{shared_path}/tmp/sockets/puma.sock" +bind "unix://#{base_path}/tmp/puma.sock" port Integer(ENV['PUMA_PORT'] || 4000) worker_timeout Integer(ENV['PUMA_TIMEOUT'] || 30) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b78ad50 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3" + +services: + web: + image: ensl/ensl.org:latest + volumes: + - "./public:/var/www/public" + ports: + - "4000:4000" + depends_on: + - db + - memcached + # - redis + db: + image: mariadb:latest + volumes: + - "./db_data:/var/lib/mysql" + - "./ext/mysql.conf.d:/etc/mysql/conf.d" + environment: + - MYSQL_DATABASE + - MYSQL_USER + - MYSQL_USERNAME + - MYSQL_PASSWORD + - MYSQL_ROOT_PASSWORD +# - MYSQL_ROOT_HOST + memcached: + image: memcached:latest + #redis: + # image: redis diff --git a/env/test.sh b/env/test.sh new file mode 100755 index 0000000..897d48f --- /dev/null +++ b/env/test.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +export RACK_ENV=test +export RAILS_ENV=test +export APP_SECRET=e0cdcb729c4b21d5259e957a2ffc13a3 + +export DEPLOY_PATH=/var/www + +export PUMA_WORKERS=1 +export PUMA_MIN_THREADS=1 +export PUMA_MAX_THREADS=16 +export PUMA_PORT=3000 +export PUMA_TIMEOUT=30 + +#export MYSQL_HOST="${MYSQL_PORT_3306_TCP_ADDR:-localhost}" +export MYSQL_DATABASE=ensl +export MYSQL_USER=root +export MYSQL_HOST="mysql" +export MYSQL_USERNAME=root +export MYSQL_ROOT_PASSWORD=test +export MYSQL_PASSWORD=test +export MYSQL_ROOT_HOST=172.% +export MYSQL_CONNECTION_POOL=32 + +exec "$@" diff --git a/ext/mysql.conf.d/opt.cnf b/ext/mysql.conf.d/opt.cnf new file mode 100644 index 0000000..a9545ea --- /dev/null +++ b/ext/mysql.conf.d/opt.cnf @@ -0,0 +1,31 @@ +[mysqld] + +skip-host-cache +skip-name-resolve + +key_buffer = 150M +max_allowed_packet = 20M +thread_stack = 196K +thread_cache_size = 16 + +max_connections = 96 +table_cache = 2000 +table_definition_cache = 800 +thread_concurrency = 128 + +query_cache_limit = 30M +query_cache_size = 150M +open_files_limit = 1800 +join_buffer_size = 1M +sort_buffer_size = 1M +read_buffer_size = 128K +tmp_table_size = 500M +max_heap_table_size = 500M + +innodb_buffer_pool_size = 1280M +innodb_flush_log_at_trx_commit = 1 +innodb_thread_concurrency = 16 +innodb_flush_method = O_DIRECT +#innodb_additional_mem_pool_size = 20M +innodb_file_per_table = 1 +transaction-isolation = READ-COMMITTED diff --git a/script/entry.sh b/script/entry.sh new file mode 100755 index 0000000..06580cd --- /dev/null +++ b/script/entry.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +env +cd /var/www +source /var/www/.env +rm -rf /var/www/public/assets +mv /home/web/assets /var/www/public/ +chown -R web:web /var/www/public + +#bundle exec rake assets:precompile +su -c "cd /var/www && bundle exec puma -C config/puma.rb" -s /bin/bash -l web diff --git a/script/test.sh b/script/test.sh new file mode 100755 index 0000000..e8fe1ed --- /dev/null +++ b/script/test.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +# Undo the `bundle --deployment --without development test` +# settings baked into the prod-ready Docker image's .bundle/config +bundle config --delete without +bundle config --delete frozen + +# Install gems in development and test groups +bundle + +# Ensure database exists and has latest migrations +bundle exec rake db:create +bundle exec rake db:migrate + +# Run tests +bundle exec rake