複数のコンテナを docker logs で個別に追っていると、障害のときに「どのコンテナの、いつのログか」を後から探すのが負担になります。ログを一か所へ集めたくなりますが、ログパイプラインは導入できた瞬間より、流れないとき・変換で落ちるとき・出力先に書けないときに運用差が出ます。
本記事は、使い捨てのDocker環境でVector(timberio/vector)を最小構成で立て、正常系を確認したうえで、わざと失敗条件を作って「どう見えるか」を実コマンドと実ログで記録した検証ログです。Vectorを家庭ラボに常用する前に、最低限どの失敗を先に試しておくべきかを判断材料として整理します。
検証環境
- 使い捨ての一時VM上のDocker(検証後に作業ディレクトリごと破棄)
- Docker version 29.6.0
- イメージ: timberio/vector:latest-debian(vector 0.56.0)
- 構成: file source → remap transform(VRL)→ file sink の最小3段
- 検証日時: 2026-06-25(UTC)
本検証は「一度実行して観測した」レベルの記録です。長期運用・高負荷・複数同時投入時の挙動は対象外です。
最小構成と正常系の確認
作業ディレクトリを作り、Vectorのバージョンを確認します。
$ docker --version
Docker version 29.6.0, build fb59821
$ mkdir -p issue168-lab/config issue168-lab/in issue168-lab/out
$ docker run --rm timberio/vector:latest-debian --version
vector 0.56.0 (x86_64-unknown-linux-gnu 2026-06-03)
設定ファイル config/vector.toml は次の内容です。入力ディレクトリの .log を読み、message を 設定項目 としてパースし、目印フィールドを足してファイルへ書き出すだけの構成です。
[sources.in_files]
type = "file"
include = ["/var/in/*.log"]
read_from = "beginning"
[transforms.parse]
type = "remap"
inputs = ["in_files"]
source = '''
. = parse_json!(string!(.message))
.processed_by = "vector-verify"
'''
[sinks.out_file]
type = "file"
inputs = ["parse"]
path = "/var/out/processed.log"
encoding.codec = "json"
まず静的検証(validate)を実行します。
$ docker run --rm -v "$PWD/config:/etc/vector:ro" timberio/vector:latest-debian validate /etc/vector/vector.toml
√ Loaded ["/etc/vector/vector.toml"]
√ Component configuration
√ Health check "out_file"
------------------------------------
Validated
構文・コンポーネント構成・出力先のヘルスチェックが通りました。次に実データを流します。
$ printf '{"event":"login","user":"alice"}\n{"event":"logout","user":"bob"}\n' > in/app.log
$ docker run --rm \
-v "$PWD/config:/etc/vector:ro" \
-v "$PWD/in:/var/in" \
-v "$PWD/out:/var/out" \
timberio/vector:latest-debian -c /etc/vector/vector.toml
起動ログで対象ファイルを検出している様子が確認できます。
設定項目 vector: Vector has started. version="0.56.0"
設定項目 source{component_id=in_files}: Starting file server. include=["/var/in/*.log"]
設定項目 source{component_id=in_files}: Found new file to watch. file=/var/in/app.log
出力 out/processed.log は次の通りで、JSONがパースされ processed_by が付与されています。
{"event":"login","processed_by":"vector-verify","user":"alice"}
{"event":"logout","processed_by":"vector-verify","user":"bob"}
ここまでが「うまくいったパイプライン」です。重要なのはこの先です。
失敗系1: 入力パスを間違えると、何も言わずに何も出ない
include を存在しないパス(/var/in/nonexistent/*.log)に変えた設定で同じように起動します。
設定項目 vector: Vector has started. version="0.56.0"
設定項目 vector::topology::builder: Healthcheck passed.
設定項目 source{component_id=in_files}: Starting file server. include=["/var/in/nonexistent/*.log"]
Vectorは正常に起動し、ヘルスチェックも通り、エラーも警告も出しません。しかし出力先には新しいログが一切増えません(前回の processed.log が残っているだけです)。
これが家庭ラボで一番こわい見え方です。「ツールは動いている、エラーも出ていない、でもログが流れていない」。入力パスの誤りはエラーではなく沈黙として現れるため、出力側の件数を能動的に見ない限り欠落に気づけません。
失敗系2: VRL変換エラーは「欠落」ではなく「未整形混入」として見える
次に、JSONでない行を混ぜた入力を流します。
$ printf 'this is not json\n{"event":"ok","user":"carol"}\n' > in/bad.log
実行ログには parse_json の失敗が 設定項目 として明示されます。
設定項目 transform{component_id=parse component_type=remap}: vector::internal_events::remap: Mapping failed with event. error="function call error for \"parse_json\" at (4:34): unable to parse json: expected ident at line 1 column 2" error_type="conversion_failed" stage="processing"
ここで注意したいのは出力側の挙動です。失敗した行はドロップされず、生のメッセージのまま出力先へ通過しました。
{"file":"/var/in/bad.log","host":"<container-id>","message":"this is not json","source_type":"file","timestamp":"2026-06-25T..."}
{"event":"ok","processed_by":"vector-verify","user":"carol"}
つまり、今回の構成では変換エラーは「ログが消える」のではなく「整形されていない素のイベントが混ざる」形で現れました。ERRORログを見ていれば失敗自体は気づけますが、出力ファイルだけを眺めていると、整形済みと未整形が同居していることに気づきにくい点は覚えておく価値があります。
(補足: parse_json! の挙動はVRLのエラー処理・ドロップ設定に依存します。本記事は今回の最小構成での観測結果であり、drop_on_error などを明示すると振る舞いは変わります。)
失敗系3: 出力先権限エラー(今回は明示文言を捕捉できず)
出力先ディレクトリから書き込み権限を外して(chmod a-w)実行しました。
$ ls -ld out
dr-xr-xr-x 2 root root 4096 out
意図としては「書けない出力先で sink がどんなエラーを出すか」を見るものでしたが、今回取得できた実行ログには permission / denied などの明示的なエラー文言を捕捉できませんでした。権限を外した状態を再現したことは確認できましたが、症状(どのログにどう出るか)の観測は不十分です。
したがって本項は「再現は試みたが、出力先権限エラーの明確なエラー表示は今回は確認できなかった」というcaveat(制約)として扱います。出力先権限は実運用で詰まりやすい箇所なので、採用前に各自の環境でログ表示を別途確認することをおすすめします。
設定ロールバックの確認
権限を戻し(chmod u+w)、正常な設定で validate と実行をやり直しました。
$ ls -ld out
drwxr-xr-x 2 root root 4096 out
√ Loaded ["/etc/vector/vector.toml"]
√ Component configuration
√ Health check "out_file"
Validated
設定項目 vector: Vector has started. version="0.56.0"
設定項目 source{component_id=in_files}: Found new file to watch. file=/var/in/app.log
設定をファイル単位で切り替えるだけなので、壊れた設定から元の設定へ戻すのは validate → 再実行で確認できました。設定をファイルとして版管理しておけば、ロールバックは難しくありません。
validateで分かること・分からないこと
今回の観測ではっきりしたのは、静的検証(validate)の守備範囲です。
- validateが検出する: 構文エラー、コンポーネント構成の不整合、出力先のヘルスチェック
- validateが検出しない: 入力パスの不存在(対象ファイルが0件でも通る)、データ依存の変換失敗(実際に変なデータが来て初めて起きる)
つまり validate が通ることは「設定として読める」ことの保証であって、「ログが期待通り流れる」ことの保証ではありません。流れているかは出力側の件数と内容で確認する必要があります。
比較: どの運用を選ぶか
読者が実際に比べている3つの選択肢を、今回の観測軸で整理します。
| 観点 | docker logs を個別に見る | Vectorでローカルファイルへ集約 | Lokiなどへ送る前段にVectorを置く |
|---|---|---|---|
| 設定検証のしやすさ | 設定不要 | validateで静的検証でき、ファイル単位でロールバック可 | Vector部分は同様。送信先側の検証が別途必要 |
| ログ欠落時の気づきやすさ | 各コンテナを見れば分かるが横断は手作業 | 入力パス誤りは沈黙=出力件数を見ないと気づけない | 集約後の可視化で気づきやすくなる可能性 |
| 変換エラーの見え方 | 整形しないので発生しない | ERRORログに出るが、今回は生イベントが混入する形で通過 | 同様。送信前に未整形が混ざるリスクは残る |
| 復旧・ロールバックの簡単さ | 復旧の概念がない | 設定ファイル切替で容易 | Vector側は容易、送信先依存の障害は別問題 |
小さく始めるなら、まずVectorでローカルファイルへ集約し、流れ方と壊れ方を観測してから、必要に応じてLokiなどの前段として使うのが無理のない順序です。
採用前に最低限試すべき失敗
家庭ラボでVectorを常用する前に、本記事で観測した範囲では次の4点を自分の環境で先に試しておくと、運用時の判断が速くなります。
- 入力パスをわざと間違える: エラーが出ず出力が止まる「沈黙の欠落」を体験しておく。
- JSONでない行を混ぜる: ERRORログの場所と、出力に未整形イベントが混ざるかを確認する。
- 出力先の権限を外す: 自分の環境でどんなエラー表示になるかを実際に見ておく(今回は明示文言を捕捉できなかったため、各自での確認を推奨)。
- 壊れた設定から validate → 再実行で戻す手順を一度通しておく。
Vectorは小さな構成で「流れない・落ちる・書けない」を安全に再現できます。導入できるかではなく、壊れたときにどう見えるかを先に知っておくことが、家庭ラボのログ整理を後悔なく進める近道です。
後始末
検証で使ったコンテナと作業ディレクトリは、検証後にすべて削除しました。使い捨て環境で試して破棄する流れにしておくと、本番環境を汚さずに失敗条件を観測できます。
この検証を回している環境
この検証は、自宅の常設ラボ(使い捨てVM/LXCを回す母艦+GPU+VLAN分離ネットワーク)で動かしています。使っている機材と選定理由、全体構成は1本にまとめています。
ラボ構成のまとめを見る →