Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The introduction of mRuby on Container /mruby-o...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for KONDO Uchio KONDO Uchio
September 08, 2016

The introduction of mRuby on Container /mruby-on-container

@ RubyKaigi 2016, Kyoto
2016/09/08
#container #linux

Avatar for KONDO Uchio

KONDO Uchio

September 08, 2016
Tweet

More Decks by KONDO Uchio

Other Decks in Technology

Transcript

  1. FH*TTVF 8IFOUSZJOH%PDLFSJO%PDLFS VTFGVM GPS*OGSB$*  TPNFQBDLBHFTNBEFUSPVCMFJO JOTUBMMJOH FTQFDJBMMZIUUQE BOE TZTUFNE

     5IJTJTCFDBVTFPMEFSWFSTJPOTPGBVGT CVJMEEPFTO`UTVQQPSUpMFDBQBCJMJUJFT
 #VU*DPVMEOPUEFTDSJCFUIFCVHJO EFUBJMBUUIBUUJNF
  2. 0QFSBUJPOT PQFSBUJPOT w &WFSZEBZIBSEEJTLDMFBOVQPQFSBUJPOʜʜ • docker ps -a | \


    grep Exited | \
 awk '{ print $1 }' | \
 xargs docker rm -f w *ODSFBTJOH-" $16VTBHF NFNPSZʜʜ w *XBTMFTTGBNJMJBSBOEIBEMFTTLOPXIPXTBCPVU%PDLFSPSDPOUBJOFST JOUFSOBM TP*IBETPNFUSPVCMFT
  3. -9$JO4RBMF w -9$MPPLFENPSFMJHIUXFJHIUBOEVOEFSTUBOEBCMFUPNFUIBOEPDLFS w FH w $BOVTFCJOENPVOUSBUIFSUIBOBVGT PSBMJLF  w

    $BODIPPTFOPOFUXPSLJTPMBUJPO QMBZJOHJTPMBUJPOJOTJEFUIFDPOUBJOFS
 TPVOEFSTUBOEBCMFBOEDPOUSPMMBCMFGPSNFBUUIBUUJNF
  4. *OTUBMMBUJPO w 1BDLBHFTBSFBWBJMBCMFJOQBDLBHFDMPVE
 IUUQTQBDLBHFDMPVEJPVE[VSBIBDPOJXBJOTUBMM # example for deb-ish distro curl

    -s https://packagecloud.io/install/repositories/udzura/ haconiwa/script.deb.sh | sudo bash apt-get update apt-get install haconiwa apt-get install lxc lxc-templates # Required to bootstrap fs
  5. 'JSTU DSFBUFUIFDPOpHpMFGSPNCPJMFSQMBUF $ haconiwa new test.haco assign new haconiwa name

    = haconiwa-0491a405 assign rootfs location = /var/lib/haconiwa/0491a405 create test.haco
  6. *OTUBMMTTIE DIBOHFTTIEDPOpH Haconiwa.define do |config|
 #...
 config.provision do |p| p.run_shell

    <<-SHELL apk add --update openssh sed -i 's/#Port.*/Port 2222/' /etc/ssh/sshd_config # NOTE: insecure but an example below sed -i 's/#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config sed -i 's/#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config echo root:r00t | chpasswd ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key SHELL end #... end
  7. $IBOHFlJOJUzDPNNBOEUPTTIE UIFOJOWPLFlSVOz Haconiwa.define do |config| # The container name and

    container's hostname: config.name = "haconiwa-0491a405" # The first process when invoking haconiwa run: # config.init_command = "/bin/bash" # To: config.init_command = %w(/usr/sbin/sshd -D) # And uncomment: config.daemonize!
 #...
 end $ haconiwa run test.haco
  8. .PSFBCPVU%4- w :PVDBODPOpHVSFOBNFTQBDF DHSPVQ DBQBCJMJUZ NPVOUQPJOU MJNJU TFUVJE TFUHJEʜʜWJBIBDPpMF JGOFDFTTBSZ

    TFFDPNNFOUT  w #PJMFSQMBUF`TEFGBVMU w *UVOTIBSFTOBNFTQBDFTVUTJQDQJENOUCVUOFUVTFS w *USFNPVOUTTPNFNPVOUQPJOUTJOBOFXOBNFTQBDF FHQSPD TZT
  9. /BNFTQBDFVOTIBSFUPTFF654OBNFTQBDF puts "Before unshare:" RunCmd.new("sample1").run "uname -a" # This puts

    “localhost” # Then unsure the UTS namespace Namespace.unshare Namespace::CLONE_NEWUTS Procutil.sethostname "rubykaigi.example.org" puts "After new namespace:" RunCmd.new("sample1").run "uname -a" # This puts “rubykaigi.example.org” and parent remains unchanged
  10. DISPPUBOECJOENPVOU w DISPPU  DISPPU   w $IBOHFTUIFSPPUEJSFDUPSZPGUIFDBMMJOHQSPDFTT w

    CJOENPVOU w *TVTFGVMUPVTFTPNFEJSFDUPSJFTPSpMFTGSPNBOPUIFSMPDBUJPO w 0SUPBEETPNFTVCEJSFDUPSJFTFYUSBBUUSJCVUFT FHSFBEPOMZ
  11. /BNFTQBDF DISPPUTNBMMFTUDPOUBJOFS # The smallest container with mruby: Namespace.unshare(Namespace::CLONE_NEWNS|Namespace::CLONE_NEWPID) m

    = Mount.new m.make_private "/" root = "/var/lib/haconiwa/125f23ef" m.bind_mount root, root, readonly: true # To make readonly Dir.chroot root Dir.chdir "/" c = Process.fork { m.mount "proc", "/proc", :type => "proc" Exec.exec "/bin/sh" } puts "Container exited: #{Process.waitpid2(c).inspect}"
  12. DHSPVQT $POUSPM(SPVQT w 5IFGFBUVSFPG-JOVYLFSOFM w 5IJTlQSPWJEFTGPSHSPVQJOHPGUBTLTBOESFTPVSDFUSBDLJOHBOEMJNJUBUJPOTGPS UIPTFHSPVQTz GSPNNBODHSPVQT  

    w 5IJTGFBUVSFGPSIBOEMJOHSFTPVSDFTCZHSPVQTPGUBTLTJTVTFGVMUPNBLF
 lBDPOUBJOFS JOEFFEBHSPVQPGQSPDFTTFT zMPPLTMJLFBOJOEJWJEVBM04
  13. FHDQVTFUMJNJUBUJPOCZDHSPVQ cpuset = Cgroup::CPUSET.new "rubykaigi001" cpuset.cpus = "0-1"; cpuset.mems =

    "0" cpuset.create # Comment out to use full-core cpuset.attach def fib(n); n < 2 ? 1 : fib(n-2) + fib(n-1); end procs = (1..4).to_a.map do Process.fork do loop { fib(rand(1000)) } end end procs.each {|pid| Process.waitpid pid }
  14. FHESPQQJOHUJNFDPOUSPMQSJWJMFHFGSPNlSPPUz # Will be dropped after the process invoke execve(2)

    Capability.drop_bound Capability.from_name("cap_sys_time") exec "/bin/bash" # Then to be new program... root@localhost:~# hacorb mruby/caps.rb # new process below root@localhost:~# date -s 'Thu, 21 Dec 95 14:44:05 JST' date: cannot set date: Operation not permitted # even a root Wed Dec 20 21:44:05 PST 1995 root@localhost:~# date Thu Aug 25 20:40:59 PDT 2016 # unchanged
  15. $PODMVTJPOUIFDPOUBJOFSXFDBMMJT *TPMBUJPOT -JOVYOBNFTQBDF DISPPU CJOENPVOU ʜ -JNJUBUJPOT DHSPVQT DBQBCJMJUJFT SFTPVSDFMJNJU

    ʜ 8JUIUIFTF UIFlQSPDFTTzDBOCFUSFBUFEBTBOl04zCZEFWFMPQFST "OEUIFTFDBOCFDPOUSPMMFECZIBDPOJXB`T3VCZ%4-
  16. ':*0$*TQFDJpDBUJPOT w *UEFpOFTXIBUGFBUVSFTUPCFTVQQPSUFECZ0$*DPOUBJOFS
 FH/BNFTQBDFT $POUSPMHSPVQT 1SPDFTTDPOpHVSBUJPO TVDIBTDBQBCJMJUJFT 6TFS OBNFTQBDFNBQQJOHT TFDDPNQʜʜ

    w IUUQTHJUIVCDPNPQFODPOUBJOFSTSVOUJNFTQFDCMPCNBTUFSDPOpHNE w IUUQTHJUIVCDPNPQFODPOUBJOFSTSVOUJNFTQFDCMPCNBTUFSDPOpHMJOVYNE w #58 0$*TQFDSFGFSTUPMJOVYBOETPMBSJTTQFDJpDDPOpH w *UJTMJLFMZUIBU)BDPOJXBDBOQSPWJEFUIFBCTUSBDUMBZFSGPSSFTPVSDF MJNJUBUJPO OBNFTQBDF pMFTZTUFN CFUXFFOLFSOFMT w *UXJMMCFSFMFBTFEGBSJOUIFGVUVSF NBZCF
  17. 'JSTUJNQMFNFOUBUJPOPG)BDPOJXB w *O$3VCZ w /BNFTQBDFIBOEMJOHCZ,FSOFMTZTDBMM w %JSDISPPUNPVOU  DBMMCZ,FSOFMTZTUFN w

    %PJOHBpMFPQFSBUJPO PQFOSFBEXSJUF GPSDHSPVQ w 6TJOH''*GPSDBQBCJMJUJFT EFQFOEFOUPOMJCDBQ  w "OE1SPDFTTGPSL,FSOFMFYFDUIFHPMEFOUXJO
  18. &TQFDJBMMZ TPNFSFTUSJDUJPOTJOTZTUFNDBMMT w $3VCZJTJOWPLFEJONVMUJUISFBEFENPEF 5IJTJTCFDBVTFPGBUJNFSUISFBEXIJDIJTVTFGVMUPBMNPTUBMMPGMBOHVBHFVTF DBTFT#VUʜʜ [vagrant@udzura ~]$ ruby -e

    'loop { sleep 1 }' & [1] 7560 [vagrant@udzura ~]$ ls -l /proc/7560/task/ total 0 dr-xr-xr-x 6 vagrant vagrant 0 8݄ 25 11:38 7560 dr-xr-xr-x 6 vagrant vagrant 0 8݄ 25 11:38 7561
  19. *OTQFDUJOHNBOBCPVUOBNFTQBDFTZTDBMMT w NBOQBHFPGVOTIBSF   w $-0/&@/&81*% TJODF-JOVY ʜʜ
 $-0/&@/&81*%BVUPNBUJDBMMZJNQMJFT$-0/&@5)3&"%BTXFMMʜʜ


    
 &33034
 &*/7"-$-0/&@5)3&"% $-0/&@4*()"/% PS$-0/&@7.XBTTQFDJpFE JOqBHT BOEUIFDBMMFSJTNVMUJUISFBEFE w IUUQNBOPSHMJOVYNBOQBHFTNBOVOTIBSFIUNM
  20. *OTQFDUJOHNBOBCPVUOBNFTQBDFTZTDBMMT   w NBOQBHFPGTFUOT   w "QSPDFTTNBZOPUCFSFBTTPDJBUFEXJUIBOFXNPVOUOBNFTQBDFJGJUJT NVMUJUISFBEFE

    w "NVMUJUISFBEFEQSPDFTTNBZOPUDIBOHFVTFSOBNFTQBDFXJUITFUOT  w ʜʜ w IUUQNBOPSHMJOVYNBOQBHFTNBOTFUOTIUNM
  21. &YBNQMFNSVCZMJOVYOBNFTQBDF w 8SJUFBTNBMMGVODUJPOXJUIBOBJWFVTFPGTFUOT   w 5IFOBUUBDIUPBNPEVMF static mrb_value mrb_namespace_setns_by_fd(mrb_state

    *mrb, mrb_value self) { mrb_int fileno, nstype; int ret; mrb_get_args(mrb, "ii", &fileno, &nstype); ret = setns((int)fileno, (int)nstype); if (ret < 0) { mrb_sys_fail(mrb, "setns failed"); } return mrb_fixnum_value(ret); } /* ... */ void mrb_mruby_namespace_gem_init(mrb_state *mrb) { struct RClass *namespace; namespace = mrb_define_class(mrb, "Namespace", mrb->object_class); mrb_define_class_method(mrb, namespace, "setns_by_fd", mrb_namespace_setns_by_fd, MRB_ARGS_REQ(2)); }
  22. 'PS3VCZJTI"1*MPPLT KVTUEPXSJUF w *IBWFJNQMFNFOUFEVTFSGSJFOEMZQBSBNFUFSBSHVNFOUTJOUIFSVCZMBZFS class Namespace def self.setns(flag, options) fd

    = options[:fd] pid = options[:pid] if fd setns_by_fd(fd, flag) elsif pid setns_by_pid(pid, flag) else raise ArgumentError, "Options :fd or :pid must be specified" end end end
  23. FHSFTPVSDFMJGFDZDMFNBOBHFNFOU w NSVCZIBT($ w :PVKVTUDBO w CJOEZPVS$PCKFDUUP
 3VCZPCKFDUXIFODSFBUFE w SFHJTUFSUIFGSFFJOHGVODUJPO


    GPS$PCKFDU
 XIFO3VCZPCKFDUJT($`FE static void mrb_cap_context_free(mrb_state *mrb, void *p) { mrb_cap_context *ctx = (mrb_cap_context *)p; cap_free(ctx->cap); mrb_free(mrb, ctx); } static const struct mrb_data_type mrb_cap_context_type = { "mrb_cap_context", mrb_cap_context_free, }; mrb_value mrb_cap_init(mrb_state *mrb, mrb_value self) { mrb_cap_context *cap_ctx; //... DATA_TYPE(self) = &mrb_cap_context_type; DATA_PTR(self) = cap_ctx; //... } &YBNQMFBUNSVCZDBQBCJMJUZ
  24. FHl4USJOHzIBOEMJOHBOETPPO w )VNBOTXBOUFEUPBWPJEEJSFDUMZIBOEMJOHPGDIBS<>`T w NSVCZ`T4USJOHDMBTTQSPWJEFT w 3FTPVSDFIBOEMJOHJO3VCZMBZFS w 4USJOHNPEJpDBUJPOUPPMDIBJOT w

    :PVDBODBMM3VCZ`TQPXFSGVMBOE
 qFYJCMF4USJOHNFUIPETFWFOJO
 $XPSME w 'PSBSSBZIBTIBOEPUIFSPCKFDUT 
 TBNFBTBCPWF mrb_value member; mrb_get_config_value(mrb, "member", "o", &member); mrb_value mnum = mrb_funcall(mrb, member, "size", 0); int i; int seki_num = mrb_fixnum(mnum); for (i = 0; i < seki_num; i++) { mnum = mrb_funcall(mrb, member, "size", 0); mrb_value seki = mrb_funcall(mrb, mrb_top_self(mrb), "rand", 1, mnum); mrb_p(mrb, mrb_funcall(mrb, member, "[]", 1, seki)); mrb_funcall(mrb, member, "delete_at", 1, seki); } 5IFlTFLJHBFNSVCZzDPEF IUUQTHJUIVCDPNNBUTVNPUPSNSVCZDPOpHCMPCNBTUFSFYBNQMFTFLJHBFTFLJHBFD
  25. NSVCZDMJGPSDPNNBOEMJOFUPPM w 7FSZIFMQGVMGPSJNQMFNFOUJOHBDPNNBOE BOEJU`TCJOBSZ  w haconiwaCJOBSZJTDSFBUFEVOEFSUIFNSVCZDMJEJSFDUPSZMBZPVU UIBOLT w *IBWFBEEFETPNFDVTUPNJ[BUJPOTUP3BLFpMF

    w FH w NSVCZWFSTJPOMPDLFSDIFDLTPVUTQFDJpDWFSTJPOPGNSVCZGSPNHJUIVCXIFOCVJMEJOH w NSVCZNSCHFNWFSTJPOEFUFDUPS w POCVJMEJOH HFOFSBUFTBpMFXIJDIIBTBMMPGNSCHFNBOENSVCZWFSTJPOIBTIFT JTQPTTJCMF BOEUIFO FNCFETUIJTUPBDPNNBOE w "OE*IBWFDSFBUFENSVCZBSHUBCMFGPSQBSTJOHBSHVNFOUT  w IUUQTHJUIVCDPNVE[VSBNSVCZBSHUBCMF
  26. ':*UIFDPOUBJOFSNSCHFNT*VTFPSDSFBUF GFBUVSFT DPVOUFSQBSUNSVCZHFN -JOVYOBNFTQBDF mruby-linux-namespace DISPPUBOECJOENPVOU mruby-dir mruby-mount(*) DHSPVQT mruby-cgroup

    -JOVYDBQBCJMJUJFT mruby-capability 3FTPVSDFMJNJUT mruby-resources 0UIFST GPSLFYFDXBJU mruby-process mruby-exec(*) w "DUVBMMZ UIFTFBSFBMSFBEZVTFECZUIFIBDPSCFYBNQMF OPUQVCMJTIFEUPNHFNMJTU
  27. 1SFQBSFDPOUBJOFSCBTFBT Haconiwa.define do |config| suffix = UUID.secure_uuid("%4x%4x") config.name = "haconiwa-#{suffix}"

    config.init_command = ["/bin/sleep", UUID.secure_random(30..60).to_s] config.daemonize! root = Pathname.new("/var/lib/haconiwa/common") config.chroot_to root #... end
  28. 1SFQBSFXBUDIFS%4-BT Haconiwa.watch do |config| config.watch :cluster do |event| if event.cluster.count

    < 5 # Should be spawned 1 by 1 Haconiwa.spawn "/etc/haconiwa/haco.d/sleeper.haco" end end end
  29. *OUIFGVUVSF CFESFBNJFSBOENPSFBNCJUJPVT w &WFOU%4-DBOCFSFHJTUFSFEUPFUDE PSTPNFXIFSFHPPETUPSBHF  w &BDIDPOUBJOFSTVQFSWJTPSNJHIUCFMJOLFEJOBXFBLSFMBUJPOTIJQ 
 BOEUIFOFMFDUUIFMFBEFS

    
 UIFOUIFMFBEFSDBOXBUDIUIFDMVTUFSBOEpSFUIFSFHJTUFSFEFWFOUT w 5IJTMPPLTMJLFBLFSOFMMFTT 410'MFTTBSDIJUFDUVSFUIBUNBZCFXPSLT w #VUOPJNQMFNFOUBUJPOZFUʜ  w *IBWFGPVOENSVCZ$JNQMFNFOUBUJPOPGSBGU TP*`MMUSZOFBSGVUVSF
  30. 4VQFSWJTPS 5IF$POUBJOFS 1SPDFTT FUDE FUDE FUDE 0WFSMBZ/FUXPSLT 4VQFSWJTPS 5IF$POUBJOFS 1SPDFTT

    4VQFSWJTPS 5IF$POUBJOFS 1SPDFTT 47TBSF$MVTUFSECZ3BGU 5IF)PTU "1*BDDFTT
  31. 3FGFSFODFT w 3FGFSFODFT BMMJO+BQBOFTF  w 4RBMFTUBMLPGNJOF w IUUQTTQFBLFSEFDLDPNVE[VSBQIQQBBTTRBMFXP[IJFSVKJTIV w

    $POUBJOFSUVUPSJBMT w IUUQTTQFBLFSEFDLDPNIBZBKPUVLVUVUFYVFCVMJOVYLPOUFOBGBMTFMJDF CZ!IBZBKP  w IUUQTTQFBLFSEFDLDPNUFOGPSXBSEPTDLZPUP CZ!UFOGPSXBSE  w IUUQTTQFBLFSEFDLDPNVE[VSBUIFTLFMUPOPGXIBMFT