SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|

2021-02-02(Tue) 手軽にコンテナ間で情報共有、時計製作進行中

  唐突だが、docker環境のコンテナ間で情報を共有したくなった。

  画像の説明

  と、これについては、後半で。

  調べると、いろいろとソレ用の方法がありそうなのだが、極力シンプルな方法がいい。各コンテナは連携する部分はあるものの、非常に疎だし、個々に独立して立ち上げたりもしたいのだ。

  動作イメージとしては、主に予定表をサービスするコンテナと、主な機能に加えて直近の予定を表示するコンテナという感じ。片方はファイルを更新するが、片方はファイルを参照するだけ、みたいな。

  そこで、PVの下のファイルをハードリンクで共有する方法を思いついた。もはや、dockerの機能ですらない。共有する対象はgdbmのデータベースファイルだ。

  まずは、ファイルを更新する側のコンテナを準備する。最近自作したsinatraの骨格コンテナを使うが、それには深い意味はない。

/root/docker # mkdir gdbm_writer
/root/docker # cd gdbm_writer
/root/docker/gdbm_writer # git clone https://gitlab.com/Furutanian/sinatra_skelton
/root/docker/gdbm_writer # ln -s sinatra_skelton/Dockerfile .
/root/docker/gdbm_writer # cp sinatra_skelton/docker-compose.yml .
/root/docker/gdbm_writer # vi docker-compose.yml 
/root/docker/gdbm_writer # diff sinatra_skelton/docker-compose.yml docker-compose.yml 
<             sinatra_skelton-alpha
>             sinatra_skelton-alpha-writer

  共有するファイルを配置するPVを準備。

/root/docker/gdbm_writer # mkdir pv; chown 1000:1000 pv

  ファイルを更新するテスト用スクリプトを書く。

/root/docker/gdbm_writer # vi sinatra_skelton/gdbm_writer.rb 
/root/docker/gdbm_writer # cat sinatra_skelton/gdbm_writer.rb 
#!/usr/bin/env ruby
 
require 'gdbm'
 
db = GDBM.new('data/time.gdbm')
loop {
	db['now'] = Time.now.to_s
	sleep 1
}
/root/docker/gdbm_writer # chmod 755 sinatra_skelton/gdbm_writer.rb

  ついでにファイルを参照するテスト用スクリプトも書く。

/root/docker/gdbm_writer # vi sinatra_skelton/gdbm_reader.rb
/root/docker/gdbm_writer # cat sinatra_skelton/gdbm_reader.rb
#!/usr/bin/env ruby
 
require 'gdbm'
 
db = GDBM.new('data/time.gdbm', 0666, GDBM::NOLOCK | GDBM::READER)
loop {
	p db['now']
	sleep 1
}
/root/docker/gdbm_writer # chmod 755 sinatra_skelton/gdbm_reader.rb

  ファイルを更新する側のコンテナを立ち上げる。

/root/docker/gdbm_writer # docker-compose build
/root/docker/gdbm_writer # docker-compose up -d

  次に、ファイルを参照する側のコンテナを準備する。基本、更新する側のコンテナの丸コピー。docker-compose.ymlでコンテナ名だけ変えておく。

/root/docker/gdbm_writer # cd ..
/root/docker # cp -a gdbm_writer gdbm_reader
/root/docker # cd gdbm_reader
/root/docker/gdbm_reader # vi docker-compose.yml 
/root/docker/gdbm_reader # diff sinatra_skelton/docker-compose.yml docker-compose.yml 
<             sinatra_skelton-alpha
>             sinatra_skelton-alpha-reader
<             - 8080:8080
>             - 8081:8080

  ファイルを参照する側のコンテナを立ち上げる。

/root/docker/gdbm_reader # docker-compose up -d
/root/docker/gdbm_reader # cd ..

  ファイルを更新する側のコンテナに入って、ファイルを更新するテスト用スクリプトを起動する。

/root/docker # docker exec -it sinatra_skelton-alpha-writer /bin/bash
[user@4dc50fc6e6a7 sinatra_skelton]$ ./gdbm_writer.rb 

  別端末から、ファイルを参照する側のコンテナに入って、ファイルを参照するテスト用スクリプトを起動する。

/root/docker # docker exec -it sinatra_skelton-alpha-reader /bin/bash
[user@c0a2ec346ec8 sinatra_skelton]$ ./gdbm_reader.rb 
Traceback (most recent call last):
	2: from gdbm_reader.rb:5:in `<main>'
	1: from gdbm_reader.rb:5:in `new'
gdbm_reader.rb:5:in `initialize': No such file or directory - data/time.gdbm (Errno::ENOENT)

  ……と、起動しない。ファイルを参照する側のPVには何もないから当たり前。

  一度コンテナを出て、ファイルを更新する側のPVの下のファイルをハードリンクで共有状態にしてやる。

/root/docker # ln gdbm_writer/pv/time.gdbm gdbm_reader/pv

  改めて、ファイルを参照する側のコンテナに入って、ファイルを参照するテスト用スクリプトを起動する。

/root/docker # docker exec -it sinatra_skelton-alpha-reader /bin/bash
[user@c0a2ec346ec8 sinatra_skelton]$ ./gdbm_reader.rb 
"2021-02-02 20:29:28 +0900"
"2021-02-02 20:29:28 +0900"
"2021-02-02 20:29:28 +0900"
 :

  読めた。当たり前ではあるが。

  ここでのミソは、ファイルを参照する側では「GDBM::NOLOCK | GDBM::READER」を指定することだ。

  それと面白いのは、ファイルを更新する側では、データベースファイルを開きっぱなしにして、その内容を毎秒のように更新しているのだが、ファイルを参照する側では、参照開始時点の内容に固定され(ているように見え)ること。相手がデータベースだと考えれば、好ましい挙動かもしれない。

  ちなみに、ファイルを参照する側で、更新をしようとすると、ロックに失敗する。当たり前だが、コンテナ間でも排他が効くのだ。

[user@c0a2ec346ec8 sinatra_skelton]$ ./gdbm_writer.rb
Traceback (most recent call last):
	2: from gdbm_writer.rb:5:in `<main>'
	1: from gdbm_writer.rb:5:in `new'
gdbm_writer.rb:5:in `initialize': Resource temporarily unavailable - data/time.gdbm (Errno::EAGAIN)

  実際は、更新側も参照側も、ここまでデータベースファイルを開きっぱなしにすることはなく、こまめにクローズするだろうから、実用上は問題ないだろう。

  今回は、gdbmのデータベースファイルを共有したが、別に通常のファイルであっても、片方はファイルを更新するが、片方はファイルを参照するだけ、という運用なら、ほとんど問題は起きないのではないかと思う。

  ただし、アトミック性を期待して、更新側でテンポラリファイルに生成してからリネーム、などということをすると、ハードリンクが途切れてしまうのでダメだ。信頼性が必要な場合は、やはりgdbmだな。

  それと、家の電波掛時計の調子が悪かったり、テレワークのために始業終業のチャイムが鳴ってほしかったり、筋トレのインターバルのためにサッと秒数を読み取りたかったり、ずーっと購入したまま放置してあった16セグのLEDを活用したかったり、久々にハンダゴテを握りたかったり、ということで、デジタル置時計を作ることにした。

  先々週の金曜(1/22)に具体的な構想を始めてから、PICによる制御ソフトとハンダ付けを同時進行で進めているのだが、オレ史上で最大のハンダ付け点数である。ヤケクソ面倒くさい。普通はこのレベルならプリント基板を発注するわなぁ……。

  とりあえず、手持ちのFETが足りないので、2ケタをダイナミック点灯させるトコまでは成功しているが、死ぬほどハンダ付けして、まだ4ケタ。完成は8ケタの予定なんですが……気が遠くなるわ。

  早く製作しないと、せっかく買った16セグのLEDの部品が遺品になってしまう、と思ったのだが、いつ購入したものなのやら記録がない。と、このブログを検索すると、なんか違う16セグも購入してるやんけ……確かに部品棚に残ってるわ……。

  このままだと、自分の戒名は「秋月院部品遺平光居士」とかになってしまう。気張って製作を進めなければならんな……。