Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
マルチテナント・ウェブアプリケーションの実践
Search
FUJI Goro
December 09, 2017
Technology
14
9.7k
マルチテナント・ウェブアプリケーションの実践
Kibelaのマルチテンシーを解説します。 #railsdm 2017 の資料です。
FUJI Goro
December 09, 2017
Tweet
Share
More Decks by FUJI Goro
See All by FUJI Goro
ステートレスなLLMでステートフルなAI agentを作る - YAPC::Fukuoka 2025
gfx
7
1.6k
How to Boost Your Code with WebAssembly
gfx
2
3k
AssemblyScriptでライブラリコードの高速化をしてみる
gfx
5
3.2k
実践TypeScriptトークバトル
gfx
1
1.2k
歴史的経緯の説明 as code
gfx
7
2.9k
Elasticsearchによる 全文検索の実装 in Rails
gfx
6
9.7k
すばらしきGraphQLのSEKAIへようこそ
gfx
20
9.6k
How to choose the ORM on Android
gfx
1
4.4k
How Do We Get Along With Static Types
gfx
5
3.4k
Other Decks in Technology
See All in Technology
LLM-Readyなデータ基盤を高速に構築するためのアジャイルデータモデリングの実例
kashira
0
250
評価駆動開発で不確実性を制御する - MLflow 3が支えるエージェント開発
databricksjapan
1
190
AIと二人三脚で育てた、個人開発アプリグロース術
zozotech
PRO
1
730
プロンプトやエージェントを自動的に作る方法
shibuiwilliam
10
8.6k
EM歴1年10ヶ月のぼくがぶち当たった苦悩とこれからへ向けて
maaaato
0
280
「Managed Instances」と「durable functions」で広がるAWS Lambdaのユースケース
lamaglama39
0
320
寫了幾年 Code,然後呢?軟體工程師必須重新認識的 DevOps
cheng_wei_chen
1
1.4k
MapKitとオープンデータで実現する地図情報の拡張と可視化
zozotech
PRO
1
140
多様なデジタルアイデンティティを攻撃からどうやって守るのか / 20251212
ayokura
0
460
ChatGPTで論⽂は読めるのか
spatial_ai_network
9
28k
AI-DLCを現場にインストールしてみた:プロトタイプ開発で分かったこと・やめたこと
recruitengineers
PRO
2
140
非CUDAの悲哀 〜Claude Code と挑んだ image to 3D “Hunyuan3D”を EVO-X2(Ryzen AI Max+395)で動作させるチャレンジ〜
hawkymisc
2
190
Featured
See All Featured
Reflections from 52 weeks, 52 projects
jeffersonlam
355
21k
Music & Morning Musume
bryan
46
7k
Site-Speed That Sticks
csswizardry
13
1k
Facilitating Awesome Meetings
lara
57
6.7k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Visualization
eitanlees
150
16k
Automating Front-end Workflow
addyosmani
1371
200k
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Embracing the Ebb and Flow
colly
88
4.9k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.3k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
Raft: Consensus for Rubyists
vanstee
141
7.2k
Transcript
ϚϧνςφϯτɾΣϒΞϓ Ϧέʔγϣϯͷ࣮ફ #railsdm 2017/12/08 by @__gfx__ (FUJI Goro)
ࣗݾհ • ౻ ޗ (@__gfx__) • Bit JourneyͰKibelaͱ͍͏ใڞ༗πʔϧΛ։ൃத • TypeScript,
GraphQL, React Native͋ͨΓʹڵຯ͋ Γ • ࠓճ͢ͷKibelaͷཪଆʹ͍ͭͯ
͜͜ͰҰݴ: GraphQL͍͍ͧ • RESTful API ͷସͱͯ͠࡞ΒΕͨRPC༷ • RESTfulϥΠΫͳϦιʔεࢦͱRPCͷܕ҆શੑͷ͍͍ͱ͜ͲΓ • υΩϡϝϯτγεςϜแ͍ͯͯ͠API
console͕࠷ߴͷ্͕Γ • KibelaͷWeb APIGraphQLΛ࠾༻ • ෦APIঃʑʹஔ͖͑த • ެ։APIGraphQLʹ͢Δ༧ఆ
Kibela
Kibelaʹ͍ͭͯ
αʔϏεͱͯ͠ͷKibela • BtoBͷSaaS: Software as a Service • BlogʢʹݸਓͷΞΠσΞʣͱWikiʢʹ৫ͷͨΊͷใʣͱ͍ ͏̎ͭͷੑ࣭ͷυΩϡϝϯτΛॻ͚Δͷ͕ಛͷใ
ڞ༗πʔϧ • GitHubޓͷMarkdown (≒CommonMark)Ͱॻ͚Δ • ڝ߹: Confluence, Google Docs
ϚϧνςφϯτɾΣϒΞϓϦ • ࠓճͷςʔϚʮϚϧνςφϯτɾΣϒΞϓϦ (MTWA)ʯɺSaaSʹ͓͍ͯ1ͭͷγεςϜͰ෮ͷ৫ ʢ㲈اۀɾஂମʣͷνʔϜΛಉډͤ͞ΔΣϒΞϓϦέʔ γϣϯ • ͜ͷτʔΫʹ͓͍ͯʮMTWAͰ͋Δ͜ͱʯΛ୯ʹʮϚ ϧνςφϯγʔʯͱ͍͏ •
KibelaςφϯτΛʮνʔϜʯͱݺͼ৫୯Ґͱ͍ͯ͠Δ
BtoBͷΣϒαʔϏε㲈MTWA • ͜ͷτʔΫͰMTWAͱ͍͏ͱ͖ “BtoBͷΣϒ αʔϏε” ͱ΄΅Ձ • اۀͳͲͷஂମͷ୯ҐͰ͋Δςφϯτʹ෮ͷ ϢʔβʔΞΧϯτ͕ॴଐ͢Δܥͯ͢MTWA •
গͷαʔόʔΠϯελϯεͰଟͷސ٬ʹαʔ ϏεΛఏڙͰ͖ΔͨΊίεύ͕Α͍
Ϛϧνςφϯγʔͷڞ༗Ϩϕϧ • Ҿ༻: Web ΞϓϦέʔγϣϯΛϚϧνςφϯτܕ SaaS ιϦϡʔγϣϯʹม͢Δ - IBM https://www.ibm.com/developerworks/jp/cloud/library/cl-multitenantsaas/
1. ϋʔυΣΞͱϏδωεϩδοΫͷΈͷڞ༗ 2. (1)ʹՃ͑ͯΞϓϦέʔγϣϯϓϩηεͷڞ༗ 3. (2)ʹՃ͑ͯσʔλϕʔεͷڞ༗
MTWAͷઃܭͱ࣮
Ϛϧνςφϯγʔͷઃܭ • ΞΧϯτͷڞ༗Ϩϕϧͷઃܭ • URLͷ໊લۭؒͷઃܭ • ετϨʔδͷ໊લۭؒͷઃܭ • ໊લۭؒΛอূ͢ΔͨΊͷΈͷઃܭ
Kibela (3) DBͷڞ༗ • ڞ༗Ͱ͖ΔϦιʔεͯ͢ڞ༗ • ʮ࠷ޮతͳਅͷϚϧνςφϯγʔʯ • by IBM
• ͨͩ͠Kibelaͷ߹ɺPostgreSQLͷschemaʹ ΑͬͯRDBMSͷ໊લۭ͍ؒͯ͠Δʢޙड़ʣ
MTWAͷΞΧϯτϞσϧ • Ϛϧνςφϯγʔʹ͓͍ͯΞΧϯτϞσϧ2छྨ͋Δ • αʔϏεશମͰΞΧϯτΛڞ༗͢Δ • ྫ: GitHub, npmjs.org •
ςφϯτ͝ͱʹΞΧϯτΛ࡞͢Δ • ྫ: Slack, G Suite, Kibela
GitHubܕ - ΞΧϯτάϩʔόϧ • GitHubܕʮਓʯʹϑΥʔΧεͨ͠ΞΧϯτϞσϧ • ݸਓΞΧϯτͱࣄΞΧϯτͷ۠ผ͕ᐆດʹͳΓ͕ͪ • GitHubͷ߹ɺࣄͷ࣮Λݸਓͷ࣮ʹඥ͚͍͢ϝ Ϧοτ͋Δ
• ϋϯυϧωʔϜ͔͠ެ։ͯ͠ͳ͍ΞΧϯτ͕୭͔ͩΘ͔Βͳ ͘ͳΓ͕ͪ • ཧா…
Slackܕ - ςφϯτ͝ͱʹΞΧϯτΛ࡞ • Ϛϧνςφϯγʔͱͯͪ͜͠Β͕ඪ४త • ΞΧϯτཧ͕ϢʔβʔʹҕͶΒΕͯࡶʹͳΓ͕ͪ • ྫ: ͍·ௐͨΒ1passwordͷதʹslack
account͕20 ݸҎ্͋ͬͨ • G Suite SSOͳͲͰҰݩཧ͢Εݸਓ͕ΞΧϯτ ཧ͢Δඞཁͳ͘ͳΔ
Kibelaͷܾఆ: Slackܕ • GitHubܕͷʮ୭͔ͩΘ͔Βͳ͍ʯ͕հ͗͢Δ • ͲͷςφϯτͰಉ໊͡લʢ㲈ຊ໊ʣ͕ڧཁ͞ΕΔ ͷ·͘͠ͳ͍ͱߟ͑ͨ • झຯ༻ͱࣄ༻Ͱ໊લΛม͍͑ͨ͜ͱ͋Δ •
SlackܕϢʔβʔʹ෮ͷΞΧϯτཧΛڧ͍Δ ͜ͱʹͳΔ͕ɺͦ͜SSOͰ͋ΔఔղܾͰ͖Δ
URLͷ໊લۭؒ: domain vs path • URLͷ໊લۭؒ: subdomainϨϕϧͰ • $team.kibe.la ͱ͍͏υϝΠϯʹνʔϜ໊ΛؚΉܗ
• ηογϣϯCookieνʔϜ͝ͱʹಠཱ͠ɺڞ༗͠ͳ͍ • ϩʔΧϧͰͷ։ൃϧʔϓόοΫυϝΠϯΛར༻ • $team.lvh.me:3000 ͳͲ
ετϨʔδͷ໊લۭؒ • RDBMSKVSͰςφϯτ͝ͱͷσʔλʹ໊ લۭؒΛ͚ͭͯଞͷςφϯτͷσʔλͱࠞ͡ Βͳ͍Α͏ʹ͢Δ • PostgreSQL, memcached, Redis, Elasticseach,
S3, CloudFront, temporary files ͳͲετϨʔδʹؔΘΔͯ͢ͷ໘Ͱ໊લ ۭؒʹΑΔ͕ඞཁ
RDBMSͷ໊લۭؒ • PostgreSQL database - schema - table ͱ͍͏ ֊ߏ
• ҟͳΔschemaಉ໊͡લͷtableΛ࣋ͯΔ • schemaͷ୳ࡧڥมPATHͷΑ͏ʹߦΘΕΔ • ࢀߟ: https://www.postgresql.org/docs/current/static/ddl-schemas.html
PostgreSQLͷschemaͷઃఆ • apartment gemΛར༻ • RackϛυϧΣΞͱͯ͠ಈ͖ɺαϒυϝΠϯΛPg schemaͱΈͳ ͯ͠ `SET search_path
TO $subdomain,public` ͢Δ • subdomain = schema nameͱ͢Δ͜ͱͰɺDBଓͳ͠ʹ search_pathͷઃఆ͕Ͱ͖Δ • ͦͷ͔ΘΓsubdomain (≒team name) ͷϦωʔϜ࣌ʹschema nameม͑Δඞཁ͕͋Δ
Schema͕૿͖͑ͯͯ • team͕૿͑Δͱmigrationʹ͕͔͔࣌ؒΔΑ͏ʹ ͳ͖ͬͯͨ • σʔλྔతʹશવେͨ͜͠ͱͳ͍͕ͣͩ… • ·ͩಛʹରॲ͢Δ΄ͲͰͳ͍ͷͰ์ஔ • ͍ͣΕDBΠϯελϯεͷׂ͕ඞཁ͔
͜͜·Ͱ͍͍Μͩ ͜͜·Ͱ…
KVS, S3, શจݕࡧΤϯδϯ, etc. • DBͷ໊લۭؒঢ়ଶͱಉظ͠ͳ͍ • search_pathPgͷίωΫγϣϯ͝ͱͷઃఆ ͷͨΊ •
ϛυϧΣΞʹΑͬͯඞ໊ͣ͠લۭؒΛ αϙʔτ͍ͯ͠ͳ͍
ૉͳൃ # models/team.rb def self.switch!(name) Apartment::Tenant.switch!(name) MemcachedNamespace.switch!(name) RedisNamespace.switch!(name) AwsS3Namespace.switch!(name) ElasticsearchNamespace.switch!(name)
end
ݱࡏͷKibela • apartment gem͕ A::T.switch! ͚͔ͩ͠ݟͳ ͍͜ͱ͕͋ͬͯϛεΛ༠ൃ͕ͪͩͬͨ͠ • ϚϧνεϨουԽͰόάΛ༠ൃ͕ͪ͠ #
models/team.rb def self.switch!(name) Apartment::Tenant.switch!(name) end
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}” }, }
Redis by redis-namespace • Redis.current = Redis::Namespace.new(…) ͕switchͩͬͨ • ͔͠͠redis.rbΛΈΔͱRedis.current͕Ϋϥεมͩʂʂʂ
• JobQueue͕sidekiqͳͷͰεϨουηʔϑͰͳ͍ͱ͍͚ ͳ͍ • ϞϯΩʔύονΛ͋ͯͯRedis.currentΛ࠶ఆٛ͢Δ͜ͱʹ ͨ͠
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
Elasticsearch • શจݕࡧΤϯδϯ • KibelaͰϚωʔδυαʔϏεΛར༻ • e.g. Amazon Elasticserach Service
• ༻ޠ: index = DBͷtable, document = row
Elasticsearch namespacing v1 • ॳindexΛteam͝ͱʹ࡞͍ͬͯͨ • e.g “kibela-#{Rails.env}-#{team_id}-blog” • team͕૿͑ΔʹͭΕindexͷ࠶ߏங
ʢ㲈migrationʣʹ࣌ؒʢۙͩͱ9࣌ؒʣ͔͔ΔΑ ͏ʹ…
Elasticsearch namespacing v2 • Railsͷ̼odel͝ͱʹͨͩ1ͭͷindexΛ࡞ • documentͷteam_idͰϑΟϧλϦϯά͢Δ filtered aliasΛ࡞ͬͯࢀর͢ΔΑ͏ʹͨ͠ •
index࠶ߏஙരʢ2࣌ؒʣͰऴΘΔΑ͏ʹ • ͔͠͠ϦΫΤετܹʹॏ͘ͳͬͨͷͰௐࠪத
ͦͷଞS3ͳͲ • ॳ subdomain (team name) Ͱ໊લۭؒΛ ࡞͍ͬͯͨ • renameʹରԠ͢ΔͨΊ్தͰ
team id Ͱ໊લ ۭؒΛ࡞ΔΑ͏ʹมߋ
Analytics • schema͕େྔʹ͋ΔDBʹੳΫΤϦଧͯͳ ͍ • team_id Λ༩ͨ͠ੳDBΛόονͰ࡞͢ Δ͜ͱʹͨ͠
Testing • rspecͷbefore/afterͰςφϯτͷsetup/teardownΛ͍ͯͨ͠ Βܹʹॏ͔ͬͨʢ͋ͨΓ·͑ʣ • before(:suite) / after(:suite) Ͱςφϯτͷ४උΛ͢ΔΑ͏ʹ •
namepsacingͷςετͳ͔ͳ͔͍͠ • ଞͷςφϯτΛ࡞ͬͯΞΫηεՄೳੑΛςετͨ͠Γ͢Δ • Ϛϧνςφϯγʔ x ϚϧνεϨουఘΊ…
ࠓޙͷల։ • ςφϯτΛ·͙ͨΞΧϯτใඞཁͬΆ͍ • ͝ͱʹॴଐνʔϜͯ͢ʹϩάΠϯ͢Δ ͷ͕ඇৗʹ໘ͳͨΊ • ϚϧνεϨου x Ϛϧνςφϯτͷςετ
• ·ͨϚϧνεϨουΛશʹఘΊΔҰख
·ͱΊ • Ϛϧνςφϯγʔ༷ΛܾΊΔͷ͕େม • URLɾΞΧϯτɾετϨʔδͳͲߟ͑Δ͜ͱ͕ଟ͍ • ಛʹετϨʔδͷ໊લۭ͕ؒͰָ͍͠ʢͭΒ͍ʣ • Ϛϧνςφϯγʔʹڵຯ͋Ε @__gfx__
ʹDM͍ͩ͘͞ • We are hiring!