きり丸の技術日記

技術検証したり、資格等をここに残していきます。

PyCharmでuvから生成される.venvをSDKに指定する

始めに

小ネタ。

PyCharmにてryeuvを直接指定することはまだできませんが、uvから生成される.venvは指定可能だということに気付いていなかったのでそれを記載するだけのメモです。

環境

  • PyCharm Professional
    • 2024.3

※ PyCharm Communityでもできるかもしれませんが検証してません。なお、IntelliJ IDEA Ultimateではだめでした。

実装

  1. Python Interpreterを開きます

  1. .venvのPythonを指定します

特に指定していない限り、uvを実行したディレクトリの.venv/bin/python.venv/bin/python3等にバイナリファイルが配備されているので指定してください。

非表示になっているパターンもありますので、その場合は修正してください。

終わりに

ずーっとJavaの現場にいたこともあり、最近までJetBrainsのAll Products Packではなく、IntelliJ IDEA Ultimateを無理やり拡張して使用していました。IntelliJ IDEA UltimateではあくまでJavaがメインだったので、.venvをSDKに指定する等がうまくできなかったようなのですが、値上げをきっかけに購入したことでPythonを書く際のコーディング力が増しました。

All Products Packを使い倒せるように、可能であればプログラマとしての副業もしていきたいです。

ビルド時間短縮のために途中ステージをpushする

始めに

弊社のシステムではECSを使用しているのですが、ここ最近Dockerイメージのビルド時間が大幅に延長されてしまっていました。そのうち、大幅な時間を占めているのがライブラリのインストール時間で、CPUの使用率が高くなって応答が非常に遅くなっていました。

uv.lock等のロックファイルが取り扱われている環境であればインストールでは常に同じライブラリが使用されるものですし、ライブラリインストールが完了した状態のイメージをRepositoryにアップロードすることで短縮することを目指しました。

今回の記事では、Dockerのマルチステージビルドを扱って処理時間を短縮することを目指します。

環境

  • Docker
    • 21以降
  • GitHub Actions

実装

Repositoryの作成

ライブラリをインストールしたイメージをアップロードするためのRepositoryを使用します。

dev-dependenciesありのイメージと、なしのイメージをアップロードしたかったので、2つ用意しました。

マルチステージビルドができるDockerファイルの作成

マルチステージビルドができるDockerファイルを用意します。全体の内容については今回の解説には不要なので省きます。

今回の場合、次のステージを用意しました。

  1. base
    1. 一番基準になるステージです。本番とベースイメージを分けたい場合はdev_base, prod_baseが生まれます
  2. dev_runtime
    1. 開発用のイメージを作るためのステージ。4. testで流用する。
  3. dev
    1. ローカル用ステージ
  4. test
    1. 2.のdev_runtimeを使用してCIを高速で処理させる
  5. prod_runtime
    1. 本番用イメージ。dev-dependenciesを抜いているだけ
  6. prod
    1. 本番用ステージ
ARG RUNTIME_TAG=latest

# ベースイメージ
FROM python:3.12-slim AS base

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV UV_PROJECT_ENVIRONMENT="/usr/local/"

WORKDIR /app

COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

# 1. 開発用ランタイムのビルド
FROM base AS dev_runtime

COPY src /app/src
COPY README.md pyproject.toml .python-version uv.lock ./
COPY tests /app/tests
COPY alembic /app/alembic
COPY alembic.ini .env ./
RUN uv sync --frozen --no-cache --dev

## ローカルはあんまりここを分けるメリットがない
## 2. 開発用ランタイムを使用して起動
FROM dev_runtime AS dev

COPY src /app/src

CMD ["uv", "run", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--reload"]

## 3. test 用
FROM kirimaru/fastapi-practice_dev-runtime:${RUNTIME_TAG} AS test
ARG RUNTIME_TAG

# NOTE: compose ファイルでマウントするなら不要
COPY src /app/src

# 4. 本番用ランタイムのビルド
FROM base AS prod_runtime

COPY README.md pyproject.toml .python-version uv.lock ./

RUN uv sync --frozen --no-cache

# 5. 本番用ランタイムを使用して起動
FROM kirimaru/fastapi-practice_prod-runtime:${RUNTIME_TAG} AS prod
ARG RUNTIME_TAG

COPY src /app/src
COPY .env ./

CMD ["uv", "run", "uvicorn", "src.main:app", "--host", "0.0.0.0"]

ロックファイルのハッシュ取得

ロックファイルのハッシュを取得し、それをDockerのタグにします。そうすることで、ライブラリの更新がされたかどうかをチェックできます。また、念のためDockerのイメージ名と混ざらないようにpython-等のprefixも付与しています。

export LOCK_HASH=python-$(sha1sum < uv.lock | cut -d' ' -f1)
# Docker側でわかりやすい変数に変更
export RUNTIME_TAG=$LOCK_HASH

Dockerイメージのpull

過去に作成したイメージがあればそれを取得します。

docker pull kirimaru/fastapi-practice_dev-runtime:$RUNTIME_TAG

Dockerイメージのbuild and push

pullしたイメージが存在しない場合、イメージをビルドします。作成したイメージをRepositoryにpushします。

if [ $? -ne 0 ]; then
  docker buildx build --target dev_runtime -t kirimaru/fastapi-practice_prod-runtime:$RUNTIME_TAG .
  docker push kirimaru/fastapi-practice_dev-runtime:$RUNTIME_TAG
fi

pullしたイメージを使用する

開発用ビルドイメージをもとにテストしたいので、compose.ymlのtargetに定義したtestを指定します。また、タグをロックファイルのハッシュにしているので、パラメータとして渡してあげます。

services:
  api:
    build:
      context: .
      target: ${BUILD_TARGET:-dev}
      args:
        - RUNTIME_TAG=${RUNTIME_TAG}
export BUILD_TARGET=test
docker compose up -d

ソースコード

終わりに

1回あたり全体で20分くらいかかっていたのを10分弱まで省略できました。特にlintするだけのCIでは、イメージのpullがなくなったことで10分弱かかっていたのを2分程度で完了するほど高速処理になっています。

CIが遅くて悩んでいる方はぜひ参考にしてみてください。

Basic認証込みのNGINXイメージを扱う(beevelop/nginx-basic-auth)

簡単にBasic認証を実装したり検証したりするためにNGINXを使用しつつ、ついでにBasic認証まで含めているDockerイメージがあったので素振りしました。

環境

  • Docker
    • 26.0.0
  • beevelop/nginx-basic-auth

対応

localhost:80にアクセスしたらBASIC認証がかかっており、認証成功したらlocalhost:8080で公開しているサイトに対してリダイレクトしている例です。BASIC認証はデフォルトで次のパスワードになっています。

  • id
    • foo
  • password
    • bar
services:
  web:
    image: dockercloud/hello-world
  auth:
    image: beevelop/nginx-basic-auth
    ports:
      - 8080:80
    links:
      - web:web

設定できる環境変数は少ないので、Docker ImageのGitHubを読むと確認できます。

備考

クラスメソッド株式会社様が公開しているページもあったのですが、記事で使用しているイメージのquay.io/dtan4/nginx-basic-auth-proxyが古すぎるのでエラーで起動しませんでした。今回紹介したイメージは2023年10月19日に最新がプッシュされているので、イメージが古いということは無さそうです。

docker run -d -p 80:80 quay.io/dtan4/nginx-basic-auth-proxy
Unable to find image 'quay.io/dtan4/nginx-basic-auth-proxy:latest' locally
latest: Pulling from dtan4/nginx-basic-auth-proxy

docker: [DEPRECATION NOTICE] Docker Image Format v1 and Docker Image manifest version 2, schema 1 support is disabled by default and will be removed in an upcoming release. Suggest the author of quay.io/dtan4/nginx-basic-auth-proxy:latest to upgrade the image to the OCI Format or Docker Image manifest v2, schema 2. More information at https://docs.docker.com/go/deprecated-image-specs/.
See 'docker run --help'

終わりに

BASIC認証を元に検証したい内容があったのですが、Dockerイメージで最初からBASIC認証がかかっているとありがたいですね。

普通に調べるとNGINXの設定を頑張って設定する記事しか見つかりませんでした。もちろん、正攻法なのでこちらの記事が多いことはいいことですが。

簡単に検証できる環境ができてうれしいです。

参考情報