Your SlideShare is downloading. ×
0
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Angels And Daemons
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Angels And Daemons

7,593

Published on

Tammer Saleh, …

Tammer Saleh,
tsaleh@thoughtbot.com
Thoughtbot, Inc.

Published in: Technology
5 Comments
14 Likes
Statistics
Notes
  • Impressive presentation of 'Angels And Daemons'. You've shown your credibility on presentation with this slideshow. This one deserves thumbs up. I'm John, owner of www.freeringtones.ws/ . Hope to see more quality slides from you.

    Best wishes.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Great demonstration about the need to innovate company models; how you can represent them succinctly; along with the intent to make advancement initiatives actionable. Superb use of images and obvious to see illustrative samples.
    Anisa
    http://financejedi.com http://healthjedi.com
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Daemons present unique problems, hence copious projects to try to solve those problems. Shameless plug: I wrote Rooster to manage dozens of daemons, and it's working great for us: http://github.com/findchris/rooster/
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hmm it prolly should. I haven't tried it this way. Also try the gem daemonize. RubyYarv seems to have Process.daemonize too, which is cool.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • shouldn't this read:
    STDIN.reopen
    STDOUT.reopen etc....

    Peter Burkholder
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
7,593
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
129
Comments
5
Likes
14
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Angels & Daemons Supporting your rails application with custom daemons Tammer Saleh, tsaleh@thoughtbot.com Thoughtbot, Inc. 1
  • 2. Our Problem • Customer database in Rails • Needed client-side integration -- mail, address book, outlook • LDAP to ActiveRecord Gateway • Uses the ruby-ldapserver gem • Returns results from the DB 2
  • 3. Our Solution Active Rails Record DB LDAP Server Client Server 3
  • 4. LDAP AR Gateway http://thoughtbot.com/projects/ldap-ar-gateway http://svn.thoughtbot.com/ldap-activerecord-gateway/ But how do we make it stick around? 4
  • 5. cups NFSd Mongrel Apache Syslog Cron Samba BackgroundDRb sshd MySQL Memcached FTPd 5
  • 6. What’s a Daemon? Less than gods (OS & kernel), but more than mortals (programs) From the Unix FAQ: A daemon process is usually defined as a background process that does not belong to a terminal session. • No parent process • No terminal • Runs in background 6
  • 7. Daemonizing Fork off and die def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 7
  • 8. Daemonizing Become a session and process group leader def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 8
  • 9. Daemonizing Fork off and Die (again) def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 9
  • 10. Daemonizing Set umask def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 10
  • 11. Daemonizing Change to / def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 11
  • 12. Daemonizing Close inherited files def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 12
  • 13. Daemonizing Reopen stdio def daemonize Kernel.fork and Kernel.exit Process.setsid Kernel.fork and Kernel.exit File.umask 0 Dir.chdir '/' ObjectSpace.each_object(IO) {|io| io.close rescue nil} STDIN.open( '/dev/null') STDOUT.open('/dev/null', 'a') STDERR.open('/dev/null', 'a') end 13
  • 14. Daemonizing (the easy way) require 'daemons' Daemons.daemonize loop { # ..or whatever.. conn = accept_conn() serve(conn) } Daemon gem: http://daemons.rubyforge.org 14
  • 15. Great! Now what? • Interacting with Rails • Starting and stopping your daemon • Ensuring only one is running at a time • Config files • Logging • Security 15
  • 16. Interacting with Rails Load the Rails environment - DB, Models, Libraries, etc require quot;/path/to/config/environment.rbquot; if not defined? RAILS_ROOT raise RuntimeError, quot;Cannot load rails environmentquot; end 16
  • 17. Interacting with Rails Threads and Rails ActiveRecord::Base.allow_concurrency = true Mysql::Error: Lost connection to MySQL server during query: 17
  • 18. Starting & Stopping UNIX Init Scripts #!/usr/bin/env ruby basedir = File.expand_path(File.join(File.dirname(__FILE__), quot;..quot;)) require File.join(basedir, quot;libquot;, quot;serverquot;) case ARGV[0] when quot;startquot;: Server.new(ARGV[1]).start when quot;stopquot;: Server.new(ARGV[1]).stop when quot;restartquot;: Server.new(ARGV[1]).restart else puts quot;Usage: #{File.basename(__FILE__)} {start|stop|restart} argquot; end 18
  • 19. Starting & Stopping OS X Launchd <?xml version=quot;1.0quot; encoding=quot;UTF-8quot;?> <!DOCTYPE plist PUBLIC quot;-//Apple Computer//DTD PLIST 1.0//ENquot; quot;http://www.apple.com/DTDs/PropertyList-1.0.dtdquot;> <plist version=quot;1.0quot;> <dict> <key>My Daemon</key> <string>localhost.etc.rc.command</string> <key>ProgramArguments</key> <array> <string>/path/to/daemon</string> <string>argument1</string> <string>argument2</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> http://developer.apple.com/macosx/launchd.html 19
  • 20. Pid Files class PidFile def initialize(file) @file = file end def pid File.file?(@file) and IO.read(@file) end def remove if self.pid FileUtils.rm @file end end def create File.open(@file, quot;wquot;) { |f| f.write($$) } end end 20
  • 21. Configuration Files rails_dir: /path/to/rails/application/ debug: false # comment complex: part1: <%= puts 'true' %> part2: false @config = YAML.load( ERB.new( File.read(quot;config_filequot;) ).result ).symbolize_keys 21
  • 22. Logging @logger = Logger.new(quot;#{basedir}/log/server.logquot;, 7, 1048576) @logger.level = @config[:debug] ? Logger::DEBUG : Logger::INFO @logger.datetime_format = quot;%H:%M:%Squot; 22
  • 23. Dropping Privileges def become_user(username = 'nobody', chroot = false) user = Etc::getpwnam(username) Dir.chroot(user.dir) and Dir.chdir('/') if chroot Process::initgroups(username, user.gid) Process::Sys::setegid(user.gid) Process::Sys::setgid(user.gid) Process::Sys::setuid(user.uid) end 23
  • 24. Testing • Spawn daemon before tests • Complicated • Broken daemon may not die • Tests interfere with each other 24
  • 25. Testing Mocking makes this much safer require quot;daemonquot; class DaemonTest < Test::Unit::TestCase include Daemon def test_should_take_steps_to_daemonize Kernel.expects(:fork).times(2).returns(true) Kernel.expects(:exit).times(2) Process.expects(:setsid) File.expects(:umask).with(0) Dir.expects(:chdir).with('/') ObjectSpace.each_object(IO) { |io| io.expects(:close) } STDIN.expects( :reopen).with(quot;/dev/nullquot;) STDOUT.expects(:reopen).with(quot;/dev/nullquot;, quot;aquot;) STDERR.expects(:reopen).with(quot;/dev/nullquot;, quot;aquot;) daemonize end end 25
  • 26. Alternatives Rake tasks or ./script/runner • Easy to write • Launch via cron or by hand • Only good for single-shot or periodic tasks • Overlapping execution problem • Crontabs can be a pain 26
  • 27. Alternatives nohup • Simple: just traps HUP signal • Easy to use (“nohup script.rb”) • Not as powerful or configurable 27
  • 28. Alternatives inetd & xinetd • Easily write internet services • Get tcp wrappers for free (usually) • Not very efficient: must spawn application for each connection • Not as portable 28
  • 29. Final Tips • Daemonize as soon in your code • Rescue anything that could fail • logger.debug with vigor 29
  • 30. Where can we go from here? • Other ways of interfacing with your data • WebDAV • SNMP • FTP • System monitoring • Long-running or CPU intensive tasks 30
  • 31. Where can we go from here? • Interfacing with other apps through REST • FTP Highrise • IRC Campfire proxy 31
  • 32. Where can we go from here? Highrise to LDAP proxy http://svn.thoughtbot.com/highrise-ldap-proxy 32
  • 33. Thanks to Thoughtbot.com Brian Candler for the ruby-ldapserver gem http://raa.ruby-lang.org/project/ruby-ldapserver/ 33

×