Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
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.8k
How to Boost Your Code with WebAssembly
gfx
2
3.1k
AssemblyScriptでライブラリコードの高速化をしてみる
gfx
5
3.3k
実践TypeScriptトークバトル
gfx
1
1.2k
歴史的経緯の説明 as code
gfx
7
2.9k
Elasticsearchによる 全文検索の実装 in Rails
gfx
6
9.7k
すばらしきGraphQLのSEKAIへようこそ
gfx
20
9.8k
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
社内ワークショップで終わらせない 業務改善AIエージェント開発
lycorptech_jp
PRO
1
340
作るべきものと向き合う - ecspresso 8年間の開発史から学ぶ技術選定 / 技術選定con findy 2026
fujiwara3
4
470
EKSで実践する オブザーバビリティの現在地
honmarkhunt
2
300
既存のログ監視システムをクラウドっぽく実装してみた
tjmtrhs
0
190
「静的解析」だけで終わらせない。 SonarQube の最新機能 × AIで エンジニアの開発生産性を本気で上げる方法
xibuka
2
270
GitHub Copilot CLI 現状確認会議(2026年2月のすがた)
torumakabe
4
600
AIで「ふとした疑問」を即座に検証する 〜定量で圧倒するN1理解〜
kakehashi
PRO
3
730
「技術的にできません」を越えて価値を生み出せ──研究開発チームをPMが率いて生み出した価値創出
hiro93n
1
320
OCI技術資料 : 外部接続 VPN接続 詳細
ocise
1
10k
生成AI素人でも玄人でもない私がセイセイAIチョットワカルために勉強したこと
wkm2
2
310
技術選定 したい人 したくない人
shirayanagiryuji
0
350
使って学ぼう MCP (と GitHub Codespaces)
tsubakimoto_s
1
220
Featured
See All Featured
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
160
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
220
Navigating Weather and Climate Data
rabernat
0
120
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
59
Code Review Best Practice
trishagee
74
20k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.4k
KATA
mclloyd
PRO
35
15k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
620
Navigating Team Friction
lara
192
16k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
95
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
180
Prompt Engineering for Job Search
mfonobong
0
180
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!