Namespace APIを用いた
マルチテナント型Webアプリの実践
2017/03/02(木)
@やっぱり App Engine ja Night #1
The Go gopher was designed by Renee French.
The gopher stickers was made by Takuya Ueda.
Licensed under the Creative Commons 3.0
Attributions license.
自己紹介
メルカリ/ソウゾウ
上田拓也
twitter: @tenntenn
■ コミュニティ活動
Google Cloud Platform User Group (GCPUG) Tokyo
Goビギナーズ
golang.tokyo
Go Conference
■ 業務
GAE/Goでメルカリアッテを作ってます
GoやGCPコミュニティを盛り上げる仕事
Gopherを描く仕事(LINEスタンプ)
2
アッテ開発の技術 : Golang と Google App Engine
アジェンダ
■ 複数アプリにバナーを配信するツールの開発
● バナー配信ツールと必要とされた背景
● 柔軟さと運用のしやすさの実現
● 複数アプリから利用する
● デモ
■ Namespace APIとマルチテナント型アプリ
● Namespace APIとは
● マルチテナント型とは
● マルチテナント型アプリの実現
■ まとめ
4
複数のアプリに
バナーを配信するツールを作る
5
バナー配信ツール
■ 何をするツールか?
● モバイルアプリにバナーを配信する
● バナーの管理や配信条件を設定する
6
バナー配信ツールの要件
■ 複数のアプリから利用する
● メルカリアッテとメルカリから利用
● サービス間で干渉させたくない
● サービスごとの特有の処理は入れない
■ わかりやすいUI
● 非エンジニアが使う
● サービスごとに別な人が入力する
■ デバッグがしやすい
● 個人環境や開発環境が作りやすい
7
柔軟性と運用のしやすさの実現
■ コア部分は柔軟に
● 配信条件は式で表現
● 関数や変数も使えるようにする
■ UIはわかりやすく
● JSON Editorを用いて自動生成
● 複雑な式は入力させず組み合わせる
8
String(os) == "ios"
この話は別の機会に!
複数のアプリから利用する
■ Namespace APIを使う
● Namespaceを分けることで干渉させない
● サービスごとの設定はデータとして保存
○ ソースコードの中には一切書かない
● 管理コンソールのエンドポイントも分ける
9
デモ
10
Namespace APIと
マルチテナント型のWebアプリ
11
Namespace APIとは?
12
■ Namespace API
● Namespaceを分けることのできるAPI
● 利用可能なAPI
○ Datastore, Memcache, Task Queue, Search
■ Namespaceを分ける理由
● Namespace間でデータの干渉を防ぐ
● マルチテナント型のWebアプリを作れる
マルチテナント型のWebアプリとは?
■ マルチテナント型
● 1つのシステムを複数のユーザ(企業など)
に提供する
■ GAEでの実現方法
13
Application
Datastore
NS1 NS2 NS3
Memcache
NS1 NS2 NS3
Namespaceを設定する
■ Contextに埋め込む
■ Datastoreにアクセスする
14
newCtx, err := appengine.Namespace(ctx, ns)
...
err = datastore.Get(newCtx, key, &val)
Contextを差し替えるだけ!
Namespaceを取得する
■ 取得する関数はないのでラップする
15
type keyType string
const key keyType = "namespace"
func WithContext(ctx context.Context, ns string)
(context.Context, error) {
c, err := appengine.Namespace(ctx, ns)
if err != nil {...}
return context.WithValue(c, key, ns), nil
}
func FromContext(ctx context.Context) string {
ns, _ := ctx.Value(key).(string)
return ns
}
ホスト名でNamespaceを切り替える
■ Context付きのハンドラを作る
16
type AEHandler interface {
ServeHTTP(c context.Context,
w http.ResponseWriter,
r *http.Request) error
}
type AEHandlerFunc func(...) error
func (f AEHandlerFunc) ServeHTTP(...) error {
return f(c, w, r)
}
省略
ホスト名でNamespaceを切り替える
■ ミドルウェアでNamespaceを切り替える
17
func WithNS(h AEHandler) AEHandler {
return AEHandlerFunc(func(...){
ns, err := FindNS(c, r.Host)
if err != nil {...}
c, err = WithNamespace(c, ns)
if err != nil {...}
})
}
ホスト名で探す
DatastoreにNamespaceを保存
URLとルーティングルール
■ GAEのURLは以下のようにアクセスできる
● Inst: インスタンス(数値)
● Ver: バージョン
● Serv: サービス
● AppID: アプリケーションID
■ ゆるいルーティング
● インスタンス、バージョン、サービスが存在しないとデ
フォルトのものにルーティングされる
● インスタンスorバージョンの代わりにNamespaceを書く
18
<Inst>-dot-<Ver>-dot-<Serv>-dot-<AppID>.appspot.com
<NS>-dot-<Ver>-dot-<Serv>-dot-<AppID>.appspot.com
管理用APIの分離
■ Namespaceを保存するNamespaceは?
● 管理用のNamespaceは統一しておく
● アプリ全体の共通設定などに使う
○ Namespaceの管理など
■ 管理用のAPIは管理者権限を要求する
● Namespaceの保存などは管理者だけ
● app.yamlで設定しておく
19
- url: /admin/api/ns/.*
script: _go_app
login: admin
secure: always
Cloud Console上での扱い
■ Cloud Datastore
■ Memcahe
20
マルチテナント型にした利点
■ 他のサービス(アプリ)に影響与えない
● 見かけ上別のものになってる
● データが競合しない
■ 開発環境をすぐ作れる
● Namespaceを登録すれば作れる
● 同じサービス(アプリ)内でも別の用途に環境
を提供できる
○ バナーだけじゃなく、他のコンテンツも
21
マルチテナント型にしときの課題
■ Namespace間の設定の移行
● 開発環境を用意する際にほしい
● 移行するAPIを用意する必要がある
■ バグが出ると全滅する
● すべて同じソースコードを使ってる
● バージョンをうまく使って移行する
■ ローカルでのデバッグが面倒
● ローカルのコンソールのNamespaceの対応が貧弱
● MemcacheはNamespaceを指定できない
22
まとめ
■ 柔軟さと運用のしやすさのバランスは大事
● 柔軟でも使いづらかったら意味がない
● 工夫次第で両立することは可能
■ GAEでマルチテナント型のアプリは簡単
● Namespace APIが使える
● バージョン/サービスとうまく組み合わせる
■ GAEで社内ツールを作ると便利
● アクセス制御も簡単
● G Suite のアカウントも使える
23
golang.tokyo ☓ GCPUG
24
4月開催予定@メルカリ
発表者募集中!
Thank you!
twitter: @tenntenn
Qiita: tenntenn
connpass: tenntenn
25

Namespace API を用いたマルチテナント型 Web アプリの実践