DockerビルドでPythonをソースからビルドするとGCCがSegmentation faultする話

DockerビルドでPythonをソースからビルドするとGCCがSegmentation faultする話

こんにちは!Qualitegプロダクト開発部です!


本日は Docker環境でPythonをソースからビルドした際に発生した、GCCの内部コンパイラエラー(Segmentation fault) について共有します。

一見すると「リソース不足」や「Docker特有の問題」に見えますが、実際には PGO(Profile Guided Optimization)とLTO(Link Time Optimization)を同時に有効にした場合に、GCC自身がクラッシュするケースでした。

ただ、今回はDockerによって問題が隠れやすいという点もきづいたので、あえてDockerを織り交ぜた構成でのPythonソースビルドとGCCクラッシュについて実際に発生した題材をもとに共有させていただこうとおもいます

同様の構成でビルドしている方の参考になれば幸いです

TL;DR

  • Docker内でPythonを --enable-optimizations --with-lto 付きでソースビルドすると
    GCCが internal compiler error(Segmentation fault)で落ちることがある
  • 原因は PGOビルド中(プロファイル生成段階)にGCCの最適化パスがクラッシュする可能性
  • リソース不足ではなく、コンパイラ内部エラー(ICE)
  • 解決策は 最適化フラグを外すこと
    実行性能差は 数%〜20%程度(用途依存) で、CIやDockerでは安定性優先が無難


発生した問題

Dockerイメージ内で Python をソースからビルドしていたところ、make の途中で突然ビルドが失敗してしまいました

ログの本質的に重要な部分を抜粋すると次のような状態です

during RTL pass: sched2
In function 'zlib_Compress_flush':
internal compiler error: Segmentation fault
make[1]: *** [Makefile: profile-gen-stamp] Error 2
make: *** [Makefile: profile-run-stamp] Error 2

ポイントは以下です。

  • internal compiler error
    → ユーザーコードではなく、GCC自身がクラッシュ
  • during RTL pass: sched2
    → GCCバックエンド最適化パス中の異常
  • profile-gen-stamp の失敗
    → PGOの「プロファイル生成用ビルド」段階で停止

重要な事実:落ちたのは「PGOの第1段階」

当初、「PGOの2回目(-fprofile-use)」で落ちたのでは?と疑いましたが、
実際のログを精査すると -fprofile-generate が付いた段階、つまり

PGOの“プロファイル生成用ビルド”

で GCC が Segmentation fault を起こしていることがわかります

つまり、これは以下を意味します

  • Python側のコード不具合ではない
  • 実行フェーズにすら到達していない
  • PGO + LTO が有効な状態で、GCCの最適化処理が破綻している可能性が高い

なぜ今まで問題なかったのか?

ちなみに、この問題は、ある日突然発生したように見えました。

が、実際には、以前から潜在的に存在していた可能性があります。

Dockerビルドキャッシュの影響

Dockerは RUN 命令単位でビルド結果をキャッシュします。

  • Dockerfileが変更されない限り
    → Pythonのビルド工程はキャッシュから復元
  • Dockerfileを少しでも変更すると
    → その行以降のキャッシュはすべて無効化

キャッシュが無効化された理由

Dockerfileに何らかの変更を加えると、その行以降のすべてのキャッシュが無効化されます。

つまり、

「たまたまキャッシュが使われていただけで、
実際には壊れたビルド手順がずっと潜んでいた」

という状態でした


PGO(Profile Guided Optimization)とは

PGOは、プログラムの実行傾向を元に最適化を行う仕組みです。

Pythonの --enable-optimizations は、内部的に以下の流れを取ります。

  1. プロファイル生成用にビルド(-fprofile-generate)
  2. 生成したPythonを実行してプロファイル収集
  3. 収集した情報を使って再ビルド(-fprofile-use)

今回の問題は 1番目の段階ですでにGCCがクラッシュしています。

PGOビルドの流れ

さらに今回のビルドでは --with-lto を指定していました。

LTOを有効にすると、

  • コンパイル単位をまたいだ最適化
  • GCC内部の解析対象が大幅に増加

してくれます

さらに、

PGO + LTO を同時に有効化すると、

  • プロファイル情報
  • 中間表現(RTL)
  • 複雑な最適化パス

が重なり、GCC内部の既知・未知のバグを踏み抜きやすい状態になってしまいます、結果的に。

今回の sched2 パスでのSegfaultは、まさにその典型例でした。。


解決策

そこで、今回のような問題への解決策としていくつかあげてみたいとおもいます

方法1: PGOとLTOを無効化(推奨)

結論からいうと、この方法1を採用しました。

configureのオプションについて変更を示します

変更前

./configure --enable-optimizations --with-lto

変更後

./configure

メリット

  • 確実にビルドが成功する
  • ビルド時間が大幅に短縮される(PGOは2回ビルド)
  • CI/CDやDocker環境で安定

デメリット

  • 実行性能が 数%〜20%程度低下する可能性
    (用途・ベンチマークに強く依存ではあります)

多くのサーバ用途・バッチ用途では 実用上問題にならないケースがほとんどです。

方法2: LTOのみ無効化

./configure --enable-optimizations
  • PGOは維持
  • LTOによる複雑化を回避

ただし GCCのPGO関連バグ自体は残るため、環境によっては再発する可能性ありです

方法3: 並列度を下げる(非推奨)

make -j2
  • メモリ圧迫を緩和できる場合はある
  • しかし ICEは本質的に解決しない
  • ビルド時間が著しく増加

方法4: ソースビルドを避ける

# deadsnakes PPAからインストール
RUN add-apt-repository ppa:deadsnakes/ppa \
    && apt-get install -y python3.13
  • ディストリビューション提供のPython
  • 信頼できるpre-builtパッケージ

を使うことで、この種の問題は完全に回避できます。


推奨するDockerfile例(安定性重視)

方法1を採用してビ安定的にビルドをしたいときは以下のようにします。今回はDockerつかってるのでDockerfileは以下のような感じになります

# ===========================================
# Python(ソースビルド・安定版)
# ===========================================
# 注意:
# --enable-optimizations / --with-lto は
# GCC内部エラー(ICE)を引き起こす可能性があるため使用しない

ARG PYTHON_VERSION=3.13.5

RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz \
    && tar xzf Python-${PYTHON_VERSION}.tgz \
    && cd Python-${PYTHON_VERSION} \
    && ./configure \
    && make -j$(nproc) \
    && make install \
    && cd .. \
    && rm -rf Python-${PYTHON_VERSION} Python-${PYTHON_VERSION}.tgz

教訓

1. Dockerキャッシュは問題を隠す

キャッシュは便利ですが、「壊れた手順がたまたま実行されていない」だけの場合があります。
定期的な --no-cache ビルドは重要です

2. 最適化フラグは安定性とトレードオフ

PGOやLTOは強力ですが、DockerやCIでは安定性優先が現実的です

3. internal compiler error は疑うべき

Segmentation fault + internal compiler error は
ほぼ確実に コンパイラ側の問題です。
コードを疑う前に、ビルドフラグを疑いましょう

それでは、次回またお会いしましょう!

Read more

Ubuntu 24.04 の GPU サーバーが shutdown で固まる。犯人は HWE カーネルだった

Ubuntu 24.04 の GPU サーバーが shutdown で固まる。犯人は HWE カーネルだった

Ubuntu 24.04 の GPU 推論サーバーが shutdown で固まり、電源長押しになった。前回ブートのジャーナルで追うと、HWE カーネル 7.0 系と NVIDIA 595 open の組み合わせで CUDA プロセスの SIGKILL 直後にページ管理の異常が記録されていた。カーネルと対応する NVIDIA モジュールの組を GA 6.8 系に切り替えて正常な停止を確認し、うっかり HWE に戻らない 4 層の防御まで入れた記録。

By Qualiteg プロダクト開発部
ゼロから作るコーディングエージェント【第5回】ローカルLLMを一級市民にする。16GBのGPU2枚でWebシステムを自走で作らせるまで

ゼロから作るコーディングエージェント【第5回】ローカルLLMを一級市民にする。16GBのGPU2枚でWebシステムを自走で作らせるまで

RTX A5000(24GB)1枚で始めて、ローカルモデル7種類が全滅。RTX A4000(16GB)2枚にQwen3.6-35B-A3BをAWQ 4bitで載せ、fp8のKVキャッシュと98kの文脈で、認証つきWebシステムを3本連続93.8〜96.5点で作らせるまでの構成と落とし穴。モデルの癖、量子化の劣化、文脈長、壊れたツール呼び出しの直し方を、測った数字で書きます。

By Qualiteg プロダクト開発部
ゼロから作るコーディングエージェント【第4回】自走の規律は「人がいない場面」のためのもの。人がいるなら止まって聞く

ゼロから作るコーディングエージェント【第4回】自走の規律は「人がいない場面」のためのもの。人がいるなら止まって聞く

評価ランで鍛えた「止まらない」規律を、人が答えられる場面にまで効かせていました。曖昧な指示に聞き返したモデルを押し戻して勝手に作らせる、断られた確認を形を変えて7回聞き直す、他人のプロセスの止め方を教える。6種類の使い方を演じて出た19件から、人がいる場面の設計と、設定の不備を1行で止める話を書きます。

By Qualiteg プロダクト開発部