はじめに
Pythonプロジェクトでは、従来 requirements.txt
のみを使って依存関係を管理しているケースが多いです。しかし、requirements.txt
はトップレベル依存とサブ依存(再帰的に必要なライブラリ)が混在しており、バージョン固定や更新制御が難しいという課題があります。
本記事ではまず、requirements.in
とは何か、なぜ導入するのかを解説し、その後に arm64環境とx86環境 双方で安定したビルドを行うための Dockerfile × pip-tools 構成をご紹介します。
requirements.in
とは
- トップレベル依存のみを記述するためのファイル。
- 例: アプリが直接使う
flask
やrequests
などを列挙。 - サブ依存のバージョン固定やハッシュ管理は行わない。
requirements.in のサンプル
# requirements.in
flask>=2.0,<3.0 # Web フレームワーク
requests==2.28.1 # HTTP クライアント
pydantic # バリデーションライブラリ
python-dotenv>=0.21.0 # 環境変数管理
- トップレベル依存のみを記述するためのファイル。
- 例: アプリが直接使う
flask
やrequests
などを列挙。 - サブ依存のバージョン固定やハッシュ管理は行わない。
メリット
- 可読性向上: 必要なライブラリだけが一覧化され、プロジェクトの依存構造が読みやすくなる。
-
再現性の強化:
pip-compile
コマンドで全依存を解決し、正確なバージョンをrequirements.txt
に出力できる。 -
アップグレード管理:
pip-compile -U
で依存の一括アップデートが可能。
基本の流れ
# トップレベル依存を requirements.in に記述
echo "flask" > requirements.in
echo "requests" >> requirements.in
# 全依存を解決し、バージョン固定した requirements.txt を生成
pip-compile requirements.in
# ロックされた依存をインストール
pip-sync requirements.txt
1. アーキテクチャ対応の課題
近年、Apple M1/M2(arm64)やラズパイなどのarm64環境が普及しつつあります。一方で、従来のIntel/AMD(x86_64)環境も依然として広く使われています。これら両者で同じPythonアプリを再現性高く動かすには、依存パッケージのビルド・管理が大きな課題となります。
-
依存ライブラリのバイナリ互換性:
numpy
やpillow
などにはプラットフォーム依存のWheelが存在し、バージョンによっては片方でのみビルドに失敗する場合があります。 - 環境差異による不具合: 開発者のローカル環境(例:M1 Mac)とCI/CDランナー(例:x86_64)で挙動が異なることがあります。
これらを避けるために、ビルドタイミングでアーキテクチャを判別し、適切な依存セットをインストールできるようにします。
2. pip-toolsで依存関係を一元管理
pip-tools
は、requirements.in
→ requirements.txt
の2段階管理を可能にするツールです。
2.1 基本コマンド
# トップレベル依存を書く
echo "flask" > requirements.in
# 依存を解決しバージョン固定
pip-compile requirements.in
# 環境を同期(不要なパッケージを削除)
pip-sync requirements.txt
-
pip-compile
:requirements.in
に書かれたライブラリとその再帰的依存を解決し、バージョン付きでrequirements.txt
を生成 -
pip-sync
:requirements.txt
と完全に一致する環境に同期
2.2 アーキ対応ファイルの生成
# x86_64 用
pip-compile --output-file=requirements-x86.txt requirements.in
# arm64 用
pip-compile --output-file=requirements-arm64.txt requirements.in
これで、プラットフォームごとに最適化されたロックファイルを保持できます。
3. Dockerfileでプラットフォーム切り替え
TARGETARCH
(docker buildx
が渡すビルド変数)を利用して、アーキテクチャに応じた requirements-*.txt
を選択します。
FROM python:3.10-slim
ARG TARGETARCH
ENV TARGETARCH=${TARGETARCH}
WORKDIR /app
# 共通の入力ファイル
COPY requirements.in .
# アーキ別のロックファイルを requirements.txt にリネーム
COPY requirements-${TARGETARCH}.txt requirements.txt
RUN pip install --upgrade pip \
&& pip install -r requirements.txt
COPY ./src /app
CMD ["python", "main.py"]
ビルド例
# x86_64 向け
docker buildx build --platform=linux/amd64 \
--build-arg TARGETARCH=x86 \
-t myapp:x86 .
# arm64 向け
docker buildx build --platform=linux/arm64 \
--build-arg TARGETARCH=arm64 \
-t myapp:arm64 .
4. CI/CD自動化のヒント
-
ワークフロー分岐: GitHub Actions などで
matrix.platform: [linux/amd64, linux/arm64]
を使い分け -
ロックファイル更新: 両アーキ別に
pip-compile
を実行し、PRで一括レビュー
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
platform: [amd64, arm64]
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.10
- name: Generate lock
run: |
pip install pip-tools
pip-compile --output-file=requirements-${{ matrix.platform }}.txt requirements.in
まとめ
-
requirements.in
でトップレベル依存を整理し、pip-compile
で再帰的にバージョンを固定 -
pip-sync
でクリーンな環境を維持 -
docker buildx
とARG TARGETARCH
によるプラットフォーム対応 - CI/CD で自動化すれば、どのマシンでも同じビルド結果を得られる