TT Lab
はじめる
学ぶ 学習パス コース

デバッグ実戦

うちのサーバでは動くんですが

TT Labで続きを見る

一言でいうと

同じコードがここでだけ動かないとき、犯人はたいてい、環境変数、エンコーディング、権限、そしてシェルの値とプロセスの値が違うという事実のどれかです。

なぜ必要なのか

FDEが最もよく耳にする言葉が、「うちのサーバーでは動くんですが」です。そして、たいていその言葉は本当です。コードは同じで、環境が違います。

環境の違いは、コードの欠陥より見つけにくいものです。コードは読めば見えますが、環境には読むものがないからです。そのため、順序が必要です。

どう動くのか

現場で実際によく出る順に、4つを見ます。

1つ目は環境変数です。ないと失敗することを教えてくれるのは、たいていエラーメッセージ自身です。missing env API_TOKENのように。問題は、このメッセージを見ても「設定したんですが」という答えが返ってくる場合で、ほぼ必ず設定した場所と読む場所が違います。シェルでexportした値は、そのシェルの子プロセスにしか渡りません。systemdで起動したサービスは、その値を見られません。

2つ目はエンコーディングです。ハングルの顧客データで、圧倒的によく起きます。Windows環境で作られたCSVは、UTF-8ではなくEUC-KR(CP949)であることが多く、このファイルをUTF-8で読むと、文字化けするかデコードエラーで落ちます。fileコマンドがエンコーディングを常に正確に当てられるわけではないので、ハングルが文字化けして見えたら、iconv -f EUC-KR -t UTF-8で変換を試してみるのが最も早い方法です。

ここでの落とし穴は、自動変換に頼ることです。誤ったエンコーディングで読まれた文字列は黙って通過してデータベースに保存され、数週間後に「検索できない」という報告になって返ってきます。エンコーディングは、読む時点で確定させる必要があります。

3つ目は権限です。特に、資格情報ファイルがそうです。トークンファイルが644で置かれていると、同じサーバーの他のユーザー全員が読めてしまい、セキュリティ点検でこれ1つでプロジェクト全体の信頼が削られます。FDEは他人の家の鍵を扱う人なので、ここは特に重いのです。最小権限は礼儀ではなく、生き残るためのルールです。

4つ目は、シェルの値とプロセスの値が違うことです。これが最もよく人をだまします。ファイルを開く数の上限を例にすると、シェルでulimit -nが1048576を示していても、実際に動いているサービスの上限は1024かもしれません。確認すべき場所はシェルではなく、/proc/<PID>/limitsです。

同じ落とし穴が、何重にも繰り返されます。/etc/security/limits.confはPAMのログインセッションにしか適用されず、systemdのサービスには適用されません。コンテナの中のfreeやnprocはホストの値を示しますが、実際の制限はcgroupにあります。原則は1つです。設定ファイルではなく、実行中のプロセスで実測してください。

現場での姿

環境チェックスクリプトを1つ作っておくと、これらすべてを肩代わりしてくれます。必要な環境変数、ファイルの存在と権限、ロケール、データにアクセスできるかを順に確認し、最初の失敗で理由を言って止まるスクリプトです。

このようなスクリプトの価値は、診断時間ではなく会話の質にあります。「動かないんですが」の代わりに「envcheckがAPI_TOKENがないと言っています」という文が行き交えば、顧客企業の担当者とのやり取りが、3往復から1往復に減ります。

違いをなくす方向へ進む

環境の違いをうまく見つけることも重要ですが、より良い道は、違いが生まれる余地を減らすことです。引き継いだシステムを少しずつその方向へ押していくことも、FDEが残す価値です。

依存するものを一覧にします。このプログラムが何を必要としているかがどこにも書かれていないと、新しい環境で何が欠けているかを知る方法がありません。必要な環境変数、アクセスすべきパスとアドレス、必要なコマンドとそのバージョンです。この一覧が、そのまま先ほど作ったチェックスクリプトの中身になります。

デフォルト値を置いても、黙って済ませません。値がないときにもっともらしいデフォルト値に戻れば楽ですが、その事実がログに残らないと、後で「なぜ違う結果になるんですか」を説明できません。デフォルト値を使ったという事実そのものを1行残すだけで、調査時間が大きく減ります。

開始時に一度に確認して、落ちます。必要なものがないとき、実行の途中のあちこちで失敗する代わりに、開始してすぐすべてを検査し、ないものをまとめて知らせて止まるほうがよいです。そうすれば、3往復かかるものが1回で終わります。

環境をコードで書きます。手で設定したものは再現されず、再現されないものは、2つの環境がなぜ違うのかを説明する方法がありません。コンテナイメージでも構成管理ツールでも、その環境を作る手順がファイルとして残っていれば、「ここでだけ動かない」という文自体が成り立ちにくくなります。

この4つを一度に求めると、顧客は負担に感じます。そのため、今回経験した事故1つから始めるのがよいです。今回API_TOKENのせいで半日を使ったので、まずそれをチェックスクリプトに入れましょうという提案は受け入れられやすく、そのように1行ずつ増えたスクリプトが、数か月後にはかなり使えるドキュメントになります。

次のラボですること

環境チェックスクリプトが失敗している状態から始めて、失敗の原文を確保し、欠けている環境変数を特定し、EUC-KRのハングルCSVをUTF-8に復元し、トークンファイルに最小権限を適用して、チェックを通します。