こんにちは!日々のPython開発、本当にお疲れ様です。
突然ですが、皆さんはPythonで書いたコードがDockerコンテナの中で動いている時、バグに直面してどうやってデバッグしていますか?
「あちこちに `print()` を仕込んではコンテナをビルドし直し、ログを確認する……」なんて泥臭いやり方で、貴重な時間を溶かしていませんか?
もし心当たりがあるなら、今日でその非効率なスタイルとはお別れしましょう。
今回は、Python標準の最強デバッガー `pdb`(および高機能版の `ipdb`)を、Docker環境下で完璧に動かすための極意を伝授します。
これをマスターすれば、コンテナの奥底でうごめくバグの尻尾をその場でガッチリ掴み、変数の値を自由自在に書き換えながら原因を瞬時に特定できるようになります。毎日のコーディングが劇的に楽になりますよ。さあ、一緒に扉を開けましょう!
—
1. なぜDocker環境でのpdbは一筋縄ではいかないのか?
まず、敵を知るために「なぜDocker×pdbはハマりやすいのか」そのメカニズムをサクッと整理しておきましょう。
通常のローカル環境であれば、ターミナルでPythonスクリプトを実行し、コード内に `breakpoint()` と書いておけば、そこで処理がピタッと止まり、あなたのキー入力を待ち受けてくれます。
しかし、これがDockerコンテナの中になると話が変わります。
- 標準入出力(stdin/stdout)の壁: コンテナはバックグラウンドで孤立して動いているため、あなたの手元のキーボードからの「デバッグの指示(次へ進め、変数の中身を見せろ等)」が、コンテナ内のPythonプロセスに届きません。
- TUI(テキストUI)の喪失: `pdb` は対話型のツールです。プロセスがアタッチされていないと、入力を受け付けずにそのままフリーズするか、プロセスが即座に異常終了してしまいます。
この壁を突破し、「手元のターミナルとコンテナ内のPythonを直接繋ぐ」 ための正しい武装方法を、これからステップ・バイ・ステップで解説します。
—
2. 基礎セットアップ:Docker環境をデバッグ仕様にする
まずは、コンテナ内で `pdb`(今回はよりリッチなカラー表示や補完が効く `ipdb` も含めて)を動かすための舞台を整えます。
準備するファイル構成
今回はシンプルかつ実践的な構成として、以下の3つを用意します。
1. `Dockerfile`
2. `docker-compose.yml`
3. `app.py`(わざとバグを仕込んだサンプル)
① Dockerfile
Python環境の構築と、デバッグに必須のパッケージをインストールします。
軽量かつ堅牢なPython公式イメージを採用
FROM python:3.11-slim
コンテナ内の作業ディレクトリを指定
WORKDIR /app
デバッグを快適にするため、ipdb とその依存関係をインストール
※ ipdbは内部でpdbを拡張しているため、使い勝手が圧倒的に向上します
RUN pip install –no-cache-dir ipdb
アプリケーションのコードをコンテナにコピー
COPY . /app
コンテナ起動時にPythonスクリプトを実行
CMD [“python”, “app.py”]
② docker-compose.yml (ここが最重要のキモ!)
Docker環境で `pdb` を動かす場合、最も重要なのは 「標準入力をつなぎっぱなしにする(TtyとStdinを有効にする)」 ことです。ここを忘れると絶対にデバッグできません。
version: ‘3.8’
services:
web:
build: .
container_name: python_debug_lab
# 【最重要】ホスト側の端末とコンテナを標準入出力で結合する設定
stdin_open: true # -i オプション相当(標準入力を開く)
tty: true # -t オプション相当(擬似TTYを割り当てる)
volumes:
# ソースコードをホストと同期させ、コンテナを再ビルドせずに即座に反映させる
- .:/app
> 先輩からのアドバイス: `stdin_open: true` と `tty: true`。この2つの呪文をComposeファイルに書くことで、Dockerコンテナがあなたのデバッグコマンドを受け入れる準備を完了します。
③ app.py (デバッグ対象のコード)
意図的にバグ(ゼロ除算)を仕込んだスクリプトです。ここにブレークポイントを仕掛けます。
import time
def calculate_discount(price, rate):
# あえてここで処理を止めるためのブレークポイントを設置
# Python 3.7以降なら標準で用意されている関数です
breakpoint()
discounted_price = price / (1 – rate)
return discounted_price
if __name__ == “__main__”:
print(“=== デバッグ検証アプリ起動 ==-“)
price = 1000
rate = 0.0 # これが原因で ZeroDivisionError が起きる想定
result = calculate_discount(price, rate)
print(f”割引後の価格: {result}”)
—
3. 精度高いHelloWorld!実際にデバッグセッションを体験する
さあ、舞台は整いました。実際にコンテナを立ち上げ、`pdb`(ipdb)の世界に飛び込んでみましょう。
Step 1: コンテナをビルドして起動する
ターミナルを開き、以下のコマンドを叩いてコンテナをフォアグラウンドで起動します。
docker compose up –build
コンテナが起動すると、`app.py` が実行され、あっという間に `breakpoint()` の箇所で処理が一時停止します。Dockerのログ出力(ターミナル)を見てみてください。次のような画面になっているはずです。
python_debug_lab | === デバッグ検証アプリ起動 ==-
python_debug_lab | > /app/app.py(6)calculate_discount()
python_debug_lab | -> discounted_price = price / (1 – rate)
python_debug_lab | (Pdb)
キターーー!`(Pdb)` というプロンプト(入力待ち受け)が出ていますね!これが、コンテナの中のPythonプロセスと直接対話している証拠です。
Step 2: pdbの基本コマンドで内部を覗き見する
ここから先は、手元のキーボードでコマンドを叩き、コンテナの脳内をハックしていきます。
1. 変数の値を確認する (`p` コマンド)
現在の `price` や `rate` がどうなっているか確認してみましょう。
(Pdb) p price
1000
(Pdb) p rate
0.0
2. 変数の値をその場で書き換えてみる!
「もし `rate` が `0.5` だったらどうなるか?」を試してみましょう。コードを書き直す必要はありません。その場で変数を書き換えられます。
(Pdb) rate = 0.5
(Pdb) p rate
0.5
3. 次の行へ進める (`n` = next コマンド)
書き換えた状態で、次の計算処理を実行してみます。
(Pdb) n
> /app/app.py(7)calculate_discount()
-> return discounted_price
エラーにならずに無事次の行へ進めました!計算結果を確認してみましょう。
(Pdb) p discounted_price
2000.0
4. デバッグを終了して続行する (`c` = continue コマンド)
検証が終わったら、プログラムを最後まで走らせます。
(Pdb) c
これでプログラムが無事に終了し、コンテナの処理も完了します。
—
4. 【応用】すでにバックグラウンドで動いているコンテナに飛び込む(`docker attach`)
「コンテナを `docker compose up -d` でバックグラウンド起動しちゃったよ!」
「すでに動いている本番・検証用コンテナのなかに、後から割り込んでデバッグしたい!」
そんなシチュエーション、実務ではよくありますよね。その場合は `docker attach` を使います。
手順
1. バックグラウンドで起動中のコンテナ名(またはID)を確認します。
docker ps
2. 稼働中のコンテナの標準入出力セッションにアタッチします。
docker attach python_debug_lab
3. もし途中で `breakpoint()` にヒットしていれば、そのまま手元のターミナルに `(Pdb)` プロンプトが出現し、対話デバッグを開始できます。
> ⚠️ 現場で役立つ超重要アテンション(注意点):
> `docker attach` を使っている最中に、キーボードでうっかり `Ctrl + C` を押さないでください!
> 通常、`Ctrl + C` はプロセスを中断するためのものですが、Dockerアタッチ中にこれをやると、コンテナ内のPythonプロセスそのものが強制終了(SIGINT)してコンテナがダウンしてしまいます。
> デバッグセッションから安全に抜け出したいときは、`c` (continue) で最後まで走らせるか、`q` (quit) でデバッガーを終了させてください。
—
まとめ:あなたの開発スピードを何倍にも跳ね上げるために
お疲れ様でした!ここまで読み進めていただいたあなたは、もう「printデバッグの呪縛」から解放される準備ができています。
- Dockerでpdbを使うなら `stdin_open: true` と `tty: true` が必須。
- `breakpoint()` を仕込んでおけば、コンテナが自動で立ち止まって対話の準備をしてくれる。
- 動いているコンテナには `docker attach` で華麗に接続する(ただし Ctrl + C の誤爆には注意!)。
この手法を身につければ、複雑なマイクロサービスが絡み合うコンテナ群の中であっても、まるで自分の手足のようにコードの挙動をコントロールできるようになります。
難しく考える必要はありません。まずは手元の小さなプロジェクトで、今日の夜でも試してみてください。「お、ここで値を変えられるぞ!」という感動が、あなたのエンジニアライフをさらに楽しくしてくれるはずです。それでは、快適なデバッグライフを!