@m_seki の

I like ruby tooから引っ越し

DockerでdRubyするために数年ぶりにcommitした、いい話

なんでDocker...

不本意な事情でDockerを調べる時期と、RubyKaigiのCFPが重なった。
CFPのころは集中して作業できてたんだけど、旅費のための再演とかでぶっつり中断しちゃってた。

このエントリーはDockerの入門とかそう言うんじゃなくて、中断しがちな自分の作業を再開するときのための日記。

なお、こういう中断しなくてもすむように、スライドスポンサー募集してます!

恒例!スライドに名前を入れる券 2018-2019
The right to put your sticker on my MacBookAir, again.


最初の実験

最初にどんな準備したか忘れちゃったので、もうDocker動くようになったあとのメモ。
rubyのイメージを起動するとirbが動く。

$ docker run -it ruby
irb(main):001:0> 

へー。シェルが動くのかと思ったら、だいたい合ってた。じゃあとりあえずdRubyしてみよう。

irb(main):001:0> require 'drb'
=> true
irb(main):002:0> DRb.start_service(nil, {}); DRb.uri
=> "druby://0b0be9d41935:34963"
irb(main):003:0> 

えー。このURI("druby://0b0be9d41935:34963")は気に入らない気がする。URIを省略したとき、ホスト名を補うからか。

きっとDockerの起動方法とか工夫したらうまくできるんだろけど、カジュアル層にはめんどくさい。

次のターン

こんなユーティリティー作れば良いんだろうな。

def make_drb_uri(port=0)
  name = Socket.getaddrinfo(Socket.gethostname, nil)[0][3]
  "druby://#{name}:#{port}"
end

実際にやってみた。

$ docker run -it ruby
irb(main):001:0> def make_drb_uri(port=0)
irb(main):002:1>   name = Socket.getaddrinfo(Socket.gethostname, nil)[0][3]
irb(main):003:1>   "druby://#{name}:#{port}"
irb(main):004:1> end
=> :make_drb_uri
irb(main):005:0> require 'drb'
=> true
irb(main):006:0> DRb.start_service(make_drb_uri, {}); DRb.uri
=> "druby://172.17.0.2:45267"

IPアドレス表記になったぞ!しめしめ。

数年ぶりのcommit

これを.irbrcに書いてDockerfileでCPすれば良い。ってところまでやって、drb/drb.rb直すべきだなって思ってしまった。
久しぶりに読んだらgetaddrinfo使うなど、どなたかいろいろ直してくださったみたいでどこを改造したらいいかわからなかったけどなんとかなったと思う。
ChangeLogがなくなってから初めてのコミットだったかも。(k0kubunさんをコミッタにコミットしたのはあったか)

でまだそれが含まれたイメージはない(作り方よくしらない)ので.irbrcを使って新しいコードをモンキーパッチで実験。

以下の内容を .irbrc に。

require 'drb/drb'

module DRb
  class DRbTCPSocket
    def self.getservername
      host = Socket::gethostname
      begin
        Socket::getaddrinfo(host, nil,
                                  Socket::AF_UNSPEC,
                                  Socket::SOCK_STREAM,
                                  0,
                                  Socket::AI_PASSIVE)[0][3]
      rescue
        'localhost'
      end
    end

    def self.open_server(uri, config)
      uri = 'druby://:0' unless uri
      host, port, _ = parse_uri(uri)
      config = {:tcp_original_host => host}.update(config)
      if host.size == 0
        host = getservername
        soc = open_server_inaddr_any(host, port)
      else
        soc = TCPServer.open(host, port)
      end
      port = soc.addr[1] if port == 0
      config[:tcp_port] = port
      uri = "druby://#{host}:#{port}"
      self.new(uri, soc, config)
    end
  end
end

Dockefileはこう。

FROM ruby
COPY .irbrc /
$ docker build -t test/ruby .
...
$ docker run -it test/ruby
irb(main):001:0> require 'drb'
=> true
irb(main):002:0> DRb.start_service(nil, {}); DRb.uri
=> "druby://172.17.0.2:39415"

よしよし。きっといつか、.irbrc作らなくてもrubyのイメージを使うだけでよくなるぞ。

最初にやりたかったこと

二つのターミナルでrubyコンテナを起動し、それぞれのirbで対話的にdRubyを動かしていちゃいちゃする。

### Terminal A
$ docker run -it test/ruby
irb(main):001:0> require 'drb'
irb(main):002:0> inbox = {}
irb(main):003:0> DRb.start_service(nil, inbox); DRb.uri
=> "druby://172.17.0.2:33215"
### Terminal B
$ docker run -it test/ruby
irb(main):001:0> require 'drb'
irb(main):002:0> inbox = {}
irb(main):003:0> DRb.start_service(nil, inbox)
irb(main):004:0>DRb.uri
=> "druby://172.17.0.3:35641"
irb(main):005:0> there = DRbObject.new_with_uri('druby://172.17.0.2:33215')
irb(main):006:0> there['greeting'] = 'hello'
=> "hello"
### Terminal A
irb(main):004:0> inbox
=> {"greeting"=>"hello"}
### Terminal B
irb(main):007:0> there['outlet'] = $stdout
=> #<IO:<STDOUT>>
### Terminal A
irb(main):005:0> inbox 
=> {"greeting"=>"hello", "outlet"=>#<DRb::DRbObject:0x000055f1db015248 @uri="druby://172.17.0.3:35641", @ref=47265107100840>}
irb(main):006:0> inbox['outlet'].puts('fire')   
=> nil
### Terminal B (操作するんじゃなくて"fire"が表示されてる)
irb(main):007:0> there['outlet'] = $stdout
=> #<IO:<STDOUT>>
irb(main):008:0> fire

いい話はどこ

ゼロコンフィギユレーションでdRubyできるよー、というのと、久しぶりにコミットしたのと、スライドスポンサー募集しているあたり。

あわせて読みたい

The dRuby Book: Distributed and Parallel Computing with Ruby

The dRuby Book: Distributed and Parallel Computing with Ruby