Improve Docker setup (PR #497)

This commit is contained in:
wvengen 2017-10-01 13:57:36 +02:00 committed by GitHub
parent 01950b48a1
commit 0363f2dadc
13 changed files with 236 additions and 108 deletions

View file

@ -1,12 +1,43 @@
.git .git
.idea .gitignore
.sass-cache
.bundle
.rake_tasks*
db/*.sqlite3
log log
config/database.yml node_modules
doc tmp/*
public/assets public/assets
public/system public/system
tmp/* public/uploads
vendor/bundle vendor/bundle
# no configuration
config/*.yml
!config/i18n-js.yml
# IDEs, Developer tools
.idea
.loadpath
.project
.sass-cache
.rbenv-version
.get-dump.yml
.bash_history
.localeapp
nbproject/
.*.sw?
*~
coverage coverage
*.sql* tags
doc/app/
doc/api/
.yarddoc/
rspec.failures
# Capistrano etc.
Capfile
config/deploy
config/deploy.rb
Gemfile.capistrano*

50
.gitignore vendored
View file

@ -1,25 +1,41 @@
log/*.log .bundle
tmp/**/* .rake_tasks*
config/app_config.yml
config/database.yml
config/initializers/secret_token.rb
db/*.sqlite3 db/*.sqlite3
nbproject/ log
node_modules
tmp
public/assets
public/system
public/uploads
vendor/bundle
# no configuration
config/*.yml
!config/i18n-js.yml
config/environments/development.rb config/environments/development.rb
*.swp
*~ # IDEs, Developer tools
public/**/*_cached.*
.idea .idea
.loadpath
.project
.sass-cache
.rbenv-version
.get-dump.yml .get-dump.yml
.sass-cache/ .bash_history
.localeapp
nbproject/
.*.sw?
*~
coverage
tags
doc/app/ doc/app/
doc/api/ doc/api/
.yarddoc/ .yarddoc/
# Deployment tools
Capfile
config/deploy.rb
config/deploy/*
.localeapp
log/localeapp.yml
.bundle
rspec.failures rspec.failures
# Capistrano etc.
Capfile
config/deploy
config/deploy.rb
Gemfile.capistrano*

View file

@ -1,45 +1,58 @@
FROM ruby:2.3 FROM ruby:2.3
RUN apt-get update && \ RUN supercronicUrl=https://github.com/aptible/supercronic/releases/download/v0.1.3/supercronic-linux-amd64 && \
apt-get install --no-install-recommends -y cron && \ supercronicBin=/usr/local/bin/supercronic && \
rm -rf /var/lib/apt/lists/* && \ supercronicSha1sum=96960ba3207756bb01e6892c978264e5362e117e && \
apt-get clean curl -fsSL -o "$supercronicBin" "$supercronicUrl" && \
echo "$supercronicSha1sum $supercronicBin" | sha1sum -c - && \
chmod +x "$supercronicBin"
ENV RAILS_ENV=production \ ENV PORT=3000 \
SMTP_SERVER_PORT=2525 \
RAILS_ENV=production \
RAILS_LOG_TO_STDOUT=true \
RAILS_SERVE_STATIC_FILES=true RAILS_SERVE_STATIC_FILES=true
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY . ./
RUN echo $SOURCE_COMMIT > REVISION
COPY . ./
# install dependencies and generate crontab
RUN buildDeps='libmagic-dev' && \ RUN buildDeps='libmagic-dev' && \
apt-get update && \ apt-get update && \
apt-get install --no-install-recommends -y $buildDeps && \ apt-get install --no-install-recommends -y $buildDeps && \
rm -rf /var/lib/apt/lists/* && \ echo 'gem: --no-document' >> ~/.gemrc && \
bundle install --deployment --without development test && \ bundle config build.nokogiri "--use-system-libraries" && \
bundle install --deployment --without development test -j 4 && \
apt-get purge -y --auto-remove $buildDeps && \ apt-get purge -y --auto-remove $buildDeps && \
echo "Foodsoft::Application.config.secret_token = ENV['SECRET_KEY_BASE']" > config/initializers/secret_token.rb && \ rm -Rf /var/lib/apt/lists/* /var/cache/apt/* ~/.gemrc ~/.bundle && \
mkdir -p log && \ \
ln -sfn /dev/stdout log/production.log && \ bundle exec whenever >crontab
bundle exec whenever --update-crontab
# Add a temporary mysql-server for assets precompilation # compile assets with temporary mysql server
RUN export DATABASE_URL=mysql2://localhost/temp && \ RUN export DATABASE_URL=mysql2://localhost/temp && \
export SECRET_KEY_BASE=thisisnotimportantnow && \
export DEBIAN_FRONTEND=noninteractive && \ export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \ apt-get update && \
apt-get install -y mysql-server && \ apt-get install -y mysql-server && \
/etc/init.d/mysql start && \ /etc/init.d/mysql start && \
cp config/app_config.yml.SAMPLE config/app_config.yml && \ cp config/app_config.yml.SAMPLE config/app_config.yml && \
bundle exec rake db:setup && \ bundle exec rake db:setup assets:precompile && \
bundle exec rake assets:precompile && \ rm -Rf config/app_config.yml tmp/* && \
rm config/app_config.yml && \
rm -rf tmp/* && \
/etc/init.d/mysql stop && \ /etc/init.d/mysql stop && \
rm -rf /run/mysqld /tmp/* /var/lib/mysql /var/log/mysql* && \ rm -Rf /run/mysqld /tmp/* /var/tmp/* /var/lib/mysql /var/log/mysql* && \
apt-get purge -y --auto-remove mysql-server && \ apt-get purge -y --auto-remove mysql-server && \
rm -rf /var/lib/apt/lists/* rm -Rf /var/lib/apt/lists/* /var/cache/apt/*
# Make relevant dirs writable for app user
RUN mkdir -p tmp && \
chown nobody tmp
# Run app as unprivileged user
USER nobody
EXPOSE 3000 EXPOSE 3000
# cleanup, and by default start web process from Procfile
ENTRYPOINT ["./docker-entrypoint.sh"] ENTRYPOINT ["./docker-entrypoint.sh"]
CMD ["rails", "server", "--binding", "0.0.0.0"] CMD ["./proc-start", "web"]

4
Procfile Normal file
View file

@ -0,0 +1,4 @@
web: bundle exec rails server --binding=0.0.0.0 --port=$PORT
worker: QUEUE=foodsoft_notifier bundle exec rake resque:work
mail: bundle exec rake foodsoft:reply_email_smtp_server
cron: supercronic crontab

View file

@ -40,7 +40,7 @@ Foodsoft::Application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true config.force_ssl = ENV["RAILS_FORCE_SSL"] != "false"
# Set to :debug to see everything in the log. # Set to :debug to see everything in the log.
config.log_level = :info config.log_level = :info
@ -49,7 +49,13 @@ Foodsoft::Application.configure do
# config.log_tags = [ :subdomain, :uuid ] # config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups. # Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) # https://github.com/heroku/rails_12factor/issues/25#issuecomment-231103483
if ENV["RAILS_LOG_TO_STDOUT"].present?
STDOUT.sync = true
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Use a different cache store in production. # Use a different cache store in production.
# config.cache_store = :mem_cache_store # config.cache_store = :mem_cache_store

View file

@ -0,0 +1,25 @@
# Be sure to restart your server when you modify this file.
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Foodsoft::Application.config.secret_key_base = begin
if (token = ENV['SECRET_KEY_BASE']).present?
token
elsif Rails.env.production? || Rails.env.staging?
raise "You must set SECRET_KEY_BASE"
elsif Rails.env.test?
SecureRandom.hex(30) # doesn't really matter
else
sf = Rails.root.join('tmp', 'secret_key_base')
if File.exists?(sf)
File.read(sf)
else
puts "=> Generating initial SECRET_KEY_BASE in #{sf}"
token = SecureRandom.hex(30)
File.open(sf, 'w') { |f| f.write(token) }
token
end
end
end

View file

@ -1,10 +0,0 @@
# Be sure to restart your server when you modify this file.
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Foodsoft::Application.config.secret_key_base = 'you really really need to change me!'
# When you're upgrading from Rails 3, it's ok to keep using `secret_token`.
# http://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html

View file

@ -1,8 +1,67 @@
Deployment # Deployment
=========
Setup ## Docker
-----
_This section is a work in progress._
### Build
To build the docker image, run:
docker build --tag foodsoft:dev --rm .
There is also an [official production docker image](https://hub.docker.com/r/foodcoops/foodsoft/),
which will let you avoid this step.
### Run (basic)
You'll need to set at least the following environment variables:
* `SECRET_KEY_BASE` - random string of 30+ characters, try `rake secret`
* `DATABASE_URL` - pointing to your MySQL installation (`mysql2://user:pass@mysql.host/foodsoftdb?encoding=utf8`)
* `REDIS_URL` - pointing to your Redis instance (`redis://redis.host:6379`)
You'll also need to supply the Foodsoft configuration file, for example by
mounting it as a volume. Copy `config/app_config.yml.SAMPLE` to `config/app_config.yml`
and customize the settings.
Then run the webserver, exposing port 3000 on the current host:
docker run --name foodsoft_web -p 3000 \
-e SECRET_KEY_BASE -e DATABASE_URL -e REDIS_URL -e RAILS_FORCE_SSL=false \
-v `pwd`/config/app_config.yml:/usr/src/app/config/app_config.yml:ro \
foodsoft:dev
This should get you started. But first you'll need to populate the database:
docker run --name foodsoft_setup --rm \
-e SECRET_KEY_BASE -e DATABASE_URL -e REDIS_URL \
-v `pwd`/config/app_config.yml:/usr/src/app/config/app_config.yml:ro \
foodsoft:dev bundle exec rake db:setup
To run the worker (recommended!), supply a different command
(see [Procfile](../Procfile) for other types):
docker run --name foodsoft_worker \
-e SECRET_KEY_BASE -e DATABASE_URL -e REDIS_URL \
-v `pwd`/config/app_config.yml:/usr/src/app/config/app_config.yml:ro \
foodsoft:dev ./proc-start worker
To also run the cronjobs, start the previous command but substituting
`mail` with `cron`. That should give you the ingredients for a production-setup.
With the help of a front-end webserver doing ssl, of course.
### Run (docker-compose)
In practice, you'd probably want to use docker-compose. If you know Docker well enough,
you'll have no problem to set this up. For inspiration, look at the
[foodcoops.net production setup](https://github.com/foodcoops/foodcoops.net).
## Capistrano
### Setup
1. Initialise your [Capistrano](http://capistranorb.com/) setup 1. Initialise your [Capistrano](http://capistranorb.com/) setup
@ -16,8 +75,7 @@ Setup
2. Adapt your configuration in `config/deploy.rb` and `config/deploy/*.rb` 2. Adapt your configuration in `config/deploy.rb` and `config/deploy/*.rb`
Deploy ### Deploy
------
On your first deploy you should run (choose either staging or production) On your first deploy you should run (choose either staging or production)

View file

@ -141,18 +141,7 @@ explained here.
Edit `app_config.yml` to suit your needs or just keep the defaults for now. Edit `app_config.yml` to suit your needs or just keep the defaults for now.
4. **Secret token** 4. **Create database (schema) and load defaults**
The user session are stored in cookies. Do avoid misusing the cookies and
its sensitive information, rails will encrypt it with a token. So copy the
config file
cp config/initializers/secret_token.rb.SAMPLE config/initializers/secret_token.rb
and modify the token!! You can run `bundle exec rake secret`
5. **Create database (schema) and load defaults**
rake db:setup rake db:setup
@ -160,7 +149,7 @@ explained here.
password 'secret'. password 'secret'.
6. (optional) Get **background jobs** done 5. (optional) Get **background jobs** done
Time intensive tasks may block the web request. To run these in a separate Time intensive tasks may block the web request. To run these in a separate
task, you can install Redis and enable Resque: task, you can install Redis and enable Resque:
@ -173,7 +162,7 @@ explained here.
`resque-web`. `resque-web`.
7. (optional) **View mails in browser** instead in your logs 6. (optional) **View mails in browser** instead in your logs
We use mailcatcher in development mode to view all delivered mails in a We use mailcatcher in development mode to view all delivered mails in a
browser interface. Just install mailcatcher with gem install mailcatcher browser interface. Just install mailcatcher with gem install mailcatcher

View file

@ -1,13 +1,9 @@
Running foodsoft in production Running Foodsoft in production
============================== ==============================
As you might have noticed, documentation is scarce and insufficient. If you As you might have noticed, documentation is scarce and insufficient. If you
intend to deploy foodsoft in production, we would love to guide you through the intend to deploy foodsoft in production, we would love to guide you through the
process. You can contact the mailing list process. You can contact the mailing list
[foodsoft-discuss](http://foodsoft.51229.x6.nabble.com/foodsoft-discuss-f5.html), [foodsoft-discuss](http://foodsoft.51229.x6.nabble.com/foodsoft-discuss-f5.html).
or mail some of us directly at
[developers@foodcoop.nl](mailto:developers@foodcoop.nl) or foodsoft (at)
foodcoops.net.
* [Passenger](https://www.phusionpassenger.com/) may be convenient. Please see our wiki page: [Deployment notes](https://github.com/foodcoops/foodsoft/wiki/Deployment-notes).
* Redis is required for production by default.

View file

@ -1,19 +1,7 @@
#!/bin/sh #!/bin/sh
set -e set -e
CRONTAB="$(crontab -l)" # allow re-using the instance - https://stackoverflow.com/a/38732187/2866660
CRONTAB_ENV=""
if test -n "$BUNDLE_APP_CONFIG"; then
CRONTAB_ENV="${CRONTAB_ENV}BUNDLE_APP_CONFIG=$BUNDLE_APP_CONFIG\n"
fi
if test -n "$DATABASE_URL"; then
CRONTAB_ENV="${CRONTAB_ENV}DATABASE_URL=$DATABASE_URL\n"
fi
echo "$CRONTAB_ENV$CRONTAB" | crontab -
if [ -f tmp/pids/server.pid ]; then if [ -f tmp/pids/server.pid ]; then
rm tmp/pids/server.pid rm tmp/pids/server.pid
fi fi

View file

@ -33,7 +33,6 @@ namespace :foodsoft do
setup_app_config setup_app_config
setup_development setup_development
setup_database setup_database
setup_secret_token
start_mailcatcher start_mailcatcher
puts yellow "All done! Your foodcoft should be running smoothly." puts yellow "All done! Your foodcoft should be running smoothly."
start_server start_server
@ -44,7 +43,6 @@ namespace :foodsoft do
task :stock_config do task :stock_config do
setup_app_config setup_app_config
setup_development setup_development
setup_secret_token
end end
end end
end end
@ -103,16 +101,6 @@ def setup_development
reminder(file) reminder(file)
end end
def setup_secret_token
file = 'config/initializers/secret_token.rb'
return nil if skip?(file)
puts yellow "Generating secret_token and writing to #{file}..."
Rake::Task["secret"].reenable
secret = capture_stdout { Rake::Task["secret"].invoke }
%x( touch #{Rails.root.join("#{file}")}; echo 'Foodsoft::Application.config.secret_key_base = "#{secret.chomp}"' > #{Rails.root.join("#{file}")} )
end
def start_mailcatcher def start_mailcatcher
return nil if ENV['MAILCATCHER_PORT'] # skip when it has an existing Docker container return nil if ENV['MAILCATCHER_PORT'] # skip when it has an existing Docker container
mailcatcher = ask("Do you want to start mailcatcher?\nOptions:\n(y) Yes\n(n) No", ["y","n"]) mailcatcher = ask("Do you want to start mailcatcher?\nOptions:\n(y) Yes\n(n) No", ["y","n"])

24
proc-start Executable file
View file

@ -0,0 +1,24 @@
#!/bin/sh
#
# Run single command from Procfile
#
# This script is a basic replacement for foreman when running on Docker. When
# starting the docker instance, specify as command `script/start <type>`.
# `type` is looked up in `Procfile` and the command is started. This allows e.g.
# docker-compose to run multiple process/dyno types from a single image, without
# needing to know the exact command (which may change over time).
#
if [ ! "$1" ]; then
echo "Usage: $0 <process type>" 1>&2
echo
echo "Note that some process types need the PORT environment variable."
exit 1
fi
CMD=`cat Procfile | grep "^$1:" | cut -d: -f2-`
if [ ! "$CMD" ]; then
echo "Process type $1 not found in Procfile" 1>&2
exit 1
fi
exec /bin/sh -c "$CMD"