mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-30 19:08:10 -05:00
Merge pull request #423 from nverba/heroku-integration
Basic Heroku integration
This commit is contained in:
commit
025dd86e26
9 changed files with 221 additions and 10 deletions
1
.env.sample
Normal file
1
.env.sample
Normal file
|
@ -0,0 +1 @@
|
||||||
|
RAILS_ENV=development
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -69,5 +69,8 @@ chef/tmp/*
|
||||||
# .procfile
|
# .procfile
|
||||||
.procfile
|
.procfile
|
||||||
|
|
||||||
|
# .env, local environment variables for use with foreman
|
||||||
|
.env
|
||||||
|
|
||||||
# exclude our git version file for now
|
# exclude our git version file for now
|
||||||
config/version.rb
|
config/version.rb
|
||||||
|
|
|
@ -16,7 +16,7 @@ MessageBus.on_disconnect do |site_id|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Point at our redis
|
# Point at our redis
|
||||||
MessageBus.redis_config = YAML::load(File.open("#{Rails.root}/config/redis.yml"))[Rails.env].symbolize_keys
|
MessageBus.redis_config = YAML.load(ERB.new(File.new("#{Rails.root}/config/redis.yml").read).result)[Rails.env].symbolize_keys
|
||||||
|
|
||||||
MessageBus.long_polling_enabled = SiteSetting.enable_long_polling
|
MessageBus.long_polling_enabled = SiteSetting.enable_long_polling
|
||||||
MessageBus.long_polling_interval = SiteSetting.long_polling_interval
|
MessageBus.long_polling_interval = SiteSetting.long_polling_interval
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# If Mini Profiler is included via gem
|
# If Mini Profiler is included via gem
|
||||||
if defined?(Rack::MiniProfiler)
|
if defined?(Rack::MiniProfiler)
|
||||||
|
|
||||||
Rack::MiniProfiler.config.storage_options = YAML::load(File.open("#{Rails.root}/config/redis.yml"))[Rails.env].symbolize_keys
|
Rack::MiniProfiler.config.storage_options = YAML.load(ERB.new(File.new("#{Rails.root}/config/redis.yml").read).result)[Rails.env].symbolize_keys
|
||||||
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore
|
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore
|
||||||
|
|
||||||
# For our app, let's just show mini profiler always, polling is chatty so nuke that
|
# For our app, let's just show mini profiler always, polling is chatty so nuke that
|
||||||
|
|
|
@ -19,3 +19,13 @@ staging:
|
||||||
|
|
||||||
production:
|
production:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
|
|
||||||
|
# Example for using environment variables
|
||||||
|
#
|
||||||
|
# production:
|
||||||
|
# uri: <%= uri = URI.parse(ENV['OPENREDIS_URL']) if ENV['OPENREDIS_URL'] %>
|
||||||
|
# host: <%= uri.host if uri %>
|
||||||
|
# port: <%= uri.port if uri %>
|
||||||
|
# password: <%= uri.password if uri %>
|
||||||
|
# db: 0
|
||||||
|
# cache_db: 2
|
159
docs/HEROKU.md
Normal file
159
docs/HEROKU.md
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
# Basic Heroku deployment
|
||||||
|
|
||||||
|
This guide takes you through the steps for deploying Discourse to the [Heroku](http://www.heroku.com/) cloud application platform. If you're unfamiliar with Heroku, [read this first](https://devcenter.heroku.com/articles/quickstart). The basic deployment of Discourse requires several services that will cost you money. In addition to the [750 free Dyno hours](https://devcenter.heroku.com/articles/usage-and-billing) provided by Heroku, the application requires one additional process to be running for the Sidekiq queue ($34 monthly), and a Redis database plan that supports a minimum of 2 databases (average $10 monthly).
|
||||||
|
|
||||||
|
For details on how to reduce the monthly cost of your application, see the Advanced Heroku deployment instructions (coming soon).
|
||||||
|
|
||||||
|
## Download and configure Discourse
|
||||||
|
|
||||||
|
1. If you haven't already, download Discourse and create a new branch for your Heroku configuration.
|
||||||
|
|
||||||
|
git clone git@github.com:discourse/discourse.git
|
||||||
|
cd discourse
|
||||||
|
git checkout -b heroku
|
||||||
|
|
||||||
|
2. Modify `production:` in the redis.yml file to use environment variables provided by Heroku and the Redis provider of your choice.
|
||||||
|
|
||||||
|
*config/redis.yml*
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
production:
|
||||||
|
uri: <%= uri = URI.parse(ENV['OPENREDIS_URL']) if ENV['OPENREDIS_URL'] %>
|
||||||
|
host: <%= uri.host if uri %>
|
||||||
|
port: <%= uri.port if uri %>
|
||||||
|
password: <%= uri.password if uri %>
|
||||||
|
db: 0
|
||||||
|
cache_db: 2
|
||||||
|
|
||||||
|
3. Comment out or delete `config/redis.yml` from .gitignore. We want to include redis.yml when we push to Heroku.
|
||||||
|
|
||||||
|
*.gitignore*
|
||||||
|
|
||||||
|
- config/redis.yml
|
||||||
|
+ # config/redis.yml
|
||||||
|
|
||||||
|
4. Commit your changes.
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "ready for Heroku"
|
||||||
|
|
||||||
|
|
||||||
|
## Configure Heroku
|
||||||
|
|
||||||
|
1. Create the heroku app. This automatically creates a git remote called heroku.
|
||||||
|
|
||||||
|
heroku create your-app-name
|
||||||
|
|
||||||
|
2. Add a suitable Redis provider from [Heroku add-ons](https://addons.heroku.com/), (this service will cost you money).
|
||||||
|
|
||||||
|
heroku addons:add openredis:micro
|
||||||
|
|
||||||
|
3. Add the [Heroku Scheduler](https://addons.heroku.com/scheduler) add-on, this saves us from running a separate clock process, reducing the cost of the app.
|
||||||
|
|
||||||
|
heroku addons:add scheduler:standard
|
||||||
|
|
||||||
|
4. Generate a secret token in the terminal.
|
||||||
|
|
||||||
|
rake secret
|
||||||
|
|
||||||
|
5. Push the secret to the stored heroku environment variables, this will now be available to your app globally.
|
||||||
|
|
||||||
|
heroku config:add SECRET_TOKEN=<generated secret>
|
||||||
|
|
||||||
|
##### The next step is optional, as it is still in experimental 'labs' status with Heroku. You can choose to precompile your assets locally before deployment instead. If you do choose to precompile locally, remember to do it each time, before you deploy. For more information on this experimental feature see [Heroku Labs: user-env-compile](https://devcenter.heroku.com/articles/labs-user-env-compile).
|
||||||
|
|
||||||
|
6. Make the environment variables available to heroku during deployment.
|
||||||
|
|
||||||
|
heroku labs:enable user-env-compile -a your-app-name
|
||||||
|
|
||||||
|
**Caveat:** If you should need to change or add environment variables for any reason, you will need to remove `user-env-compile`, then re-apply it after making the changes. This will then require you to make a commit, even if it is an empty commit, and then push to Heroku for the changes to be applied.
|
||||||
|
|
||||||
|
If needed, you can remove the user-env-compile option with this command.
|
||||||
|
|
||||||
|
heroku labs:disable user-env-compile -a your-app-name
|
||||||
|
|
||||||
|
7. Push your heroku branch to Heroku.
|
||||||
|
|
||||||
|
git push heroku heroku:master
|
||||||
|
|
||||||
|
8. Migrate and seed the database.
|
||||||
|
|
||||||
|
heroku run rake db:migrate db:seed_fu
|
||||||
|
|
||||||
|
##### You should now be able to visit your app at http://`<your-app-name>`.herokuapp.com
|
||||||
|
|
||||||
|
## Configure the deployed application
|
||||||
|
|
||||||
|
1. Log into the app, using your preferred auth provider.
|
||||||
|
|
||||||
|
2. Connect to the Heroku console to make the first user an Admin.
|
||||||
|
|
||||||
|
heroku run console
|
||||||
|
|
||||||
|
3. Enter the following commands.
|
||||||
|
|
||||||
|
u = User.first
|
||||||
|
u.admin = true
|
||||||
|
u.approved = true
|
||||||
|
u.save
|
||||||
|
|
||||||
|
4. Provision the Heroku Scheduler
|
||||||
|
|
||||||
|
This will allow Heroku Scheduler to cue up tasks rather than running a separate clock process.
|
||||||
|
In the [Heroku dashboard](https://dashboard.heroku.com/apps), select your app, then click on **Heroku Scheduler Standard** under your Add-ons.
|
||||||
|
|
||||||
|
Next, add a Job for each of the following:
|
||||||
|
|
||||||
|
##### TASK: `rake enqueue_digest_emails` FREQUENCY: `Daily` NEXT RUN: `06:00`
|
||||||
|
|
||||||
|
##### TASK: `rake category_stats` FREQUENCY: `Daily` NEXT RUN: `04:00`
|
||||||
|
|
||||||
|
##### TASK: `rake calculate_avg_time` FREQUENCY: `Every 10 minutes`
|
||||||
|
|
||||||
|
##### TASK: `rake feature_topics` FREQUENCY: `Every 10 minutes`
|
||||||
|
|
||||||
|
##### TASK: `rake calculate_score` FREQUENCY: `Every 10 minutes`
|
||||||
|
|
||||||
|
##### TASK: `rake calculate_view_counts` FREQUENCY: `Every 10 minutes`
|
||||||
|
|
||||||
|
##### TASK: `rake version_check` FREQUENCY: `Daily` NEXT RUN: `01:00`
|
||||||
|
|
||||||
|
5. Start Sidekiq
|
||||||
|
|
||||||
|
In the [Heroku dashboard](https://dashboard.heroku.com/apps), select your app and you will see the separate processes that have been created for your application under Resources. You will only need to start the sidekiq process for your application to run properly. The clock process is covered by Heroku Scheduler, and you can even remove this from the Procfile before deploying if you so wish. The worker process has been generated as a Rails default and can be ignored. As you can see **the Sidekiq process costs $34 monthly** to run. If you want to reduce this cost, check out the Advanced Heroku deployment(coming soon).
|
||||||
|
|
||||||
|
Click on the check-box next to the Sidekiq process and click Apply Changes
|
||||||
|
|
||||||
|
##### Your Discourse application should now be functional. However, you will still need to configure mail functionality and file storage for uploaded images. For some recommendations on doing this within Heroku, see the Advanced Heroku deployment guide (coming soon).
|
||||||
|
|
||||||
|
## Running the application locally
|
||||||
|
|
||||||
|
Using Foreman to start the application allows you to mimic the way the application is started on Heroku. It loads environment variables via the .env file and instantiates the application using the Procfile. In the .env sample file, we have set `RAILS_ENV='development'`, this makes the Rails environment variable available globally, and is required when starting this application using Foreman.
|
||||||
|
|
||||||
|
##### Create the .env file
|
||||||
|
|
||||||
|
*.env*
|
||||||
|
|
||||||
|
RAILS_ENV='development'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###Foreman commands:
|
||||||
|
|
||||||
|
|
||||||
|
##### Create the database
|
||||||
|
|
||||||
|
bundle exec foreman run rake db:create
|
||||||
|
|
||||||
|
##### Migrate and seed the database
|
||||||
|
|
||||||
|
bundle exec foreman run rake db:migrate db:seed_fu
|
||||||
|
|
||||||
|
##### Start the application using Foreman
|
||||||
|
|
||||||
|
bundle exec foreman start
|
||||||
|
|
||||||
|
##### Use Rails console, with pry
|
||||||
|
|
||||||
|
bundle exec foreman run rails console
|
|
@ -4,8 +4,9 @@
|
||||||
class DiscourseRedis
|
class DiscourseRedis
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@config = YAML::load(File.open("#{Rails.root}/config/redis.yml"))[Rails.env]
|
@config = YAML.load(ERB.new(File.new("#{Rails.root}/config/redis.yml").read).result)[Rails.env]
|
||||||
redis_opts = {:host => @config['host'], :port => @config['port'], :db => @config['db']}
|
redis_opts = {:host => @config['host'], :port => @config['port'], :db => @config['db']}
|
||||||
|
redis_opts[:password] = @config['password'] if @config['password']
|
||||||
@redis = Redis.new(redis_opts)
|
@redis = Redis.new(redis_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,14 +37,14 @@ class DiscourseRedis
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.new_redis_store
|
def self.new_redis_store
|
||||||
redis_config = YAML::load(File.open("#{Rails.root}/config/redis.yml"))[Rails.env]
|
redis_config = YAML.load(ERB.new(File.new("#{Rails.root}/config/redis.yml").read).result)[Rails.env]
|
||||||
redis_store = ActiveSupport::Cache::RedisStore.new "redis://#{redis_config['host']}:#{redis_config['port']}/#{redis_config['cache_db']}"
|
redis_store = ActiveSupport::Cache::RedisStore.new "redis://#{(':' + redis_config['password'] + '@') if redis_config['password']}#{redis_config['host']}:#{redis_config['port']}/#{redis_config['cache_db']}"
|
||||||
redis_store.options[:namespace] = -> { DiscourseRedis.namespace }
|
redis_store.options[:namespace] = -> { DiscourseRedis.namespace }
|
||||||
redis_store
|
redis_store
|
||||||
end
|
end
|
||||||
|
|
||||||
def url
|
def url
|
||||||
"redis://#{@config['host']}:#{@config['port']}/#{@config['db']}"
|
"redis://#{(':' + @config['password'] + '@') if @config['password']}#{@config['host']}:#{@config['port']}/#{@config['db']}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
36
lib/tasks/scheduler.rake
Normal file
36
lib/tasks/scheduler.rake
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
desc "This task is called by the Heroku scheduler add-on"
|
||||||
|
|
||||||
|
# Every day at 6am
|
||||||
|
task :enqueue_digest_emails => :environment do
|
||||||
|
Jobs::EnqueueDigestEmails.new.execute(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every day at 4am
|
||||||
|
task :category_stats => :environment do
|
||||||
|
Jobs::CategoryStats.new.execute(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every 10 minutes
|
||||||
|
task :calculate_avg_time => :environment do
|
||||||
|
Jobs::CalculateAvgTime.new.execute(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every 10 minutes
|
||||||
|
task :feature_topics => :environment do
|
||||||
|
Jobs::FeatureTopics.new.execute(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every 10 minutes
|
||||||
|
task :calculate_score => :environment do
|
||||||
|
Jobs::CalculateScore.new.execute(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every 10 minutes
|
||||||
|
task :calculate_view_counts => :environment do
|
||||||
|
Jobs::CalculateViewCounts.new.execute(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every day
|
||||||
|
task :version_check => :environment do
|
||||||
|
Jobs::VersionCheck.new.execute(nil)
|
||||||
|
end
|
|
@ -56,7 +56,8 @@ module RailsMultisite
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.current_hostname
|
def self.current_hostname
|
||||||
ActiveRecord::Base.connection_pool.spec.config[:host_names].first
|
config = ActiveRecord::Base.connection_pool.spec.config
|
||||||
|
config[:host_names].nil? ? config[:host] : config[:host_names].first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue