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

【AI×CAD 第5回】CADだけでは伝わらない「なぜ」を残す。設計知の伝承をローカルLLMで始めるための考え方

【AI×CAD 第5回】CADだけでは伝わらない「なぜ」を残す。設計知の伝承をローカルLLMで始めるための考え方

CADには最終的に「何を作ったか」は残るのに、その形にした「なぜ」は形状だけからは分かりません。幾何の事実から問いを立てて設計者に答えてもらい、形状と判断理由を一緒に残す。変更前後の比較も使いながら、設計知の伝承とローカルLLMの活用を業務から考えます。

By Qualiteg コンサルティング
ゼロから作るコーディングエージェント【第1回】「最後までやりきる」が難しい理由と、ターンを回す係と止める係を分ける設計

ゼロから作るコーディングエージェント【第1回】「最後までやりきる」が難しい理由と、ターンを回す係と止める係を分ける設計

コーディングエージェントを自作すると、いちばん難しいのはループを回すことではなく「いつ止めるか」を決めることでした。あるランでは、300ターン中234ターンでツールが1度も呼ばれていませんでした。この数字を出発点に、ターンを回す係と止めてよいか決める係を分け、押し戻しの送り方を直すまでを書きます。

By Qualiteg プロダクト開発部
【AI×CAD 第4回】3Dデータを渡さずにAIに設計をレビューさせる。幾何解析を先に済ませ、LLMには構造化した解析結果だけを渡す

【AI×CAD 第4回】3Dデータを渡さずにAIに設計をレビューさせる。幾何解析を先に済ませ、LLMには構造化した解析結果だけを渡す

機密の3DデータをクラウドAIに上げなくても、設計のAIレビューはできます。CADASのAI所見は、幾何解析エンジンが歯数・連動・穴・フィレットを先に解析し、LLMにはその構造化データ(1KBほど)だけを渡します。実際の所見・送信したJSON・悪用対策まで、実物で解説します。

By Qualiteg コンサルティング
Raspberry Pi Zero Wで外出先からサーボを動かす。ヘッドレスセットアップからハードウェアPWM、WireCanalでの公開まで

Raspberry Pi Zero Wで外出先からサーボを動かす。ヘッドレスセットアップからハードウェアPWM、WireCanalでの公開まで

2017年の初代Raspberry Pi Zero Wにサーボをつなぎ、Windows PCだけでヘッドレスセットアップして、インターネット越しにcurl一発でサーボを動かすまでの全手順です。ソフトウェアPWMで震えるサーボをハードウェアPWMで止め、その差をHTTPのAPIで切り替えられるようにして、WireCanalの無料プランでポート開放なしに公開しました。書き込み184秒・起動5分・切替1秒など、すべて実測値つきです。

By Join us, Michele on Qualiteg's adventure to innovation