fav2mark アプリ開発メモ

43

Click here to load reader

Upload: causeless

Post on 22-Jun-2015

2.367 views

Category:

Technology


5 download

TRANSCRIPT

Page 1: Fav2mark アプリ開発メモ

Pythonでアプリ開発メモ

Page 2: Fav2mark アプリ開発メモ

whoami

ko-zu ◦ Python だいたい

◦ Javascript (web/userscripts) ときどき

◦ C, Java (Android) まれに

◦ twitter @cause_less

◦ http://causeless.hatenablog.jp/

◦ http://causeless.seesaa.net/

Page 3: Fav2mark アプリ開発メモ

開発史

前提

ビジネスロジック ◦ OAuth ◦ データストア

並列化 ◦ 非同期フレームワーク

◦ gevent

管理 ◦ タスク管理

◦ プロセス管理

◦ サーバー管理

Page 4: Fav2mark アプリ開発メモ

動機

締め切り間際のコンテスト発見

◦ このこん http://www.conoha.jp/conocon 6日前

自分が使うもの作ろう

◦ Twitter-はてブ連携の不満部分を実装

◦ アグリゲーションっぽいことしたい

多少触ったことがあるツールで

◦ 最低限セキュアに書けるライブラリで組む

Page 5: Fav2mark アプリ開発メモ

fav2mark https://fav2mark.usb0.net/

favorite: Twitterのお気に入りツイート

bookmark: はてブのブックマーク

fav → mark ◦ URLを共有できると(自分が)便利!

なんのひねりもない……

1ユーザならcronでも出来る。

貧弱なVPS(openvz)でどこまで捌けるだろうか?

Page 6: Fav2mark アプリ開発メモ

環境

Python2.6+ ◦ Flask, Requests, OAuthlib, Celery, gevent …

Redis ◦ redis-py

MongoDB ◦ MongoEngine (Google AppEngine風 Mapper)

当然全てOSS ◦ MongoDBはAGPL

◦ ライブラリはAPL/BSD/MIT/Python

Page 7: Fav2mark アプリ開発メモ

一日目

前提

ビジネスロジック ◦ OAuth → Requests + OAuthLib

◦ データストア → Redis + MongoEngine

並列化 ◦ 非同期フレームワーク

◦ gevent

管理 ◦ タスク管理

◦ プロセス管理

◦ サーバー管理

Page 8: Fav2mark アプリ開発メモ

基本設計

1ページアプリ

◦ OAuthでTwitterアクセス権限をもらう

◦ OAuthではてブ書込権限をもらう

◦ ちょっとした同期設定

◦ Flaskフレームワークで構築

バックグラウンドタスク

◦ Twitter APIからFavoritesを取得

◦ URLを抽出してはてブ Atom APIにPost

Page 9: Fav2mark アプリ開発メモ

OAuth?

エンドユーザーからWebサービスの アクセス権(認可)を貰う仕組み

◦ パスワードは渡さない(≠合鍵)

◦ ユーザーは何時でも個別に無効化出来る

ユーザー認証にも使えなくはない

◦ sign in with twitter

◦ でもopenid (=IDを証明) とは別物!

Page 10: Fav2mark アプリ開発メモ

OAuth

OAuthライブラリ多すぎ ◦ python-oauth (EOL?)

◦ python-oauth2

◦ python-oauthlib

◦ OAuth認可フローはWebと密結合

=フレームワークとのバインディング多数

非標準パラメータ対応のばらつき ◦ はてなAPIのscopeなど

Page 11: Fav2mark アプリ開発メモ

OAuth

危なっかしいライブラリが……

アクセストークン管理 ◦ flask-oauthはaccess tokenをsigned cookieに保存しているような?

HTTPS証明書検証 ◦ 信頼チェーンとCN/SANsチェックしている?

セキュリティ上の前提がドキュメントにない なるべく単機能で疎結合なものがほしい

Page 12: Fav2mark アプリ開発メモ

requests-oauthlib

pip install requests-oauthlib

Webフレームワーク非依存

ドキュメントが比較的まとも

HTTPカスタム認証プラグインとして実装

>>> oauth = OAuth1(client_key,

client_secret=client_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, verifier=verifier) >>> r = requests.post(url=access_token_url, auth=oauth)

Page 13: Fav2mark アプリ開発メモ

Requests

pip install requests

多機能HTTPクライアント実装

◦ コネクションプール

◦ HTTPS証明書検証(Mozilla準拠CA内蔵) ◦ doc/en と doc/jp (古いけど)

◦ ヘッダ処理・リダイレクト処理

短縮URL展開も手軽に

>>> res = requests.get(“http://bit.ly/1eb9WfE”) >>> print res.url u'http://fav2mark.usb0.net/'

Page 14: Fav2mark アプリ開発メモ

Redis

オンメモリKey-Value+ Storage ◦ セッション・キャッシュ・ロック等に利用

memcachedの置き換えが楽 ◦ key長の制限無し ◦ redis-py実装のredis API対応が素直

python-memcachedとは何だったのか……

再起動してもデータが(大抵)消えない

シングルスレッド =トランザクションが低コスト

Page 15: Fav2mark アプリ開発メモ

MongoEngine

ObjectDictMapper? ◦ Django/AppEngineライクなモデル記述

まだ開発途上 (doc)

◦ ネスト要素の取扱が重い

◦ シリアライズ重い

◦ 致命的なバグ・未実装が…… #537 にハマったり

素のpymongoで十分だった気もする

Page 16: Fav2mark アプリ開発メモ

ロジック実装レシピ

requests-oauthlib ◦ 認証周りはシンプルなものを ◦ セキュリティ甘いライブラリ多い

httpはpython-requestsで

ACI(D)はRedisで ◦ redis-clusterが出たら全部Redisにするかも

MongoDBでスキーマレス ◦ Python上のObjectMappingが課題

Page 17: Fav2mark アプリ開発メモ

二日目

前提

ビジネスロジック ◦ OAuth ◦ データストア

並列化 ◦ 非同期フレームワーク

◦ gevent

管理 ◦ タスク管理

◦ プロセス管理

◦ サーバー管理

Page 18: Fav2mark アプリ開発メモ

フロー

Twitterから favorites取得

共有済みかDBでチェック

短縮URLを展開

個別にブックマークを生成

◦ 全てI/O待ちで時間が掛かる……

◦ 並列化するしかない

Page 19: Fav2mark アプリ開発メモ

並列化

WebAPI経由のサービス

DBクエリ ◦ …CPUはほとんどiowaitしている

プロセス・スレッドを増やす? ◦ メモリ不足

◦ openvz VPSのプロセス数制限

◦ CPythonのLock問題

→非同期APIを使う

Page 20: Fav2mark アプリ開発メモ

非同期化手法

例)Twisted(Asyncフレームワーク doc) フローを(人力で)

「短くてブロックしないコルーチン」 に分割

コールバックで遅延評価 コンテキストはクロージャか引数で維持

コンテキストがわかりづらい 分岐の記述が煩雑 他のライブラリをどう組み込む?

Page 21: Fav2mark アプリ開発メモ

gevent

pip install gevent 非同期処理「自動化」ライブラリ (doc/jp) libev+マイクロスレッド(greenlet) ◦ 単一スレッドでイベント待ち ◦ 一応Windowsでも動く

よく使うAPIのAsync版を提供 ◦ socket I/O ◦ file I/O ◦ process/signal

Page 22: Fav2mark アプリ開発メモ

gevent.monkey.patch_all()

非同期化作業は面倒 ◦ ブロックする処理は決まったAPI

◦ 他の処理はほとんど一瞬

……なら手作業で遅延評価に書き直す意味は?

同期APIを非同期APIに「パッチ」 ◦ ブロックする代わりに他のタスクに コンテキストスイッチ

◦ 協調的マルチタスクをほぼ自動化

Page 23: Fav2mark アプリ開発メモ

gevent 協調的マルチタスク

同期的プログラミングがそのまま使える

◦ タスク(Greenlet)は普通のPython関数

◦ 外部ライブラリもほぼ無変更で並列化

greenlet数で並列度は制御可能

◦ 純イベント駆動と違い未発火イベントの リークやソケット溢れを気にする必要なし

割り込みされない

◦ 重い処理はsleep(0)して分割

Page 24: Fav2mark アプリ開発メモ

並列化レシピ

from gevent import monkey monkey.patch_all()

greenlet間の排他制御はthreadと同様

あとはタスク管理に任せる

Page 25: Fav2mark アプリ開発メモ

三日目

前提

ビジネスロジック

◦ OAuth

◦ データストア

並列化

◦ 非同期フレームワーク

◦ gevent

管理

◦ タスク管理 → Celery

◦ プロセス管理 → uWSGI

◦ サーバー管理 → Ansible

Page 26: Fav2mark アプリ開発メモ

Celery

pip install celery

タスクキュー・メッセージ管理

◦ 各種バックエンド対応 amqp/redis/RDB

ワーカープロセス管理 (celeryd)

◦ タスクをモジュールとしてロード・実行

◦ greenletタスクに対応

cronライクジョブ実行 (celerybeat)

Page 27: Fav2mark アプリ開発メモ

Celery

マルチアプリ対応が貧弱

◦ タスクをモジュール化して同じワーカープロセスに読ませる……面倒

ログ管理が貧弱

◦ 安全にlogrotate出来ない

◦ loggingモジュール依存でutf-8が通らない

Page 28: Fav2mark アプリ開発メモ

uWSGI

pip install uwsgi

アプリケーション・サーバー

◦ php-fpmみたいなもの

◦ configファイルからWebアプリを起動

◦ Webアプリ以外の外部deamonも対応

豊富(過ぎる?)アプリ管理機能

◦ プロセス数管理(thread/greenletも対応)

◦ アプリリロード、ウォームアップ etc

Page 29: Fav2mark アプリ開発メモ

uWSGI

全部uwsgiでプロセス管理

◦ Flask アプリのホストとリロード

◦ Celery Worker タスクモジュールのリロード

◦ redis/mongodb 専用インスタンス

◦ 設定の一元化・アプリ別設定の簡素化

Page 30: Fav2mark アプリ開発メモ

uWSGI

uwsgi本体はC

◦ ビルド・依存解決が面倒

pip (pythonのパッケージマネージャ)は ビルド時に依存Cパッケージ教えてくれない

Pythonバージョン依存

◦ 別VerのPythonでvirtualenv化できない

◦ uwsgiを複数走らせる or libpython.so を動的リンクするようビルド

Page 31: Fav2mark アプリ開発メモ

環境管理

自前でプロセスの面倒を見る部分 ◦ uwsgi

◦ nginx

依存関係 ◦ パッケージ管理

◦ C/Pythonライブラリのビルド

◦ 設定ファイルの統合 etc

…少なくなったけど手作業管理するのは厳しい

Page 32: Fav2mark アプリ開発メモ

Ansible

pip install ansible リモートサーバー管理ツール ◦ Chef/Vagrantと違ってデプロイ済みサーバー 管理・維持の簡略化に特化

低レベルのシスオペ向け ◦ sshでコマンドを叩く+α ◦ テスト機構は未実装

別のVPSやVagrantでテストラン

冪等性からの逸脱に寛容 ◦ Changeイベントハンドラがある ◦ 冪等性を担保しないshell command

Page 33: Fav2mark アプリ開発メモ

Ansible

Inventory ◦ サーバーの静的リスト AWS等の自動追加もできなくないけど……

◦ 各サーバーの振る舞い(role)を一元管理

Playbook ◦ リモートに流し込むコマンド(task)のリスト 複雑な条件分岐は書きにくい

$ ansible –i inventory.ini all –m yum –a name=nginx $ ansible-playbook –i inventory.ini myconfig.yml

Page 34: Fav2mark アプリ開発メモ

Ansible

単純な設定同期・管理は非常に楽 ◦ sshでログイン出来る環境とpython2.5+があれば 始められる

ルール変更や失敗時の追跡が難しい ◦ playbook編集時は差分をチェックして 変更前後の挙動を把握していないとハマる

ドキュメントが分散 ◦ 開発中でAPIがよく変わる ◦ 暗黙の変数やパス展開が多く分かりにくい ◦ テンプレート展開を入れ子に適用していて エスケープに困る

Page 35: Fav2mark アプリ開発メモ

サーバーデプロイ

今のところ手動(VPS)

Ansible+ローカルyum repo

◦ 何故かubuntuでrpm弄ってる……

セットアップ時間の大部分が SSH設定+システムのアップデート

OSテンプレート更新進捗どうですか?@VPSの中の人

Page 36: Fav2mark アプリ開発メモ

動いた!

約3日 ◦ OAuth対応が一番かかった気がする ◦ cat *.py | wc –l 2000くらい

エラー処理・一貫性維持は簡略化 ◦ Twitter APIが明確なので依存 ◦ 稀に多重POSTしてもAPI的に許容範囲

性能は未検証 ◦ APIのテストはダミー相手 ◦ 100並列 1000qps 程度を想定

Page 37: Fav2mark アプリ開発メモ

ボトルネック?

MongoDB Read ◦ MongoDBレプリケーションで自動化

MongoDB write ◦ 同期済みTweetIDのwriteが増える Redisに置き換え?

or MongoDBシャーディングで対応? (ToDo)

Redis ◦ KVSとして使う限り考慮する必要無さそう Web側はビジター増えないアプリ バックエンドはユーザーIDでシャーディング可能

Page 38: Fav2mark アプリ開発メモ

ボトルネック

CeleryのCPU負荷 ◦ Redis側は余裕なので分散

◦ (デ)シリアライズが重い模様 フル機能が不要であればRedisのリストで もっと軽量に書けそう

ソケット数・バッファメモリ ◦ カーネル共有(openvz)だと厳しい

◦ KVMか専用サーバーへ

CPUが律速 ◦ レイテンシは並列化で対応してCPUが安い所へ

Page 39: Fav2mark アプリ開発メモ

セキュリティ

SSL ◦ Forward Secrecy取得

エンコード・正規化(XSS) ◦ 全てテンプレートエンジン利用

セキュアセッション ◦ Redisで実装 fixation対策他

CSRF ◦ flask_wtf.csrf ベース

セキュリティヘッダ ◦ X-Frame-Options 他

追加対策(ToDo) ◦ サイト識別性向上

◦ DoS耐性向上

Page 40: Fav2mark アプリ開発メモ

機能追加 ToDo

もう少しまともなURLフィルタリング ◦ 現状twitter側のsensitive/withheld依存

最新の同期ツイート表示

共有ツイート数カウンタ ◦ トップにもう少しコンテンツほしい

自動デプロイ

性能テスト・モニタ自動化

Page 41: Fav2mark アプリ開発メモ

まとめ

Requests-OAuthlib便利

geventで簡単並列化

Ansibleでサーバー管理

全部(?) Pythonで! (uwsgi pipで入るし……)

Page 42: Fav2mark アプリ開発メモ

参考・素材・ライブラリ OAuth

◦ http://oauth.net/

◦ http://wiki.oauth.net/w/page/12238520/Logo ( CC-BY-SA3.0, Chris Messina)

Twitter API

◦ https://dev.twitter.com/

◦ https://dev.twitter.com/terms/api-terms

はてな API

◦ http://developer.hatena.ne.jp/ja/license

◦ http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth

Requests (APL)

◦ http://docs.python-requests.org/en/latest/

Requests-oauthlib (MIT)

◦ https://github.com/requests/requests-oauthlib

◦ http://requests-oauthlib.readthedocs.org/en/latest/oauth1_workflow.html

MongoDB (AGPL)

◦ http://www.mongodb.org/

◦ MongoEngine (MIT) http://mongoengine.org/

Redis (BSD)

◦ http://redis.io/

◦ https://github.com/antirez/redis-io

◦ Redis-py (MIT) https://github.com/andymccurdy/redis-py

Page 43: Fav2mark アプリ開発メモ

参考・素材・ライブラリ Flask (BSD)

◦ http://flask.pocoo.org/

◦ http://flask.pocoo.org/snippets/75/

◦ flask-wtf https://flask-wtf.readthedocs.org/en/latest/

Gevent (MIT) ◦ http://www.gevent.org/

◦ http://methane.github.io/gevent-tutorial-ja/

Celery (BSD) ◦ http://www.celeryproject.org/

◦ http://docs.celeryproject.org/en/latest/userguide/workers.html

uWSGI (GPLv2) ◦ https://github.com/unbit/uwsgi

◦ http://uwsgi-docs.readthedocs.org/en/latest/

◦ http://uwsgi-docs.readthedocs.org/en/latest/AttachingDaemons.html

Ansible (GPLv3) ◦ http://www.ansibleworks.com/

◦ https://github.com/ansible/ansible