Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Fav2mark アプリ開発メモ

7,086 views

Published on

Published in: Technology
  • Be the first to comment

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

×