Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DockerとPythonで理解する TaskQueueサーバー

DockerとPythonで理解する TaskQueueサーバー

https://supporterzcolab.com/event/375/
で話した内容です。

K.Himeno

May 16, 2018
Tweet

More Decks by K.Himeno

Other Decks in Technology

Transcript

  1. アジェンダ • “プロセス”のライフサイクル • コンテナ化 • “イベント”駆動アーキテクチャ ◦ Task Queueサーバーの設計と実装

    ◦ ライブラリやマネージドサービスの紹介 • まとめ サンプルコード:https://github.com/Himenon/TaskQueueLearn ハッシュタグ:#spzcolab
  2. 実行単位 - プロセス - Linuxカーネルからみた処理の単位 - プログラムの実行単位の一つ - ライフライクルをもつ -

    ジョブ - シェルからみた処理の単位 - スレッド - プロセス内の単独の実行フロー
  3. プロセスの状態遷移 プロセス生成 実行待ち状態 スリープ状態 実行状態 ゾンビ状態 プロセス終了 終了処理を呼ぶ イベント発生 イベント待ち

    CPU実行権を失う CPU実行権を得る Ref: [試して理解] の仕組み 〜実験と図解で学ぶとハードウェアの基礎知識 図 04−12
  4. プロセスの生成:Fork 1 親 子 fork プロセス プロセスをforkすると、 - 子プロセス(サブプロセス)が生成される -

    子プロセスは、親のメモリの内容をコピー - コピーオンライト - 親と子は独立している - 通信には、プロセス間通信( IPC: InterProcess Commmunication)を利用する 例)WEBサーバーによる複数リクエストの受付 親のリソース 子のリソース
  5. プロセスの生成:Fork - Cで見る 親 子 fork プロセス // Ref: リスト03-01

    fork.c プログラム 説明用に諸々省きました int main(void) { pid_t ret; // ココは親プロセスしか来ない領域 ret = fork(); // 親プロセスには0, 子プロセスには1, 失敗したら-1 if (ret == -1) { // forkに失敗したときの処理 } if (ret == 0) { // 子プロセスの処理 } else { // 親プロセスの処理 } } Ref: [試して理解] Linuxのしくみ〜実験と図解で学ぶ OSとハードウェアの基礎知識
  6. fork プロセス _exit() プロセスの終わり 親 子 fork プロセス wait 親

    子 ゾンビ化 _exit() メモリ解放 されてない
  7. デバッグコード 標準パッケージを利用 - os.getpid() :現在のpidを取得 - os.getppid():親のpidを取得 右のコードの様にinfo関数を定義。 後で使います import

    os def info(title): template = """ | {title} | -------------------------- | module name : {name} | parent pid : {ppid} | current pid : {pid} """ print(template.format(title=title, name=__name__, ppid=os.getppid(), pid=os.getpid() ))
  8. 検証:forkされていることをpidで確認する // mp001.py from multiprocessing import Process def myfunc(name): info('Sub')

    print('Hello', name) if __name__ == '__main__': info('Main') p = Process(target=myfunc, args=('おけまる',)) p.start() p.join() 出力結果 | Main | -------------------------- | module name : __main__ | parent pid : 110 | current pid : 243 | Sub | -------------------------- | module name : __main__ | parent pid : 243 | current pid : 244 Hello おけまる
  9. 検証:subprocessを実行させない // mp002.py def myfunc(name): info('Sub') print('Hello', name) if __name__

    == '__main__': info('Main') p = Process(target=myfunc, args=('おけまる',)) while True: sleep(1) p.start() p.join() p.start()を実行していないので、プロセスが分岐していな いことがわかります。 じれったいですね。
  10. 検証:ゾンビ化してることを確認する // mp003.py def myfunc(name): info('Sub') print('Hello', name) if __name__

    == '__main__': info('Main') p = Process(target=myfunc, args=('おけまる',)) p.start() while True: sleep(1) p.join() p.join()の手前で止めました。サブプロセスがゾンビ化 (Z)されていることが確認できます。 グレちゃいました。
  11. 検証:joinで終了していることを確認する // mp004.py def myfunc(name): info('Sub') print('Hello', name) if __name__

    == '__main__': info('Main') p = Process(target=myfunc, args=('おけまる',)) p.start() p.join() while True: sleep(1) p.join()を実行すると、サブプロセスが終了していることが 確認できます。 じれったいですね。
  12. スレッドとは? • プロセス内の単独実行フロー • メモリ空間をスレッド単位で共有 ◦ システムリソースの共有 • Kernerl内部ではプロセスとスレッドはほぼ区別が無い •

    コンテキストスイッチの切り替えが高速 • スレッドセーフかどうか考慮する必要がある ◦ 複数のスレッドコンテキストから呼ばれることを前提にして いないライブラリを使用するとバグの温床になる Ref: naoyaのはてなダイアリー マルチスレッドのコンテキスト切り替えに伴うコスト Ref: Wikipedia - Native POSIX Thread Library 親 子 pthread_cre ate プロセス リソース共有
  13. 検証:threadのpidを確認してみる1 // th001.py import threading def myfunc(name): info('Sub') print('Hello', name)

    while True: sleep(1) if __name__ == '__main__': info('Main') for i in range(5): t = threading.Thread(target=myfunc, args=('おはよー!',)) t.start() $ python th001.py | Main | -------------------------- | module name : __main__ | parent pid : 110 | current pid : 619 | Sub | -------------------------- | module name : __main__ | parent pid : 110 | current pid : 619 Hello おはよー! | Sub | -------------------------- | module name : __main__ | parent pid : 110 | current pid : 619 Hello おはよー! 親 Thread 0 Thread 1
  14. ここまでのまとめ • プロセスの状態遷移について学んだ。 - 生成 - 実行待ち状態 - 実行状態 -

    スリープ - ゾンビ化 - 終了 • Pythonでプロセスとスレッドの挙動を確認した。 もっと知りたい人は... Pythonの3系では他にも便利なパッケージ(concurrentとか)がある。 詳しく知りたい人は、公式ドキュメント: 17. 並行実行 を参照
  15. 参考 ウェブサイト • Tech Village - スタックと割り込み -- プログラムが動く仕組みを知ろう •

    naoyaのはてなダイアリー マルチスレッドのコンテキスト切り替えに伴うコスト • Wikipedia - Native POSIX Thread Library • The Python-list Archives - Get thread pid • Python 標準ライブラリ | 17. 並行実行 書籍 • ふつうのLinuxプログラミング 第2版 | 青木 峰郎 著 • [試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識 | 武内覚 著 • 入門 Python 3 | Bill Lubanovic 著、斎藤 康毅 監訳、長尾 高弘 訳 • 新しいLinuxの教科書 | 三宅 英明、大角 祐介 著
  16. まず、仮想化とは? コンテナ型 - Pack: ミドルウェア - Base: ホストOS ホスト型 -

    Pack: OS - Base: ホストOS ハイパーバイザ型 - Pack: サーバー全体 - Base: ハードウェア それぞれ、抽象化の度合い・方法が違う OS Container Runtime VM Process Process Process OS Process Process Container Container Hardware Hypervisor OS OS Hardware OS 非仮想化 コンテナ型仮想化 ホスト型仮想化 ハイパーバイザ型仮想化 非仮想化 OSより上側の仮想化 OS以下の仮想化 Ref: Dockerが注目されている理由を探る - Think IT
  17. コンテナランタイム ランタイムって? コンテナの実行環境・展開・操作(作成・削除)・ パッケージングなど管理に必要な環境を提供する もの OCI (Open Container Initiative) が標準化を進めている

    Ref: Dockerだけじゃないコンテナ runtime徹底比較@makocchi その他のRuntime - Docker - 現在の主流 - Frakti - hypervisor経由で展開 - cri-o - Kubernetes専用 - Containerd - Docker(>=v1.11)に含まれる
  18. HOST OS システムリソース コンテナを実現するためには? - namespaceによる分離 ◦ uts:システム情報(uname系) ◦ ipc:プロセス間通信

    ◦ pid:プロセスの分離、PID制御 ◦ net:ネットワーク制御 ◦ mnt:ファイルシステム ◦ user:uid, gid - cgroups(Control Groups)による分離 ◦ システムリソース(CPU/Memory/Disk/Network) uts ipc pid net mnt user Ref: Think IT - コンテナ技術の基礎知識 システムリソース uts ipc pid net mnt user コンテナ コンテナ
  19. まとめ - コンテナ技術は特殊な技術ではなく、 リソースの名前空間の分離 や、 システムリソースの割当 によって実現されている。 - コンテナ内でミドルウェアや依存関係が独立しているため、開発のしやすさが向上し、 本番と一致されることも可能になっている。

    - ただし、コンテナはホスト OSの1プロセスとして扱われていたり、 他の名前空間に接続しようと思えばでき るので、コンテナ技術を支えている技術に対して無知ではいけない。 - また、近い内にDocker以外のランタイムが台頭してきてもおかしくないので、 その場しのぎの理解だと置いていかれる。 - 理解して使えば、従来よりも開発が楽しくなるので、もっと技術を楽しもう!
  20. コンテナ系の情報収集 たくさん書いてもアレなので、公式や、一次情報に近いメディアを選定 抑えておくべき公式 - Cloud Native Computing Foundation - Kubernetes

    メディア - InfoQ - Containers Content On InfoQ - Publickey - 「Docker / コンテナ / 仮想化」カテゴリの記事一覧 - Think IT - 仮想化/コンテナ 記事一覧 実装やその他、基本的な情報を得るには ... - Linux Containers - Qiita - コンテナ・デザイン・パターンの論文要約 @MahoTakara ※ Linux系の知識を予めつけた上で読むと、理解が進みます。 コミュニティ(connpass / slack) - Kubernetes Meetup Tokyo - Docker Meetup Tokyo - GCPUG - Cloud Native Meetup Tokyo コンテナ技術について、相当熱いです。 常に、connpassの倍率が2〜3倍くらいあります。
  21. どんな場合に使う? • 非同期処理 • 長時間掛かるような処理(サーガ) ◦ メールの送信処理 ◦ PDFの生成(グラフとか) ◦

    スクレイピング ◦ Githubのリポジトリのインポート • 分散処理 ◦ 長期処理を複数のワーカーに任せる • マイクロサービス化 ◦ 疎結合化 ◦ イベント駆動型アーキテクチャ • 結果整合性が許容される場合 次のページからは実装例を紹介します。 learn002: シンプルな実装 learn003: WEBサーバーを挟んだ実装 learn004: pub/subを使った実装 余談: redashの機構
  22. [learn002] シンプルなTask Queue Client Broker Worker ・タスクの登録を行う 例) - CUI

    - WEB Server ・仲介人 例) - Redis - RabbitMQ ・メインロジック 例) - 長期プロセス
  23. [learn003] Flask + Redisで実装するタスクキュー WEB Server Worker Client Broker Store

    【要件】 Clientは /task?number=[整数] へPOSTリクエストを送信し、レスポンスとして resuit_idを受け取 る。次に、Clientは受けとったresult_idを元に、/task?result_id=[受け取った値] へGETリクエ ストを送信し、結果を受け取るまで、ポーリングする。 ※ポーリング:定期的な問い合わせ 8. 結果の問い合わせ ポーリング 1. タスクの登録 2. Queueの登録 3. QueueのPolling 6. 結果の保存 7. 結果の取得 5. タスクの実行 ※矢印は問い合わせの方 向
  24. [learn003]の問題 - ポーリングし続けると、負荷が掛かるのでは? - Server側にCache-controlのmax-ageなどを設定しましょう。 - Client側は、Exponential Backoff(指数関数的後退)などを用いて、リトライ間隔を広げたり、timeoutの設定をしましょ う。 -

    Redisってインメモリじゃなかったっけ?落ちたら終わりでは? - Redisのスナップショット機能を使う手があります。 - 分散処理するときに重複して処理が実行されない? - 重複実行をどこで食い止めるか、は要件次第にもなりますが、キューシステム側(Broker)にトランザクションの機能が あれば、そちらで食い止める事ができます。 - タスクに識別子をもたせ、クライアント側でその識別子を確認することで防ぐことがあります。 - ただ、どうしても信頼性が重要であれば、このアーキテクチャはふさわしくありません。 まだまだ、考えることは山程あります。
  25. 余談:redashの構成 Server Broker Worker Scheduler Result Store Client Redash の構成がこのような形です。

    flask + celeryで構成されています。 Schedulerを入れることで、 cron job(定期実行)が可能なアーキテクチャになっています。
  26. [learn004] Pub/Subについて • Pub/Subはキューを分散させるのが得意 ◦ 自前でチャット機能などが作れるので、試してみると面白い ▪ WEBサーバーをかませる場合は、 Polling方式か、WebSocketを利用する •

    Subscribeするためにマルチスレッド化する必要があるので、なかなか大変 • 【追記】この話はマルチサーバーアーキテクチャの話です(下記リンク) ◦ Subscriber側は同じロジックをためたコンテナをスケールさせてしまうと、メッセージキューが来た時 に同じ処理が走るので、異なる処理を走らせたい + スケールさせたい場合は、前段にロードバラン サーが必要(下図) Publisher Pub/Sub Subscriber (Loadbalancer) Worker Worker Ref: Amazon ElastiCache for Redis を使ったChatアプリの開発
  27. Message Queue • Apache kafka • RabbitMQ • Akka •

    ActiveMQ • NServiceBus • MassTransit Cloud • Cloud Pub/Sub (GCP) • AWS SQS ライブラリやクラウド系サービス Task Queue • Celery • rq • Sidekiq (Ruby) Cloud • Google App Engine • AWS Batch(なんか違う気がする) • Azure : ServiceBus Queue …. とか
  28. さらなる情報:次に何したら良いってい人向け • Kubernetes ◦ オートスケール機能や、死活監視をさせたい場合 ◦ デファクトスタンダード ◦ ただ、一人でやるもんじゃないって、誰かが言ってた(実際そう) •

    コンテナのデザイン・パターンについて知りたい ◦ Qiita - コンテナ・デザイン・パターンの論文要約 @MahoTakara ◦ 実はいつの間にかやってました、みたいなことがよくある • デプロイしたいな〜 ◦ Herokuの無料枠にdockerをデプロイできるのでおすすめです
  29. 参考図書 • ★ エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践 ) • ★

    実践ドメイン駆動設計 (Object Oriented SELECTION) • Redis入門 インメモリKVSによる高速データ管理 • RDB技術者のためのNoSQLガイド • NOSQLの基礎知識 (ビッグデータを活かすデータベース技術 ) • マイクロサービスアーキテクチャ Sam Newman (著), 佐藤 直生 (監修), 木下 哲也 (翻訳)