【実務・中級編】Python環境のポータビリティを極める:uvとPoetryで作る「ネット遮断環境」用オフラインキャッシュ戦略 – ビルド・パッケージ管理ツール生産性向上バイブル

Python環境のポータビリティを極める:uvとPoetryで作る「ネット遮断環境」用オフラインキャッシュ戦略

テックリードの皆さん、日々のセキュアな開発環境構築で頭を悩ませていませんか?

「金融・医療・公共系の案件で、開発サーバーが完全にネットから遮断されている」
「DockerビルドのたびにPyPIへの外向き通信がタイムアウトし、CI/CDパイプラインが爆発する」
「プロキシを通すためにSSL証明書のエラーと毎朝格闘している」

ネットが繋がらない環境(エアギャップ環境)におけるPythonのパッケージ管理は、多くのエンジニアにとって長年の悪夢でした。`pip install` のたびに発生する依存関係解決のオーバーヘッド、C言語拡張モジュールのビルド(`gcc` のエラー)、そして何より「ローカルで動いたのにオフライン環境に持っていったら動かない」という絶望感。

しかし、Rust製超高速パッケージマネージャー `uv` と、依存関係解決のデファクトスタンダードである `Poetry` を正しく組み合わせ、キャッシュの内部挙動を完全にハックすれば、この課題は劇的に解決します。

本記事では、マニュアルをなぞるだけの解説は一切行いません。ツール内部でキャッシュがどのようにストレージに格納され、どのようにリンクされているのかというメカニズムを紐解き、物理メディアやセキュアな踏み台サーバーを経由して「一滴のネットワークも通さずに完全に再現性のあるPython環境を構築するプロの戦略」をコードと設定ファイルのフルセットでお伝えします。

—

1. ツール内部のデータフロー:なぜ「オフライン環境」でパッケージ管理が破綻するのか

まず、Pythonのパッケージマネージャーが裏側で何をしているのか、そのデータフローを直視しましょう。

通常、`pip` や `Poetry` は、リモートのPyPI(Python Package Index)にHTTPSリクエストを送り、メタデータ(`METADATA`や`sdist`/`wheel`のハッシュ値)を取得し、依存関係のDAG(有向非巡回グラフ)をメモリ上で解決してからダウンロードを行います。

[PyPI / Remote] —> (HTTPS) —> [Resolver] —> [Build Backend (PEP 517)] —> [Site-Packages]

インターネットが遮断された環境では、この最初のステップ(リモートとの通信)で即座に破綻します。ここで重要になるのが、「メタデータの解決に必要な情報」と「実際のバイナリ(Wheel)」を事前にローカルストレージ(キャッシュディレクトリ)に完璧な形で集約し、オフラインモード(`–offline`)で強制的にそれらを読ませるというアプローチです。

ここで登場するのが、Astral社が開発したRust製の怪物ツール `uv` です。`uv` は `pip` の数倍〜数十倍の速度を誇るだけでなく、ファイルシステムレベルでハードリンクやシンボリックリンクを駆使した極めて高度なグローバルキャッシュ機構を持っています。この特性を利用すれば、数ギガバイトに及ぶ機械学習ライブラリ群であっても、瞬時にオフライン環境へデプロイできます。

—

2. Poetry + uv のハイブリッド戦略:ロックファイルの厳密性と爆速のオフライン転送

チーム開発におけるバージョン固定には `Poetry` の `poetry.lock` の厳密性が不可欠ですが、いかんせん依存関係の解決とダウンロードに時間がかかります。そこで、「依存関係の解決とロックファイルの生成はPoetryで行い、実際のダウンロードと環境構築の爆速化・オフライン化は `uv` に委譲する」という最強の役割分担を採用します。

実用的な設定ファイル構成 (`pyproject.toml`)

以下の `pyproject.toml` は、Poetryで管理しつつ、内部のバックエンドとして `uv` の高速なエコシステムをシームレスに組み込むための実践的な構成です。

[tool.poetry]
name = “secure-enterprise-backend”
version = “1.0.0”
description = “完全ネット遮断環境で稼働する高信頼バックエンドサービス”
authors = [“DevOps Architecture Team “]
readme = “README.md”
packages = [{include = “app”, from = “src”}]

[tool.poetry.dependencies]
python = “^3.11”
業務で頻繁に使われる重い依存関係の例
fastapi = “0.110.0”
uvicorn = {extras = [“standard”], version = “0.28.0”}
sqlalchemy = “2.0.28”
psycopg2-binary = “2.9.9”
pydantic = “2.6.4”

[tool.poetry.group.dev.dependencies]
pytest = “8.1.0”
ruff = “0.2.2”

[build-system]
PEP 517に準拠したビルドシステム
requires = [“poetry-core>=1.7.0”]
build-backend = “poetry.core.masonry.api”

[tool.poetry.cli]
Poetryが内部でuvをエンジンとして利用するための設定(対応バージョンによる)
基本的には poetry install の代わりに uv pip を併用するワークフローを推奨

—

3. キャッシュディレクトリを物理メディアで持ち運ぶ:ストレージ設計とアーキテクチャ

ネット遮断環境(オンプレミスの閉域網、政府系・金融系のセキュアゾーン)へパッケージを持ち込むには、USBメモリやセキュアな踏み台サーバーを経由した物理的、あるいは限定的なネットワーク転送が必要です。

`uv` は、デフォルトで以下の場所にグローバルキャッシュを保持します。

  • Linux: `~/.cache/uv`
  • macOS: `~/Library/Caches/uv`
  • Windows: `C:\Users\\AppData\Local\uv\uv\Cache`

このキャッシュディレクトリ構造は非常にクリーンであり、中身は `archive-v0` や `wheels-v0` といったハッシュベースのストレージ構造になっています。つまり、このディレクトリ構造ごと持ち運べば、オフライン環境でも完全なキャッシュヒットを狙えます。

ステップ1:オンライン環境(基地局)でのキャッシュ収集とホイール固め

インターネット接続がある開発端末(またはCIサーバー)で、ターゲット環境が必要とするすべてのパッケージのWheel(ビルド済みバイナリ)とキャッシュを収集します。

1. プロジェクトの依存関係を完全に解決しつつ、uvのキャッシュディレクトリに全パッケージをダウンロード
–all-extras や –dev などのフラグで必要なスコープを網羅する
uv pip install –requirement <(poetry export -f requirements.txt --output - --without-hashes) --dry-run 2. 上記で蓄積された uv のキャッシュディレクトリ(~/.cache/uv)を、 持ち運び用のポータブルストレージ(USB等)へ丸ごとアーカイブ化する tar -zcvf uv-offline-cache.tar.gz -C ~/.cache uv これで、数メガバイトから数ギガバイトに及ぶキャッシュの塊(`uv-offline-cache.tar.gz`)が完成します。これをセキュリティチェックに通し、オフライン環境へと持ち込みます。

ステップ2:オフライン環境(ターゲット)でのキャッシュ展開とデプロイ

閉域網内のターゲットサーバーに到着したら、持ち込んだアーカイブを展開し、`uv` のオフラインモードを起動します。

1. 持ち込んだキャッシュアーカイブを、ターゲット環境のuvキャッシュディレクトリに展開
mkdir -p ~/.cache
tar -zxvf uv-offline-cache.tar.gz -C ~/.cache

2. 仮想環境を作成
python -m venv .venv
source .venv/bin/activate

3. 完全オフラインモード(–offline)およびローカルキャッシュのみ参照(–no-index)を指定してインストール
これにより、PyPIへの名前解決や接続試行が一切発生せず、ローカルキャッシュから爆速で環境が構築される
uv pip install –offline –no-index \
–requirement <(poetry export -f requirements.txt --output - --without-hashes) このワークフローを導入することで、これまで数十分かかっていた環境構築がわずか数秒で完了し、タイムアウトエラーとは完全に決別できます。

—

4. プロの実践テクニック:`pip wheel` を用いたビルド済みパッケージの配布手法

環境によっては、Pythonのバージョンやアーキテクチャ(x86_64 vs aarch64)がオンライン環境とオフライン環境で異なる場合があります。例えば、「MacBookでパッケージを収集し、本番のLinux(CentOS / RHEL / Ubuntu)サーバーにデプロイする」というクロスプラットフォームの要件です。

単純にキャッシュをコピーするだけでは、環境差異によるバイナリ不一致(C拡張モジュールのビルドエラーなど)が発生します。これを防ぐためには、ターゲット環境のOS・アーキテクチャに完全に一致したWheelファイルを明示的にビルド・収集する必要があります。

ターゲット環境専用のWheelバンドル作成スクリプト

オンライン側のビルドサーバー等で、以下のスクリプトを実行し、ターゲット環境(例: Linux x86_64, Python 3.11)向けのWheelをローカルディレクトリに固めて出力します。

!/usr/bin/env bash
target-wheel-bundler.sh
目的: オフライン環境(Linux x86_64 / Python 3.11)向けの依存パッケージ一式をWheelとしてローカルに保存する

set -eu0pipefail

出力先ディレクトリの作成
OUTPUT_DIR=”./offline-wheels”
rm -rf “$OUTPUT_DIR”
mkdir -p “$OUTPUT_DIR”

echo “==> Poetryからrequirements.txtをエクスポート中…”
poetry export -f requirements.txt –output requirements.lock –without-hashes

echo “==> ターゲット環境(Linux x86_64, Python 3.11)に合致するWheelを一括ダウンロード・ビルド…”
pipのプラットフォーム指定オプションを駆使して、ターゲットの仕様に強制的に合わせる
pip download \
–dest “$OUTPUT_DIR” \
–platform manylinux2014_x86_64 \
–implementation cp \
–python-version 3.11 \
–abi cp311 \
–no-deps \
-r requirements.lock

ついでに、依存するサブパッケージも含めて確実にダウンロードするために標準ダウンロードも実行
pip download \
–dest “$OUTPUT_DIR” \
-r requirements.lock

echo “==> 完了! ‘${OUTPUT_DIR}’ ディレクトリをオフライン環境へ転送してください。”

オフライン環境側では、この `offline-wheels` ディレクトリをそのまま指定してインストールを行います。

オフライン環境でのデプロイコマンド
python -m venv .venv
source .venv/bin/activate

ローカルのWheelディレクトリのみを参照し、ネット接続を完全に遮断してインストール
pip install –no-index –find-links=./offline-wheels -r requirements.lock

この手法をとることで、C言語のコンパイラ(`gcc` や `make`)が一切インストールされていない硬質な本番サーバーであっても、安全かつ確実に依存関係をデプロイできます。

—

5. チーム開発で役立つ設定の共有化ルールと自動化

個人のローカル環境ごとにキャッシュの持ち込み方法がバラバラでは、チーム開発として破綻します。オフライン環境向けの開発フローを組織標準として定着させるための「共有ルール」と「Makefile」による自動化を提供します。

チーム共有用 `Makefile` のベストプラクティス構成

プロジェクトのルートに以下の `Makefile` を配置し、開発者がコマンド一発でキャッシュの生成やオフラインインストールを行えるように標準化します。

.PHONY: help export-deps bundle-offline install-offline

デフォルトターゲット
help:
@echo “=== オフライン環境対応 Python開発支援コマンド ===”
@echo ” make export-deps : Poetryから依存関係をロック・エクスポート”
@echo ” make bundle-offline : オンライン環境でオフライン用キャッシュアーカイブを作成”
@echo ” make install-offline: オフライン環境でキャッシュから爆速環境構築”

1. 依存関係の確実なエクスポート
export-deps:
@echo “-> poetry.lockを元にrequirements.txtを生成中…”
poetry export -f requirements.txt –output requirements.txt –without-hashes

2. オンライン側でのバンドル作成
bundle-offline: export-deps
@echo “-> uvとpipを使用してオフライン用キャッシュとホイールをパッケージング中…”
mkdir -p .offline-cache
# uvのキャッシュをローカルに書き出し、またはターゲット用にpip downloadを実行
pip download –dest .offline-cache -r requirements.txt
tar -zcvf enterprise-offline-bundle.tar.gz .offline-cache requirements.txt
@echo “-> 完成: enterprise-offline-bundle.tar.gz をセキュアに転送してください。”

3. オフライン側での環境構築
install-offline:
@echo “-> オフライン環境での仮想環境構築を開始…”
python -m venv .venv
@echo “-> 仮想環境をアクティベートしてローカルホイールからインストール…”
./.venv/bin/pip install –no-index –find-links=.offline-cache -r requirements.txt
@echo “-> セットアップが完了しました。”

—

現場のテックリードからのメッセージ

ネット遮断環境やセキュアな閉域網における開発は、往々にして「開発体験の低下」とトレードオフになりがちです。「ローカルなら動くのに」という言い訳は、プロのエンジニアリングチームにおいては許されません。

今回紹介した `uv` の超高速グローバルキャッシュ機構 と `Poetry` / `pip` を組み合わせたオフライン・ホイール・バンドル戦略 をマスターすれば、インターネットの接続有無は単なる「インフラの仕様の違い」に過ぎなくなります。

ツールの内部挙動を深く理解し、物理メディアや転送パイプラインまで含めた「環境のポータビリティ」を設計すること。それこそが、どんな過酷なセキュリティ要件のプロジェクトであっても、開発スピードを極限まで高め続ける真のDevOpsエンジニアの姿です。さあ、明日からのデプロイメントを劇的に変えていきましょう。

タイトルとURLをコピーしました