巨人の足元でたじlog

そうして言葉を軽んじるから――― 君は私の言葉を聞き逃す

docker-composeのコンテナ内でcronを動かせたけど、railsのrakeタスクが実行できない

昨日、コンテナ内でcronを実行すること自体は成功したのだが、どうもrailsアプリのrakeタスクを実行する際に、(おそらく)権限の問題で実行できていなかったので、それを修正する。

目標は1時間以内(本当は30分で終わらせたいところ)

まずは現状を確認。 containerにexecする。ホストにて。

$ docker-compose exec app bash

rootでbashにログインできました。私はrootです。

# whoami
root

crobntabを確認する

crontab -e 

→なにもない。(コメントがありますが、有効な記述はない。)

次に、/etc/cron系を調べてみます。

/etc/crontab

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --reporrt /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --reporrt /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --reporrt /etc/cron.monthly)

これはデフォルトで入っていたやつでした。

他の/etc/cron系のファイルを見てみても、(見てないですが)なさそうだったので、特にcron系は設定されていない状態なはずです。

ここから、一つずつ動作を確かめていきませう。

進め方を決めます。
テスト環境にて
1. crontabの動作を確認する(権限必要ないファイルに書き込み)
2. railsのdummyのrakeタスクを実行.ログをファイル出力
3. 本番のrakeタスクを実行。
4. Dockerfile変更→build→run→cronの動作確認
5. 本番に反映

テスト(ローカル)環境と本番環境をごっちゃにしていたので、どこのcrnotabが有効かわからなくなってしまっていた。

テスト環境のコンテナのcronの状況確認

crontab -l
* * * * *   echo "Hello $(date)" >>/var/log/cron.log 2>&1
* * * * *   /usr/local/bundle/bin/rake status:testman  >>/var/log/cron.log 2>&1
* * * * *   pwd >> /var/log/cron.log 2>&1

一番上を確認する。 これ以外は一旦コメントアウトにする。

* * * * *   echo "Hello $(date)" >>/var/log/cron.log 2>&1

出力先をきれいにしておきます

rm -rf /var/log/cron.log

cron実行想定時間後

# cat  /var/log/cron.log
Hello Wed Sep  5 14:59:01 UTC 2018

OKです。ファイルの書き込み(なければ作成)もOKでした。 以下のcronだけを残します。

* * * * *   /usr/local/bundle/bin/rake status:testman  >>/var/log/cron.log 2>&1

ちなみにrakeタスクのstatus:testmanは以下

desc "test"
task :testman => :environment do
    File.open("db/hoge.csv","a") do |f|
    f.puts(Time.now)
    end
end

コレ自体はちゃんと動作することは確認した。

しかし、cronが動かない。 出力先の/var/log/cron.logを見てみると、

/usr/bin/env: 'ruby': No such file or directory

予想: rakeタスクのパスがなんかどっかおかしいんだろ?

それっぽいstack overflow
やっぱりそれっぽいことを言われている。
$HOMEにpathを追加すれば行けるよ的なこと言っているが、あんまりいじりたくない。いじったらその分Dockerfileもいじらなくてはいけなくなるので、厄介。絶対厄介。

まずコンテナ内のrubyのパスを確認してみる。

# which ruby
/usr/local/bin/ruby

...しかしこれがわかって何になるのか。


bundle execすればいい話?
ちょっとdocker-composeのマウントとかにいろいろ紛れてvendorの場所をあまり意識していなかったが、それか?

printenvで表示されるpathに
/usr/local/binは含まれているので、rubyを探せる。

あ、よし。
cronにprintenvさせればいいのだな。

HOME=/root
LOGNAME=root
PATH=/usr/bin:/bin
SHELL=/bin/sh
PWD=/root

やはり、pathがなかった。
正当な解決までの道のりは遠そうだったので、次の方針で進める。
cronでsh /opt/hoge.sh >> /var/log/cron.log
として、
/opt/hoge.shファイルにて

export PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
/usr/local/bundle/bin/rake status:testman

これで行けそう。

# sh /opt/cron.sh

これは成功した!
次に、cronから呼び出す。

だめ。
/var/log/cron.log

rake aborted!
No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)

おっとこれは、rakeファイルを実行する場所が悪かったようでした。
/opt/cron.sh

export PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd /webapp
/usr/local/bundle/bin/rake status:testman

場所を移動して実行するように変更.

rake aborted!
Bundler::GemNotFound: Could not find rake-12.3.1 in any of the sources
/webapp/config/boot.rb:3:in `<top (required)>'
/webapp/config/application.rb:1:in `require_relative'
/webapp/config/application.rb:1:in `<top (required)>'
/webapp/rakefile:4:in `require_relative'
/webapp/rakefile:4:in `<top (required)>'
(See full trace by running task with --trace)

わからん。
bundle execしないとだめってこと?
一旦それでやってみるよ?
/opt/cron.shを以下に変更

export PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd /webapp
/usr/local/bin/bundle exec rake status:testman

結果、だめ。
/var/log/cron.log

bundler: failed to load command: rake (/usr/local/bundle/bin/rake)
Bundler::GemNotFound: Could not find rake-12.3.1 in any of the sources
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/spec_set.rb:91:in `block in materialize'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/spec_set.rb:85:in `map!'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/spec_set.rb:85:in `materialize'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/definition.rb:171:in `specs'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/definition.rb:238:in `specs_for'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/definition.rb:227:in `requested_specs'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/runtime.rb:108:in `block in definition_method'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/runtime.rb:20:in `setup'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler.rb:107:in `setup'
  /usr/local/lib/ruby/site_ruby/2.5.0/bundler/setup.rb:20:in `<top (required)>'
  /usr/local/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
  /usr/local/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'

これはなんだ。

これは/otp/cron.shが悪い。
そもそも、bashからsh /opt/cron.shを実行しても同様のエラーになるので、cornの罠とかじゃなく、bundle exec とかは必要ないはず。

ってかそもそもbundle execなしで普通にbashからrakeタスク実行できていたので、同じrootユーザーなのでできるはず。
考えよう。

考えてもわからん。
無能すぎて泣ける。
明日は時間トレなさそうなのでもう諦める。
むしろ一回一回コンテナを起動してしまうことは甘んじて受け入れて、ホストのcronで不なコンテナを消し込みに行こうかな。

全然解決できなさすぎて、セーブポイントから結構進めてたどり着く最初の中ボスみたいなの10回くらい倒せなくて諦めた小5くらいにやってた犬夜叉のゲーム思い出したわ。