Raccoon Tech Blog [株式会社ラクーン 技術戦略部ブログ]

株式会社ラクーン 技術戦略部より、tipsやノウハウなど技術的な話題を発信いたします。

2015年12月

RubyKaigi2015レポート

メリークリスマス!

技術開発部のおおはらです。

本日12月25日はクリスマスでもありますが、Ruby2.3のリリース日でもあります!

毎年クリスマスにRubyはバージョンアップリリースを続けており、 開発や利用が盛んであることはRubyエンジニアにとって喜ばしい限りです。

ちなみに私が開発を担当しているコレックはRuby on Railsを使用しています。

さて、先日開催されたRubyKaigi2015に参加してきましたので、そのレポートをお送りしたいと思います。

RubyKaigi2015

1日目

Ruby3 challenges

  • Yukihiro "Matz" Matsumotoさん(rubyの作者)の基調講演
  • Rubyコミュニティのスローガン「MINASWAN」の紹介
  • Ruby2.3.0の新機能の紹介
    • did_you_mean gemがデフォルトでバンドルされる
      • タイポした時に正しいと思われる変数名やメソッド名などを教えてくれる
    • grep_vメソッド
      • パターンにマッチしないものを集める(grep -v)
    • digメソッド
      • 深い階層の要素(例:array[0][1][2])をエラー無く取得
    • indented here document
      • sql = <<~SQLとするとインデントの浅いところに合わせて先頭を削除
    • safe navigation operator(ぼっち演算子) &.
      • &.が膝を抱えて座って、石を寂しそうにながめている人に見えることからぼっち演算子と命名。面白くて覚えやすい!
      • user && user.accountuser&.accountのように簡潔に書ける。 railsのtry!と同じ。
    • Ruby3でStringがimmutableになることへの移行パス(マジックコメント)
  • Ruby3の進捗
    • Ruby2.0と比較して3倍の速度向上を目標
    • そのためにconcurrency(並行性)やJITコンパイラの導入を検討中
  • http://togetter.com/li/911263

Compiling Ruby scripts

  • 笹田耕一さん。Rubyコミッタ。Heroku社でのMatzチームの一員。
  • 性能改善のためにRubyスクリプトの実行前コンパイルを検討中
  • 作成したISeq(InstructionSequence)シリアライザ、デシリアライザの効果
    • 高速起動
    • メモリ削減
    • コンパイル済みコードを別プロセスで共有できる
  • Ruby2.3のsample/iseq_load.rbで試せる
  • 2015_RubyKaigi2015.pdf

Saving people from typos

  • Yuki Nishijimaさん。Pivotal Labs社。kaminari gemのメンテナ
  • Ruby2.3から同梱されるようになったdid_you_mean gemの仕組みの解説
  • spell checkerとmonkey patchで構成
  • spell checker
    • dictionary, control mechanism, optimizationで構成
  • dictionary
    • Symbol.all_symbolsなどで辞書を作成、エラータイプで辞書を分類
  • control mechanism
    • ミスタイプをLevenshtein距離、ミススペルをJaro-Winkler距離+Prefix bonuxで評価
  • monkey patch
    • NameError#to_sをオーバーライドして出力
    • 例外が発生した場所を特定するためにRuby2.3以前はC拡張を使っていたが、Ruby2.3から追加されたNameError#receiverが使えるようになった
  • http://togetter.com/li/911325

Time flies like an arrow; Fruit flies like a banana: Parsers for Great Good

Fast Metaprogramming with Truffle

  • Kevin Menardさん。Oracle社のエンジニア。
  • JRubyとTruffleを使うとメタプログラミングによる実行効率が落ちない
    • 例:method_missing の実行効率が落ちない
  • http://togetter.com/li/911356

High Performance Template Engine: Guide to optimize your Ruby code

  • Kohei Suzukiさん、Takashi Kokubunさん。両名ともクックパッド社。
  • Suzukiさんはfamlテンプレートエンジンを作成
  • benchmark-ips gemでベンチマークし、stackprof, rblineprof gemでプロファイリング、コード改善
  • slimテンプレートエンジンと同じバックエンドのtempleを使用
  • クックパッド社でfamlを本番投入している
  • KokubunさんはHamlitテンプレートエンジンを作成
  • stringを先につなげて高速化
  • いらない機能・オプションを削って高速化
  • Famlより高速だが互換性を一部捨てた
  • Q.同じ会社の同じ部署にいるなら、統合して互換性を失わないモードをオプションで提供しないのはなぜか
    • A. Hamlitは自分の勉強のために作りたかったから。作ったついでにベンチマークで勝ちたかった。
  • https://speakerdeck.com/k0kubun/high-performance-template-engine

TRICK 2015: The second Transcendental Ruby Imbroglio Contest for RubyKaigi


2日目

Keynote

  • KOSAKI Motohiroさん。Rubyコミッタ、Linuxカーネルコミッタ。
  • Linuxデバッグツールの紹介
    • Ftrace
      • Linuxのイベントをトレース
    • Perf tools(パフォーマンスツール群)
      • perf top 実行中のプロセスの関数ごとのtop
      • perf trace 全プロセスが呼んだシステムコールを記録
    • System Tap
      • デバッグスクリプト言語
      • 言語仕様があまり良くない(loopがないなど)
      • CとRubyでシステムコールが取れる
  • http://togetter.com/li/911612

The history of testing framework in Ruby

  • Kouhei Sutouさん。クリアコード社の代表。
  • /¥Atest.+unit¥z/iにマッチするtest unitの違いがわかるようになる話
  • Ruby1.3
    • testsupp
  • Ruby1.4
    • RubyUnit
  • Ruby1.6
    • Lapidary
    • Test::Unit(RubyUnit + Lapidary)
  • Ruby1.8
    • Test::Unit
      • rubyが標準でバンドル。require 'test/unit'することからtest/unitとも表現される
      • メンテナが交代したが、test/unitが複雑でメンテできなくなった
  • Ruby1.9
    • Rubyはtest/unitをバンドルしなくなった
      • test-unit gemとしてリリース(メンテナがSutouさん)
    • minitest
      • 作者はtest/unitのメンテナ)
      • Ruby開発者がminitestのラッパーとして新しくtest/unitを作成してrubyにバンドル
  • Ruby2.1
    • minitest5で互換性のない変更が入った
    • ruby自身のテストにはminitest4を使用
    • ユーザには最新のminitestを提供
  • Ruby2.2
    • minitest5をバンドル
    • 再びtest-unit gemをバンドル(for user)
    • ruby自身のテストにはフォークしたminitest4とtest/unitを使う
  • http://slide.rabbit-shocker.org/authors/kou/rubykaigi-2015/

Turbo Rails with Rust

The worst Ruby codes I've seen in my life

Ruby for one day game programming camp for beginners

  • Ippei Obayashiさん。京大マイコンクラブのメンバー。東北大助教授。
  • 京大マイコンクラブの新入生歓迎イベントとして1日ゲームプログラミングを開催
  • プログラミング初心者である参加者の8割ほどが、動くゲームを完成
  • 成功の原因を考察
    • 参加者と同数のクラブのメンバーがマンツーマンで教えた
    • 1日という締め切りがあること
      • できることの見積がしやすい
      • 型を教えない、グローバル変数で頑張る
      • インストールを簡単にする工夫(ゲームのスターターキット)
  • http://togetter.com/li/911699

Ruby meets Go

Building CLI Apps for Everyone

making robot with mruby

LT

Update Early, Update Often

Automating View Internationalization in Ruby on Rails

  • Rails2から3に変わるときにデフォルトでhメソッド(html_escape)が実行されるのと同じように、I18nのtメソッドやlメソッドがデフォルトで実行されても良いのでは?という提案

A new testing framework Rgot

Building an Unbreakable MRI-based Embedded Computer Appliance

Do you trust that certificate?

How I debugged debugger

Padrino Travel Guide

What I learned by implementing a Ruby VM in Erlang

Rubygemsで作るお手軽データ分析基盤 〜あるいは 私はどうやって他人の褌で相撲を取ったか〜

  • gem選びの基準はシンプル、読みやすい、プルリクの送りやすさ
  • プルリクを送る時に、自分たち固有の機能か、汎用な機能かを切り分けないとマージされにくい

Rationalを最適化してみた

The Mythical Creatures of Summer of Code

  • Rails Girls Summer of Codeの紹介

3日目

Writing web application in Ruby

Refinements - the Worst Feature You Ever Loved

  • Paolo Perrottaさん。Metaprogramming Rubyの著者。
  • Ruby2.1から正式に導入されたRefinementsの解説
  • モンキーパッチは便利だが影響範囲が大きい。その範囲を制限するのがRefinements。
  • あるmodule内で、拡張したいクラスをrefineの引数として定義し、usingキーワードを記述したレキシカルスコープでのみ有効となる
module A
  refine String do
    def shout
      upcase + "!"
    end
  end
end

Class C
  using A
    "hello".shout # => "HELLO!"
  end
end

C.class_eval do
  "hello".shout # => NoMethodError
end

Discussion on Thread between version 1.8.6 and 2.2.3

Plugin-based software design with Ruby and RubyGems

  • Sadayuki Furuhashiさん。トレジャーデータ社の設立者。Fluentdの作者。
  • Fluentd
    • ログの集約ツール
    • RubyGems.orgのgemを使ったプラグインベースのアーキテクチャ
  • Embulk
    • CSVなどのデータ変換・投入ツール
    • RubyGems.orgのgemとJRubyを使ったプラグインベースのアーキテクチャ
  • 課題
    • Ruby VMのバージョン衝突
  • http://www.slideshare.net/frsyuki/pluginbased-software-design-with-ruby-and-rubygems

Request and Response

  • Aaron Pattersonさん。Ruby,Rails,Rackのコミッタ。講演は日本語(すごい)
  • Railsではリクエストをパースするために数十のrack middlewareを呼び出している
  • 開発環境高速化のためにhttp2を使えるライブラリを書いてみた
  • Rack3ではAPIが多分変わる
  • http://togetter.com/li/912368

Actor, Thread and me

  • Masatoshi SEKIさん。Rubyコミッタ。
  • アクターモデルのおさらい
  • 要素としては、アクター、メッセージ、エーテル(メッセージングシステム)
  • アクターは他のアクターにメッセージを送る。自分の都合の良い時に自分宛てのメッセージを受け取る
  • アクターが返事待ちになってシステムが止まることがある
  • https://speakerdeck.com/m_seki/actor-thread-and-me-rubykaigi2015

Keynote


初めてRubyKaigiに参加したのですが、今年はRuby3リリースに向けてどのように高速化していくか、という話題が多かったです。

Rubyそのものの実装の話が多かったので、C言語未経験の自分にとっては少し難解な部分もありました。

Rubyのカンファレンスではありますが、Ruby以外の言語や設計、テスト手法などに触れることができ、幅広く勉強できたと思います。

来年は9月8日~10日に京都国際会館で開催されるようなので今から楽しみです!

iptablesを利用してNATサーバを構築

こんにちは、えのもとです。

弊社では本番リリース直前検証環境として、ステージング環境を利用しています。
この環境が古くなってしまったため、環境再構築を実施することになりました。

再構築する上で、今後のこともあるので再構築しやすい環境に切り替えることにしました。
構築上でポイントとなったのは以下の2つです。
  • 本番環境はVMで稼働しているので、そのバックアップを活用する
  • サーバ設定(ネットワーク設定など)はできる限り変更したくない
これらを達成するため、閉鎖されたネットワークを構築しその中に環境を構築することにしました。そこで問題となるのは閉鎖された環境内外との通信なのですが、iptablesを利用してnatサーバを構築することで解決しました。今回はこのiptablesの検証の様子を紹介します。

NATとiptablesについて

今回環境を構築するに当たり、以下を参考にしました。

@IT:第10回 IPパケットの構造とIPフラグメンテーション
@IT:natテーブルを利用したLinuxルータの作成

要点だけ羅列すると以下のような内容になります。
  • パケット内IPヘッダに送信元アドレスと宛先アドレスが記載されている。
  • 送信元アドレスを変換するNATのことをSNAT
    宛先アドレスを書き換えるNATのことをDNATと呼ぶ。
  • iptablesのSNATはPOSTROUTINGチェインで、DNATはPREROUTINGチェインで行う。


検証内容と環境の紹介


存在しないIPアドレス宛てのパケットをiptablesでnatし、別のIPアドレスで受けとることができるか検証しました。検証には以下のような環境を利用しました。

セグメント
  1. A(192.168.100.0/24)
  2. B(192.168.200.0/24)
仮想マシン
  1. vm01           eth0:【 192.168.100.1 】
  2. vm02           eth0:【 192.168.100.2 】 eth1:【 192.168.200.2 】
  3. vm03           eth0:【 192.168.200.3 】
  4. gateway_A   eth0:【 192.168.100.254 】
  5. gateway_B   eth0:【 192.168.200.254 】

eno_2015120901

vm01から存在しないIP【 192.168.100.3 】 宛にパケットを送り、
vm02でNATさせてvm03のIP【 192.168.200.3 】へ届けます。
vm01からするとIP【 192.168.100.3 】 が存在するように見えます。(赤線)
実際はvm02を経由してセグメントBにいるvm03へ届きます。(青線)

eno_2015120902


事前確認

iptablesに余計な設定があると誤作動の原因になるので、一旦どのサーバもクリアしておきます。

# iptables クリア
iptables -F
iptables -F -t nat
# iptables 確認
iptables -L
iptables -L -t nat
# iptables 保存
service iptables save

また、vm02はパケットを転送する必要があるので以下の設定を加えます。
vi /etc/sysctl.conf
#net.ipv4.ip_forward=0
net.ipv4.ip_forward=1

sysctl -p



ステップ 1 : vm01にルーティング追加

手始めにvm01から【 192.168.100.3 】宛のpingを実行し、その様子をvm01とvm02の両方でtcpdumpを使いパケットをキャプチャして確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.1 icmp_seq=2 Destination Host Unreachable
From 192.168.100.1 icmp_seq=3 Destination Host Unreachable
From 192.168.100.1 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:28:24.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:25.003469 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:26.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:28.004484 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46

vm01から【 192.168.100.3 】へパケットを送る場合、同一セグメント内なのでゲートウェイなどは経由せず直接通信することになります。直接通信するためには相手のMACアドレスを知る必要があります。ARPリクエストをブロードキャストへ送信しMACアドレスを探している様子が確認できました。

これを無理やりvm02へ送るようにvm01にスタティックルーティングを追加します。

# vm01
[root@vm01 ~]# ip route add 192.168.100.3 via 192.168.100.2 dev eth0
[root@vm01 ~]# ip route show
192.168.100.3 via 192.168.100.2 dev eth0
192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.1
169.254.0.0/16 dev eth0  scope link  metric 1002
default via 192.168.100.254 dev eth0

再度vm01から【 192.168.100.3 】へpingを実行し確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.2: icmp_seq=2 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=3 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=4 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2 icmp_seq=2 Destination Host Unreachable
From 192.168.100.2 icmp_seq=3 Destination Host Unreachable
From 192.168.100.2 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:48:19.136230 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 1, length 64
13:48:20.135528 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 2, length 64
13:48:21.135501 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 3, length 64
13:48:22.135497 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 4, length 64

【 192.168.100.3 】宛のパケットがvm02へ届くようになりました。



ステップ2 : vm02にDNAT設定を追加

次に存在しない宛先IPアドレス【 192.168.100.3 】をvm03【 192.168.200.3 】変換するため、vm02のiptablesにDNATの設定を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A PREROUTING -d 192.168.100.3 -i eth0 -j DNAT --to-destination 192.168.200.3
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination


vm01から【 192.168.100.3 】へpingを実行し、vm02とvm03で確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.318556 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 1, length 64
13:55:28.318586 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.318498 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 2, length 64
13:55:29.318513 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.318483 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 3, length 64
13:55:30.318496 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.318515 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 4, length 64
13:55:31.318529 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.381794 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.381711 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.381702 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.381730 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

vm03にvm01のPINGが届いていることが確認できました。



ステップ3 : vm02にSNAT(NAPT)設定を追加

vm01から送るpingは無事vm03へ届けることができたのですが、vm03がセグメントAの情報を持っていないため、送信元であるvm01に返信することができません。そのため、vm02へSNAT(NAPT)を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A POSTROUTING -o eth1 -s 192.168.100.0/24  -j MASQUERADE
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  192.168.100.0/24     0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
64 bytes from 192.168.100.3: icmp_seq=1 ttl=63 time=0.235 ms
64 bytes from 192.168.100.3: icmp_seq=2 ttl=63 time=0.164 ms
64 bytes from 192.168.100.3: icmp_seq=3 ttl=63 time=0.164 ms

# vm02
[root@vm02 ~]# tcpdump -i eth0 src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915680 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915507 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915500 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915525 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 4, length 64

# vm02
[root@vm02 ~]# tcpdump -i eth1 src host 192.168.200.2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915714 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915522 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915513 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915546 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.200.2 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
14:04:40.978924 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.978721 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.978718 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.978754 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

無事にpingが返ってくることが確認できました。



まとめ

作成した環境を利用して、どんなサービスが利用できるのかも検証しました。

動作OK : ssh http https dns mysql
動作NG : ftp oracle

ipヘッダ以外でipのやり取りをするものについては正常に動作しないのですが、動作したものについては、特に問題もなく使えることが分かりました。

以上です。何かの役に立てばと思います。
記事検索