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.7k
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
セキュリティについて学ぶ会 / 2026 01 25 Takamatsu WordPress Meetup
rocketmartue
1
270
Data Hubグループ 紹介資料
sansan33
PRO
0
2.7k
2026年、サーバーレスの現在地 -「制約と戦う技術」から「当たり前の実行基盤」へ- /serverless2026
slsops
2
200
顧客との商談議事録をみんなで読んで顧客解像度を上げよう
shibayu36
0
130
データの整合性を保ちたいだけなんだ
shoheimitani
7
2.7k
2026年はチャンキングを極める!
shibuiwilliam
9
1.9k
なぜ今、コスト最適化(倹約)が必要なのか? ~AWSでのコスト最適化の進め方「目的編」~
htan
1
100
仕様書駆動AI開発の実践: Issue→Skill→PRテンプレで 再現性を作る
knishioka
2
560
セキュリティ はじめの一歩
nikinusu
0
1.5k
ZOZOにおけるAI活用の現在 ~開発組織全体での取り組みと試行錯誤~
zozotech
PRO
4
4.7k
クレジットカード決済基盤を支えるSRE - 厳格な監査とSRE運用の両立 (SRE Kaigi 2026)
capytan
6
2.4k
Embedded SREの終わりを設計する 「なんとなく」から計画的な自立支援へ
sansantech
PRO
2
2k
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
97
6.5k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Everyday Curiosity
cassininazir
0
130
Documentation Writing (for coders)
carmenintech
77
5.2k
The Invisible Side of Design
smashingmag
302
51k
Done Done
chrislema
186
16k
From π to Pie charts
rasagy
0
120
Testing 201, or: Great Expectations
jmmastey
46
8k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
440
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
50
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
0
310
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!