Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

デプロイを任されたので、教わった通りにデプロイしたら障害になった件 ~俺のやらかしを越えてゆけ~

Techouse
October 25, 2024

デプロイを任されたので、教わった通りにデプロイしたら障害になった件 ~俺のやらかしを越えてゆけ~

Techouse

October 25, 2024
Tweet

More Decks by Techouse

Other Decks in Programming

Transcript

  1. ©Techouse All Rights Reserved P5 ビッグサイトの思い出 • 営業イベントにSalesとして参加 ◦ 出展者がブースでサービスを宣伝し、

    来場者は興味のあるブースで話を聞く • 弊社サービス『クラウドハウス』 も出展 ◦ ⽣⾝の相⼿に対し、⾃分の⾔葉で ⾃社プロダクトを売ることに挑戦 来場者でごった返す会場
  2. ©Techouse All Rights Reserved P8 ビッグサイトの思い出 • あの会社はああやってサービスを訴求しているのか… • 企業の担当者はそういう課題感を持ってるのか…

    • 俺が作ったあの超⼤作機能、相⼿に全然刺さらねえ… ⇒これがプロダクト作りに⽋かせないinsightってやつか…!
  3. ©Techouse All Rights Reserved P10 ビッグサイトの思い出 〜郷に⼊っては郷に従え〜
 実際にあった怖い話 • エンジニアも顧客の前に⽴ってみてはいかが? ◦

    ただし、⾝なりには気をつけよう! ◦ 我々エンジニアは時にあり得ない判断を平然と やってのける
  4. ©Techouse All Rights Reserved P11 上野 友輔@izumitomo • 株式会社Techouse ◦

    去年ブースでガラポンやってた会社 • Rails歴 2年 ◦ 普段はRails x GraphQL で開発 • Kaigi on Rails初登壇🙌 ⾃⼰紹介
  5. ©Techouse All Rights Reserved P14 デプロイとは • デプロイとは開発したアプリケーションを本番環境に移⾏し、 実際にユーザが利⽤できる状態にする⼀連のプロセス ◦

    CI/CDプロセスが整備されると⾃然と⽬が向かなくなる ◦ 特に初学者はブラックボックスになりがち ◦ そしてなにかとやらかす 󰷻 😑 🤯
  6. ©Techouse All Rights Reserved P15 今⽇話すこと‧持ち帰ってもらえるもの 今⽇話すこと • デプロイで実際にやらかした失敗とその対応 持ち帰ってもらえるもの

    • Railsのダウンタイムのないデプロイにおける注意点 • 初学者がデプロイに⽬を向けることのメリット
  7. ©Techouse All Rights Reserved P24 • あっさりデプロイを託されるも、流⽯に不安だったので どのようにデプロイされているかを軽く整理 ◦ RailsアプリケーションをAmazon

    ECS on Fargateで管理 ◦ GitHub Actionでスクリプトを実⾏してデプロイ ◦ ゼロダウンタイムデプロイでサービスは閉塞しない
  8. ©Techouse All Rights Reserved P25 デプロイの流れ Docker Build ECR push

    migration実⾏ コンテナの更新 mainにMerge
  9. ©Techouse All Rights Reserved P28 • さっそくデプロイの機会が訪れる ◦ ある機能開発でDBの変更のみを先に本番に反映しておくことに ▪

    テーブルにカラムを追加するだけの変更で追加したカラムに 依存するアプリケーションコードは存在しない
  10. ©Techouse All Rights Reserved P47 周辺知識の整理 • バインドのタイミングに関して2つの⼿法がある ◦ 静的プレースホルダ

    ▪ データベース側でバインドする ◦ 動的プレースホルダ ▪ アプリケーション側でバインドする
  11. ©Techouse All Rights Reserved P48 周辺知識の整理 • バインドのタイミングに関して2つの⼿法がある ◦ 静的プレースホルダ

    ▪ データベース側でバインドする ▪ PreparedStatementに関係があるのはこっち ◦ 動的プレースホルダ ▪ アプリケーション側でバインドする
  12. ©Techouse All Rights Reserved P49 静的プレースホルダのバインドの流れ 1. アプリケーション側は以下の2つを送る a. プレースホルダが⼊ったSQL⽂

    b. 実際のパラメータの値 2. データベース側はバインドを⾏い、SQL⽂を実⾏する SELECT * from users where id = $1 AND LIMIT $2 
 $1 = 1, $2 = 1 SELECT * from users where id = 1 AND LIMIT $2 1
 1

  13. ©Techouse All Rights Reserved P50 1. アプリケーション側は以下の2つを送る a. プレースホルダが⼊ったSQL⽂ SQL⽂の構造が確定しているのでSQL実⾏前に構⽂解析ができる

    ⇒構⽂解析済みのSQL⽂をPreparedStatementとして、コネクションが  切れるまで保持する b. 実際のパラメータの値 2. データベース側はバインドを⾏い、SQL⽂を実⾏する SELECT * from users where id = $1 AND LIMIT $2 
 静的プレースホルダのバインドの流れ
  14. ©Techouse All Rights Reserved P51 PreparedStatementについて • PREPARE⽂で作成し、EXECUTE⽂で実⾏できる EXECUTE⽂の実⾏結果
 (SELECT

    name FROM users where id = 1 LIMIT 1 が実⾏される)
 PREPARE⽂の実⾏結果 (返り値はPREPARE)
  15. ©Techouse All Rights Reserved P58 1. アプリケーション側は以下の2つを送る a. プレースホルダが⼊ったSQL⽂ ⇒構⽂解析済みのSQL⽂をPreparedStatementとして、コネクションが 

    切れるまで保持する b. 実際のパラメータの値 2. データベース側はバインドを⾏い、SQL⽂を実⾏する SELECT * from users where id = $1 AND LIMIT $2 
 再掲:静的プレースホルダのバインドの流れ
  16. ©Techouse All Rights Reserved P62 古いPreparedStatement テーブルにカラムが追加され、PreparedStatementが古くなると • 構⽂解析時に確定したresult_types •

    実際にstatementを実⾏して返ってきた結果の型 が異なる状態に陥ってしまう 発⽣した例外クラス:ActiveRecord::PreparedStatementCacheExpired メッセージ:ERROR: cached plan must not change result type
  17. ©Techouse All Rights Reserved P64 Railsの中を読む • 発⽣した例外を元に調べるとすぐに該当のコードが⾒つかった • トランザクションの中のみraiseするようになっている

    ◦ トランザクション外だとstatementを削除してリトライする ◦ トランザクション内ではPostgresql仕様上、リトライで解消できない activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb(7.2-stable)

  18. ©Techouse All Rights Reserved P73 余談:MySQLについて • RailsのPreparedStatementのデフォルトの利⽤設定について、 MySQLは利⽤しない設定になっている •

    MySQLでもRails7.2以降からデフォルトで利⽤する動きがあった ◦ しかしバグ報告により現在もデフォルトは利⽤しない設定となっている
  19. ©Techouse All Rights Reserved P76 若かりし頃 
 ⼀つ⼀つ丁寧にデプロイしていたあの頃も今は昔 • DBとアプリケーション間の不整合は起きるか?

    • 仮に起きる場合、どのような問題が発⽣する? • その場合の対処は何が考えられる? • …
  20. ©Techouse All Rights Reserved P86 現状把握 • 調べてみると確かに顧客のエクスポートのジョブのステータス が「実⾏中」のまま数時間が経過していた ◦

    本来であればエクスポートは⻑くても数分で終わる ◦ 既にそのジョブはSidekiqのWorker内に存在しない ▪ ジョブの実⾏状況を監視するSidekiq Web UIというツールがある
  21. ©Techouse All Rights Reserved P87 現状把握
 • ログをもとにエクスポート処理のコードを読みながら 原因を探っていく ◦

    しかし原因がわからない… ▪ ローカルで⾊々動かしてみても特に何も得られず ▪ 異常終了しているのに例外を検知できていないのが謎
  22. ©Techouse All Rights Reserved P106 おさらい:シグナル • シグナルとはソフトウェア割り込みの⼀種 ◦ 特定のイベント発⽣時にプロセスに通知するための仕組み

    ◦ Ctrl-CやCtrl-Zで常⽇頃お世話になっているアレ • SIGTERMシグナルはプロセスの安全な終了のために使われる ◦ SIGTERM受信時の動作はプログラムに委ねられている ▪ 例えばプロセスの強制終了を伝えるSIGKILLを受信した場合、 制御はOSに委ねられるためプログラムは関与できない
  23. ©Techouse All Rights Reserved P109 おさらい:シグナル 別のターミナルから kill -SIGTERM 25988 

    を実⾏してSIGTERMを送ると、sleepは以下のように終了する
  24. ©Techouse All Rights Reserved P111 quiet ジョブ実⾏中 キュー SIGTERM キュー

    もうすぐ⾃分停⽌するんで 新規ジョブは受け付けません
  25. ©Techouse All Rights Reserved P164 • 運⽤でたまたま回避できていた ◦ Sidekiq Web

    UIを使えばプロセスをquietにできるので 可能ではある • 全事業部で修正を進めていくことになった
  26. ©Techouse All Rights Reserved P165 余談:技術責任者に報告 • ECSのタスク定義というインフラ周りの設定に⼿を⼊れたこと もあり、今回の件を技術責任者のYさんに伝えておくことに ◦

    Yさんは社内の全プロダクトをインフラ⾯から⼿厚くサポート ◦ 過去にYさんがECSタスクのオートスケールイン‧スケールアウト でGraceful Shutdownを導⼊しようとしていた ▪ もしかしたら今回の件が役に⽴つかもしれない
  27. ©Techouse All Rights Reserved P169 
 
 現状、原因としてはいくつか考えられて、 まずECSのstopTimeoutが短すぎるという可能性、 そしてそもそものシグナルハンドラにバグがある厄介な

    パターン、別の可能性として間に挟まっているCDNのタ イムアウトが考慮できてないのもありえて… ……だから………かもしれないし……
  28. ©Techouse All Rights Reserved P170 
 
 現状、原因としてはいくつか考えられて、 まずECSのstopTimeoutが短すぎるという可能性、 そしてそもそものシグナルハンドラにバグがある厄介な

    パターン、別の可能性として間に挟まっているCDNのタ イムアウトが考慮できてないのもありえて… ……だから………かもしれないし…… そもそもSIGTERM届いてないかもです…
  29. ©Techouse All Rights Reserved P175 まとめ(初学者向け) • デプロイ業務とやらかしを通じて多くの学びを得た ◦ 普段の開発業務では関⼼すら持てていなかった技術領域に

    デプロイという⾃然な切り⼝から⾶び込むことができた ◦ 当時まだ駆け出しだった⾃分にとって⼤きな財産になった • 今⼀度デプロイに⽬を向けてみてはいかが?
  30. ©Techouse All Rights Reserved P176 まとめ(初学者をフォローするミドル層向け) • 若⼿の成⻑を促すために、時には⾝の丈以上の責任ある仕事を 託す勇気と、⼼置きなくチャレンジできる環境づくりが⼤切 ◦

    全⼒のチャレンジにはやらかしがつきもの ▪ でもそのやらかしは必ず彼らの糧になる ◦ 転ばぬ先の杖というより、転んだ先の杖を⽤意しておく ▪ 当時の⾃分にデプロイを託すというチームの決断の意味と、 その環境を整えてくれていたことを後になって気付いた