1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

devcontainerでフロントとバックの環境を構築してコンテナ開発アレルギーを克服する

Last updated at Posted at 2024-10-28

はじめに

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 などで調査できる

事故ってた時期のDockerfileのかけら
  :
# 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 に追加するのは思考の妨げになる。

そこで

  • /workspaceWORKDIR に指定する 公式: WORKDIR
    • コンテナはホストOSとは独立した環境であるため、Linuxシステムに従ったディレクトリ配置をしなくてもよい
    • /workspace 配下にはプロジェクトに必要なファイルのみが存在するため、コンテナ内の環境をシンプルに保つことができる

以上の理由から、ホームディレクトリを直接 WORKDIR に設定するのは避けたほうが良い。

基本的な Docker-desktop の調査方法

Containers

logs

コンテナのログをみることができる
image.png

inspect

コンテナの環境変数を見ることができる
image.png

exec

コンテナのなかでコマンドを打って調査することができる
(使えるときとアップグレードしろって言われる時がある)

files

コンテナのファイルをツリーで見ることができる

Builds

ビルド中にエラーで落ちたときに途中経過を見ることができる
image.png
image.png

backend

なんべんもやり直してこの図を書いたよ... :sob:
コンテナの ライフタイム を意識すると、activateが2回ある理由などの解像度が深まる

.devcontainer\devcontainer.json

最初にハマりがちなのが、親ディレクトリに遡ることはできませんという仕様

Dockerfile 内では、親ディレクトリに遡ることはできません。これはセキュリティとコンテナの隔離を保つためです。具体的には、以下のような制限があります:

ビルドコンテキストの制限:

Dockerfile は、指定されたビルドコンテキスト内でのみファイルをコピーしたり、アクセスしたりできます。ビルドコマンドを実行する際に指定したディレクトリ(例えば、docker build -t myapp .. を指定した場合、そのディレクトリがビルドコンテキストになります)に制限されます。

.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
.devcontainer\Dockerfile
FROM python:3.13.3-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 . $WORKDIR

# ユーザー作成
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 が打てるようになる

.vscode\launch.json
{
    "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

(これは今回使っていない。バックエンドとフロントエンドを連動させるようなときに使う)

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

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

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

.gitignore
__pycache__/
.pytest_cache/
*.log
*.swp

.env
.venv/

requirements.txt

requirements.txt
fastapi==0.115.8
uvicorn==0.34.0
pytest==8.3.4

setup.sh

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 "仮想環境のセットアップが完了しました。"

README.md

README.md
# docker_world
## 処理の流れ
以下は、`devcontainer.json` を基点に Docker コンテナを利用して Python 開発環境をセットアップするプロセスを示しています。
### 1. **`devcontainer.json` の読み込み**
- Visual Studio Code の Dev Container 定義ファイル。
- 定義された設定に基づき、コンテナのビルド・起動、およびセットアップを行います。

### 2. **Dockerfile のビルド**
- `devcontainer.json``"build"` セクションに従い、`Dockerfile` を使用して Docker イメージをビルドします。
- 必要なスクリプト(例: `setup.sh` など)がビルド時にコンテナ内にコピーされます。

### 3. **Docker コンテナの起動**
- ビルドした Docker イメージからコンテナを作成します。
- この段階で必要な環境変数設定(例: `containerEnv`)、作業ディレクトリ設定(例: `workspaceFolder`)が反映されます。

### 4. **作業ディレクトリの設定**
- コンテナ内の作業ディレクトリを `/workspace` に設定します。
- これは `devcontainer.json``"workspaceFolder": "/workspace"` によって制御されています。

### 5. **`setup.sh` の実行**
- `devcontainer.json``"postCreateCommand"` オプションを使用して、起動されたコンテナ内で `setup.sh` を実行します。
- スクリプト内で行われる主な処理:
    - 仮想環境 (`.venv`) の作成とシェル内での一時的な有効化。
    - `requirements.txt` に記載された Python パッケージのインストール。

### 6. **仮想環境の再度有効化**
- 仮想環境は `setup.sh` 実行時に一時的に有効化されますが、シェルセッション終了後は無効になります。
- そのため、`devcontainer.json` の以下の設定により、VS Code 内で仮想環境を再度有効化します:

  "python.defaultInterpreterPath": "/workspace/.venv/bin/python"

- 開発環境において、この設定により `.venv` が自動的に Python インタープリタとして認識され、デフォルトで使用可能になります。

### 7. **VS Code のカスタマイズ設定の適用**
- 以下の拡張機能とエディタ設定が自動的に適用されます:
    - **フォーマッタ**: `ms-python.black-formatter`
    - **静的解析ツール**: `ms-python.flake8`
    - **API テストツール**: `humao.rest-client`

- 開発効率を高めるために、以下の VS Code 設定も反映されます:
    - **コード整形**: `"editor.formatOnSave": true, "editor.formatOnPaste": true`

## シーケンス図
以下に、上記プロセスをマーメイド記法でシーケンス図として示します。

sequenceDiagram
    participant DevContainer as devcontainer.json
    participant Docker as Dockerfile
    participant DevContainerInstance as (devcontainer)
    participant SetupScript as setup.sh

    DevContainer->>DevContainer: build context を `..` に設定
    DevContainer->>Docker: Dockerfile をビルド
    activate Docker
    activate DevContainerInstance
    Docker->>DevContainerInstance: イメージ作成 & devcontainer 起動
    Docker->>DevContainerInstance: workdirを `/workspace` に設定
    Docker->>DevContainerInstance: 必要なfileを $WORKDIR に転送
    Docker->>DevContainerInstance: `vscode` ユーザー作成
    Docker->>DevContainerInstance: パッケージをインストール
    Docker->>DevContainerInstance: 作業ディレクトリの所有者を設定
    Docker->>DevContainerInstance: `vscode` にユーザー変更
    deactivate Docker
    deactivate DevContainerInstance

    DevContainer->>SetupScript: postCreateCommand 実行
    activate SetupScript
    SetupScript->>DevContainerInstance: 仮想環境を作成 & activate
    activate DevContainerInstance
    SetupScript->>DevContainerInstance: pip install -r requirements.txt
    deactivate DevContainerInstance
    deactivate SetupScript
    
    DevContainer->>DevContainerInstance: workspaceFolder で workdirを `/workspace` に設定
    activate DevContainerInstance
    DevContainer->>DevContainerInstance: python.defaultInterpreterPath で仮想環境を activate
    deactivate DevContainerInstance

## 設定の重要ポイント
1. **仮想環境のセットアップ**:
    - `devcontainer.json``"postCreateCommand": "/workspace/setup.sh"` により仮想環境が作成され、必要な Python パッケージがインストールされます。

2. **仮想環境の自動有効化**:
    - `"python.defaultInterpreterPath": "/workspace/.venv/bin/python"` によって、仮想環境がデフォルトの Python インタープリタとして設定されます。

3. **カスタマイズ設定**:
    - `devcontainer.json` に定義された拡張機能やエディタ設定を利用することで、開発作業を即座に始められる環境が用意されます。

確認

devcontainerを起動

左下のへんなマークを押して Reopen in Conteiner
image.png

venv起動

image.png

uvicorn起動

image.png

pytest起動

image.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?