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

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

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

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

3a206ec7df76023cca167a8886b69672?s=128

K.Himeno

May 16, 2018
Tweet

More Decks by K.Himeno

Other Decks in Technology

Transcript

  1. DockerとPythonで理解する TaskQueueサーバー 2018/05/16@サポーターズColab 19:30 - 21:30 Twitter@himenoglyph

  2. プレゼンター 姫野滉盛 Twitter@himenoglyph Github@Himenon 4月に退職して 5月はニート 6月からまた働きます

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

    ◦ ライブラリやマネージドサービスの紹介 • まとめ サンプルコード:https://github.com/Himenon/TaskQueueLearn ハッシュタグ:#spzcolab
  4. 目的の整理 • アプリケーションのエンジニアは、コンテナ技術とは何かを把握した上で利用する、 どこまでが境界なのかを検討する。 • イベント駆動型のアーキテクチャとは何なのかを知る。 • Task Queueサーバーの実装パターンを学ぶ。 •

    DockerとPythonを使って、Task Queue Serverの作り方を学ぶ。 • OSSやマネージドサービスを確認する。
  5. ミスコミュニケーション Ref: Software Design 2018年5月号 試して理解Linuxのしくみ(第1回)

  6. プロセスの ライフサイクル

  7. 実行単位 - プロセス - Linuxカーネルからみた処理の単位 - プログラムの実行単位の一つ - ライフライクルをもつ -

    ジョブ - シェルからみた処理の単位 - スレッド - プロセス内の単独の実行フロー
  8. ps 現在動作しているプロセス を表示するコマンド $ ps PID TTY TIME CMD 1853

    pts/0 00:00:00 ps 32712 pts/0 00:00:00 bash
  9. プロセスとOSの関係 関数 システムコール Ref: [試して理解] の仕組み 〜実験と図解で学ぶとハードウェアの基礎知識 図 02−01 ユーザープログラム OS外ライブラリ

    OSライブラリ カーネル ハードウェア ユーザーモード カーネルーモード
  10. プロセスの状態遷移 プロセス生成 実行待ち状態 スリープ状態 実行状態 ゾンビ状態 プロセス終了 終了処理を呼ぶ イベント発生 イベント待ち

    CPU実行権を失う CPU実行権を得る Ref: [試して理解] の仕組み 〜実験と図解で学ぶとハードウェアの基礎知識 図 04−12
  11. 複数プロセスと論理CPU1個 コンテキストスイッチ - プロセス0からプロセス1にスイッチするときの過程 - 複数のプロセスが1つのCPUを共有するための仕組み プロセス0 プロセス1 論理CPU プロセス0

    プロセス1 プロセス0 プロセス0 プロセス1 実行状態 スリープ状態 実行待ち状態 アイドル状態
  12. プロセスの生成:Fork 1 親 子 fork プロセス プロセスをforkすると、 - 子プロセス(サブプロセス)が生成される -

    子プロセスは、親のメモリの内容をコピー - コピーオンライト - 親と子は独立している - 通信には、プロセス間通信( IPC: InterProcess Commmunication)を利用する 例)WEBサーバーによる複数リクエストの受付 親のリソース 子のリソース
  13. プロセスの生成: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とハードウェアの基礎知識
  14. fork プロセス _exit() プロセスの終わり 親 子 fork プロセス wait 親

    子 ゾンビ化 _exit() メモリ解放 されてない
  15. Pythonで検証する 本章のサンプルコードは Learn001 Python 3.6で検証

  16. htop プロセスの状態をインタラクティブに確認するコマンド topコマンドの上位版 https://hisham.hm/htop/ Ubuntu Manpage: htop

  17. デバッグコード 標準パッケージを利用 - 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() ))
  18. 検証: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 おけまる
  19. 検証: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()を実行していないので、プロセスが分岐していな いことがわかります。 じれったいですね。
  20. 検証:ゾンビ化してることを確認する // 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)されていることが確認できます。 グレちゃいました。
  21. 検証: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()を実行すると、サブプロセスが終了していることが 確認できます。 じれったいですね。
  22. スレッドとは? • プロセス内の単独実行フロー • メモリ空間をスレッド単位で共有 ◦ システムリソースの共有 • Kernerl内部ではプロセスとスレッドはほぼ区別が無い •

    コンテキストスイッチの切り替えが高速 • スレッドセーフかどうか考慮する必要がある ◦ 複数のスレッドコンテキストから呼ばれることを前提にして いないライブラリを使用するとバグの温床になる Ref: naoyaのはてなダイアリー マルチスレッドのコンテキスト切り替えに伴うコスト Ref: Wikipedia - Native POSIX Thread Library 親 子 pthread_cre ate プロセス リソース共有
  23. 検証: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
  24. 検証:threadのpidを確認してみる2 ps auxHで確認すると、全部同じPID。ただし、psのPID はthread数を考慮したものになっている。 ※Hオプションはスレッドをプロセスの様に表示 htopコマンドで確認すると、PIDが変わっているように見 える。 TGID(Thread Group ID)は同じ

    → htopのPIDは信じないほうがいい(誤解を招く) Ref: Get thread pid (ミスコミュニケーションが見れます)
  25. プロセスとスレッドの関係 Memory System Resource thread1() thread2() thread3() Process Process System

  26. ここまでのまとめ • プロセスの状態遷移について学んだ。 - 生成 - 実行待ち状態 - 実行状態 -

    スリープ - ゾンビ化 - 終了 • Pythonでプロセスとスレッドの挙動を確認した。 もっと知りたい人は... Pythonの3系では他にも便利なパッケージ(concurrentとか)がある。 詳しく知りたい人は、公式ドキュメント: 17. 並行実行 を参照
  27. 参考 ウェブサイト • 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の教科書 | 三宅 英明、大角 祐介 著
  28. コンテナとは?

  29. まず、仮想化とは? コンテナ型 - 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
  30. コンテナランタイム ランタイムって? コンテナの実行環境・展開・操作(作成・削除)・ パッケージングなど管理に必要な環境を提供する もの OCI (Open Container Initiative) が標準化を進めている

    Ref: Dockerだけじゃないコンテナ runtime徹底比較@makocchi その他のRuntime - Docker - 現在の主流 - Frakti - hypervisor経由で展開 - cri-o - Kubernetes専用 - Containerd - Docker(>=v1.11)に含まれる
  31. 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 コンテナ コンテナ
  32. コンテナ化によって達成すること The Twelve-Factor Appに収束します。

  33. “コンテナ”の検証 本章のサンプルコードは learn000 以下

  34. Dockerのコンテナを”プロセス”の観点から検証 目標:コンテナは名前空間で区切られたプロセスであること確認する。 検証1 ホストからコンテナのプロセスを確認する 検証2 他のコンテナのpid空間に参加する

  35. 便利なコマンド

  36. pgrep プロセス名からpidを検索する (ps + grepのようなコマンド) https://hisham.hm/htop/

  37. pstree プロセス分岐を表示 https://hisham.hm/htop/

  38. 検証環境

  39. Vagrantで検証する場合 ゲストOS(Ubuntu)上にDockerをインストールし、その上で検証。 macOS Docker Engine Vagrant (Ubuntu) Process Container

  40. Dockerのコンテナを”プロセス”の観点から検証 目標:コンテナは名前空間で区切られたプロセスであること確認する。 検証1 ホストからコンテナのプロセスを確認する 検証2 他のコンテナのpid空間に参加する

  41. 検証1:ホストからコンテナのプロセスを確認する コンテナはホストOSから見て”プロセス”か、実際に確認してみる。 予め、redis-serverとpythonのループプロセスのコンテナを起動しておきます。 Docker Engine ホストOS Process Container 同格? Process

  42. 検証1:結果 ホストOSからpstree $(pgrep dockerd)を実行することで、 dockerdからプロセスがforkされていることが確認できます。

  43. Dockerのコンテナを”プロセス”の観点から検証 目標:コンテナは名前空間で区切られたプロセスであること確認する。 検証1 ホストからコンテナのプロセスを確認する 検証2 他のコンテナのpid空間に参加する

  44. 同一のpid空間 検証2:他のコンテナのpid空間に参加する コンテナのPID空間に--pid引数を利用して、参加します。 Ref: https://docs.docker.com/engine/reference/run/#pid-settings---pid my-redis inspctor $ docker run

    --name my-redis -d redis $ docker run --pid=container:my-redis inspector bash
  45. 検証2:結果 inspector側からps -auxを実行し、pid=1がredis-serverであることを確認。 strace -p 1を実行することで、redis-serverのシステムコールが確認できる。

  46. 補足1:Docker for Macだと確認できない? (そもそもdockerdが見つからない...バグ?)

  47. 補足1:薄いVMがいる HyperkitというVMがmacOSとの間にいます。 https://github.com/moby/hyperkit macOS Docker Engine Hyperkit (VM) Process Container

    Preference > Advancedにリソースの設定がある のは、Hyperkit用です。
  48. 補足1:Macからコンテナのプロセスを確認する screenコマンドでHyperkitのttyにattachして、プロセスを確認することができます Ref: https://stackoverflow.com/questions/39739560/how-to-access-the-vm-created-by-dockers-hyperkit $ screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty $ pstree $(pgrep

    dockerd)
  49. 補足2:htopをhostから使う Htopコマンドをホスト側で使って、dockerdで絞り込めば、 常にプロセスを監視できるようになります。

  50. まとめ - コンテナ技術は特殊な技術ではなく、 リソースの名前空間の分離 や、 システムリソースの割当 によって実現されている。 - コンテナ内でミドルウェアや依存関係が独立しているため、開発のしやすさが向上し、 本番と一致されることも可能になっている。

    - ただし、コンテナはホスト OSの1プロセスとして扱われていたり、 他の名前空間に接続しようと思えばでき るので、コンテナ技術を支えている技術に対して無知ではいけない。 - また、近い内にDocker以外のランタイムが台頭してきてもおかしくないので、 その場しのぎの理解だと置いていかれる。 - 理解して使えば、従来よりも開発が楽しくなるので、もっと技術を楽しもう!
  51. コンテナ系の情報収集 たくさん書いてもアレなので、公式や、一次情報に近いメディアを選定 抑えておくべき公式 - 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倍くらいあります。
  52. PythonでDockerっぽいもの作りたい人向け • Fewbytes/rubber-docker ◦ 掲載されているプレゼン資料も面白いです。

  53. イベント駆動 アーキテクチャ

  54. イベント駆動? アーキテクチャ形式 • タスクキュー ◦ キューに登録されたタスクを逐次実行していく。 • メッセージキュー ◦ 送信側と受信側が直接通信せずに、データを伝達する。

    ◦ Pub/Subメッセージングモデル(出版・購読モデル) どちらも、非同期処理を実装する際に通る道。
  55. 非同期処理とは? 例えば、ボタンを押した時に長い処理が走っている間、次の操作ができなかったらつらい。 → 到達確認が必要なプロトコルな場合、処理している間は同期通信している。(TCP/IPのスリーハンドシェイク) ※図はイメージです

  56. タスクキューとは? タスク:分散させたい処理(ジョブとも言う) キュー:待ち行列のデータ構造 task 6 task 5 task 4 task

    3 task 2 task 1 Worker Worker Worker Client タスクキュー
  57. どんな場合に使う? • 非同期処理 • 長時間掛かるような処理(サーガ) ◦ メールの送信処理 ◦ PDFの生成(グラフとか) ◦

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

    - WEB Server ・仲介人 例) - Redis - RabbitMQ ・メインロジック 例) - 長期プロセス
  59. [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. タスクの実行 ※矢印は問い合わせの方 向
  60. [learn003] Flask + Redisで実装するタスクキュー

  61. [learn003]の問題 - ポーリングし続けると、負荷が掛かるのでは? - Server側にCache-controlのmax-ageなどを設定しましょう。 - Client側は、Exponential Backoff(指数関数的後退)などを用いて、リトライ間隔を広げたり、timeoutの設定をしましょ う。 -

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

    flask + celeryで構成されています。 Schedulerを入れることで、 cron job(定期実行)が可能なアーキテクチャになっています。
  63. [learn004] pub/sub機能を使った例 Broker Subscriber Publisher 1. Publisher(出版社)はメッセージブローカーにメッセージキューを送信 2. Subscriberは購読しているPublisherのメッセージキューを受け取る Subscriber

    Subscriber Publisher Publisher
  64. [learn004] いつ使うの? Ref: https://cloud.google.com/pubsub

  65. [learn004] Pub/Subについて • Pub/Subはキューを分散させるのが得意 ◦ 自前でチャット機能などが作れるので、試してみると面白い ▪ WEBサーバーをかませる場合は、 Polling方式か、WebSocketを利用する •

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

  67. 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 …. とか
  68. なんだ、フレームワーク使えばいいじゃない そんなことはない。 • (コンテナの)サービス間の連携は常に同一言語とは限らない ◦ 言語に依存してしまうフレームワークもある • フレームワークやサービスが隠蔽してくれているだけで、無くなったわけではないから。 • 開発に使うプロトコルはいつも同じものとは限らない。それに

    (絶対)増える。 ◦ RCP、AMQP、TCP/IP、UDP、gRPC …. その他もろもろ • 現場の技術選定にも依存するので、常に自分の知ってる技術を使っているとは限らない。
  69. まとめ • イベント駆動アーキテクチャを見た • 結果整合性を許容するようなシステム設計にすることで、プロセスを分離し、疎結 合化できることを学んだ。 • コンテナを使うことで、設計したアーキテクチャを、そのまま実装できることを学ん だ。 •

    タスクキュー・メッセージキューの実装パターンを見た。 • また、これらを実現するためのライブラリとSaaSを確認した。
  70. さらなる情報:次に何したら良いってい人向け • Kubernetes ◦ オートスケール機能や、死活監視をさせたい場合 ◦ デファクトスタンダード ◦ ただ、一人でやるもんじゃないって、誰かが言ってた(実際そう) •

    コンテナのデザイン・パターンについて知りたい ◦ Qiita - コンテナ・デザイン・パターンの論文要約 @MahoTakara ◦ 実はいつの間にかやってました、みたいなことがよくある • デプロイしたいな〜 ◦ Herokuの無料枠にdockerをデプロイできるのでおすすめです
  71. 全体のまとめ

  72. 本日のまとめ • プロセスのライフサイクルを確認した。 • コンテナ技術の仕組みをプロセスの観点から確認した。 • イベント駆動アーキテクチャとは何か、を見た。

  73. 参考図書 • ★ エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践 ) • ★

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