Fav2mark アプリ開発メモ

5,225 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,225
On SlideShare
0
From Embeds
0
Number of Embeds
3,462
Actions
Shares
0
Downloads
6
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Fav2mark アプリ開発メモ

  1. 1. Pythonでアプリ開発メモ
  2. 2. whoami  ko-zu ◦ Python だいたい ◦ Javascript (web/userscripts) ときどき ◦ C, Java (Android) まれに ◦ twitter @cause_less ◦ http://causeless.hatenablog.jp/ ◦ http://causeless.seesaa.net/
  3. 3. 開発史  前提  ビジネスロジック ◦ OAuth ◦ データストア  並列化 ◦ 非同期フレームワーク ◦ gevent  管理 ◦ タスク管理 ◦ プロセス管理 ◦ サーバー管理
  4. 4. 動機  締め切り間際のコンテスト発見 ◦ このこん http://www.conoha.jp/conocon 6日前  自分が使うもの作ろう ◦ Twitter-はてブ連携の不満部分を実装 ◦ アグリゲーションっぽいことしたい  多少触ったことがあるツールで ◦ 最低限セキュアに書けるライブラリで組む
  5. 5. fav2mark https://fav2mark.usb0.net/ favorite: Twitterのお気に入りツイート  bookmark: はてブのブックマーク  fav → mark ◦ URLを共有できると(自分が)便利! なんのひねりもない…… 1ユーザならcronでも出来る。 貧弱なVPS(openvz)でどこまで捌けるだろうか?
  6. 6. 環境  Python2.6+ ◦ Flask, Requests, OAuthlib, Celery, gevent …  Redis ◦ redis-py  MongoDB ◦ MongoEngine (Google AppEngine風 Mapper)  当然全てOSS ◦ MongoDBはAGPL ◦ ライブラリはAPL/BSD/MIT/Python
  7. 7. 一日目  前提  ビジネスロジック ◦ OAuth → Requests + OAuthLib ◦ データストア → Redis + MongoEngine  並列化 ◦ 非同期フレームワーク ◦ gevent  管理 ◦ タスク管理 ◦ プロセス管理 ◦ サーバー管理
  8. 8. 基本設計  1ページアプリ ◦ ◦ ◦ ◦  OAuthでTwitterアクセス権限をもらう OAuthではてブ書込権限をもらう ちょっとした同期設定 Flaskフレームワークで構築 バックグラウンドタスク ◦ Twitter APIからFavoritesを取得 ◦ URLを抽出してはてブ Atom APIにPost
  9. 9. OAuth?  エンドユーザーからWebサービスの アクセス権(認可)を貰う仕組み ◦ パスワードは渡さない(≠合鍵) ◦ ユーザーは何時でも個別に無効化出来る  ユーザー認証にも使えなくはない ◦ sign in with twitter ◦ でもopenid (=IDを証明) とは別物!
  10. 10. OAuth  OAuthライブラリ多すぎ ◦ python-oauth (EOL?) ◦ python-oauth2 ◦ python-oauthlib ◦ OAuth認可フローはWebと密結合 =フレームワークとのバインディング多数  非標準パラメータ対応のばらつき ◦ はてなAPIのscopeなど
  11. 11. OAuth 危なっかしいライブラリが……  アクセストークン管理 ◦ flask-oauthはaccess tokenをsigned cookieに保存し ているような?  HTTPS証明書検証  セキュリティ上の前提がドキュメントにない ◦ 信頼チェーンとCN/SANsチェックしている? なるべく単機能で疎結合なものがほしい
  12. 12. 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)
  13. 13. 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/'
  14. 14. Redis  オンメモリKey-Value+ Storage  memcachedの置き換えが楽 ◦ セッション・キャッシュ・ロック等に利用 ◦ key長の制限無し ◦ redis-py実装のredis API対応が素直  python-memcachedとは何だったのか……  再起動してもデータが(大抵)消えない  シングルスレッド =トランザクションが低コスト
  15. 15. MongoEngine  ObjectDictMapper? ◦ Django/AppEngineライクなモデル記述  まだ開発途上 (doc) ◦ ネスト要素の取扱が重い ◦ シリアライズ重い ◦ 致命的なバグ・未実装が……  #537 にハマったり  素のpymongoで十分だった気もする
  16. 16. ロジック実装レシピ  requests-oauthlib ◦ 認証周りはシンプルなものを ◦ セキュリティ甘いライブラリ多い  httpはpython-requestsで  ACI(D)はRedisで ◦ redis-clusterが出たら全部Redisにするかも  MongoDBでスキーマレス ◦ Python上のObjectMappingが課題
  17. 17. 二日目  前提  ビジネスロジック ◦ OAuth ◦ データストア  並列化 ◦ 非同期フレームワーク ◦ gevent  管理 ◦ タスク管理 ◦ プロセス管理 ◦ サーバー管理
  18. 18. フロー Twitterから favorites取得  共有済みかDBでチェック  短縮URLを展開  個別にブックマークを生成  ◦ 全てI/O待ちで時間が掛かる…… ◦ 並列化するしかない
  19. 19. 並列化   WebAPI経由のサービス DBクエリ ◦ …CPUはほとんどiowaitしている  プロセス・スレッドを増やす? ◦ メモリ不足 ◦ openvz VPSのプロセス数制限 ◦ CPythonのLock問題 →非同期APIを使う
  20. 20. 非同期化手法 例)Twisted(Asyncフレームワーク doc) フローを(人力で) 「短くてブロックしないコルーチン」 に分割   コールバックで遅延評価 コンテキストはクロージャか引数で維持  コンテキストがわかりづらい  分岐の記述が煩雑  他のライブラリをどう組み込む?
  21. 21. gevent pip install gevent 非同期処理「自動化」ライブラリ (doc/jp)  libev+マイクロスレッド(greenlet)  よく使うAPIのAsync版を提供 ◦ 単一スレッドでイベント待ち ◦ 一応Windowsでも動く ◦ socket I/O ◦ file I/O ◦ process/signal
  22. 22. gevent.monkey.patch_all()  非同期化作業は面倒 ◦ ブロックする処理は決まったAPI ◦ 他の処理はほとんど一瞬 ……なら手作業で遅延評価に書き直す意味は?  同期APIを非同期APIに「パッチ」 ◦ ブロックする代わりに他のタスクに コンテキストスイッチ ◦ 協調的マルチタスクをほぼ自動化
  23. 23. gevent 協調的マルチタスク  同期的プログラミングがそのまま使える ◦ タスク(Greenlet)は普通のPython関数 ◦ 外部ライブラリもほぼ無変更で並列化  greenlet数で並列度は制御可能 ◦ 純イベント駆動と違い未発火イベントの リークやソケット溢れを気にする必要なし  割り込みされない ◦ 重い処理はsleep(0)して分割
  24. 24. 並列化レシピ  from gevent import monkey monkey.patch_all()  greenlet間の排他制御はthreadと同様  あとはタスク管理に任せる
  25. 25. 三日目  前提  ビジネスロジック ◦ OAuth ◦ データストア  並列化 ◦ 非同期フレームワーク ◦ gevent  管理 ◦ タスク管理 → Celery ◦ プロセス管理 → uWSGI ◦ サーバー管理 → Ansible
  26. 26. Celery pip install celery  タスクキュー・メッセージ管理 ◦ 各種バックエンド対応 amqp/redis/RDB  ワーカープロセス管理 (celeryd) ◦ タスクをモジュールとしてロード・実行 ◦ greenletタスクに対応  cronライクジョブ実行 (celerybeat)
  27. 27. Celery  マルチアプリ対応が貧弱 ◦ タスクをモジュール化して同じワーカー プロセスに読ませる……面倒  ログ管理が貧弱 ◦ 安全にlogrotate出来ない ◦ loggingモジュール依存でutf-8が通らない
  28. 28. uWSGI pip install uwsgi  アプリケーション・サーバー ◦ php-fpmみたいなもの ◦ configファイルからWebアプリを起動 ◦ Webアプリ以外の外部deamonも対応  豊富(過ぎる?)アプリ管理機能 ◦ プロセス数管理(thread/greenletも対応) ◦ アプリリロード、ウォームアップ etc
  29. 29. uWSGI  全部uwsgiでプロセス管理 ◦ Flask  アプリのホストとリロード ◦ Celery Worker  タスクモジュールのリロード ◦ redis/mongodb  専用インスタンス ◦ 設定の一元化・アプリ別設定の簡素化
  30. 30. uWSGI  uwsgi本体はC ◦ ビルド・依存解決が面倒  pip (pythonのパッケージマネージャ)は ビルド時に依存Cパッケージ教えてくれない  Pythonバージョン依存 ◦ 別VerのPythonでvirtualenv化できない ◦ uwsgiを複数走らせる or libpython.so を動的リンクするようビルド
  31. 31. 環境管理  自前でプロセスの面倒を見る部分 ◦ uwsgi ◦ nginx  依存関係 ◦ パッケージ管理 ◦ C/Pythonライブラリのビルド ◦ 設定ファイルの統合 etc …少なくなったけど手作業管理するのは厳しい
  32. 32. Ansible pip install ansible  リモートサーバー管理ツール ◦ Chef/Vagrantと違ってデプロイ済みサーバー 管理・維持の簡略化に特化  低レベルのシスオペ向け ◦ sshでコマンドを叩く+α ◦ テスト機構は未実装  別のVPSやVagrantでテストラン  冪等性からの逸脱に寛容 ◦ Changeイベントハンドラがある ◦ 冪等性を担保しないshell command
  33. 33. Ansible  Inventory ◦ サーバーの静的リスト  AWS等の自動追加もできなくないけど…… ◦ 各サーバーの振る舞い(role)を一元管理  Playbook ◦ リモートに流し込むコマンド(task)のリスト  複雑な条件分岐は書きにくい $ ansible –i inventory.ini all –m yum –a name=nginx $ ansible-playbook –i inventory.ini myconfig.yml
  34. 34. Ansible  単純な設定同期・管理は非常に楽 ◦ sshでログイン出来る環境とpython2.5+があれば 始められる  ルール変更や失敗時の追跡が難しい ◦ playbook編集時は差分をチェックして 変更前後の挙動を把握していないとハマる  ドキュメントが分散 ◦ 開発中でAPIがよく変わる ◦ 暗黙の変数やパス展開が多く分かりにくい ◦ テンプレート展開を入れ子に適用していて エスケープに困る
  35. 35. サーバーデプロイ  今のところ手動(VPS)  Ansible+ローカルyum repo ◦ 何故かubuntuでrpm弄ってる……  セットアップ時間の大部分が SSH設定+システムのアップデート  OSテンプレート更新進捗どうですか?@VPSの中の人
  36. 36. 動いた!  約3日 ◦ OAuth対応が一番かかった気がする ◦ cat *.py | wc –l 2000くらい  エラー処理・一貫性維持は簡略化 ◦ Twitter APIが明確なので依存 ◦ 稀に多重POSTしてもAPI的に許容範囲  性能は未検証 ◦ APIのテストはダミー相手 ◦ 100並列 1000qps 程度を想定
  37. 37. ボトルネック?  MongoDB Read ◦ MongoDBレプリケーションで自動化  MongoDB write ◦ 同期済みTweetIDのwriteが増える  Redisに置き換え? or MongoDBシャーディングで対応? (ToDo)  Redis ◦ KVSとして使う限り考慮する必要無さそう  Web側はビジター増えないアプリ  バックエンドはユーザーIDでシャーディング可能
  38. 38. ボトルネック  CeleryのCPU負荷 ◦ Redis側は余裕なので分散 ◦ (デ)シリアライズが重い模様  フル機能が不要であればRedisのリストで もっと軽量に書けそう  ソケット数・バッファメモリ ◦ カーネル共有(openvz)だと厳しい ◦ KVMか専用サーバーへ  CPUが律速 ◦ レイテンシは並列化で対応してCPUが安い所へ
  39. 39. セキュリティ  SSL ◦ Forward Secrecy取得  エンコード・正規化(XSS) ◦ 全てテンプレートエンジン利用  セキュアセッション ◦ Redisで実装  fixation対策他 CSRF ◦ flask_wtf.csrf ベース  セキュリティヘッダ ◦ X-Frame-Options 他  追加対策(ToDo) ◦ サイト識別性向上 ◦ DoS耐性向上
  40. 40. 機能追加 ToDo  もう少しまともなURLフィルタリング ◦ 現状twitter側のsensitive/withheld依存 最新の同期ツイート表示  共有ツイート数カウンタ  ◦ トップにもう少しコンテンツほしい 自動デプロイ  性能テスト・モニタ自動化 
  41. 41. まとめ Requests-OAuthlib便利 geventで簡単並列化 Ansibleでサーバー管理 全部(?) Pythonで! (uwsgi pipで入るし……)
  42. 42. 参考・素材・ライブラリ  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 ◦ ◦  Requests (APL) ◦  http://developer.hatena.ne.jp/ja/license http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth 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
  43. 43. 参考・素材・ライブラリ  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

×