local環境
macOS Sierra 10.12.6
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin15]
Rails 5.2.1
prod環境
Heroku
背景
HerokuのFreeプラン利用してます。1ヶ月1000h稼働できるはずだけど(クレカ情報を登録済)、30分何もアクセス無いとSleepしてしまいます。
定期的に何かを公開環境で簡易的に実行したい場合に不便。ということでHerokuに定期的にHerokuから通信をしてみることでSleepしないようにしてみたい。
自分用メモですが便利そうなので公開しました。
project作成
プロジェクトでsidekiq-cronを利用する機会が多いのでRailsを選びました。
app_nameは任意で
% rails new app_name
% cd app_name
Sidekiqを使うためのcode修正
Gemfile
以下を追記
gem 'sidekiq'
routes.rb
Sidekiqの管理画面を操作するために、以下のようにする
require 'sidekiq/web'
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
mount Sidekiq::Web => '/sidekiq'
end
bundle install
% bundle install
redis起動
このままrails sしても
Error connecting to Redis on 127.0.0.1:6379 (Errno::ECONNREFUSED)
が発生してしまいますので、redisを起動します。
% redis-server&
redisがinstallされていなければ、 brew install redis
しておく。
rails sして、Pumaを起動
% rails s
=> Booting Puma
=> Rails 5.2.1 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.0 (ruby 2.5.3-p105), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
loacl環境でsidekiqの動作確認
以下へアクセスする
http://localhost:3000/sidekiq
sidekiq-cron の設定
https://github.com/ondrejbartas/sidekiq-cron
を見ながらシコシコ頑張る
Gemfile
以下を追記
gem 'sidekiq-cron'
workersを作る
mkdir app/workers
Workerを作る
require 'net/http'
class PingWorker
include Sidekiq::Worker
sidekiq_options queue: :ping
def perform(*args)
# 通信したい先のherokuのURLを指定します
uri = URI.parse("https://xxxxx.herokuapp.com/xxxxx")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
p http.get(uri.request_uri)
end
end
注意:ここでqueue: に指定した文字列は他と必ず一致させること
スケジュール登録してみる
この例では1分毎実行としましたが、実際は20分毎でよい。
my_first_job:
cron: "*/1 * * * *"
class: "PingWorker"
queue: ping
注意:ここでqueue: に指定した文字列は他と必ず一致させること
schedule.ymlを読み込む為の設定
schedule_file = "config/schedule.yml"
if File.exist?(schedule_file) && Sidekiq.server?
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file)
end
sidekiq/cron/webを requireする
require 'sidekiq/web'
require 'sidekiq/cron/web'
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
mount Sidekiq::Web => '/sidekiq'
end
sidekiq.ymlにworkerをセット
:verbose: false
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:concurrency: 25
:queues:
- default
- ping
注意:ここでqueuesに指定した文字列は他と必ず一致させること
bundle install
bundle install
sidekiqを起動する
% bundle exec sidekiq -C config/sidekiq.yml
m,
`$b
.ss, $$: .,d$
`$$P,d$P' .,md$P"'
,$$$$$bmmd$$$P^'
.d$$$$$$$$$$P'
$$^' `"^$$$' ____ _ _ _ _
$: ,$$: / ___|(_) __| | ___| | _(_) __ _
`b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` |
$$: ___) | | (_| | __/ <| | (_| |
$$ |____/|_|\__,_|\___|_|\_\_|\__, |
.d$$
local環境でsidekiq-cronの動作確認
puma起動してから以下へアクセスする
http://localhost:3000/sidekiq
sidekiqを起動したコンソールではこのように表示されます
$$^' `"^$$$' ____ _ _ _ _
$: ,$$: / ___|(_) __| | ___| | _(_) __ _
`b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` |
$$: ___) | | (_| | __/ <| | (_| |
$$ |____/|_|\__,_|\___|_|\_\_|\__, |
.d$$ |_|
#<Net::HTTPOK 200 OK readbody=true>
#<Net::HTTPOK 200 OK readbody=true>
:
:
herokuで実行する
local環境で動作確認ができたのでherokuにアップします。
gitへのcommit
% git add -A
% git commit -m "First Commit."
sqlite3はherokuアップ時にエラーになるので事前にGemfile修正
コメントアウトする
#gem 'sqlite3'
そして、bundle install
を実施する
herokuにpushする
# APPNAMEは自身のheroku app名を指定する
% heroku login
% # 既に作ってしまっている場合は以下コマンド heroku create APPNAME
% heroku git:remote --app APPNAME
% git push heroku master
Counting objects: 124, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (108/108), done.
Writing objects: 100% (124/124), 29.05 KiB | 0 bytes/s, done.
Total 124 (delta 6), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
:
https://APPNAME.herokuapp.com/ deployed to Heroku
ただここでredisが無かったり、sidekiqが動かなかったりで苦戦
以下苦戦した後の対処です。
herokuのredis設定が必要なので設定
plugin install
heroku plugins:install heroku-redis
data.heroku.comにてinstall
以下へアクセスして、heroku-redisをappへinstallする
https://data.heroku.com/
configを確認
REDIS_URLが表示される
% heroku config -a APPNAME | grep REDIS_URL
8:REDIS_URL: redis://xxxxxxxxxx
initializersで確認したREDIS_URLを指定
schedule_file = "config/schedule.yml"
Sidekiq.configure_server do |config|
if Rails.env.production?
config.redis = { url: ENV.fetch('REDIS_URL') }
end
end
Sidekiq.configure_client do |config|
if Rails.env.production?
config.redis = { url: ENV.fetch('REDIS_URL') }
end
end
if File.exist?(schedule_file) && Sidekiq.server?
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file)
end
sidekiqは単純には動かないのでunicornを利用
以下を参考にさせていただきました。
3年以上前の記事ですが大変参考になりました!
Gemfile
gem 'unicorn'
Procfile
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
config/unicorn.rb
timeout 15
preload_app true
worker_processes 3
# whatever you had in your unicorn.rb file
@sidekiq_pid = nil
before_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
Process.kill 'QUIT', Process.pid
end
if Rails.env.production?
# tmpフォルダは自分で作らないとherokuは作ってくれません
spawn("mkdir -p tmp/pids")
@sidekiq_pid ||= spawn("bundle exec sidekiq -C config/sidekiq.yml")
Rails.logger.info('Spawned sidekiq #{@sidekiq_pid}')
end
end
after_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
end
end
sidekiqのconcurrency は小さめにする
これが大きいと、
Redis::CommandError (ERR max number of clients reached):
が発生します。
:concurrency: 2
再度 heroku へpush
git add -A
git commit -m "push heroku!"
git push heroku master
heroku ログ確認
% heroku logs --tail
2018-11-01T07:59:55.019594+00:00 app[web.1]: I, [2018-11-01T07:59:55.019486 #4] INFO -- : reaped #<Process::Status: pid 11 exit 0> worker=0
2018-11-01T07:59:55.019726+00:00 app[web.1]: I, [2018-11-01T07:59:55.019681 #4] INFO -- : reaped #<Process::Status: pid 16 exit 0> worker=1
2018-11-01T07:59:55.019844+00:00 app[web.1]: I, [2018-11-01T07:59:55.019799 #4] INFO -- : reaped #<Process::Status: pid 20 exit 0> worker=2
2018-11-01T07:59:55.019939+00:00 app[web.1]: I, [2018-11-01T07:59:55.019890 #4] INFO -- : master complete
2018-11-01T07:59:55.182263+00:00 heroku[web.1]: Process exited with status 0
2018-11-01T07:59:56.000000+00:00 app[api]: Build succeeded
2018-11-01T07:59:59.734590+00:00 heroku[web.1]: Starting process with command `bundle exec unicorn -p 55787 -c ./config/unicorn.rb`
2018-11-01T08:00:04.109925+00:00 app[web.1]: I, [2018-11-01T08:00:04.109783 #4] INFO -- : Refreshing Gem list
2018-11-01T08:00:12.210910+00:00 app[web.1]: I, [2018-11-01T08:00:12.210791 #4] INFO -- : listening on addr=0.0.0.0:55787 fd=9
2018-11-01T08:00:12.233668+00:00 app[web.1]: I, [2018-11-01T08:00:12.233473 #4] INFO -- : Spawned sidekiq #{@sidekiq_pid}
2018-11-01T08:00:12.240924+00:00 app[web.1]: I, [2018-11-01T08:00:12.240706 #4] INFO -- : Spawned sidekiq #{@sidekiq_pid}
2018-11-01T08:00:12.247828+00:00 app[web.1]: I, [2018-11-01T08:00:12.247086 #11] INFO -- : worker=0 ready
2018-11-01T08:00:12.248878+00:00 app[web.1]: I, [2018-11-01T08:00:12.248768 #4] INFO -- : Spawned sidekiq #{@sidekiq_pid}
2018-11-01T08:00:12.257032+00:00 app[web.1]: I, [2018-11-01T08:00:12.256900 #4] INFO -- : master process ready
2018-11-01T08:00:12.257692+00:00 app[web.1]: I, [2018-11-01T08:00:12.257631 #4] INFO -- : reaped #<Process::Status: pid 9 exit 0> worker=unknown
2018-11-01T08:00:12.257880+00:00 app[web.1]: I, [2018-11-01T08:00:12.257807 #4] INFO -- : reaped #<Process::Status: pid 13 exit 0> worker=unknown
2018-11-01T08:00:12.260889+00:00 app[web.1]: I, [2018-11-01T08:00:12.260717 #15] INFO -- : worker=1 ready
2018-11-01T08:00:12.280155+00:00 app[web.1]: I, [2018-11-01T08:00:12.280036 #4] INFO -- : reaped #<Process::Status: pid 17 exit 0> worker=unknown
2018-11-01T08:00:12.291409+00:00 app[web.1]: I, [2018-11-01T08:00:12.291227 #19] INFO -- : worker=2 ready
2018-11-01T08:00:12.703542+00:00 heroku[web.1]: State changed from starting to up
2018-11-01T08:00:28.187287+00:00 app[web.1]: #<Net::HTTPOK 200 OK readbody=true>
:
:
ブラウザでherokuにアクセスして最終チェック
動いてる!感動!
対象のherokuへも通信はきちんと送られていたのでこれでやりたいことは一旦できました。
これで通信先増やせば対象Herokuは眠らなくなるはずです。
仕事ではマネージメントばかりで全くエンジニアっぽいことしていなかったのですが、ちょっとエンジニアっぽいことができて楽しかった
特にheroku楽しいですね!便利で最高
備考
sidekiqって結構前から停滞中だったんですね。知りませんでした。
https://qiita.com/k5trismegistus/items/9fa92bcf24fce7681ee5#no-longer-maintained