ubuntu22.04を24.04に変更したら、`pip`のインストールで"externally-managed-environment"というエラーが発生した

# はじめに

以下は、VSCode Dev Containers用の`Docker files`です。`Ubuntu 22.04`をベースイメージにしています。


```Dockerfile:Dockerfile.jammy
FROM ubuntu:22.04

# Install prerequisites
RUN set -x \
    && export DEBIAN_FRONTEND=noninteractive \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
        gnupg2 \
        software-properties-common \
        language-pack-ja \
        tzdata \
        curl \
        lsb-release \
    && apt-get -y clean \
    && rm -rf /var/lib/apt/lists/*

# Set locale & timezone
RUN update-locale LANG=ja_JP.UTF-8 LANGUAGE=ja_JP:ja \
    && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && echo "Asia/Tokyo" > /etc/timezone

ENV LANG=ja_JP.UTF-8
ENV LC_ALL=ja_JP.UTF-8
ENV LC_CTYPE=ja_JP.UTF-8

# Install packages
RUN set -x \
    # 最新のPythonバージョンをインストールするために、deadsnakes PPAを追加
    && add-apt-repository ppa:deadsnakes/ppa \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
        python3.12-dev \
    && apt-get -y clean \
    && rm -rf /var/lib/apt/lists/*

# Python / pip
RUN ln -s $(which python3.12) /usr/bin/python
RUN curl -sSL https://bootstrap.pypa.io/get-pip.py | python -
    
```

以下のコマンドは、ビルドしたイメージ上で実行しました。

```
root@43e0c835ea78:/# python --version
Python 3.12.4

root@43e0c835ea78:/# pip --version
pip 24.1.2 from /usr/local/lib/python3.12/dist-packages/pip (python 3.12)
```

# 何が起きたのか
最新のUbuntuバージョンを利用するために、ベースイメージをUbuntu 22.04からUbuntu 24.04に変更しました。しかし、`docker buildx build`に失敗しました。
`pip`のインストールで失敗していました。

```
$ docker buildx build --file Dockerfile.noble .
[+] Building 13.9s (9/9) FINISHED                                                                                                                                                                 docker:default
 => [internal] load build definition from Dockerfile.noble                                                                                                                                                  0.0s
 => => transferring dockerfile: 1.14kB                                                                                                                                                                      0.0s
 => [internal] load metadata for docker.io/library/ubuntu:24.04                                                                                                                                             0.0s
 => [internal] load .dockerignore                                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                                             0.0s
 => [1/6] FROM docker.io/library/ubuntu:24.04                                                                                                                                                               0.0s
 => CACHED [2/6] RUN set -x     && export DEBIAN_FRONTEND=noninteractive     && apt-get update     && apt-get install -y --no-install-recommends         gnupg2         software-properties-common          0.0s
 => CACHED [3/6] RUN update-locale LANG=ja_JP.UTF-8 LANGUAGE=ja_JP:ja     && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime     && echo "Asia/Tokyo" > /etc/timezone                                  0.0s
 => CACHED [4/6] RUN set -x     && add-apt-repository ppa:deadsnakes/ppa     && apt-get update     && apt-get install -y --no-install-recommends         python3.12-dev     && apt-get -y clean     && rm   0.0s
 => CACHED [5/6] RUN ln -s $(which python3.12) /usr/bin/python                                                                                                                                              0.0s
 => ERROR [6/6] RUN curl -sSL https://bootstrap.pypa.io/get-pip.py | python -                                                                                                                              13.9s
------
 > [6/6] RUN curl -sSL https://bootstrap.pypa.io/get-pip.py | python -:
13.75 error: externally-managed-environment
13.75
13.75 × This environment is externally managed
13.75 ╰─> To install Python packages system-wide, try apt install
13.75     python3-xyz, where xyz is the package you are trying to
13.75     install.
13.75
13.75     If you wish to install a non-Debian-packaged Python package,
13.75     create a virtual environment using python3 -m venv path/to/venv.
13.75     Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
13.75     sure you have python3-full installed.
13.75
13.75     If you wish to install a non-Debian packaged Python application,
13.75     it may be easiest to use pipx install xyz, which will manage a
13.75     virtual environment for you. Make sure you have pipx installed.
13.75
13.75     See /usr/share/doc/python3.12/README.venv for more information.
13.75
13.75 note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
13.75 hint: See PEP 668 for the detailed specification.
------
Dockerfile.noble:38
--------------------
  36 |     # Python / pip
  37 |     RUN ln -s $(which python3.12) /usr/bin/python
  38 | >>> RUN curl -sSL https://bootstrap.pypa.io/get-pip.py | python -
  39 |
  40 |
--------------------
ERROR: failed to solve: process "/bin/sh -c curl -sSL https://bootstrap.pypa.io/get-pip.py | python -" did not complete successfully: exit code: 1
```

以下のコマンドは、エラーが起きた`RUN curl -sSL https://bootstrap.pypa.io/get-pip.py | python -`をコメントアウトして作成したイメージ上で実行しました。

```
root@c89918743e60:/# python --version
Python 3.12.3
```

# `pip`のインストールに失敗した原因
Ubuntu 24.04の`python`パッケージでは、PEP668に対応しているためです。

PEP668については、以下のサイトが参考になりました。

https://blog.jp.square-enix.com/iteng-blog/posts/00043-play-with-the-pep668/



>提案されているのは、OS側のpython3のディレクトリにEXTERNALLY-MANAGEDファイルをおいたら、そこはpython用のモジュール管理コマンドの影響から保護してね！という内容です。

Ubunutu 24.04では、`/usr/lib/python3.12`に`EXTERNALLY-MANAGED`というファイルが存在していることを確認しました。

```
root@c89918743e60:/# python -c 'import sysconfig; print(sysconfig.get_path("stdlib"))'
/usr/lib/python3.12

root@c89918743e60:/# ls /usr/lib/python3.12/EXTERNALLY-MANAGED -l
-rw-r--r-- 1 root root 645  4月 10 14:33 /usr/lib/python3.12/EXTERNALLY-MANAGED

root@c89918743e60:/# ls /usr/lib/python3.12/EXTERNALLY-MANAGED -l
-rw-r--r-- 1 root root 645  4月 10 14:33 /usr/lib/python3.12/EXTERNALLY-MANAGED

root@c89918743e60:/# cat /usr/lib/python3.12/EXTERNALLY-MANAGED
[externally-managed]
Error=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. Make
 sure you have python3-full installed.

 If you wish to install a non-Debian packaged Python application,
 it may be easiest to use pipx install xyz, which will manage a
 virtual environment for you. Make sure you have pipx installed.

 See /usr/share/doc/python3.12/README.venv for more information.
 ```

また、Ubunutu 22.04では、`/usr/lib/python3.12`に`EXTERNALLY-MANAGED`というファイルは存在しないことを確認できました。


# 解決方法
メッセージにある通り、`pipx`パッケージをインストールして、`pip`でなく`pipx`を利用するようにしました。


```Dockerfile
FROM ubuntu:24.04

# Install prerequisites
RUN set -x \
    && export DEBIAN_FRONTEND=noninteractive \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
        gnupg2 \
        software-properties-common \
        language-pack-ja \
        tzdata \
        curl \
        lsb-release \
    && apt-get -y clean \
    && rm -rf /var/lib/apt/lists/*

# Set locale & timezone
RUN update-locale LANG=ja_JP.UTF-8 LANGUAGE=ja_JP:ja \
    && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    && echo "Asia/Tokyo" > /etc/timezone

ENV LANG=ja_JP.UTF-8
ENV LC_ALL=ja_JP.UTF-8
ENV LC_CTYPE=ja_JP.UTF-8

# Install packages
RUN set -x \
    # 最新のPythonバージョンをインストールするために、deadsnakes PPAを追加
    && add-apt-repository ppa:deadsnakes/ppa \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
        python3.12-dev \
        pipx \
    && apt-get -y clean \
    && rm -rf /var/lib/apt/lists/*

# Python / pipx
RUN ln -s $(which python3.12) /usr/bin/python
RUN pipx ensurepath

    

```

# 参考サイト
https://mackro.blog.jp/archives/22013252.html

https://uepon.hatenadiary.com/entry/2024/03/07/011738


https://gihyo.jp/admin/serial/01/ubuntu-recipe/0767#sec3_h4