Azure Functions公式Pythonベースイメージで pip install したCLIが見つからない理由と対処法
Azure Functionsでコンテナデプロイを行う際に、Microsoft公式が提供しているPythonベースイメージを利用しています。
このベースイメージを利用したコンテナイメージを作成時に、詰まったポイントについてまとめます。
詰まったポイント
以下のMicrosoft公式が提供しているPythonイメージをベースイメージとして利用し、pipでuvをインストールした際に問題が起こりました。
※ uv を例にしていますが、pip install した他のライブラリのCLIでも同じです
以下のようなDockerfileを用意してビルドしたところ、最後のRUNで「uv: command not found」のエラーが出たのです。エラーの通り、uv の実行ファイルが配置されているディレクトリに PATH が通っていない状態ですね。
FROM mcr.microsoft.com/azure-functions/python:4-python3.14
...(中略)
RUN pip install uv
...(中略)
RUN uv ○○
(そもそもuvをpipで入れなければいいでしょって話は今回置いといてください。)
今回は uv をインストールしようとしていますが、「pipで入れた何らかのライブラリのCLIコマンドをたたく」というシチュエーションはほかにもあるかもなので、今回はあくまでuvを例に説明します。
uv公式が推奨しているインストール方法はこちらです
一方、ベースイメージにDocker公式のPython slim系を使った場合は、問題なく処理が通ります(パスが通っています)。
FROM python:3.14-slim
...(中略)
RUN pip install uv
...(中略)
RUN uv ○○
この挙動の差分が気になったので、調べてみました。
原因:pip install で配置されるコンソールスクリプト(実行ファイル)に PATH が通っていない
はい。原因は見出しの通りになります。ベースイメージとしてAzure FunctionsのPython用公式イメージを使った場合、pip install uv を実行すると、uv の実行ファイルが /opt/python/{該当バージョン}/bin/ に配置されます(この辺りの詳細は原因調査の章へ)。こちらにはパスが通っていないのでuv コマンドはそのままでは実行できません
一方で、Docker公式が提供しているPython slimイメージに関しては、pip install uv時の実行ファイルが /usr/local/bin/ に配置されます。/usr/local/bin/にはパスが通っているので、後続の処理で uv コマンドが問題なく実行可能です。
対策:python -m を付けてuvのコマンド実行するか、/opt/python/3/bin/にパスを通す
原因は上記の通りですので、簡単な対策としては2点かと。
(※そもそもpipを使ってuvを入れない!というのがありますが、置いといて...)
-
pip installで入れたライブラリのCLIコマンドはpython -mを付けて実行する -
pip installで実行ファイルがインストールされる/opt/python/3/bin/にパスを通す
まずは1つ目から。
pip install uv まで実行されたイメージを用意してインタラクティブモードで立ち上げ・接続します。この状態で以下のコマンドをじっこうすると...。
root@d8daf34b07c3:/# uv --version
bash: uv: command not found
root@d8daf34b07c3:/# python -m uv --version
uv 0.9.21
python -m uv は、PATH 上の実行ファイルを探索するのではなく、Python の import パス上にある uv モジュールの __main__ を直接実行します。このため、パスが通っていない場所に実行ファイルがなくても、Pythonが参照できる位置にあれば問題なく実行できます。
次に2つ目。
これは単純で、パスを通してしまえばいいという話です。
Dockerfile内に ENV PATH=/opt/python/3/bin:${PATH} を追加すればOKです。
FROM mcr.microsoft.com/azure-functions/python:4-python3.14
ENV PATH=/opt/python/3/bin:${PATH}
※ここで/opt/python/{該当バージョン}/bin/にパスを通すのではなく、/opt/python/3/bin/としている点については、原因究明の章のステップ4を見てもらえると。
基本的には python -m uv のように python -m を付けて実行する方法の方がオススメかなと思います。
この方法であれば、実行ファイルがどのディレクトリに配置されているかや、ベースイメージごとの PATH 設定やインストール先の差異に影響されにくく、再利用性の高い Dockerfile を書くことができます。
この辺りは、用途に応じて使い分けるのが良いのかな~と思っています。
(繰り返しになりますが、uv に関してはそもそも pip install せず、uv 公式で紹介されているインストール方法を採用するのが良いかと。)
おまけ:原因調査
原因と対策は上記の通りなので、こちらは原因を見つけるまでの流れを簡単に。
まじめに公式のベースイメージのDockerfileを追えばわかるのかもしれませんが、今回はインタラクティブモードで愚直に追ってみました。
調査の流れ
- 各ベースイメージをインタラクティブモードで実行する
-
pip -Vでpip自体のインストール先を見る -
pip install uvしてみてライブラリの保存位置を確認する -
pipのパスを確認する
1. 各ベースイメージをインタラクティブモードで実行する
Microsoft公式のAzure FunctionsのPython用イメージと、Docker公式が出しているPythonのslimイメージをインタラクティブモードで起動します。
起動コマンドはそれぞれ以下の通りです
# Azure Functions
docker run -it --rm --entrypoint /bin/bash mcr.microsoft.com/azure-functions/python:4-python3.14
# Python slim
docker run -it --rm --entrypoint /bin/bash python:3.14-slim
2. pip -V でpip自体のインストール先を見る
pip -Vを実行すると、そのpipのバージョンとインストール先が見えます。
まずは、Azure Functions用から。
root@13d98e8caadd:/# pip -V
pip 25.2 from /opt/python/3.14.0rc2/lib/python3.14/site-packages/pip (python 3.14)
/opt/python/3.14.0rc2/ 配下に入ってますね。この pip を使ってインストールされたライブラリは、基本的にこの配下に配置されます。(実行ファイルは/opt/python/3.14.0rc2/bin/ 配下に)
一方で、Python slimイメージでは
root@10232723a4e1:/# pip -V
pip 25.3 from /usr/local/lib/python3.14/site-packages/pip (python 3.14)
/usr/local/ 配下にpipがあることがわかるかと思います。
3. pip install uv してみてライブラリの保存位置を確認する
次に pip install uv をしてみて、実際にどこに保存されているのか確認します。
まずは、Azure Functions用から。
root@13d98e8caadd:/# pip install uv
root@13d98e8caadd:/# pip show uv
Name: uv
Version: 0.9.21
Summary: An extremely fast Python package and project manager, written in Rust.
Home-page: https://pypi.org/project/uv/
Author:
Author-email: "Astral Software Inc." <hey@astral.sh>
License:
Location: /opt/python/3.14.0rc2/lib/python3.14/site-packages
Requires:
Required-by:
予想通り、/opt/python/3.14.0rc2/ 配下に入っていることがわかるかと思います。
Python slimイメージでは…
root@10232723a4e1:/# pip install uv
root@10232723a4e1:/# pip show uv
Name: uv
Version: 0.9.21
Summary: An extremely fast Python package and project manager, written in Rust.
Home-page: https://pypi.org/project/uv/
Author:
Author-email: "Astral Software Inc." <hey@astral.sh>
License:
Location: /usr/local/lib/python3.14/site-packages
Requires:
Required-by:
こちらも予想通り、/usr/local/ 配下に入っていることがわかるかと思います。
4. pip のパスを確認する
ここで、気になるのがそもそも今使っている pip コマンドはどこのパス経由で使っているのか、です。気になるので確認してみます。
Azure Functions用の場合
root@13d98e8caadd:/# type -a pip
pip is /usr/bin/pip
pip is /bin/pip
どうやらpipコマンドを実行すると /usr/bin/pip を見に行っているみたいです。ls -l で実態を見てみます。
root@13d98e8caadd:/# ls -l /usr/bin/pip
lrwxrwxrwx 1 root root 21 Dec 31 05:05 /usr/bin/pip -> /opt/python/3/bin/pip
よくみると、どうやら /usr/bin/pip はシンボリックリンクのようですね。nameiコマンドで、もう少し見てみます。
root@13d98e8caadd:/# namei /usr/bin/pip
f: /usr/bin/pip
d /
d usr
d bin
l pip -> /opt/python/3/bin/pip
d /
d opt
d python
l 3 -> 3.14.0rc2
d 3.14.0rc2
d bin
- pip
namei の出力はシンボリックリンクのつながりを示しています。このことからわかるのは...
-
/usr/bin/pipは/opt/python/3/bin/pipを指すシンボリックリンク。 -
/opt/python/3はさらに/opt/python/3.14.0rc2を指すシンボリックリンク。
結果として、pipコマンド実行時の最終実体は /opt/python/3.14.0rc2/bin/pip (実ファイル)ということがわかります。
よって、pip install uv とすると /opt/python/3.14.0rc2/bin/ 配下に uv の実行ファイルが設置されるというわけですね。
そして、環境変数にはこの部分のパスが通っていないのでエラーとなるわけです
root@13d98e8caadd:/# echo "$PATH"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
このため、対策の1つとして環境変数に「/opt/python/3/bin/ のパスを通す」というのが入ったというわけです。(/opt/python/3.14.0rc2/bin/ だとわかりにくいので、ラップしてくれている /opt/python/3/bin/ を指すようにしました。)
一方でPython slimの方を見てみると…
root@10232723a4e1:/# namei /usr/local/bin/pip
f: /usr/local/bin/pip
d /
d usr
d local
d bin
l pip -> pip3
- pip3
見ての通り、pipは /usr/local/ 配下にあるので、実行ファイルは /usr/local/bin/ 配下に設置されます。ここのパスは問題なく通っているので、特に何もせずとも uv コマンドが実行できるということですね。
まとめ
今回は、Azure Functionsのコンテナデプロイ用イメージを作成する際に、Microsoft公式が提供するPythonイメージを使ったときに詰まった点についてまとめました。
どなたかの参考になれば幸いです🙋♂️
参考
Discussion