0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

デプロイのたびに互いの成果を消していた — 2セッションで3回やった巻き戻し事故

0
Posted at

📝 この記事は forge.workstyle.tech に掲載した記事の転載です。

同じマイクロサービス群を、2つの作業系が並行して触っていた。片方は音声パイプライン、もう片方は会話品質。担当範囲は分かれていて、触るファイルもほぼ重ならない。

それでも3回、お互いの成果を本番から消した

1回目: フロントエンドの5コミットが消えた

フロントの修正を1つ入れるために、main からブランチを切ってビルドし、デプロイした。

ユーザーから報告が来た。

レコードがなくなり、以前消したはずの画面が復活しています。

撤去したはずの画面が戻り、追加したはずの機能が消えていた。調べると、稼働していたイメージは main から焼かれていなかった

$ grep -l "virtualpv:1.0.407" ~/kaniko-builds/*.yaml | xargs grep -- --branch
  --branch feat/character-list-preview

そのブランチは main より5コミット進んでいた。main 基点で焼いた時点で、その5件が本番から消えた。

このリポジトリでは main が稼働の正本ではなかった。 未マージのブランチを数えると、54件・12件・6件…と積み上がっていた。「main は最新」という一般的な前提が、この環境では成り立っていない。

直前のタグへ kubectl set image で戻して復旧し、正しい基点に cherry-pick して焼き直した。

2回目: バックエンドの17版が消えた

1回目の教訓として、こういう手順を決めた。

# 焼く前に、稼働版の基点ブランチに対して自分が取りこぼしていないか
git log --oneline HEAD..<稼働版の基点>

そして次のビルドで、私はこれを実行した上で事故を起こした。

バックエンドを焼く前、稼働Podに入って自分の変更が入っていることを確認した。

>>> inspect.signature(judge_transcript).parameters['max_inserted'].default
1     # 私が入れた値。稼働版は私の変更を含んでいる

これで安心して main 基点で焼いた。結果、もう一方の17版が消えた。会話ログの記録が止まり、60分間ぶん失われた。

確かめたのは「稼働イメージが私の変更を含んでいるか」だった。確かめるべきは「私のビルドが稼働イメージを含んでいるか」だ。この2つは別の命題なのに、同じものとして扱っていた。

差分を取れば一目で分かる状態だった。

$ git log --oneline <自分のHEAD>..<稼働版の基点> | wc -l
20

焼く前にこれを1回実行していれば防げた。手順は決めていたのに、リポジトリが変わったら実行していなかった。fvではマニフェストの --branch まで辿ったのに、バックエンドではPod内の値を1つ見て済ませている。

3回目: 手順を守った側が被害を受けた

しばらくして、逆方向で起きた。

私がバックエンドを直してデプロイした。そのあと焼き直しの監視に移り、main へ push するのを忘れた

もう一方は正しい手順を踏んでビルドした。自分のブランチを稼働版の基点と突き合わせ、取りこぼしがないことを確認している。そのとき main にも相手のブランチにも私の変更は無かったので、確認は通過した。そして私の変更が消えた。

確認する側がどれだけ正しくても、相手が push していなければ防げない。

ブランチ確認では足りなかった

3回目のあと、もう一方が別のリポジトリで4回目を寸前で止めた。使ったのは、稼働Podのファイルと自分のブランチの sha256 突合だった。

pipeline.py     一致
brain_talk.py   ⚠️差分  稼働=1a02…  自分=341e…
app.py          ⚠️差分  稼働=8a41…  自分=4110…

差分の中身は、私が入れた配信の復帰対策3件だった。フルビルドしていれば全部落ちていた。

このとき本人は基点ブランチを取り違えていた(feature ブランチだと思っていたが、実際は main から焼かれていた)。git log HEAD..<基点> は基点が分かっている前提の手順なので、思い込みがあると素通りする。sha256 突合はブランチ名を知らなくても成立する。

もう一つ分かったのは、ファイルの存在確認では足りないことだ。私は2回目の事故のあと「相手の conversation_log.py があるか」を確認するようにしたが、今回問題になった2ファイルは存在するが中身が違う状態だった。存在チェックでは検出できない。

結論: 2つで足りる

3回の事故から抽出すると、必要なのは2つだけだった。

① デプロイ直後に main へ push する。

これが本体。両者がこれを守れば、main が常に稼働と一致するので、②は自然に通る。3回目の事故は、これを私が破った結果だった。

② 焼く前に、稼働Podと自分のブランチを sha256 で突き合わせる。

これは保険。①が守られていない相手がいても、これで止まる。

kubectl exec -n <ns> deploy/<dep> -- sh -c \
  'cd /app && find apps/services -name "*.py" | sort | xargs sha256sum' > /tmp/pod.txt
(cd <repo> && find apps/services -name "*.py" | sort | xargs sha256sum) > /tmp/br.txt
diff /tmp/pod.txt /tmp/br.txt   # 差分が自分の変更分だけかを見る

②だけでは3回目が防げず、①だけでは1回目が防げない。両方が要る。

付随して踏んだもの

kubectl rollout status の完了を信じてはいけない。 「successfully rolled out」と出た直後に、新旧2つのPodが並んでいた。

exista-voicepipe-<rs-old>-lpqtx   Running  ...:0.7.14
exista-voicepipe-<rs-new>-cv92x  Running  ...:0.7.15

数秒後に収束したが、その間に検証していれば「効くPodと効かないPod」に振り分けられて、再現しない挙動に悩むことになる。デプロイ後はPod数を直接数える。

似た事故を、私は生成バッチでも踏んでいる。200本のクリップを生成している最中にPodが入れ替わり、前半と後半が別の設定で作られた素材ができあがった。ログを見て「新しいゲートが1回も発動していない」ことに気づくまで、原因が分からなかった。

ビルド定義を使い回さない。 過去のマニフェストを sed でタグだけ書き換えて使っていたが、--branch の書き換えを忘れて1回目の事故を起こしている。用途ごとに分けて残すほうがいい。実際、もう一方は「基点タグは稼働中のタグにすること」という注意書き込みで定義を残すようにした。

タグの上書きに注意。 過去に && の連鎖が切れてタグの bump が飛び、古いタグに新しい中身を push した事故があった。そのタグへのロールバックは、もう当時の挙動に戻らない。ロールバック先を選ぶとき、この履歴を知らないと詰む。

一般化できること

この事故は「並行作業だから起きた」ように見えるが、単独でも起きると思う。

1回目の直接の原因は、「main が稼働の正本である」という一般的な前提が、この環境では成り立っていなかったことだった。デプロイ用のブランチが正本で、mainは古い。この状態は、リリースフローが整っていない環境ではよくある。

そして単独作業でも、過去の自分が別ブランチで焼いていれば同じことが起きる。1か月前の自分は他人と変わらない。

確認すべきは「自分が何を足したか」ではなく「いま動いているものを、自分のビルドが含んでいるか」だ。 前者は覚えているが、後者は覚えていない。だから機械的に突き合わせる。


シリーズ: 拡散TTSから実用ボイスを量産する

キャプション1行から声を設計し、学習コーパスを製造して、役割別の実用ボイスを量産するまでの記録です。この記事は第4部 運用にあたります。

← 前: 登録経路が4つ、管理画面が0
→ 次: 測れていないものを閾値で追い込むと、必ず失敗する

シリーズ全18本
  1. 音質で選んだTTSが、会話には遅すぎた
  2. 声をガチャで引く
  3. 「ナレーターっぽい声」を24候補から機械に選ばせる
  4. 品質ゲートを厳しくするほど、棒読みが生き残る
  5. 学習後に話速は変えられない
  6. 生成するたび「録音場所」が変わるTTS
  7. クリップ1本の粗さが、スタイル全体を嗄れさせる
  8. AIが「こんにちわー」と伸ばす癖は、どこで身についたのか
  9. 「少々」が「しょも」になる — 許可文字リストが日本語を削っていた
  10. ハルシネーション対策のコードが、ハルシネーションの時だけ動かなかった
  11. 品質ゲートが許した「3文字」が、モデルの口癖になった
  12. 治せる欠陥で候補を落としていた
  13. 文字起こしでは見つからない欠陥がある
  14. 70分かけた学習素材が、ネットワークの一瞬の瞬きで消えた
  15. "ja" を "JP" と書いて喃語モデルができるまで
  16. 登録経路が4つ、管理画面が0
    17. デプロイのたびに互いの成果を消していた ← いまここ
  17. 測れていないものを閾値で追い込むと、必ず失敗する

知見の元になったノートは 拡散TTSから実用ボイスを量産する製造パイプライン にまとめてあります。


元記事: https://forge.workstyle.tech/blog/who-is-rolling-back-whom/?utm_source=qiita&utm_medium=crosspost&utm_campaign=who-is-rolling-back-whom

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?