はじめに
Docker開発が苦手!俺は業務ロジックを作りたい!しかしこの手の開発手法は当面なくなりそうにないので、とにかく仕事に近い感じのアレンジをしながら作ってみる。アレルギーを克服しろ!記事の量は最低限に!
最低限のファイル構成
こんな感じ(モノRepositoryはベストプラクティスではない。現場がそうだってだけ)
docker_world
├─.devcontainer
│ ├─ devcontainer.json
│ └─ Dockerfile
├─.vscode
│ └─ launch.json
├─dev
│ └─ docker-compose.yml
├─src
│ ├─ main.py
│ └─tests
│ └─ test_main.py
├─ .gitignore
├─ README.md
├─ requirements.txt
└─ setup.sh
gitの設定
改行コードのLF統一
Git による改行コード管理が自動化され(ローカルでコミットされる際に CRLF を LF に自動変換します。)、リポジトリ内の改行コードをすべて LF で統一できるようになる。仕事でも環境面で大ハマリする筆頭だと思う
git config --global core.autocrlf input
whoami
whoami
※Dockerfileを試行錯誤するときは sleep infinity を使え
こうすると $PATH に追加すべきパスがなにかわかんねーってときにコンテナの世界にい続けたまま which uvicorn
などで調査できる
:
# uvicorn を起動コマンドとして設定
# CMD ["python", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["sleep", "infinity"]
※Dockerfileにおける WORKDIR を /workspace に統一する理由
/workspace
自体はルートに作った任意のディレクトリです
Dockerfile
において WORKDIR
を /home/vscode
など一般ユーザの「ホームディレクトリ」に設定していた時期があった。なぜかというとさくらのVPSでログインするとホームディレクトリでスタンバイされるから。
ホームディレクトリには 意外に多くの隠しファイル が生成される。
.cache/
.vscode-server/
.ssh/
.gitconfig
.bash_history
これらのファイルは環境依存の設定や一時的なデータであり、プロジェクトのコード管理には不要である。これらのファイルを .gitignore
に追加するのは思考の妨げになる。
そこで
-
/workspace
をWORKDIR
に指定する 公式: WORKDIR- コンテナはホストOSとは独立した環境であるため、Linuxシステムに従ったディレクトリ配置をしなくてもよい
-
/workspace
配下にはプロジェクトに必要なファイルのみが存在するため、コンテナ内の環境をシンプルに保つことができる
以上の理由から、ホームディレクトリを直接 WORKDIR
に設定するのは避けたほうが良い。
基本的な Docker-desktop の調査方法
Containers
logs
inspect
exec
コンテナのなかでコマンドを打って調査することができる
(使えるときとアップグレードしろって言われる時がある)
files
コンテナのファイルをツリーで見ることができる
Builds
backend
なんべんもやり直してこの図を書いたよ...
コンテナの ライフタイム
を意識すると、activateが2回ある理由などの解像度が深まる
.devcontainer\devcontainer.json
{
"name": "FastAPI Dev Container",
"build": {
"dockerfile": "./Dockerfile",// TODO: のちほど 親ディレクトリの本番用 ../Dockerfile を利用 + features でセットアップする形にする
"context": ".." // 必要: buildの基準位置を示す
},
// "runArgs": ["--network=dev_network"],
"containerEnv": {
"SHELL": "/bin/bash"
},
"postCreateCommand": "/workspace/setup.sh",
"workspaceFolder": "/workspace", // 必要: セットアップ完了後に cd する
"customizations": {
"vscode": {
"settings": {
"python.defaultInterpreterPath": "/workspace/.venv/bin/python", // 必要: セットアップ完了後に activate する
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave":true,
"editor.formatOnPaste": true
},
"extensions": [
"ms-python.black-formatter",
"ms-python.flake8",
"humao.rest-client"
]
}
}
}
.devcontainer\Dockerfile
わからないと大ハマリするのが linux のファイル構造。workspaces
のなかには docker_world
というフォルダがあって、どうやらここが host - コンテナ間 の窓口らしい
(.venv) vscode@01d3e9f0e986:/workspace$ ls -la /
total 100
drwxr-xr-x 1 root root 4096 Mar 5 21:08 .
drwxr-xr-x 1 root root 4096 Mar 5 21:08 ..
-rwxr-xr-x 1 root root 0 Mar 5 21:08 .dockerenv
drwxr-xr-x 2 root root 4096 Feb 24 09:00 bin
drwxr-xr-x 2 root root 4096 Aug 15 2024 boot
:
drwxr-xr-x 3 root root 4096 Mar 5 21:08 vscode
drwxr-xr-x 1 vscode vscode 4096 Mar 5 21:08 workspace ←つくったディレクトリ
drwxr-xr-x 3 root root 4096 Mar 5 21:08 workspaces
FROM python:3.12.9-slim-bullseye
# 共通変数
ARG WORKDIR=/workspace
ARG USER_NAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ENV TZ=Asia/Tokyo
# `WORKDIR` で指定したカレントディレクトリは、ビルドセッション中のみ適用されます。
WORKDIR $WORKDIR
# チョンボしないで必要なものを $WORKDIR に転送しましょう
COPY ./.vscode $WORKDIR/.vscode
COPY ./setup.sh $WORKDIR
COPY ./requirements.txt $WORKDIR
COPY ./src $WORKDIR/src
# ユーザー作成
RUN groupadd --gid $USER_GID $USER_NAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USER_NAME
# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
bash curl git openssh-client zip
# 作業ディレクトリの所有者を設定
RUN chown -R $USER_NAME:$USER_NAME $WORKDIR
# ユーザー変更
USER $USER_NAME
# TODO: のちに本番用Dockerfileに移すと良い
#CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
.vscode\launch.json
F5
でサーバー起動と pytest
が打てるようになる
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Serve",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"src.main:app",
"--reload",
"--port", "8000",
"--host", "0.0.0.0",
],
},
{
"name": "Run Tests",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["-vv"]
}
]
}
dev\docker-compose.yml
(これは今回使っていない。バックエンドとフロントエンドを連動させるようなときに使う)
services:
fastapi:
build:
context: .
dockerfile: .devcontainer/Dockerfile
volumes:
- .:/workspace
ports:
- "8000:8000"
networks:
default:
name: dev_network
src\tests\test_main.py
from src.main import reverse
def test_reverse():
assert reverse("hello") == "olleh"
assert reverse("") == ""
assert reverse("123") == "321"
assert reverse("あいう") == "ういあ"
src\main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
def reverse(text: str) -> str:
"""
与えられた文字列を逆順にして返す。
この関数はpytestのテスト用に作成されています。
Args:
text (str): 逆順にする文字列。
Returns:
str: 逆順になった文字列。
"""
return text[::-1]
.gitignore
__pycache__/
.pytest_cache/
*.log
*.swp
.env
.venv/
requirements.txt
fastapi==0.115.8
uvicorn==0.34.0
pytest==8.3.4
setup.sh
#!/bin/bash
# 共通変数
BASE_DIR="/workspace"
VENV_DIR="$BASE_DIR/.venv"
# 仮想環境の作成
if [ ! -d "$VENV_DIR" ]; then
echo "仮想環境を作成中..."
python -m venv $VENV_DIR
else
echo "仮想環境は既に存在します。"
fi
# 仮想環境をアクティベート(現在のシェルセッション内でのみ有効)
source $VENV_DIR/bin/activate
# 仮想環境の確認
echo "現在のPython環境: $(which python)"
# 仮想環境が有効か確認
if [ "$VIRTUAL_ENV" != "" ]; then
echo "仮想環境が有効です(\$VIRTUAL_ENV): $VIRTUAL_ENV"
else
echo "仮想環境が有効ではありません。"
exit 1
fi
# requirements.txt の内容をインストール
if [ -f "$BASE_DIR/requirements.txt" ]; then
echo "requirements.txt からパッケージをインストール中..."
pip install --upgrade pip
pip install --no-cache-dir -r $BASE_DIR/requirements.txt
else
echo "requirements.txt が見つかりませんでした。"
fi
echo "仮想環境のセットアップが完了しました。"