はじめに
ECS/FargateでTomcatとFluent Bitを別コンテナとして起動し、同じVolumeを共有してログを収集する構成を作りました。
Tomcatが共有Volumeへログを書き込み、Fluent Bitが同じVolumeをマウントしてログを読み取る構成です。
最初に発生したのはTomcat側のPermission deniedでした。その問題を解消してTomcatが正常にログを書けるようになると、今度はFluent Bit側で次のエラーが発生しました。
[error] [input:tail:tail.0]
cannot open /var/log/tomcat/rein71/catalina.2026-01-19.log
[error] [plugins/in_tail/tail_file.c:888 errno=13]
Permission denied
GCログでも同様です。
[error] [input:tail:tail.2]
cannot open /var/log/tomcat/rein71/GC_log....txt
[error] [plugins/in_tail/tail_file.c:888 errno=13]
Permission denied
Tomcat自身はログを書けています。それなのに、同じVolumeを参照するFluent Bitはログを読めません。
今回の問題は、Tomcat単体では解決していても、ECSタスク全体では権限設計が成立していなかったことにありました。
前回は「ビルド時と実行時を分けて考える」話でした。今回は、その続きとして「コンテナ単体とタスク全体を分けて考える」話です。
- 今回の構成|Tomcatが書き、Fluent Bitが読む
- 発端はTomcat側のPermission deniedだった
- Tomcatは書けるようになった。しかしFluent Bitは読めなかった
- chmod 777で原因を権限問題に絞り込んだ
- 直接原因と設計上の原因は分けて考える
- 共有VolumeではUID/GIDをコンテナ間インターフェースとして考える
- setgidで新規ログのgroupを引き継ぐ
- umaskで新規ログのmodeを制御する
- Volumeの初期権限を直すだけでは足りない
- 恒久対策として考えられる構成
- この構成は各環境で検証が必要
- ECS/Fargateで共有Volumeを使うときの確認項目
- まとめ:共有Volumeはコンテナ間インターフェースとして設計する
今回の構成|Tomcatが書き、Fluent Bitが読む
今回の構成を単純化すると、Tomcatが共有Volumeへログを書き込み、Fluent Bitが同じVolumeをマウントしてログを読み取る構成です。

図:ECS/Fargateの共有Volume構成(Tomcat → Fluent Bit)
TomcatはWriterとして、共有Volume上の/var/log/tomcat/rein71/へcatalina.2026-01-19.logやGCログを書き込みます。Fluent BitはReaderとして同じVolumeを参照し、in_tailでこれらのログファイルを監視します。
つまり、両コンテナが直接データを受け渡しているのではなく、共有Volume上のログファイルがTomcatとFluent Bitの間のインターフェースになっています。
本記事で扱うのは、Fluent Bitのin_tailで共有Volume上のファイルを直接監視する構成です。アプリケーションの標準出力をawsfirelensログドライバ経由でFluent Bitへ渡すFireLensの一般的な構成とは異なります。
また、公式のaws-for-fluent-bitイメージをデフォルトのrootユーザーで実行している場合、本記事で扱うような読み取り権限の問題は通常表面化しません。今回の環境ではFluent Bitを非rootユーザーで実行していたため、共有Volume上のファイル権限が問題になりました。
発端はTomcat側のPermission deniedだった
今回、最初に問題になったのはFluent Bitではありません。
Tomcat側でGCログを作成できず、Javaの起動そのものが失敗していました。
Error opening log file
'/var/log/tomcat/rein71/GC_log....txt':
Permission denied
Initialization of output
'file=/var/log/tomcat/rein71/GC_log....txt' failed.
Error: Could not create the Java Virtual Machine.
Dockerfileでは対象ディレクトリへchownしていましたが、Fargateタスク起動時に同じパスへVolumeをマウントしていたため、イメージ作成時に設定したディレクトリではなく、実行時にマウントされたVolume側のファイルシステムが見えていました。
この仕組みについては、以下の記事で詳しく整理しています。

本記事では、その問題を解消した後に発生した問題を扱います。
Tomcatは書けるようになった。しかしFluent Bitは読めなかった
Tomcat側では、Volumeがマウントされた後に対象ディレクトリのowner/groupとpermissionを変更することで、ログを書き込める状態にしました。
これによってTomcatは正常に起動します。
ところが、次にFluent Bitでerrno=13が発生しました。
Linuxのerrno=13はEACCES、つまりアクセス拒否です。
ここで重要なのは、
Tomcatがログを書けることと、Fluent Bitがそのログを読めることは別の権限要件
だという点です。
例えばTomcatが所有者であるログファイルなら、owner権限によってTomcat自身は問題なく書き込めます。
一方、Fluent Bitが別UIDで実行されている場合、そのファイルのgroupまたはotherに読み取り権限がなければアクセスできません。
さらにファイル自身のrだけでは足りません。
Fluent Bitが対象ファイルへ到達するためには、ルートから対象ファイルまで、パス上に存在するすべてのディレクトリにx権限が必要です。
例えば、
/
└─ var
└─ log
└─ tomcat
└─ rein71
└─ catalina.2026-01-19.log
であれば、/var、/var/log、/var/log/tomcat、/var/log/tomcat/rein71を順に辿れる必要があります。
さらに、*.logのようなワイルドカードでファイルを探索する場合は、ディレクトリエントリを列挙するためのr権限も考慮する必要があります。
つまり確認すべきなのは、
ファイルのread権限
+
パス上の全ディレクトリのtraverse権限
+
Fluent Bit自身のUID/GID
です。
なお、Volumeをマウントしているパスより上位のディレクトリは、各コンテナイメージ側のファイルシステムです。同じVolumeを共有していても、その上位階層まで同じ状態になるわけではありません。
chmod 777で原因を権限問題に絞り込んだ
切り分けとして、対象ログのpermissionを一時的に緩める確認を行いました。
777にするとFluent Bitからログを監視できるようになったため、
Fluent Bitのtail設定
対象ログのパス
Volumeの共有そのもの
ではなく、ファイルシステム上のアクセス権限が原因であることを確認できました。
もちろん、
chmod -R 777 /var/log/tomcat/rein71
を恒久対策にはしません。
777は、原因を切り分けるための確認方法です。
さらにchmod -R 775のようにディレクトリと通常ファイルへ同じmodeを再帰的に設定する方法も避けます。通常のログファイルに実行ビットを付与する必要はないためです。
恒久対策では、ディレクトリとファイルの権限を分けて考えます。
直接原因と設計上の原因は分けて考える
今回の問題には、二つの原因があります。
技術的な直接原因
Tomcatが生成したログファイルのowner/groupとmodeが、Fluent Bit側の読み取り条件を満たしていませんでした。
Tomcatから見れば書き込めるファイルでも、別UID/GIDで動作するFluent Bitから見れば読み取れない状態です。
設計上の原因
Tomcatがログを書けることを権限設計のゴールにしてしまい、共有Volumeを利用するFluent Bitまで含めたアクセス要件になっていませんでした。
本来の要件は、
Tomcatが書ける
ではなく、
Tomcatがログを書く
↓
Fluent Bitがそのログを継続して読む
です。
ここをコンテナ単体ではなく、ECSタスク全体の要件として扱う必要があります。
共有VolumeではUID/GIDをコンテナ間インターフェースとして考える
一つの解決方法として、TomcatとFluent Bitが共有するGIDを決める設計が考えられます。
例えば、
Tomcat
UID = 1071
GID = 3000
Fluent Bit
UID = xxxx
GID = 3000
とします。
共有Volumeのログディレクトリを、
owner = 1071
group = 3000
とすれば、
Tomcatはownerとしてログを生成する
Fluent Bitはgroupとしてログを読む
という役割分担ができます。
重要なのは、3000という数値そのものではありません。
共有Volumeを境界として、WriterとReader双方から成立するUID/GIDを決めることです。
ECSのタスク定義では、Linuxコンテナの実行ユーザーをuserで指定できます。
Fluent Bitを共有GIDで実行する場合、例えば設計上は、
"user": "2000:3000"
のようにUIDとプライマリGIDを指定する方法があります。
一方、ECSにはKubernetesのように、プロセスへ補助グループを追加する supplementalGroups 相当の仕組みや、Volumeのgroup所有権を自動調整する fsGroup 相当の仕組みはありません。
必要に応じて、
タスク定義のuser
Fluent Bitのカスタムイメージ
イメージ内のuser/group定義
などを使って実行UID/GIDを設計する必要があります。
実環境では、設定値を見るだけではなく、
id
で実際にプロセスがどのUID/GID/groupsで動作しているか確認することが重要です。
また、タスク定義でuserを指定すると、コンテナのentrypoint自体もそのユーザーで実行されます。イメージのentrypointがroot権限を前提としたディレクトリ操作などを行っている場合は、別の起動エラーを起こす可能性があるため、その点も確認します。
setgidで新規ログのgroupを引き継ぐ
共有Volumeのディレクトリを最初に、
owner = 1071
group = 3000
としても、それだけでは十分ではありません。
Tomcatがその後に新しいログファイルを作成すると、新規ファイルのgroupが期待した値にならない可能性があります。
そこで利用できるのが、ディレクトリのsetgidビットです。
例えば、
chown 1071:3000 /var/log/tomcat/rein71
chmod 2750 /var/log/tomcat/rein71
とします。
先頭の2がsetgidです。
setgidを設定したディレクトリ配下で新しくファイルを作成すると、ファイルのgroupは親ディレクトリのgroupを引き継ぎます。
概念的には、
/var/log/tomcat/rein71
owner = 1071
group = 3000
setgid
│
│ Tomcatが新規作成
▼
catalina.2026-01-20.log
group = 3000
となります。
これによって、日付が変わって新しいログファイルが作成された場合でも、共有GIDを維持しやすくなります。
umaskで新規ログのmodeを制御する
groupが正しくても、ファイルにgroupの読み取り権限がなければFluent Bitは読めません。
そこで次に考えるのがumaskです。
Linuxで一般的なファイルが新規作成される場合、最終的なmodeにはプロセスのumaskが影響します。
例えば、
umask 0027
であれば、通常ファイルは基本的に、
640
rw-r-----
となります。
これなら、
owner = Tomcat
group = Fluent Bitと共有するgroup
という構成において、
Tomcat → read / write
Fluent Bit → read
others → accessなし
というアクセス制御ができます。
考え方としては、
setgid
↓
誰のgroupになるか
umask
↓
そのgroupに何を許可するか
です。
共有Volumeでは、この二つをセットで考えると整理しやすくなります。
なお、アプリケーション自身が明示的にpermissionを変更する場合など、umaskだけでは決まらないケースもあります。実際に生成されたファイルのowner/group/modeは必ず確認します。
Volumeの初期権限を直すだけでは足りない
既存記事では、権限設定用コンテナを先行起動してVolumeを初期化する方式を扱いました。
ECSにはKubernetesのような専用のinit container機能はありませんが、
権限設定用コンテナ
↓ SUCCESS
Tomcat / Fluent Bit
という依存関係を作ることができます。
SUCCESSやCOMPLETEを依存条件として使う場合、依存先コンテナをessentialコンテナにはできません。したがって、権限設定用コンテナはessential: falseとして定義する必要があります。
この方式そのものについては既存記事で詳しく説明しているため、本記事では繰り返しません。
今回重要なのは、その次です。
権限設定用コンテナが変更できるのは、基本的にタスク起動時点のVolumeの状態です。
その後にTomcatが作成するログファイルについては、
TomcatのUID
TomcatのGID
親ディレクトリのsetgid
プロセスのumask
アプリケーション自身のファイル生成方法
などによってowner/group/modeが決まります。
つまり、
Volume初期化
↓
正しい権限になった
↓
完了
ではありません。
Volume初期化
↓
Tomcat起動
↓
ログ新規生成
↓
Fluent Bitから読み取り
↓
翌日のログ新規生成
↓
引き続き読み取り
まで成立して、初めてタスク全体として権限設計が成立します。
恒久対策として考えられる構成
今回確認した事象とLinuxの権限の仕組みから整理すると、恒久対策としては次の構成が考えられます。
1. 権限設定用コンテナでVolumeを初期化する
例えば、共有Volumeのログディレクトリを、
chown 1071:3000 /mnt
chmod 2750 /mnt
とします。
2750はあくまで一例です。
必要なアクセス要件に応じてmodeを決めます。
複数階層を初期化する場合も、ディレクトリと通常ファイルを分けて設定します。
例えば、
find /mnt -type d -exec chmod 2750 {} +
find /mnt -type f -exec chmod 640 {} +
のように扱えば、ログファイルへ不要な実行ビットを付与せずに済みます。
2. Tomcatを非rootで実行する
Volumeの初期化を別コンテナへ分離すれば、Tomcat自身を権限変更のためにrootで実行する必要はありません。
Tomcatはアプリケーション実行用のUID/GIDで起動します。
3. Tomcat側でumaskを設定する
例えば、
umask 0027
としてからTomcatを起動します。
これにより、Tomcatが新しく作成するログファイルについて、共有groupから読み取り可能なmodeにすることを狙います。
4. Fluent Bitを共有GIDから読み取れる状態にする
Fluent Bit側も、共有Volume上のログを読み取れるUID/GIDで実行します。
タスク定義のuserを利用する場合は、例えば、
"user": "2000:3000"
のような指定が候補になります。
実際に使用するUID/GIDは、利用しているFluent Bitイメージやその実行方式に合わせて決めます。
5. Fluent Bit側は可能ならread-onlyでマウントする
Fluent Bitはログを読むだけであれば、共有ログVolumeをread-onlyでマウントする方が役割を明確にできます。
{
"sourceVolume": "tomcat-logs",
"containerPath": "/var/log/tomcat/rein71",
"readOnly": true
}
Fluent BitのDBファイルなど書き込みが必要なファイルは、共有ログVolumeとは別の書き込み可能な場所へ配置する必要があります。
6. 新しいログが生成された後まで確認する
起動直後のファイルだけではなく、Tomcatが新しくログを作成した後に、
ls -ld /var/log/tomcat/rein71
ls -l /var/log/tomcat/rein71
id
などで、
owner
group
mode
TomcatのUID/GID
Fluent BitのUID/GID
を確認します。
特に日付が変わってcatalina.YYYY-MM-DD.logが新しく生成された後や、GCログが新しいファイルへ切り替わった後にもFluent Bitが継続して読み取れることを確認します。
この構成は各環境で検証が必要
ここまでの構成は、今回確認した事象とLinuxのファイル権限の仕組みから整理した恒久対策案です。
実際に必要となる設定は、
使用するコンテナイメージ
Tomcat/JDKのバージョン
ログの生成方式
Fluent Bitの実行UID/GID
Fluent Bitのin_tail設定
Volumeのマウントポイント
などによって変わります。
したがって、本記事のUID/GIDやmodeをそのまま適用するのではなく、各環境で実際に生成されたファイルのowner/group/modeと、各コンテナの実行UID/GIDを確認したうえで検証してください。
ECS/Fargateで共有Volumeを使うときの確認項目
共有Volumeの権限問題を確認する際は、Writer側だけを見るのではなく、実行UID/GID、Volumeとファイルの権限、その後のファイル生成まで順に確認します。今回の事象を踏まえて、確認項目を以下に整理しました。
| 項番 | 確認項目 | 確認内容 |
|---|---|---|
| 1 | WriterのUID/GID | ログを生成するコンテナを誰で実行するか |
| 2 | ReaderのUID/GID | ログを読むコンテナを誰で実行するか |
| 3 | 実行時のid | 設定値ではなく、実際のUID/GID/groupsを確認する |
| 4 | Volumeのowner/group | マウント後の所有者・グループを確認する |
| 5 | ディレクトリ権限 | Readerがパス上の各ディレクトリを辿り、必要に応じて列挙できるか |
| 6 | ファイル権限 | Readerが実際のログファイルを読み取れるか |
| 7 | setgid | 新規ファイルへ共有groupを引き継げるか |
| 8 | umask | 新規ファイルに必要なgroup権限が付くか |
| 9 | 新規ログ生成 | 日付切替やGCログ切替後も権限が維持されるか |
| 10 | 起動順序 | Volume初期化完了後に各コンテナが起動するか |
| 11 | root権限 | 権限初期化など、rootが必要な処理だけに限定できているか |
| 12 | read-only | Reader側から不要な書き込みを防止できるか |
| 13 | タスク再作成 | 新しいFargateタスクでも同じ状態を再現できるか |
特に、起動時のVolumeのowner/groupが正しくても、その後に生成されるログのgroupとmodeがReaderの要件を満たすとは限りません。setgidとumask、新規ログ生成後の確認までを一連のテストとして扱うことが重要です。
まとめ:共有Volumeはコンテナ間インターフェースとして設計する
今回、Tomcatからログを書けるようになった時点では、権限問題を解決できたと思っていました。実際には、同じVolumeを利用するFluent Bitからログを読めず、タスク全体としてはまだ成立していませんでした。
共有Volumeでは、マウント直後のowner/groupとpermissionを整えるだけでは不十分です。その後に生成されるファイルのgroupをどう引き継ぐか(setgid)、どのmodeで作成するか(umask)、Reader側のUID/GIDから読み取れるか、そしてログが新しく作り直された後も読み続けられるかまで確認する必要があります。
TomcatとFluent Bitのように複数コンテナでVolumeを共有する場合、Volumeは単なる保存領域ではなく、コンテナ間でデータを受け渡すインターフェースになります。Writerが書けることではなく、Readerが継続して読めることが要件です。
コンテナ単体では正しい設定でも、ECSタスク全体では正しいとは限りません。
関連記事
本記事の前段となった事象です。
DockerfileでchownしたディレクトリへECS/FargateのEmpty Volumeをマウントすると、なぜ設定したはずの権限が見えなくなるのか。ビルド時のファイルシステムと実行時のVolumeを分けて整理しています。



コメント