O hirunewani blog

Q. CircleCIでPEP 668エラーが発生するようになった

Created at

いくつかのCircleCIを利用しているプロジェクトで、ある時期を境にPEP 668エラーが発生するようになった。 これはCircleCIが提供するDockerイメージのUbuntuが非自明的に上がることにより、意図せずPEP 668に従ったUbuntu 24.04になってしまったことによる。

エラーの発生

cimg/node:22.17.0またはcimg/go:1.24.5に更新されたjobで、pip install実行時に次のエラーが出力されていた。

error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try `apt install python3-xyz`, where `xyz` is the package you are trying to install.

If you wish to install a non-Debian-packaged Python package,
create a virtual environment using `python3 -m venv path/to/venv`.
Then use `path/to/venv/bin/python` and `path/to/venv/bin/pip`.
...
hint: See PEP 668 for the detailed specification.

なぜこのエラーが発生したのか

今回のケースではcimg/nodeのマイナーバージョン更新で、基盤となるUbuntuのバージョンが大幅に変わっていた。

  • cimg/node:22.16: Ubuntu 22.04.5 LTS
  • cimg/node:22.17: Ubuntu 24.04.2 LTS

Ubuntu 24.04でPEP 668が採用されたため、システムグローバルな環境へのpip installがブロックされるようになった。

またcimg/goの場合は、パッチバージョンの更新によりUbuntuのバージョンが変わっていた。

  • cimg/go:1.24.4: Ubuntu 22.04.5 LTS
  • cimg/go:1.24.5: Ubuntu 24.04.2 LTS

各イメージで利用されているOSは次のページで確認できる。

Ubuntuの更新は四半期毎に行われる

cimg/nodecimg/goが利用するLinuxのバージョンは、親イメージであるcimg/baseから継承されている。

ベースイメージであるcimg/baseと、ベースイメージを継承した子イメージについてCircleCIは次のようなポリシーを採用している。

We build a new base image each month. However, we only update the base image used in child convenience images once per quarter. These updates are not retroactively applied to existing images, but will apply to new releases of the image after the change has been made.

出展:CircleCI Convenience Images Support Policy

ベースイメージ自体は月次で更新されるが、子イメージでは四半期ごとに利用されるベースイメージが更新される。つまり、パッチやマイナーに関わらず子イメージのOSバージョンがUbuntu 22.04.5からUbuntu 24.04.2のようなジャンプをする可能性があり、これをバージョンが推測することが困難である。

このポリシーによる問題は、今までにも事例が報告されている。

PEP 668について

PEP 668は、OSのパッケージマネージャー(aptyum)とpipの競合を避けるための仕様。Ubuntu 24.04から採用されている。

要するに、システムグローバルなpip installがデフォルトでブロックされ、仮想環境(venv)での管理が必須になった。これによりerror: externally-managed-environmentエラーが発生する。

出展:PEP 668 – Marking Python base environments as “externally managed”

解決方法

いくつかの対処法があるが、対象によってCircleCI OrbやOSのパッケージマネージャーを利用することが好ましいと思われる。

1. CircleCI Orbの使用

例えばAWS CLIのようにOrbが提供されているものであれば、Orbを利用することで解決する可能性が高い。

orbs:
  aws-cli: circleci/aws-cli@5.4.1

jobs:
  build:
    docker:
      - image: cimg/node:22.17.0
    steps:
      - checkout
      - aws-cli/setup

参考:https://circleci.com/developer/ja/orbs/orb/circleci/aws-cli#commands-setup

2. パッケージマネージャーの使用

システムのパッケージマネージャーを使えば、PEP 668に従ったインストールが可能。

steps:
  - run:
      name: Install AWS CLI
      command: |
        apt install -y awscli

3. 仮想環境の使用(非推奨)

PEP 668に従う形で、Python仮想環境を作成してパッケージをインストールすることでも回避できるが、Pythonのプロダクトでも無ければ無駄が大きくなる可能性が高い。

steps:
  - run:
      name: Install AWS CLI in virtual environment
      command: |
        python3 -m venv venv
        source venv/bin/activate
        pip install awscli

4. 強制的な回避(非推奨)

--break-system-packagesフラグで強制的に回避できるが、システムの安定性を損なう可能性があるため推奨できない。

steps:
  - run:
      name: Force install AWS CLI (not recommended)
      command: pip3 install --break-system-packages awscli

GitHub Actionsとの比較

ちなみに、GitHub Actionsでは同様の問題が発生した後に対応が取り込まれ、pipがデフォルト環境にパッケージをインストールできるようになっている。

出展: