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

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

KONDO Uchio
September 08, 2016

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

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

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