マルチテナント・ウェブアプリケーションの実践

9278c3a06b8d8752fb913dea93f959c1?s=47 FUJI Goro
December 09, 2017

 マルチテナント・ウェブアプリケーションの実践

Kibelaのマルチテンシーを解説します。 #railsdm 2017 の資料です。

9278c3a06b8d8752fb913dea93f959c1?s=128

FUJI Goro

December 09, 2017
Tweet

Transcript

  1. Ϛϧνςφϯτɾ΢ΣϒΞϓ Ϧέʔγϣϯͷ࣮ફ #railsdm 2017/12/08 by @__gfx__ (FUJI Goro)

  2. ࣗݾ঺հ • ౻ ޗ࿠ (@__gfx__) • Bit JourneyͰKibelaͱ͍͏৘ใڞ༗πʔϧΛ։ൃத • TypeScript,

    GraphQL, React Native͋ͨΓʹڵຯ͋ Γ • ࠓճ࿩͢ͷ͸Kibelaͷཪଆʹ͍ͭͯ
  3. ͜͜ͰҰݴ: GraphQL͸͍͍ͧ • RESTful API ͷ୅ସͱͯ͠࡞ΒΕͨRPC࢓༷ • RESTfulϥΠΫͳϦιʔεࢦ޲ͱRPCͷܕ҆શੑͷ͍͍ͱ͜ͲΓ • υΩϡϝϯτγεςϜ΋಺แ͍ͯͯ͠API

    console͕࠷ߴͷ࢓্͕Γ • KibelaͷWeb API͸GraphQLΛ࠾༻ • ಺෦API͸ঃʑʹஔ͖׵͑த • ެ։API΋GraphQLʹ͢Δ༧ఆ
  4. Kibela

  5. Kibelaʹ͍ͭͯ

  6. αʔϏεͱͯ͠ͷKibela • BtoBͷSaaS: Software as a Service • BlogʢʹݸਓͷΞΠσΞʣͱWikiʢʹ૊৫ͷͨΊͷ৘ใʣͱ͍ ͏̎ͭͷੑ࣭ͷυΩϡϝϯτΛॻ͚Δͷ͕ಛ௃ͷ৘ใ

    ڞ༗πʔϧ • GitHubޓ׵ͷMarkdown (≒CommonMark)Ͱॻ͚Δ • ڝ߹: Confluence, Google Docs
  7. Ϛϧνςφϯτɾ΢ΣϒΞϓϦ • ࠓճͷςʔϚʮϚϧνςφϯτɾ΢ΣϒΞϓϦ (MTWA)ʯ͸ɺSaaSʹ͓͍ͯ1ͭͷγεςϜͰ෮਺ͷ૊৫ ʢ㲈اۀɾஂମʣͷνʔϜΛಉډͤ͞Δ΢ΣϒΞϓϦέʔ γϣϯ • ͜ͷτʔΫʹ͓͍ͯ͸ʮMTWAͰ͋Δ͜ͱʯΛ୯ʹʮϚ ϧνςφϯγʔʯͱ΋͍͏ •

    Kibela͸ςφϯτΛʮνʔϜʯͱݺͼ૊৫୯Ґͱ͍ͯ͠Δ
  8. BtoBͷ΢ΣϒαʔϏε㲈MTWA • ͜ͷτʔΫͰMTWAͱ͍͏ͱ͖͸ “BtoBͷ΢Σϒ αʔϏε” ͱ΄΅౳Ձ • اۀͳͲͷஂମͷ୯ҐͰ͋Δςφϯτʹ෮਺ͷ ϢʔβʔΞΧ΢ϯτ͕ॴଐ͢Δܥ͸͢΂ͯMTWA •

    গ਺ͷαʔόʔΠϯελϯεͰଟ਺ͷސ٬ʹαʔ ϏεΛఏڙͰ͖ΔͨΊίεύ͕Α͍
  9. Ϛϧνςφϯγʔͷڞ༗Ϩϕϧ • Ҿ༻: Web ΞϓϦέʔγϣϯΛϚϧνςφϯτܕ SaaS ιϦϡʔγϣϯʹม׵͢Δ - IBM https://www.ibm.com/developerworks/jp/cloud/library/cl-multitenantsaas/

    1. ϋʔυ΢ΣΞͱϏδωεϩδοΫͷΈͷڞ༗ 2. (1)ʹՃ͑ͯΞϓϦέʔγϣϯϓϩηεͷڞ༗ 3. (2)ʹՃ͑ͯσʔλϕʔεͷڞ༗
  10. MTWAͷઃܭͱ࣮૷

  11. Ϛϧνςφϯγʔͷઃܭ • ΞΧ΢ϯτͷڞ༗Ϩϕϧͷઃܭ • URLͷ໊લۭؒͷઃܭ • ετϨʔδͷ໊લۭؒͷઃܭ • ໊લۭؒΛอূ͢ΔͨΊͷ࢓૊Έͷઃܭ

  12. Kibela͸ (3) DBͷڞ༗ • ڞ༗Ͱ͖ΔϦιʔε͸͢΂ͯڞ༗ • ʮ࠷΋ޮ཰తͳਅͷϚϧνςφϯγʔʯ • by IBM

    • ͨͩ͠Kibelaͷ৔߹ɺPostgreSQLͷschemaʹ ΑͬͯRDBMSͷ໊લۭؒ͸෼཭͍ͯ͠Δʢޙड़ʣ
  13. MTWAͷΞΧ΢ϯτϞσϧ • Ϛϧνςφϯγʔʹ͓͍ͯΞΧ΢ϯτϞσϧ͸2छྨ͋Δ • αʔϏεશମͰΞΧ΢ϯτΛڞ༗͢Δ • ྫ: GitHub, npmjs.org •

    ςφϯτ͝ͱʹΞΧ΢ϯτΛ࡞੒͢Δ • ྫ: Slack, G Suite, Kibela
  14. GitHubܕ - ΞΧ΢ϯτ͸άϩʔόϧ • GitHubܕ͸ʮਓʯʹϑΥʔΧεͨ͠ΞΧ΢ϯτϞσϧ • ݸਓΞΧ΢ϯτͱ࢓ࣄΞΧ΢ϯτͷ۠ผ͕ᐆດʹͳΓ͕ͪ • GitHubͷ৔߹ɺ࢓ࣄͷ࣮੷Λݸਓͷ࣮੷ʹඥ෇͚΍͍͢ϝ Ϧοτ͸͋Δ

    • ϋϯυϧωʔϜ͔͠ެ։ͯ͠ͳ͍ΞΧ΢ϯτ͕୭͔ͩΘ͔Βͳ ͘ͳΓ͕ͪ • ؅ཧ୆ா…
  15. Slackܕ - ςφϯτ͝ͱʹΞΧ΢ϯτΛ࡞੒ • Ϛϧνςφϯγʔͱͯ͠͸ͪ͜Β͕ඪ४త • ΞΧ΢ϯτ؅ཧ͕ϢʔβʔʹҕͶΒΕͯ൥ࡶʹͳΓ͕ͪ • ྫ: ͍·ௐ΂ͨΒ1passwordͷதʹslack

    account͕20 ݸҎ্͋ͬͨ • G Suite SSOͳͲͰҰݩ؅ཧ͢Ε͹ݸਓ͕ΞΧ΢ϯτ؅ ཧ͢Δඞཁ͸ͳ͘ͳΔ
  16. Kibelaͷܾఆ: Slackܕ • GitHubܕͷʮ୭͔ͩΘ͔Βͳ͍໰୊ʯ͕໽հ͗͢Δ • ͲͷςφϯτͰ΋ಉ໊͡લʢ㲈ຊ໊ʣ͕ڧཁ͞ΕΔ ͷ΋޷·͘͠ͳ͍ͱߟ͑ͨ • झຯ༻ͱ࢓ࣄ༻Ͱ໊લΛม͍͑ͨ͜ͱ͸͋Δ •

    Slackܕ͸Ϣʔβʔʹ෮਺ͷΞΧ΢ϯτ؅ཧΛڧ͍Δ ͜ͱʹͳΔ͕ɺͦ͜͸SSOͰ͋Δఔ౓ղܾͰ͖Δ
  17. URLͷ໊લۭؒ: domain vs path • URLͷ໊લۭؒ: subdomainϨϕϧͰ෼཭ • $team.kibe.la ͱ͍͏υϝΠϯʹνʔϜ໊ΛؚΉܗ

    • ηογϣϯCookie͸νʔϜ͝ͱʹಠཱ͠ɺڞ༗͠ͳ͍ • ϩʔΧϧͰͷ։ൃ͸ϧʔϓόοΫυϝΠϯΛར༻ • $team.lvh.me:3000 ͳͲ
  18. ετϨʔδͷ໊લۭؒ • RDBMS΍KVSͰ͸ςφϯτ͝ͱͷσʔλʹ໊ લۭؒΛ͚ͭͯଞͷςφϯτͷσʔλͱࠞ͡ Βͳ͍Α͏ʹ͢Δ • PostgreSQL, memcached, Redis, Elasticseach,

    S3, CloudFront, temporary files ͳͲετϨʔδʹؔΘΔ͢΂ͯͷ৔໘Ͱ໊લ ۭؒʹΑΔ෼཭͕ඞཁ
  19. RDBMSͷ໊લۭؒ෼཭ • PostgreSQL͸ database - schema - table ͱ͍͏ ֊૚ߏ଄

    • ҟͳΔschema͸ಉ໊͡લͷtableΛ࣋ͯΔ • schemaͷ୳ࡧ͸؀ڥม਺PATHͷΑ͏ʹߦΘΕΔ • ࢀߟ: https://www.postgresql.org/docs/current/static/ddl-schemas.html
  20. PostgreSQLͷschemaͷઃఆ • apartment gemΛར༻ • Rackϛυϧ΢ΣΞͱͯ͠ಈ͖ɺαϒυϝΠϯΛPg schemaͱΈͳ ͯ͠ `SET search_path

    TO $subdomain,public` ͢Δ • subdomain = schema nameͱ͢Δ͜ͱͰɺDB઀ଓͳ͠ʹ search_pathͷઃఆ͕Ͱ͖Δ • ͦͷ͔ΘΓsubdomain (≒team name) ͷϦωʔϜ࣌ʹschema name΋ม͑Δඞཁ͕͋Δ
  21. Schema͕૿͖͑ͯͯ • team͕૿͑Δͱmigrationʹ͕͔͔࣌ؒΔΑ͏ʹ ͳ͖ͬͯͨ • σʔλྔతʹ͸શવେͨ͜͠ͱͳ͍͸͕ͣͩ… • ·ͩಛʹରॲ͢Δ΄ͲͰ͸ͳ͍ͷͰ์ஔ • ͍ͣΕDBΠϯελϯεͷ෼ׂ͕ඞཁ͔΋

  22. ͜͜·Ͱ͸͍͍Μͩ ͜͜·Ͱ͸…

  23. KVS, S3, શจݕࡧΤϯδϯ, etc. • DBͷ໊લۭؒ෼཭ঢ়ଶͱಉظ͠ͳ͍ • search_path͸PgͷίωΫγϣϯ͝ͱͷઃఆ ͷͨΊ •

    ϛυϧ΢ΣΞʹΑͬͯ͸ඞͣ͠΋໊લۭؒΛ αϙʔτ͍ͯ͠ͳ͍
  24. ૉ๿ͳൃ૝ # models/team.rb def self.switch!(name) Apartment::Tenant.switch!(name) MemcachedNamespace.switch!(name) RedisNamespace.switch!(name) AwsS3Namespace.switch!(name) ElasticsearchNamespace.switch!(name)

    end
  25. ݱࡏͷKibela • apartment gem͕ A::T.switch! ͚͔ͩ͠ݟͳ ͍͜ͱ͕͋ͬͯϛεΛ༠ൃ͕ͪͩͬͨ͠ • ϚϧνεϨουԽͰόάΛ༠ൃ͕ͪ͠ #

    models/team.rb def self.switch!(name) Apartment::Tenant.switch!(name) end
  26. Memcached for Rails Cache • namespace͸ϦΫΤετ͝ͱʹҟͳΔͷͰɺProcͱ ໊ͯ͠લۭؒߏஙϧʔνϯΛ౉͢ඞཁ͕͋Δ • Rails.cacheͷૢ࡞ͷͨͼʹຖճnamespace callback

    ͕ݺ͹Εͯจࣈྻߏங͕૸Δ # config/environments/production.rb config.cache_store = :dalli_store, elasticache, { namespace: -> { “k-#{Team.current_subdomain}” }, }
  27. Redis by redis-namespace • Redis.current = Redis::Namespace.new(…) ͕switchͩͬͨ • ͔͠͠redis.rbΛΈΔͱRedis.current͕Ϋϥεม਺ͩʂʂʂ

    • JobQueue͕sidekiqͳͷͰεϨουηʔϑͰͳ͍ͱ͍͚ ͳ͍ • ϞϯΩʔύονΛ͋ͯͯRedis.currentΛ࠶ఆٛ͢Δ͜ͱʹ ͨ͠
  28. monekey_patches/redis.rb class Redis INSTANCE = Redis.new # @return [Redis] def

    self.current RequestStore.fetch(”Redis.current/#{Team.current_subdomain}”) do if Team.current.present? Namespace.new(Team.current_subdomain, redis: INSTANCE) else INSTANCE end end def self.current=(_redis) raise "Don't use Redis.current=" end end
  29. Elasticsearch • શจݕࡧΤϯδϯ • KibelaͰ͸ϚωʔδυαʔϏεΛར༻ • e.g. Amazon Elasticserach Service

    • ༻ޠ: index = DBͷtable, document = row
  30. Elasticsearch namespacing v1 • ౰ॳindexΛteam͝ͱʹ࡞͍ͬͯͨ • e.g “kibela-#{Rails.env}-#{team_id}-blog” • team͕૿͑ΔʹͭΕindexͷ࠶ߏங

    ʢ㲈migrationʣʹ਺࣌ؒʢ௚ۙͩͱ9࣌ؒʣ͔͔ΔΑ ͏ʹ…
  31. Elasticsearch namespacing v2 • Railsͷ̼odel͝ͱʹͨͩ1ͭͷindexΛ࡞੒ • documentͷteam_idͰϑΟϧλϦϯά͢Δ filtered aliasΛ࡞ͬͯࢀর͢ΔΑ͏ʹͨ͠ •

    index࠶ߏங͸ര଎ʢ2࣌ؒʣͰऴΘΔΑ͏ʹ • ͔͠͠ϦΫΤετ͸ܹ྽ʹॏ͘ͳͬͨͷͰௐࠪத
  32. ͦͷଞS3ͳͲ • ౰ॳ͸ subdomain (team name) Ͱ໊લۭؒΛ ࡞͍ͬͯͨ • renameʹରԠ͢ΔͨΊ్தͰ

    team id Ͱ໊લ ۭؒΛ࡞ΔΑ͏ʹมߋ
  33. Analytics • schema͕େྔʹ͋ΔDBʹ෼ੳΫΤϦଧͯͳ ͍໰୊ • team_id Λ෇༩ͨ͠෼ੳDBΛόονͰ࡞੒͢ Δ͜ͱʹͨ͠

  34. Testing • rspecͷbefore/afterͰςφϯτͷsetup/teardownΛ͍ͯͨ͠ Βܹ྽ʹॏ͔ͬͨʢ͋ͨΓ·͑ʣ • before(:suite) / after(:suite) Ͱςφϯτͷ४උΛ͢ΔΑ͏ʹ •

    namepsacingͷςετ͸ͳ͔ͳ͔೉͍͠ • ଞͷςφϯτΛ࡞ͬͯΞΫηεՄೳੑΛςετͨ͠Γ͸͢Δ • Ϛϧνςφϯγʔ x ϚϧνεϨου͸ఘΊ…
  35. ࠓޙͷల։ • ςφϯτΛ·͙ͨΞΧ΢ϯτ৘ใ΋ඞཁͬΆ͍ • ୺຤͝ͱʹॴଐνʔϜ͢΂ͯʹϩάΠϯ͢Δ ͷ͕ඇৗʹ໘౗ͳͨΊ • ϚϧνεϨου x Ϛϧνςφϯτͷςετ

    • ·ͨ͸ϚϧνεϨουΛ׬શʹఘΊΔҰख
  36. ·ͱΊ • Ϛϧνςφϯγʔ͸࢓༷ΛܾΊΔͷ͕େม • URLɾΞΧ΢ϯτɾετϨʔδͳͲߟ͑Δ͜ͱ͕ଟ͍ • ಛʹετϨʔδͷ໊લۭ͕ؒ೉໰Ͱָ͍͠ʢͭΒ͍ʣ • Ϛϧνςφϯγʔʹڵຯ͋Ε͹ @__gfx__

    ʹDM͍ͩ͘͞ • We are hiring!