【Rails】unicornの謎エラー
どうもてぃです。
unicornの設定でかなり手間取りました。
環境
起きたこと
とある記事を参考にunicornを設定し、ローカルでbundle exec rails unicorn:start
したところ以下のエラーでハマりました。
I, [2019-01-15T16:48:10.401826 #1790] INFO -- : Refreshing Gem list I, [2019-01-15T16:48:11.349098 #1790] INFO -- : unlinking existing socket=/home/user/project/tmp/unicorn.sock I, [2019-01-15T16:48:11.349216 #1790] INFO -- : listening on addr=/home/user/project/tmp/unicorn.sock fd=14 E, [2019-01-15T16:48:11.349315 #1790] ERROR -- : unsupported signal SIGTEAM (ArgumentError) /home/user/project/config/unicorn.rb:11:in `trap' /home/user/project/config/unicorn.rb:11:in `block in reload' /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:543:in `spawn_missing_workers' /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:142:in `start' /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/bin/unicorn:126:in `<top (required)>' /home/user/project/vendor/bundle/ruby/2.5.0/bin/unicorn:23:in `load' /home/user/project/vendor/bundle/ruby/2.5.0/bin/unicorn:23:in `<main>'
おそらく ERROR -- : unsupported signal SIGTEAM (ArgumentError)
これが原因かと。
ローカル環境でやったのがまずかったのかなと思い、GCEのCentOS7上で環境を作ってやってみたのですが、同じエラーが出ました。
やったこと
最初もいいましたが、まず、以下の記事を参考にファイルを作成し設定をしていきました。
bundle exec rails unicorn:start
で作成したタスクを実行したのですが、上記のエラー。
そして、どれだけ探しても同じエラーで詰んでる人がいないという。
と思っていたところ、以下の記事が出てきた。
記事を参考にconfig/unicorn.rb
を変更してみました。
# unicron.rb # set lets $worker = 2 $timeout = 30 $app_dir = "/home/user/project/" #アプリの場所 $listen = File.expand_path 'tmp/unicorn.sock', $app_dir $pid = File.expand_path 'tmp/unicorn.pid', $app_dir $std_log = File.expand_path 'log/unicorn.log', $app_dir # set config worker_processes $worker working_directory $app_dir stderr_path $std_log stdout_path $std_log timeout $timeout listen $listen pid $pid # loading booster preload_app true # before starting processes before_fork do |server, worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! old_pid = "#{server.config[:pid]}.oldbin" if old_pid != server.pid begin Process.kill "QUIT", File.read(old_pid).to_i rescue Errno::ENOENT, Errno::ESRCH end end end # after finishing processes after_fork do |server, worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end
これで一旦はbundle exec rails unicorn:start
が動くようになりました。
終わりに
今回の解決策には全くなってないですが、プロジェクトの関係で早く本番環境を作らないといけないので、一旦この方法で落ち着けます。
時間のあるときにもうすこし調査してみます。
誰か解決してくれるといいんだけど。
【Ubuntu】apt update時winehqでGPGエラー
どうもてぃです。
apt-keyの更新とか設定とかメンドイですね。今回身をもって知りました。
環境
- Ubuntu 16.04.5 LTS(elementary OS 0.4.1 Loki)
現象
$ sudo apt update 無視:1 http://dl.google.com/linux/chrome/deb stable InRelease ヒット:2 http://dl.google.com/linux/chrome/deb stable Release ヒット:3 https://dl.yarnpkg.com/debian stable InRelease ヒット:4 https://download.docker.com/linux/ubuntu zesty InRelease ヒット:5 https://cli-assets.heroku.com/apt ./ InRelease ヒット:8 http://packages.elementary.io/appcenter xenial InRelease ヒット:9 http://ppa.launchpad.net/elementary-os/stable/ubuntu xenial InRelease エラー:7 https://dl.winehq.org/wine-builds/ubuntu xenial InRelease 公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY 76F1A20FF987672F ヒット:10 http://archive.ubuntu.com/ubuntu xenial InRelease ヒット:11 http://ppa.launchpad.net/gregory-hainaut/pcsx2.official.ppa/ubuntu xenial InRelease ヒット:12 http://archive.ubuntu.com/ubuntu xenial-updates InRelease ヒット:13 http://archive.ubuntu.com/ubuntu xenial-backports InRelease ヒット:14 http://ppa.launchpad.net/noobslab/pcsx2/ubuntu xenial InRelease ヒット:15 https://packagecloud.io/slacktechnologies/slack/debian jessie InRelease ヒット:16 http://archive.ubuntu.com/ubuntu xenial-security InRelease ヒット:17 http://ppa.launchpad.net/elementary-os/os-patches/ubuntu xenial InRelease ヒット:18 http://ppa.launchpad.net/philip.scott/elementary-tweaks/ubuntu xenial InRelease ヒット:19 http://ppa.launchpad.net/ubuntuhandbook1/audacity/ubuntu xenial InRelease ヒット:20 http://ppa.launchpad.net/webupd8team/java/ubuntu xenial InRelease パッケージリストを読み込んでいます... 完了 W: GPG エラー: https://dl.winehq.org/wine-builds/ubuntu xenial InRelease: 公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY 76F1A20FF987672F E: リポジトリ https://dl.winehq.org/wine-builds/ubuntu xenial InRelease は署名されていません。 N: このようなリポジトリから更新を安全に行うことができないので、デフォルトでは更新が無効になっています。 N: リポジトリの作成とユーザ設定の詳細は、apt-secure(8) man ページを参照してください。
winehq
で公開鍵がないんかな、と予想。
やったこと
まずはwikiを参照。
そしたらtopに書いてありました。
$ wget -nc https://dl.winehq.org/wine-builds/winehq.key $ sudo apt-key add winehq.key $ sudo apt update
これだけだったなんて。
終わりに
しばらくエラーが出てて更新できてなかったので、その後apt upgrade
やりました。
やはり一時情報を確認するのが大切ですね。
めでたしめでたし。
【Ubuntu】apt updateで404 not found
どうもてぃです。
いろいろ環境構築してる時にタイトルの子が出てくるとかなり困る。
なんとか解決してみました(解決できたかどうか微妙だが)。
環境
- Ubuntu 16.04.5 LTS(elementary OS 0.4.1 Loki)
やったこと
いろんな記事を試しました。
が、メインは以下の記事の手順。
今回公開鍵のエラーも出てたので以下も参考にした。
公開鍵エラーの方はなんとか直ったんですが、404 not found
エラーの方が直らない。
リポジトリはこの子。
エラー:44 http://ppa.launchpad.net/hunter-kaller/ppa/ubuntu xenial Release 404 Not Found E: リポジトリ http://ppa.launchpad.net/hunter-kaller/ppa/ubuntu xenial Release には Release ファイルがありません。 N: このようなリポジトリから更新を安全に行うことができないので、デフォルトでは更新が無効になっています。 N: リポジトリの作成とユーザ設定の詳細は、apt-secure(8) man ページを参照してください。
なので一旦リポジトリを取り直してやってみたが、、、
$ sudo add-apt-repository ppa:hunter-kaller/ppa $ sudo apt -y update ・ ・ ・ ・ ・ ・ ・ エラー:44 http://ppa.launchpad.net/hunter-kaller/ppa/ubuntu xenial Release 404 Not Found E: リポジトリ http://ppa.launchpad.net/hunter-kaller/ppa/ubuntu xenial Release には Release ファイルがありません。 N: このようなリポジトリから更新を安全に行うことができないので、デフォルトでは更新が無効になっています。 N: リポジトリの作成とユーザ設定の詳細は、apt-secure(8) man ページを参照してください。
だめだなこれは。
対処法
Releaseファイルがないということはリポジトリの更新がそもそも止まってるんじゃね?と思い、なら必要ないんじゃないかという結論に至ったので消しました。
/etx/apt/sources.list.d/
配下にhunter-kaller-ubuntu-ppa-xenial.list
がいるので、削除してしまう。
けど、もしエラーになったら面倒なので、一応バックアップとして取っておく方法にする。
$ sudo mkdir /etc/apt/backup $ sudo mv /etc/apt/sources.list.d/hunter-kaller-ubuntu-ppa-xenial.* /etc/apt/backup/ $ sudo rm -rf /var/lib/apt/lists/* $ sudo apt update
これで一旦問題なくupdateできました。
【Rails】unicorn.rbでRails.rootが使えない
どうもてぃです。
本番環境を作ってます。環境ってすごく大事ですよね。
今回何も考えずデフォルトのサーバーpumaを使っていたので、unicornに移行しようと設定していたところ起きた問題です。
環境
起きたこと
worker_processes Integer(ENV['WEB_CONCURRENCY'] || 3) timeout 15 preload_app true listen Rails.root.join('tmp', 'unicorn.sock') pid Rails.root.join('tmp', 'unicorn.pid') before_fork do |server, worker| Signal.trap 'TEAM' do puts 'Unicorn master intercepting TERM and sending myself QUIT instead' Process.kill('QUIT', Process.pid) end defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! end after_fork do |server, worker| Signal.trap 'TERM' do puts 'Unicorn worker intercepting TERM and doing nothind. Wait for master to send QUIT' end defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end stderr_path File.expand_path('log/unicorn.log', Rails.root) stdout_path File.expand_path('log/unicorn.log', Rails.root)
上記がunicornの設定ファイルです。
これを元にサーバー起動をしてみたところ、以下のエラーが発生。
# lib/tasks/unicorn.rake を実行する $ bundle exec rails unicorn:start unicorn -c /home/user/project/config/unicorn.rb -E development -D Traceback (most recent call last): 9: from /home/user/project/vendor/bundle/ruby/2.5.0/bin/unicorn:23:in `<main>' 8: from /home/user/project/vendor/bundle/ruby/2.5.0/bin/unicorn:23:in `load' 7: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/bin/unicorn:126:in `<top (required)>' 6: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/bin/unicorn:126:in `new' 5: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:77:in `initialize' 4: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:77:in `new' 3: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:77:in `initialize' 2: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:84:in `reload' 1: from /home/user/project/vendor/bundle/ruby/2.5.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:84:in `instance_eval' /home/user/project/config/unicorn.rb:5:in `reload': uninitialized constant #<Class:#<Unicorn::Configurator:0x000055a569137798>>::Rails (NameError) master failed to start, check stderr log for details rails aborted! Command failed with status (1): [unicorn -c /home/user/projec...] /home/user/project/lib/tasks/unicorn.rake:5:in `block (2 levels) in <main>' /home/user/project/vendor/bundle/ruby/2.5.0/gems/railties-5.2.1/lib/rails/commands/rake/rake_command.rb:23:in `block in perform' /home/user/project/vendor/bundle/ruby/2.5.0/gems/railties-5.2.1/lib/rails/commands/rake/rake_command.rb:20:in `perform' /home/user/project/vendor/bundle/ruby/2.5.0/gems/railties-5.2.1/lib/rails/command.rb:48:in `invoke' /home/user/project/vendor/bundle/ruby/2.5.0/gems/railties-5.2.1/lib/rails/commands.rb:18:in `<main>' /home/user/project/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require' /home/user/project/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `block in require_with_bootsnap_lfi' /home/user/project/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/loaded_features_index.rb:65:in `register' /home/user/project/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:20:in `require_with_bootsnap_lfi' /home/user/project/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:29:in `require' /home/user/project/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `block in require' /home/user/project/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency' /home/user/project/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `require' bin/rails:4:in `<main>' Tasks: TOP => unicorn:start (See full trace by running task with --trace)
たいていどこのファイルでも問題なくRailsを使えるので何も疑ってなかったのですが、色々調べたところ使えないようです。
やったこと
require 'rails'
をやってみたんですが、Rails.root
がnil
で返ってくる。
故に、joinメソッドでNoMethodErrorが起きるという事態。
そのため、大人しく絶対パスを使って書き直すことにした。
worker_processes Integer(ENV['WEB_CONCURRENCY'] || 3) timeout 15 preload_app true # listen Rails.root.join('tmp', 'unicorn.sock') # pid Rails.root.join('tmp', 'unicorn.pid') listen '/home/user/project/tmp/unicorn.sock' pid '/home/user/project/tmp/unicorn.pid' before_fork do |server, worker| Signal.trap 'TEAM' do puts 'Unicorn master intercepting TERM and sending myself QUIT instead' Process.kill('QUIT', Process.pid) end defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! end after_fork do |server, worker| Signal.trap 'TERM' do puts 'Unicorn worker intercepting TERM and doing nothind. Wait for master to send QUIT' end defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end stderr_path File.expand_path('log/unicorn.log', '/home/user/project/') stdout_path File.expand_path('log/unicorn.log', '/home/user/project/')
これで解決。
おわりに
環境変数を設定すればいいのかもなーと思いました。
本番環境ではそれで行こうかな。
【Git】現在のブランチを取得して、git branchを打たないようにする
どうもてぃです。
皆さん開発してますか?僕は全然出来てません。
時に開発すると、自分が異常にgit branch
をしていることに気づき、もうどうしようもないくらい同じコマンドを打ちたくない気持ちでいっぱいだったので、今回それを解決しました。
参考
すでに神がいました。
こちらを参考にさせていただきます、ありがとうございます。
やったこと
alias
を作成して、push時、pull時にbranchを打たなくていいようにします。
alias
は.bashrc
へ記載します。それぞれの環境で.bash_profile
などに書き換えてください(alias書いてるとこならどこでもいいんじゃないすか)。
alias push="git rev-parse --abbrev-ref HEAD | xargs git push origin" alias pull="git rev-parse --abbrev-ref HEAD | xargs git pull origin"
git rev-parse --abbrev-ref HEAD
で取得したブランチ名をxargs
コマンドで渡してあげる。これだけ。
終わりに
pushするときに多用していたgit branch
、その後にコマンドラインにあるブランチ名をコピーしてペーストする手間をなくしました。
これでbranchの煩わしさから逃れられますね。
…checkoutするときにはgit branch
が必須なんですけど。
【備忘録】Railsでcheck_box_tagがfalseのときもパラメータを飛ばしたい
Rails速習実践ガイドめっちゃいいね、どうもてぃです。
さて、昔実装したのに、しばらくやってないと忘れることよく有りますよね。
今回は備忘録として忘れてたことを書きたいと思います。
環境
やったこと
check_box_tagでパラメータを飛ばす場合、デフォルトではチェックのついたものしか飛びません。ということを完全に忘れていて、軽く30分くらい悩みました。
なので、適切な対処が必要。
まずはcheck_box_tagのリファレンスを確認。おなじみのrailsdocです。
<%= check_box_tag 'page[freezeflag]', true, false, {} %> # <input id="page_freezeflag" name="page[freezeflag]" type="checkbox" value="true" />
これ使ってみます。
check_box_tagの第一引数はパラメータのkey名 & 属性のIDとname、第二引数は渡す値、第三引数は初期値、第四引数は状態を表してます。
チェックがついていると、strongparameterの中に
{ "page" => <ActionController::Parameters { "freezeflag" => "true" } permitted: false> }
という形で入ります。チェック無しだとそもそも、params["page"]
がnil
になります。
これはデータを更新する上で不便。チェックがあるかどうかで無駄な処理を書かなきゃいけないのはスマートじゃないです。
それを解消するのがhidden_field_tag
。
必ずcheck_box_tagの前に設置しましょう!
それが以下。
<%= hidden_field_tag 'page[freezeflag]', false %> <%= check_box_tag 'page[freezeflag]', true, false, {} %> # <input id="page_freezeflag" name="page[freezeflag]" type="checkbox" value="true" />
check_box_tagにチェックが入らなければ同じ名前のhidden_field_tagの値(false
)がパラメータとして飛ぶ。
逆にチェックがついていればcheck_box_tagの方で値が上書きされるという感じ。
必ずnameは同じにしないといけないことに注意。あと最初に言ったけど、hidden_field_tagの位置にも注意。
これで今回の要件を満たせます。
番外編(おまけ)
素晴らしいメソッドを見つけました。
ActiveRecord::Type::Boolean.new.cast
です。
以下の記事に書いてありました。素晴らしいです。ありがとうございます。
なので、今回飛ばしている値を変更します。
<%= hidden_field_tag 'page[freezeflag]', 0 %> <%= check_box_tag 'page[freezeflag]', 1, false, {} %> # <input id="page_freezeflag" name="page[freezeflag]" type="checkbox" value="true" />
これで、パラメータとしてチェック済みの時は文字列の1、チェック無しだと文字列の0が飛びます。
上の記事のように、models/applilcation_record.rb
へ専用のメソッドを書いておくと。。。
def checked?(data) ActiveRecord::Type::Boolean.new.cast(data) end
checked?の引数に飛んできたチェックボックスのパラメータを渡すとtrue or falseを判定してくれます。
すばらしい。感動しました。
【Ruby】スクレイピングに精度を求めるならNokogiriではなくSeleniumだ
どうもてぃです。
とある巨大ECサイト(通称熱帯雨林)をスクレイピングして遊んでいます。
今回は nokogiri
で問題が発生したので記事にしました。
nokogiriのスクレイピング精度はあんまり良くないのを実体験で痛感しましたね。
environment
issue
ページネーション要素の取得です。
Nokogiriだと4〜6割の確率でページネーションを取得できない場合があります。
問題なのが、Seleniumだとページを読み込み終わるまで待つSelenium::WebDriver::Wait
があるのに対して、Nokogiriにはwaitが存在しません。
あと、NokogiriではJS等で動的に表示しているページは上手く取得できないことがあるとのこと。
だからSeleniumでブラウジングしてスクレイピングする方が成功確率は高いと。
最初からSelenium使えばよかったですわ。Python
もSeleniumだし。
solution
試しに、一番お手軽なスクレイピングサイト、巨大ECサイト熱帯雨林を使ってみます。
ここだと、ページネーションもありますので。
最近switchのスマブラが販売されて転売ヤーさんが乱立しているGCコンをスクレイピングしてみます。
require 'selenium-webdriver' ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36" # headlessモードのoptionをつける options = Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument("--user-agent=#{ua}") driver = Selenium::WebDriver.for(:chrome, options: options) driver.get('https://www.amazon.co.jp/gp/offer-listing/B07HC2F97Q/ref=dp_olp_new?ie=UTF8&condition=new') # ページ読み込みのwaitを設定 wait = Selenium::WebDriver::Wait.new(timeout: 10) # find_elementで単数取得 wait.until { driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") } seller_info = driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") wait.until { driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) } } pagination = driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) } # ページネーションの最後までクリックする loop.with_index(1) do |_, i| puts "==================================#{i}回目===================================" puts driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']").map(&:text) break if driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").last[-1] == '#' driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").last.click end # 二回目のget urlも問題ない driver.get('https://www.amazon.co.jp/sp?_encoding=UTF8&seller=A2W8UAZHGIMLV7') driver.close
上記ソースを超簡単に説明。
# headlessモードのoptionをつける options = Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument("--user-agent=#{ua}")
Selenium::WebDriver::Chrome::Options.new
でSeleniumのオプションを設定するインスタンスを生成します。
AWS
やGCP
で動かすことも想定にいれて、ヘッダレスでブラウザを起動しないようなオプションをつけています。
ついでにユーザーエージェントも設定してます。
# ページ読み込みのwaitを設定 wait = Selenium::WebDriver::Wait.new(timeout: 10)
スクレイピングをした際にページが全て表示されるまで待つ必要が有ります。
その設定を上記でやっています。使い方は
wait.until { driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") } seller_info = driver.find_elements(:xpath, "//h3[@class='a-spacing-none olpSellerName']/span/a") wait.until { driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) } } pagination = driver.find_elements(:xpath, "//ul[@class='a-pagination']/li/a").map { |el| el.attribute(:href) }
のように、要素を取得する前にwait.util
を設定します。簡単です。
残りは要素の取得の処理がメインですね。
loopの部分でページネーションのリンクをクリックし続ける処理をやっています。そんな大したことはやってないです。
最後に
やはり、困った時はrubydocが役に立ちます。
Class: Selenium::WebDriver::Driver — Documentation for selenium-webdriver (0.0.28)
是非参考にしてみてください。
なにかあればコメント欄で〜。
ではでは。