本番と開発の環境の違いで、本番障害を起こしてしまった…という経験がないシステムエンジニアなど存在しないと思います。
どういう点で問題が起こり、どういう対策をすべきだったのか、経験をもとに書きだしてみます。
(ここで開発と呼んでいるのは、一応本番以外のステージング、テスト環境も含みます)
ミドルウェア設定
Webサーバ
同時接続可能数(MaxClients)
開発環境では、あまり気にしないパラメータですが、本番では慎重に設定しないと次のようなトラブルを起こします。
- MaxClientsが小さすぎて、システム間連携APIがタイムアウトしてしまった。
- MaxClientsが大きすぎて、サーバのメモリが枯渇した。
本番でのトラフィックが正確に予測できなければ、デフォルトのままにしておいて、様子をみながらチューニング、の方が下手に設定するより事故が少ないように思います。
リライト
コンシューマ向けのサイトだと、SEO対策などでリライトの設定が本番環境のたくさん入っている場合があります。まったく同じ設定を開発環境に用意してない場合、URLのバッティングによるエラーが起こることがあります。
そもそもWebサーバレベルでのリライトに頼り過ぎるのは、テストしづらく保守が大変になるのでやめましょう。URLを柔軟に変更することが予測されるアプリケーションでは、ルーティングのライブラリを使うようにします。
ちなみにRails2のルーティングライブラリをJavaに移植したものがこちらになります。
https://github.com/kawasima/http-request-router
そうした上で、テスト時には、本番と同じリライト設定で実行するようにします。
DBサーバ
プロダクトによって大小違いがあるので、なんとなくOracle前提のお話として読んでください。
ステートメントの最大数
Oracleでいうところの「ORA-01000 最大オープン・カーソル数を超えました」のエラーに出くわした経験ある方は多いのではないでしょうか。DBのコネクション数はある程度予測ができますが、ステートメントの数は使っているフレームワークによって挙動が違ったりするので、ちょうどいい値を設定するのは割と難しかったりします。
開発環境では変に多くのカーソルがオープンされていなかを検出するために小さめの値にしておき、本番ではORA-01000が出てしまうリスクの対処を優先し、大きめの値にしておくのがよいかと思います。
スキーマ名
セキュリティのためという名目でスキーマを細かく分ける派閥があるようですが、そうなるとSQL中にもスキーマをつけてオブジェクトにアクセスするようになります。
そして本番と開発でスキーマ名が異なるので、本番持っていってSQLエラー、なんて話をまれに耳にします。
この対策は、とにかくアプリケーションからみたときのオブジェクト名に、開発・本番で差を生まないようにすることです。シノニムを使って一意な名付けをするのが簡単です。
シノニム/ビュー
開発では、実テーブルになっているが、本番ではシノニムやビューでアクセスという設計を、過去のしがらみなどでしなきゃいけないことがあります。例えばJDBCのDatabaseMetaDataを使って、カラムやインデックスの情報を取得しようとすると、シノニムやビューからはとれないものがあるので、そこで初めて問題発覚ということがあります。
少なくとも本番移行直前のテスト/ステージング環境は、本番環境とおなじスキーマ・オブジェクト構成にするようにしましょう。
シーケンスの値
開発と本番でどうしても同じに出来ないものの一つにシーケンスの値があります。これはデータ移行時の問題ですが、本番のデータ移行したときにシーケンスの初期値設定を忘れていて、リリース後のINSERTでの一意制約違反エラーというのを聞いたことがあります。
つい忘れがちなので、全てのシーケンスについて初期値を何に設定するかも、データ移行手順に入れるようにしましょう。
インデックス
初期リリース時点では、開発と本番で一致させてると思いますが、長く運用すると、緊急で本番環境にインデックスを作成したり、削除したりで、ズレが出てくる、というかズレがあるかどうか認知できていないことが多いでしょう。
インデックスの数は増える方向にいっても、なかなか減りはしないことでしょう。一つのテーブルに多くのインデックスが付いていると、オプティマイザの判断ミスの機会も増えます。データ件数が増えると、極端にSQLが遅くなったという場合は、不要なインデックスが無いか探し要らないものを削除すると状況が改善することがあります。
- 定期的に本番のインデックスを調査し、開発環境と合わせる。
- 不要になったインデックスを削除する
など、して突然のレスポンス低下リスクを減らしましょう。
JVM
ヒープサイズ
アプリサーバのヒープサイズは気にしても、バッチアプリケーションのヒープ設定に無頓着だったというのは時々聞きます。ヒープサイズが小さすぎて、バッチの本番初回稼働でOutOfMemoryで落ちたという経験をお持ちの方もいるでしょう。
本番で稼働するすべてのjavaコマンドに、-Xms
と-Xmx
を付けるようにしましょう。
アプリケーションの設定
ロケール
ロケールの違いは、ログが文字化けするなどの小さな問題から、致命的な問題までさまざま引き起こします。
致命的なのは、データベースで日付を文字列型に格納したり、文字列で取り出そうとすると、暗黙の型変換が行われるので、ロケールの違いによってデフォルトフォーマットが異なり、違う文字列が返ってきちゃうものです。
INSERT xxx(DATE_STR) VALUES(SYSDATE);
それで違うフォーマットで、他システムにデータ転送しようとして、軒並みエラーになるなど大変な問題につながることがあります。
また、国際化対応でクライアントからのリクエストによってロケールを切り替える機能をフレームワークが持っている場合、ロケールによっては、メッセージが出ないとか変なメッセージがでちゃうなどの問題を起こす場合があります。
application_[ロケール名].properties
みたいにファイルをロケールごとに分けて設定できるフレームワークで、このシステムは日本語しか使わないので、ということでapplication_ja.properties
のファイルしか置いてない場合、ja以外のロケールでリクエストが来ると、デフォルトのプロパティファイルが無いので、フレームワーク側でメッセージを組み立てられず、前述のような問題に繋がることがあるのです。
ロケールの問題の対策は、
- サーバのロケールは本番/開発で一致させること
- クライアントからのリクエストごとにロケールを切り替える機能をフレームワークがもつ場合は、複数のロケールでテストすること
になります。
暗号鍵
危ない話ですが、混沌としているプロジェクトだと暗号鍵の書き換えを忘れることがあります。
特にセキュリティ対策を暗号化に頼りすぎているアーキテクチャの場合は、内部の開発者に全ての本番データが筒抜けになる恐れがあるので、鍵が本番と開発で必ず鍵が異なること、移行時に確認するようにしましょう。
URL
絶対パスで書かなきゃいけないところを書き換え忘れると、リンク切れや開発環境のURLを漏洩させるなどの問題があります。
特に、
- HTTPS <-> HTTPを行き来するリンク
- 画像/Javascript/CSSのパス (本番ではCDNを使う場合など)
- リダイレクト先のURL (LocationヘッダはフルURLで書くため)
あたりは、問題起こしやすいところです。
設計時からURLの違いをどう吸収するかを、考慮しておく必要があります。
また、実店舗への予約や高額商品の購入などの機能をテストしようとするとき、リンクが本番を指していて、いつの間にか本番でそれら完了ページまでいっちゃったという事故も時々起こります。
開発環境はロゴやCSSのbodyのbackground-imageを書き換えて、一目で自分が触っている環境が分かるようにしておくとよいです。
その他
その他、環境差異として定番の以下の項目も、本番の設定が正しいかの検証を移行手順にいれましょう。
- 連携先のアドレス、タイムアウト値
- トランザクションのタイムアウト値
- メールのFromやSubjectの値
- ファイルパス
ビルド環境の違い
Javaの場合、ふつうにアプリケーションのバグなのですが、リフレクションで取得できるメソッドやフィールドの順番に依存したコードや、HashMapやHashSetなのに、キーの順番が保証されることを期待したコードがあって、コンパイラやJVMのバージョンの違いによって、開発と本番の挙動が変わることがあります。
開発と本番用のビルドでコンパイラとバージョンを合わせておくのがフェイルセーフです。
環境による差異の設定
一口に設定ファイルといっても、以下の2つの思惑があります。
- ちょっと書き換えれば、アプリケーションの挙動を変えれるよう
- 環境によって変えるべき項目を設定ファイルにまとめる
これらが入り混じると、環境ごとに何を書き換えなきゃいけないのかがわからなくなって、設定ミスによる本番障害の確率がぐんと増します。必ず、環境によって書き換えなきゃいけないものと、そうでないものは分離するようにしましょう。
まとめ
開発/本番一致は、The Twelve-Factor Applicationの重要なテーマですが、現実には、「有償のプロダクトを開発者ごとに買ってられるかっ」とか、「ローカルマシンは貧弱なんで本番と同じ設定なんかできないよー」とかで、そうはいかないことは多いでしょう。
新規開発の場合は、こういった環境差異は慎重に洗い出して、レビュー&テストやるかと思いますが、保守フェーズでは案件規模も小さく気が回らないこともあります。そんなときでも、特に、その設定変更をしたら、この機能をリリースしたら、どういう影響がでるかは、どんなに切羽詰まっていても、ちゃんと検討するにこしたことありません。