Armadilloフォーラム

コンテナ間のデータ共有(python)

k.sato

2023年7月25日 9時37分

佐藤と申します。
お世話になっております。

コンテナ間でのデータ共有について教えてください。

下記の「11.4.1. 複数コンテナで処理を行いコンテナ間でデータを共有する」を読みました。
https://manual.atmark-techno.com/armadillo-iot-g4/armadillo-base-os-dev…
やりたいことはこの通りでコンテナ1、2共にCUIアプリケーションを元に作成し、
/var/app/volumesにファイルが生成されるようになっています。

①ここは二面化されておらず、rollbackが発生しても変化はありません
②eMMCに書き込むため、電源を切るタイミングによってはデータが破損する可能性があります
③各コンテナは同じデータベースに読み書きするため、排他制御する必要があります
④USBメモリなどの外部ストレージに置き換えても問題ありません
注意点に上記と書かれているのですが
・ログする必要はなく電源投入時だけ共有したいのですが②の方法でよいのでしょうか?
・コンテナ2でファイルに書き込み、コンテナ1でそのファイルを読み込もうと考えているのですが、
 ③の排他制御をpythonでどう記述したらよいのでしょうか?

またコンテナ間で通信するという方法も読んだのですが、
pythonで実装するのに具体的な資料があれば教えていただけませんか?

以上、よろしくお願い致します。

コメント

at_dominique.m…

2023年7月25日 11時15分

佐藤さん、

お世話になっています、
マルティネです。

> 注意点に上記と書かれているのですが
> ・ログする必要はなく電源投入時だけ共有したいのですが②の方法でよいのでしょうか?

起動する度に書き込んで、前の起動の情報が不要な場合は故障の問題はないですね。書き込みの途中に電源落ちても次の起動で書き直されますので問題ないと思います。
その場合は /var/app/volumes ではなく、/tmp でもいいと思います。開発ガイドに説明がなさそうですが、コンテナのコンフィグに例えば「add_volumes /tmp/share:/share」を設定するとメモリ上だけに残るデーターを共有できます。
/tmp を使うと「データーが残らない」ことがわかりやすいと思います(コンテナ2が前の起動のデーターを間違って読むことがないです)が、メモリ上であるため量が限られていますね。
量を考えて選んでください。どちらでもいいと思います。

> ・コンテナ2でファイルに書き込み、コンテナ1でそのファイルを読み込もうと考えているのですが、
>  ③の排他制御をpythonでどう記述したらよいのでしょうか?

③ は単純に複数のコンテナで書き込むする場合の話だと思いますが、読み取りでもコンテナ1が書き込んだ後を待ってから読み取りしないといけないですね。
例えば、コンテナ1で一時的な(違う名前)のファイルで書き込んだあとにリネームして、コンテナ2でファイルが存在するのを繰り返しに待つだけでも「排他制御」になります。
次の質問とかぶりますが、一度だけ読み込む場合は os.mkfifo を使って通信するも可能ですし、ソケットでも通信が可能です(tcp の場合はコンテナ名を使えます、unix socket の場合はボリュームに設置することで共有できます)。その場合でも「排他制御」の心配がなくなりますね。

> またコンテナ間で通信するという方法も読んだのですが、
> pythonで実装するのに具体的な資料があれば教えていただけませんか?

検索すればいくらでもありますが、以下のリンクで unix socket を使って通信することはできます:
https://tokibito.hatenablog.com/entry/20150927/1443286053
(クライアント側でサーバーが起動された前に接続しようとするとエラーが発生しますので、connect() のところにリトライが必要かもしれませんが、socket_path をボリュームディレクトリ内にすると動くはずです)

よろしくお願いします。

at_dominique.m…

2023年7月25日 11時50分

佐藤さん、

連続更新ですみません。

複雑なデーター共有を行う前に、コンテナを別ける理由を簡単に説明してもらえますでしょうか?
python の場合は threadingやmultiprocessing でソレッドや子プロセスで並列実行はかのうなので、例えばネットワークか複数の GPIO の制御だけであれば複数のコンテナにしなくてもいいかもしれません。

複数のコンテナのメリットは、おおむね権利の分裂なので、セキュリティの面ではとてもいいことだと思いますが、各コンテナが似たような処理を行っている場合は共有してもいいと思っています。

よろしくお願いします。

マルティネ様

佐藤です。

> 複雑なデーター共有を行う前に、コンテナを別ける理由を簡単に説明してもらえますでしょうか?
Armadillo Base OSとpythonでどう開発してよいかがわかっていない部分もあります。
ゆくゆくはwebサーバを立ててPCと接続するような形にしたいと思っています。
RS485で下位装置からデータをポーリングしてその結果を表示するので
通常表示するデータはファイルで共有し
PCからの設定書き込みはRS485のコンテナに送信して行うようになるのかなと。

CUIアプリケーションでデバッグしながら開発する上で
それぞれの機能でコンテナが分かれていた方がよいのかと考えました。

それともpython_app.confに
set_command python3 ***.py
を追加するなどできるのでしょうか?
そちらの方が効率よいですか?

at_dominique.m…

2023年7月25日 13時59分

佐藤さん、

マルティネです。

> Armadillo Base OSとpythonでどう開発してよいかがわかっていない部分もあります。
> ゆくゆくはwebサーバを立ててPCと接続するような形にしたいと思っています。
> RS485で下位装置からデータをポーリングしてその結果を表示するので
> 通常表示するデータはファイルで共有し
> PCからの設定書き込みはRS485のコンテナに送信して行うようになるのかなと。

なるほど、RS485 の処理でプロセスがブロッキングされているので、別のプロセスでPCからの設定などを対応するということですね。

> CUIアプリケーションでデバッグしながら開発する上で
> それぞれの機能でコンテナが分かれていた方がよいのかと考えました。
>
> それともpython_app.confに
> set_command python3 ***.py
> を追加するなどできるのでしょうか?

set_command は一つしか設定できません、最後の set_command だけの残ります。
コマンドを run.sh に変更して run.sh で python3 a.py & python3 b.py の様に設定すれば、いくつかのプログラムを起動できますが、プロセスの管理機能を失います:デフォルトでは、実行するコマンドが失敗する場合にコンテナがリスタートされますが、a.py のステータスはまったく管理から外れていますし、b.py が停止されたら a.py も強制的に停止されます。

その面では、二つの python コマンドを実行したい場合はプロジェクトを二つ作る判断が正しいと思います…が、二つの python プロセスではなく、 threading か multiprocess で管理した方がプロセス同士の通信が楽なきがします。

> そちらの方が効率よいですか?

そうですね、効率としてはコンテナも結局普通のプロセスとして管理されているので、そんなに違いはありません
- ランタイムでは、起動が少し遅くなりますが、起動されたら CPU 負担が同じです。メモリの面では管理プロセスや cgroup が増えますので 1-2MB 程度のメモリが諸費されますが、python に比べたら小さい差だと思います。
- ディスクの方は単純に別のプロジェクトを使うとコンテナイメージが二重されますので、各python_app.conf で同じイメージを使うようにしないと無駄がでますね。(python_app.conf のファイル名を変えないとどれかしか起動でいないと思いますが、そこはうまく変更できましたか?)

一方、multiprocess か threading を使うと:
- 「アプリケーション」としては一個しかないので、管理するプログラムも一つでいいはずです
- python のビルトインなので、通信方法もスタンダードにできます。
- threading と queue を使って、協力するスレッドの例は https://qiita.com/virusyun/items/f3716e4ec42b6b9cd67b でわかりやすく説明されていると思いました。

なので、色々説明した後ですみませんが、一度 threading をみていただけたらいいかもしれません。

よろしくお願いします。

マルティネ様

佐藤です。

> - ディスクの方は単純に別のプロジェクトを使うとコンテナイメージが二重されますので、各python_app.conf で同じイメージを使うようにしないと無駄がでますね。(python_app.conf のファイル名を変えないとどれかしか起動でいないと思いますが、そこはうまく変更できましたか?)
片方のプロジェクトだけファイル名やフィアル内の「python_app」を違う名前に置き換えて別々に起動できています。

> - threading と queue を使って、協力するスレッドの例は https://qiita.com/virusyun/items/f3716e4ec42b6b9cd67b でわかりやすく説明されていると思いました。
ありがとうございます。
リンク先を確認してみます。

マルティネ様

佐藤です。

> > - threading と queue を使って、協力するスレッドの例は https://qiita.com/virusyun/items/f3716e4ec42b6b9cd67b でわかりやすく説明されていると思いました。
リンク先を確認したのですが、
RS485で永久ループ内でポーリングするThreadと
その他の入力待ちの永久ループするThreadとを並列処理するような作りだと
協調作業というわけではないのでQueueを実装する必要はないですか?

それともこのような実装はよくないのでしょうか?

at_dominique.m…

2023年7月26日 18時55分

佐藤さん、

マルティネです。

> リンク先を確認したのですが、
> RS485で永久ループ内でポーリングするThreadと
> その他の入力待ちの永久ループするThreadとを並列処理するような作りだと
> 協調作業というわけではないのでQueueを実装する必要はないですか?
>
> それともこのような実装はよくないのでしょうか?

ここは「ループの内容によります」としかいえません。
最初の質問にコンテナ間の通信の話もあったので、queue を使えば「スレッド間に通信する」形が一番近いと思って紹介しましたが、そもそも同じプロセスになりますので、グロバール変数か別の排他制御でもいいかもしれません。
変数の場合はロックも使う必要ありますので(参考:https://www.delftstack.com/ja/howto/python/python-thread-lock/ )、個人的には queue を使った方が「pythonらしい」と思いますが、決まったやりかたがないのでご自由に開発してください。

よろしくおねがいします

マルティネ様

佐藤です。

> 最初の質問にコンテナ間の通信の話もあったので、queue を使えば「スレッド間に通信する」形が一番近いと思って紹介しましたが、そもそも同じプロセスになりますので、グロバール変数か別の排他制御でもいいかもしれません。
> 変数の場合はロックも使う必要ありますので(参考:https://www.delftstack.com/ja/howto/python/python-thread-lock/
ロックの方法まで教えていただき、ありがとうございます。
グローバル変数を使用する方法で動作確認できました。