Armadilloフォーラム

コンテナアプリから/var/app/rollback/volumesへのtar.gzファイル展開でファイルの中身が消えた

maezawa.toshihiko

2025年1月30日 9時48分

コンテナ内部から/var/app/rollback/volumesへファイルを書き込んだところ、電源断後にファイルの中身がなくなっていました。
原因として考えられることがあればお教えください。

nodeアプリケーションプログラムとnode_modulesディレクトリを armadillo-g4 の/var/app/rollback/volumesに配置し、/var/app/rollback/volumesをadd_volumes することでアプリケーションとnode_modulesをpodmanコンテナに渡し、コンテナにてnodeアプリケーションを動かしています。

node_modulesの更新が必要だったためnode_modulesをtar.gzで固めたものをコンテナに渡し、コンテナ内shell scriptにて/var/app/rollback/volumes以下に展開しました。
node_modulesの展開直後はnodeアプリケーションは問題なく動作していたのですが、電源断からの復旧後、node_modules内の複数のファイルが破損した状態になっており、nodeアプリケーションが動作しなくなりました。

/var/app/rollback/volumes以下のnode_modulesにあらかじめファイルがある状態でのtar.gzファイルでの展開とファイルの上書きです。
/var/app/rollback/volumesはデフォルトのままbtrfsにてmountしていますので、仮にファイル展開から30秒以内に電源が落ちた場合であっても、ファイルの書き込みそのものが取り消され、展開前のディレクトリ・ファイル状態になると理解しています。(ファイルの中身が消えることはない、と理解)
にもかかわらず、複数のファイルが中身がなくなるという症状になっており原因が掴めずにいます。

これはどういったことが原因として考えられるでしょうか。

ファイル破損状況

ファイルは存在するが、中身が空(0バイトファイル)
破損しているファイルは複数(異なるディレクトリにある多くのファイルが0バイトの状態になっていた)
tar.gzの展開から電源断までは1分以上経過していたと思われる(正確な時間は不明)
再度同じtar.gzを展開したところ問題なく/var/app/rollback/volumes以下に保存され電源断でも消えなかった
/var/app/rollback/volumesのmountはデフォルトのまま

==========
製品型番:AGX4500-U00Z
Debian/ABOSバージョン:5.10.226-0-at/3.20.3-at.3
カーネルバージョン:Linux version 5.10.226-0-at (builder@9946fe113c9a) (aarch64-alpine-linux-musl-gcc (Alpine 13.2.1_git20240309) 13.2.1 20240309, GNU ld (GNU Binutils) 2.42) #1-Alpine SMP PREEMPT Fri Sep 20 04:53:40 UTC 2024
3G/LTE モジュール情報 (Debianのみ):
その他:
・container OS version
docker.io/arm64v8/node:22-slim をベースにカスタマイズ

root@cd7db61159d2:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
==========

コメント

at_dominique.m…

2025年1月30日 11時00分

maezawa.toshihikoさん

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

> /var/app/rollback/volumes以下のnode_modulesにあらかじめファイルがある状態でのtar.gzファイルでの展開とファイルの上書きです。
> /var/app/rollback/volumesはデフォルトのままbtrfsにてmountしていますので、仮にファイル展開から30秒以内に電源が落ちた場合であっても、ファイルの書き込みそのものが取り消され、展開前のディレクトリ・ファイル状態になると理解しています。(ファイルの中身が消えることはない、と理解)
> にもかかわらず、複数のファイルが中身がなくなるという症状になっており原因が掴めずにいます。

こちらを訂正させてください。
btrfs での「ファイルの中身が必ず前の状態か書き込み完了の状態」の保証はありません。
マウントオプションに「flushoncommit」を追加すると、30秒毎に実行される「チェックポイント」の際にデータもちゃんと書き込みされますので、それに近い状態になると思いますが、こちらのオプションはデフォルトではなくて、デフォルトでは metadata だけをコミット時に書き込まれます。
ただし、flushoncommit を使っても展開の途中でコミットすると展開前と展開後のファイルがまざりますので、実用性は少ないと考えています。

また、ファイルの更新方法によっては同じ効果はできます。

* GNU tar でファイルを展開すると、各ファイルを「ファイル削除、同じ名前でファイルを生成、書き込み」の繰り返しで展開を行ってますが、そうするとディレクトリの metadata をファイルの data より先に書き込む恐れがあります。
dpkg 等の「丁寧に書き込む」ツールは、「ファイルを別の名前で書き込み、fsync()、リネーム」で書き込みを行っています。

* /var/app/rollback/volumes の「ABOS で安全に更新できる」保証は swupdate がそのような書き方をしているのではなく、snapshot 機能で展開前に snapshot を生成して、snapshot の展開が完了してから snapshot を入れ替えるという仕組みです。
swupdate を使わなくてもアプリケーション内で btrfs subvolume コマンドを使えますが、仕組みとしては /var/app/volumes のみで利用できます(snapshot すると subvolume内にさらに subvolume があると複製されませんので rollback の方で snapshot を利用するとデータが更新の際に消えてしまいます)

よろしくお願いします。

maezawa.toshihiko

2025年1月30日 15時31分

マルティネ 様

ありがとございます。

なるほど、思い違いをしていたようです。
結論としては、tarで展開した場合はmetadataのみ書き込まれている(dataが書き込まれない)タイミングがある、ということですね。

今回の問題対応としてはtarでの展開shell scriptにて、展開先とは別名で展開、明示的にsync、展開先と展開したファイルのリネーム、といった手順を取る方向で行こうかと思います。

後学のため一点だけ追加で教えてください。
30秒ごとのチェックポイントでは必ずしもdataは書き込まれないと理解しました。
明示的にdataを書き込ませるためにはsyncする必要があるかと思いますが、明示的なsyncをしなかった場合にdataが書き込まれるタイミングはあり、どのタイミングで書き込むかを明示できるのでしょうか。それともいつかは書き込まれるが、それがいつになるかはわかならないとなるのでしょうか。(flushoncommitを追加しなかった場合)

at_dominique.m…

2025年1月30日 17時01分

maezawa.toshihikoさん

マルティネです。

> 結論としては、tarで展開した場合はmetadataのみ書き込まれている(dataが書き込まれない)タイミングがある、ということですね。

はい。

> 今回の問題対応としてはtarでの展開shell scriptにて、展開先とは別名で展開、明示的にsync、展開先と展開したファイルのリネーム、といった手順を取る方向で行こうかと思います。

はい、それでいいと思います。
すべての node modules を展開するのであれば別のディレクトリに展開して、最後にディレクトリだけをリネームすればいいかもしれません。
(最近の util-linux 2.40 に入った「exch」コマンドですと二つのディレクトリを入れ替えることはできますので、そういうツールを使えると一時的に何もない問題もないですね。新しすぎて debian bookworm でまだ使えませんが、debian trixie か alpine 3.20 以上で使えます)

> 後学のため一点だけ追加で教えてください。
> 30秒ごとのチェックポイントでは必ずしもdataは書き込まれないと理解しました。
> 明示的にdataを書き込ませるためにはsyncする必要があるかと思いますが、明示的なsyncをしなかった場合にdataが書き込まれるタイミングはあり、どのタイミングで書き込むかを明示できるのでしょうか。それともいつかは書き込まれるが、それがいつになるかはわかならないとなるのでしょうか。(flushoncommitを追加しなかった場合)

「sync」コマンドはデータもちゃんと書き込んでいます。
sync コマンドにいくつかの使い方がありますので、そこだけご注意ください:
* sync file [...file] (fsync の system call): そのファイルやディレクトリの内容をディスクに保存する。こちらの使い方ですと編集したすべてのファイルとディレクトリをリストする必要があります。
ファイルだけの場合ですとディレクトリのエントリが保存される保証はないので、ファイルを全く見えない恐れがあります。親ディレクトリだけの場合はファイルが書き込まれてない場合もありますので同じく 0 byte のファイルができてしまう恐れがあります。
* sync(sync の system call): すべてのファイルシステムの metadata と data をディスクに保存します。
* sync -f file (syncfs の system call): sync と同じですがそのファイルのファイルシステムのみが対象です。

よろしくお願いします