Rails で発生した Error を NewRelicに通知する
Rails アプリでは newrelic_rpm を Gemfile に書いておけば、よしなにエラーを NewRelic のダッシュボードに通知してくれます。
が、
rescue_from Exception, with: :render_500 def render_500(e = nil) logger.error e render file: 'public/500', status: :internal_server_error, layout: false end
のようなコードで Exception を握りつぶしてるとダッシュボードで通知されません。
Sending New Relic handled errors によれば、
Use the Ruby Agent API NewRelic::Agent.notice_error within your error handler, which tells the agent to send the error data to New Relic. This API call takes the exception and an optional options hash. Use this format:
とのことなので、
def render_500(e = nil) logger.error e NewRelic::Agent.notice_error(e) render file: 'public/500', status: :internal_server_error, layout: false end
と書けば ok です。
XMLにおいて アンパサンド(&)はエスケープが必要
基本的すぎる話かもしれないが... sitemap_generator で XML を生成していて気付いた。
サイトマップ ファイルは UTF-8 エンコードで作成する必要があります (ファイルを保存すると、通常は UTF-8 エンコードで保存されます)。 他の XML ファイルと同じように、URL などのデータ値では、次の文字にエンティティのエスケープ コードを使用する必要があります。
文字 | エスケープコード | |
---|---|---|
アンパサンド | & | & |
一重引用符 | ' | ' |
二重引用符 | " | " |
不等記号 (より大) | > | > |
不等記号 (より小) | < | < |
http://www.sitemaps.org/ja/protocol.html#escaping
The ampersand character (&) and the left angle bracket (<) must not appear in their literal form, except when used as markup delimiters, or within a comment, a processing instruction, or a CDATA section. If they are needed elsewhere, they must be escaped using either numeric character references or the strings " & " and " < " respectively.
と書かれている。
知らなかったー
ActiveRecordのestablish_connectionを読む
ActiveRecord における DB との接続確立方法をきちんと理解できてなかったので、pry-byebug を使いながらコードを読み解いてみる。
Railsのversion は 4.2.0 という前提で。
establish_connection
sonots さん解説の通り、Rails を起動すると establish_connection メソッドが呼び出される。
# lib/active_record/connection_handling.rb def establish_connection(spec = nil) spec ||= DEFAULT_ENV.call.to_sym resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations spec = resolver.spec(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" end remove_connection connection_handler.establish_connection self, spec end
binding.pry でデバッグ
適当な箇所に binding.pry を挿入して rails server するとコードを追いかけやすくてよい。
self は何?
[12] pry(ActiveRecord::Base)> self => ActiveRecord::Base
なぜ self が ActiveRecord::Base になるかというと、ConnectionHandling モジュールを extendしてるから。 https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/base.rb#L276
spec とは?
[11] pry(ActiveRecord::Base)> p spec :development
resolver.spec(spec) の結果は?
[1] pry(ActiveRecord::Base):1> resolver.spec(spec) => #<ActiveRecord::ConnectionAdapters::ConnectionSpecification:0x007fa52311ae20 @adapter_method="mysql2_connection", @config={:adapter=>"mysql2", :encoding=>"utf8mb4", :database=>"development", :pool=>5, :username=>"root", :password=>nil, :socket=>"/tmp/mysql.sock"}>
connection_handler とは?
def self.connection_handler ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler end def self.connection_handler=(handler) ActiveRecord::RuntimeRegistry.connection_handler = handler end self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/core.rb#L100-L102
により、ActiveRecord::ConnectionAdapters::ConnectionHandler のインスタンスということになる。
そして...
# lib/active_record/connection_handling.rb def establish_connection(spec = nil) spec ||= DEFAULT_ENV.call.to_sym resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations spec = resolver.spec(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" end remove_connection connection_handler.establish_connection self, spec end
の最後の一行は
# lib/active_record/connection_adapters/abstract/connection_pool.rb def establish_connection(owner, spec) @class_to_pool.clear raise RuntimeError, "Anonymous class is not allowed." unless owner.name owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec) end
lib/active_record/connection_adapters/abstract/connection_pool.rb に行き着く。
実は owner_to_pool に instance を代入しているだけで接続していない。
いつ接続するのか?
[Ruby] 例えば、ActiveRecord の connection_pool を止める - sonots:blog
同じく sonots さんの解説の通り、クエリが投げられる時に connection pool から取得 or 新規接続という感じになっている。
↓
↓
↓
↓
という流れで、
def new_connection Base.send(spec.adapter_method, spec.config) end
がコネクションを貼ってる箇所になり、spec.adapter_method を send で呼び出すことになる。
[10] pry(ActiveRecord::Base)> p spec.adapter_method => "mysql2_connection"
まとめ
establish_connectionでは接続確立せず、実際はクエリを投げるタイミングである。
また、spec.adapter_methodには 例えば "mysql2_connection" などが格納されており、これを動的に実行することでconfig/database.yml に書かれた DB に接続する。
ブロードキャストなICMP Echo Requestを無視する
OSはUbuntu 14.04。
結論から書くと、
echo "net.ipv4.icmp_echo_ignore_broadcasts=1" > /etc/sysctl.d/60-icmp-echo.conf service procps start
でOK(のはず)。
設定前
$ ping 192.168.33.255 PING 192.168.33.255 (192.168.33.255): 56 data bytes 64 bytes from 192.168.33.1: icmp_seq=0 ttl=64 time=0.081 ms 64 bytes from 192.168.33.20: icmp_seq=0 ttl=64 time=0.395 ms 64 bytes from 192.168.33.1: icmp_seq=1 ttl=64 time=0.125 ms 64 bytes from 192.168.33.20: icmp_seq=1 ttl=64 time=0.665 ms
設定後
$ ping 192.168.33.255 PING 192.168.33.255 (192.168.33.255): 56 data bytes 64 bytes from 192.168.33.1: icmp_seq=0 ttl=64 time=0.081 ms 64 bytes from 192.168.33.1: icmp_seq=1 ttl=64 time=0.134 ms
以下、調べたことをメモしておく。
/etc/init/procps.conf
$ cat /etc/init/procps.conf # procps - set sysctls from /etc/sysctl.conf # # This task sets kernel sysctl variables from /etc/sysctl.conf and # /etc/sysctl.d snip... task script cat /etc/sysctl.d/*.conf /etc/sysctl.conf | sysctl -e -p - end script
このタスクにより、起動時に/etc/sysctl.d/ と /etc/sysctl.conf が適用される。
デーモン化するわけじゃないことに注意。単に task として sysctl コマンドにパイプしてるつくりなので。
/etc/sysctl.d/
README曰く..
This directory contains settings similar to those found in /etc/sysctl.conf. In general, files in the 10-*.conf range come from the procps package and serve as system defaults. Other packages install their files in the 30-*.conf range, to override system defaults. End-users can use 60-*.conf and above, or use /etc/sysctl.conf directly, which overrides anything in this directory.
なので、今回の設定は /etc/sysctl.d/60-icmp-echo.conf にしておいた。
White Paper : いまさら聞けない、SSL サーバ証明書とルート証明書の関係 - Symantec
分かりやすくまとまってるので、ふと忘れてしまった時に。 https://www.jp.websecurity.symantec.com/welcome/pdf/wp_sslandroot-certificate.pdf
ついでに CSR についてもメモっておく。
CSRとは、お客様が生成し、認証局に提出する署名リクエスト(Certificate Signing Request)です。ジオトラストのSSLサーバ証明書の申請には、SSLサーバ証明書を導入する環境で生成するCSRが必要になります。CSRには、お客様の公開鍵の情報の他、生成時に指定する情報(ディスティングイッシュネーム)が含まれます。 ジオトラストのSSLサーバ証明書は、認証の後、お客様の公開鍵に署名をした上でSSL証明書として発行しています。
Ubuntuにおけるinitと起動方法
sysvinit, upstart, systemd, update-rc.d, insserv の関係性・歴史的背景がよくわからなくていろいろ調べたので、メモしておく。
基礎知識
sysvinit とは?
sysvinitはSystemV initの略で,UNIX SystemV(システムファイブ)と呼ばれるAT&T社謹製の古典的なUNIXが採用した起動メカニズムと同じ動作をするように設計されたソフトウェアです。
出典: http://gihyo.jp/dev/serial/01/sc-literacy/0013
upstart とは?
Upstart is an event-based replacement for the /sbin/init daemon which handles starting of tasks and services during boot, stopping them during shutdown and supervising them while the system is running.
It was originally developed for the Ubuntu distribution, but is intended to be suitable for deployment in all Linux distributions as a replacement for the venerable System-V init.
出典: http://upstart.ubuntu.com/
systemd とは?
systemd is a suite of basic building blocks for a Linux system. It provides a system and service manager that runs as PID 1 and starts the rest of the system.
出典: http://www.freedesktop.org/wiki/Software/systemd/
これらはいずれも PID 1として動作し、OS起動時に諸々のプロセスを立ち上げる役割を担う。
実際にどう使われるのかを理解するのにはinitとプロセス再起動 - slideshare が役に立った。
update-rc.d とは?
update-rc.d updates the System V style init script links /etc/rcrunlevel.d/NNname whose target is the script /etc/init.d/name
出典: man update-rc.d(8)
insserv とは?
insserv is a low level tool used by update-rc.d which enables an installed system init script (`boot script') by reading the comment header of the script
出典: man insserv(8)
initの調べ方
sysvinit, upstart, systemd のうちどれが使われているか調べてみた。OS は Ubuntu 14.04.2。
root@ubuntu-14:~# dpkg -S /sbin/init upstart: /sbin/init
root@ubuntu-14:~# /sbin/init --version init (upstart 1.12.1) Copyright (C) 2006-2014 Canonical Ltd., 2011 Scott James Remnant This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Ubuntu 6.10 から upstart が採用されたと https://help.ubuntu.com/community/UbuntuBootupHowto に書かれてたけど、確かにその通り。
起動方法
http://heartbeats.jp/hbblog/2013/06/service-start-stop.html によれば service コマンドを使って起動するのがオススメの様子。
man service(8)
service runs a System V init script or upstart job in as predictable an environment as possible, removing most environment variables and with the current working directory set to /.
The SCRIPT parameter specifies a System V init script, located in /etc/init.d/SCRIPT, or the name of an upstart job in /etc/init. The existence of an upstart job of the same name as a script in /etc/init.d will cause the upstart job to take precedence over the init.d script.
実行ユーザに依存しない環境変数で起動でき、sysvinit と upstart のどちらにも対応ということらしい。
# /usr/sbin/service より一部抜粋 if [ -r "/etc/init/${SERVICE}.conf" ] && which initctl >/dev/null \ && initctl version | grep -q upstart then # Upstart configuration exists for this job and we're running on upstart case "${ACTION}" in start|stop|status|reload) # Action is a valid upstart action exec ${ACTION} ${SERVICE} ${OPTIONS} ;; restart) # Map restart to the usual sysvinit behavior. stop ${SERVICE} ${OPTIONS} || : exec start ${SERVICE} ${OPTIONS} ;; force-reload) # Upstart just uses reload for force-reload exec reload ${SERVICE} ${OPTIONS} ;; esac fi # Otherwise, use the traditional sysvinit if [ -x "${SERVICEDIR}/${SERVICE}" ]; then exec env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS} else echo "${SERVICE}: unrecognized service" >&2 exit 1 fi
コードを眺めると、upstart job があればそれを優先するようになってる。
upstart を使ってみる
を参考に、カンタンな設定を書いてみる。
"hello" を /var/log/hello に書き出し、10秒 sleep するというもの。
# /etc/init/hello.conf description "hello" author "kotaroito" start on runlevel [2345] stop on runlevel [016] exec /opt/ruby/bin/ruby -e "puts 'hello'; sleep 10" >> /var/log/hello 2>&1 respawn respawn limit 10 5
$ sudo service hello start
で起動する。