概要
Cloud Runを試そうと思い、Pipenvで作ったサンプルアプリのDockerイメージをデプロイしたところ、以下のエラーが出て起動しませんでした。
Error: the command XXX could not be found within PATH.
※ XXXは対象のコマンド
画像はCloud Loggingのログです。今回は uvicorn
を使用していました。
このエラーを解消したお話です。
要約
Dockerfileに以下を追加して解消しました。
PIPENV_VENV_IN_PROJECT
は、pipenvの仮想環境をプロジェクトのディレクトリに作成するための環境変数です。
ENV PIPENV_VENV_IN_PROJECT=1
ローカルとCloud Runでコンテナの実行ユーザーが異なり、パスが合わなかったことが原因と考えられます。
環境
- Python 3.10.13
- pipenv, version 2023.10.24
- alpine3.19
詳細
エラー原因
Pipenvは、デフォルトだとプロジェクト外の場所に仮想環境を作成します。
ビルドしたときのログをみると、/root
配下に環境を作成していることがわかります。
✔ Successfully created virtual environment!
Virtualenv location: /root/.local/share/virtualenvs/app-4PlAip0Q
元々のDockerfileは以下です。プロジェクトは /app
配下です。
FROM python:3.10.13-alpine3.19
WORKDIR /app
COPY Pipfile Pipfile.lock app/ /app/
RUN pip install pipenv && pipenv sync
EXPOSE 80
ENTRYPOINT ["pipenv", "run", "serve"]
このイメージをCloud Runにデプロイすると、先のエラーが出力されます。
ここでCloud Loggingのログを見ると、仮想環境の場所として/home
配下のディレクトリが参照されていることがわかります。
Virtualenv location: /home/.local/share/virtualenvs/app-4PlAip0Q
エラーの直接原因は、PATHの違いにより作成された仮想環境を参照していないためと想定されます。
なぜPATHが違うのか
Cloud Runでのコンテナ実行ユーザーが非rootユーザーであるため、と考えられます。
ローカルではあまり意識しないかもしれませんが、コンテナのベストプラクティスとしてrootユーザーでのコンテナ実行は推奨されていません。これはOWASPの Kubernetes Top 10 で確認できます。
Application processes should not run as root: Running the process inside of a container as the root user is a common misconfiguration in many clusters. While root may be an absolute requirement for some workloads, it should be avoided when possible. If the container were to be compromised, the attacker would have root-level privileges that allow actions such as starting a malicious process that otherwise wouldn’t be permitted with other users on the system.
(Google 翻訳) アプリケーションプロセスは root として実行しないでください。コンテナ内でプロセスを root ユーザーとして実行することは、多くのクラスターでよくある構成ミスです。一部のワークロードでは root が絶対必要な場合がありますが、可能な場合は避ける必要があります。コンテナが侵害された場合、攻撃者は root レベルの権限を持ち、システム上の他のユーザーには許可されない悪意のあるプロセスの開始などのアクションを許可することになります。
Cloud Run は Google Cloud が管理する Borg (KubernetesのベースになったGoogle社内ツール) 上でコンテナが稼働するサービスです。このプラクティスと同様に、Cloud Runも非rootユーザーで実行されているのだと考えられます。
非rootユーザーでは /root
配下に作られた仮想環境は参照できず、PATHのエラーとなります。試しに、Dockerfile に非rootユーザーを追加してdocker build
& docker run
すると、同じエラーが再現できます。
FROM python:3.10.13-alpine3.19
WORKDIR /app
COPY Pipfile Pipfile.lock app/ /app/
RUN pip install pipenv && pipenv sync
+ RUN adduser -D appuser -h /home/appuser && \
+ chown -R appuser:appuser /app /home/appuser
+ USER appuser
EXPOSE 80
ENTRYPOINT ["pipenv", "run", "serve"]
-
docker run
の実行結果
✔ Successfully created virtual environment!
Virtualenv location: /home/appuser/.local/share/virtualenvs/app-4PlAip0Q
Error: the command uvicorn (from serve) could not be found within PATH.
解決方法
以下の通り、PIPENV_VENV_IN_PROJECT
の環境変数を設定します。
これにより、プロジェクト配下の /app/.venv
に仮想環境が作成され、非rootユーザーにもPATHが通ります。
FROM python:3.10.13-alpine3.19
WORKDIR /app
# pipenvの仮想環境をプロジェクトに作成
+ ENV PIPENV_VENV_IN_PROJECT=1
COPY Pipfile Pipfile.lock app/ /app/
RUN pip install pipenv && pipenv sync
EXPOSE 80
ENTRYPOINT ["pipenv", "run", "serve"]
参考
- Github の issue
- pipenv 公式ドキュメント
- Cloud Run を徹底解説! - G-gen Tech Blog