TiDB Dashboard Search Logs で PD / TiDB がダウンロードできない問題
TiDB Dashboard の Search Logs で Download を押すと TiKV だけ成功 し、PD / TiDB は 0 B で failed になる。原因は構造的なもので、TidbCluster の config に数行追加するだけで解決する。
症状
Dashboard → Cluster Info → Logs → 時間範囲指定 → Search → Download すると、Progress パネルが以下の状態になる:
コンポーネント |
結果 |
実態 |
|---|---|---|
PD ( |
|
ファイル無しでエラー |
TiDB ( |
|
同上 |
TiKV ( |
|
success 表示だが実は 0 バイト。TiKV の diagnostics gRPC は「該当ログ無し」を success+空 で返す優しい実装なので、見た目だけ通る |
原因
TiDB Dashboard の Search Logs は、各コンポーネントの ログファイル を gRPC 経由で読み取って検索する仕組み。kubectl logs で見る stdout ストリームは参照しない。
TiDB Operator のデフォルト設定では PD / TiDB / TiKV 全部とも stdout 出力のみで、ログファイルを書いていない。当初「TiKV だけ動いている」ように見えるのは上記の通り success+0B が返るからで、実際に download しても中身は空。
stdout 出力は Kubernetes 流儀として正しいが、Dashboard 側はファイルベースなので相性が悪い、という構造的な問題。
対処
TidbCluster CR の spec.pd.config / spec.tidb.config に [log.file] を追記し、ファイル出力を明示的に有効化する。
マニフェスト差分
manifests/tidb-cluster/tidb-cluster.yaml:
spec:
pd:
config: |
[dashboard]
internal-proxy = true
[log.file]
filename = "/var/log/pd/pd.log"
max-size = 300
max-days = 7
max-backups = 3
additionalVolumes:
- name: pd-log
emptyDir: {}
additionalVolumeMounts:
- name: pd-log
mountPath: /var/log/pd
tikv:
config: |
[storage.block-cache]
capacity = "4GB"
[log.file]
filename = "/var/log/tikv/tikv.log"
max-size = 300
max-days = 7
max-backups = 3
additionalVolumes:
- name: tikv-log
emptyDir: {}
additionalVolumeMounts:
- name: tikv-log
mountPath: /var/log/tikv
tidb:
config: |
[log.file]
filename = "/var/log/tidb/tidb.log"
max-size = 300
max-days = 7
max-backups = 3
ファイルパス選定の根拠:
PD: ⚠️
data-dir(/var/lib/pd) の配下に置くと PD が起動拒否 する (log directory shouldn't be the subdirectory of data directoryで CrashLoopBackOff)。data-dir とは別のディレクトリが必要なのでadditionalVolumesで emptyDir を/var/log/pdに mount してそこに書く。Pod 削除でログは消えるが、Search Logs 用途では十分TiKV: PD と同じ理由で
data-dir(/var/lib/tikv) 配下を避ける。additionalVolumesで emptyDir を/var/log/tikvに mountTiDB: TiDB Operator が
/var/log/tidbに emptyDir を mount している (slowlog 用)。ここに同居させれば追加 volume 不要。Pod 削除でログは消えるが、Search Logs 用途では十分
ローテーション値 (300MB / 7日 / 3世代) は控えめ。MiniPC のディスク容量が潤沢ではないので最大 ~900MB/Pod に収まる設定にした。
適用手順
# 1. CR 反映
# マニフェストには metadata.namespace: tidb-cluster を明記しているため
# -n を付けなくても tidb-cluster 名前空間に向く。
# (もし namespace 指定が無いと default に新規 basic クラスタを誤生成して
# PVC / PV / Pod が浮遊する事故になる)
kubectl apply -f manifests/tidb-cluster/tidb-cluster.yaml
# 2. apply が CR に反映されたか確認 (last-applied annotation を見る)
# ここに新しい [log.file] が出ていれば確実に apply 済み。
# `kubectl get tc basic -o yaml` の spec.*.config は operator が
# デフォルトを merge して `[log] [log.file] max-backups = 3` の
# ような中途半端な姿で見えることがあり、apply 済か判別しづらいので
# last-applied で判断するのが確実。
kubectl -n tidb-cluster get tc basic \
-o jsonpath='{.metadata.annotations.kubectl\.kubernetes\.io/last-applied-configuration}' \
| python3 -c "import sys,json;d=json.load(sys.stdin);print('=PD=');print(d['spec']['pd']['config']);print('=TIDB=');print(d['spec']['tidb']['config'])"
# 3. configUpdateStrategy: RollingUpdate なので PD → TiDB の順で自動ローリング
# 進捗を監視
kubectl -n tidb-cluster get pods -l 'app.kubernetes.io/instance=basic' -w
# PD 3 台 → TiDB 3 台が順次再起動して Running に戻れば完了
# (AGE が新しくなり RESTARTS がインクリメントしているのが目印)
# 4. ログファイルが生成されたか確認
kubectl -n tidb-cluster exec basic-pd-0 -- ls -la /var/lib/pd/pd.log
kubectl -n tidb-cluster exec basic-tidb-0 -- ls -la /var/log/tidb/tidb.log
# どちらも `-rw-r--r-- ... pd.log` / `tidb.log` が出ていれば OK
動作確認
# TiDB Dashboard を開く (Tailscale 経由)
open http://tidb-dashboard.<tailnet>.ts.net:2379/dashboard
# Cluster Info → Logs → 時間範囲を「Pod 再起動より後」に指定 → Search → Download
# Progress パネルで PD / TiDB / TiKV すべて `completed` になれば成功
復旧手順: 誤って default 名前空間に basic クラスタを生成してしまった場合
旧 tidb-cluster.yaml には metadata.namespace が無く、kubectl apply -f ... を -n 無しで叩くと default namespace に新規 basic TidbCluster が作られて しまい、PD 3 台 / PVC 3 個 (10Gi×3) / PV 3 個 / Service / Deployment / ConfigMap が浮く事故が発生する (本リポジトリで実際にやらかして再発防止のためマニフェストに namespace: tidb-cluster を埋めた経緯あり)。
default/basic の PD が CrashLoopBackOff している (本物の tidb-cluster/basic と peer 名衝突)、kubectl get pv に default/pd-basic-pd-* が並ぶ、/opt/local-path-provisioner に *_default_pd-basic-* ディレクトリが居る、のいずれかが見えたら以下で掃除する。
各 step は冪等。途中までしか流していない / 順序が前後しても、最後にもう一度頭から流せば残骸まで掃除される構成にしてある。 特に PV は PVC 削除後でも
claimRef.namespace="default"が残る ので、PVC を先に消してしまっても拾い直せる (Retain ポリシのため Released になっても claimRef は剥がれない)。
# 1. TidbCluster CR 削除
# operator が Pod / Service / Deployment / ConfigMap を順次片付ける。
# PVC は片付かないので次段で明示削除。
kubectl -n default delete tc basic --ignore-not-found
# 2. PVC 残骸の明示削除
kubectl -n default delete pvc -l 'app.kubernetes.io/instance=basic' --ignore-not-found
# 3. PV 削除 (Retain ポリシなので Released で残る)
# claimRef から拾うので 1/2 の前後どちらで実行しても OK。
kubectl get pv -o json \
| jq -r '.items[]
| select(.spec.claimRef.namespace=="default"
and (.spec.claimRef.name | startswith("pd-basic-")))
| .metadata.name' \
| sort -u \
| xargs -r kubectl delete pv
# 4. ノード上の local-path 実ディレクトリ削除
for n in node1 node2 node3; do
ssh $n "sudo find /opt/local-path-provisioner -maxdepth 1 -name '*_default_pd-basic-*' -exec rm -rf {} +"
done
# 5. クリーン確認
kubectl -n default get all,cm,pvc 2>/dev/null | grep -i basic && echo "❌ leftover" || echo "✅ default: clean"
kubectl get pv -o json | jq -r '.items[] | select(.spec.claimRef.namespace=="default" and (.spec.claimRef.name | startswith("pd-basic-"))) | .metadata.name' \
| sort -u | grep . && echo "❌ PV leftover" || echo "✅ PV: clean"
for n in node1 node2 node3; do
ssh $n "ls -d /opt/local-path-provisioner/*_default_pd-basic-* 2>/dev/null" \
&& echo "❌ $n: leftover" || echo "✅ $n: clean"
done
掃除後、本物の tidb-cluster/basic に対して上記「適用手順」を実行すれば復旧完了。
本物クラスタ (
tidb-cluster/basic) は phantom 側とは無関係に動き続けているので、復旧中も SQL や Dashboard は使える。慌てない。
ハマりどころ
ローリング再起動 より前 のログは取れない: stdout にしか書かれていない過去ログはファイル化できない。Search Logs は将来分のみ対象、と割り切る
TiDB の filename は
/var/lib/tidb/...ではない: TiDB pod には/var/lib/tidbボリュームが無い。書き込み可能なのは emptyDir で mount されている/var/log/tidb/(slowlog 用)PD の filename を
/var/lib/pd/...に置くと起動拒否: PD はlog directory shouldn't be the subdirectory of data directoryを出してそのまま落ちる (CrashLoopBackOff ループ。pod status はCompleted→CrashLoopBackOffを行き来する)。additionalVolumes+additionalVolumeMountsで emptyDir を/var/log/pdに mount し、そこに置くのが正解configUpdateStrategy: InPlaceのままだと反映されない: 本リポジトリの CR はRollingUpdate指定済みなので自動再起動するが、InPlaceに変えている場合は手動で Pod 削除が必要apply 済かは
kubectl get tc -o yamlでは判別しづらい: tidb-operator が CR 内の TOML を再シリアライズしてデフォルトを merge するため、spec.tidb.configを覗いても[log] [log.file] max-backups = 3のように一部キーだけ見えることがある。これは "apply されていない" のではなく "operator が merge した姿"。実際に自分が apply した内容かはmetadata.annotations."kubectl.kubernetes.io/last-applied-configuration"で確認するのが確実