# Docker Containers ガイド

このガイドは、Docker と Docker Compose の基本知識がある方向けに、実際のプロジェクト開発で Containers をどのように使うかを説明しています。

:::{admonition} 前提知識
- Docker と Docker Compose の基本的な理解
- `docker compose run` などの基本コマンド
- ターミナルでのファイル操作
:::

## はじめに：何ができるのか？

Haniwers では、Docker Containers を使用して以下のことが可能です：

- ✅ **テスト環境の隔離**: ホスト環境に影響なくテスト実行
- ✅ **環境の統一**: 開発者全員が同じ環境でテスト実行
- ✅ **依存関係の管理**: Poetry でビルドされた完全な環境
- ✅ **複数のワークフロー**: CLI テスト、ドキュメント生成、ユニットテスト

## ディレクトリ構成

```
haniwers/
├── compose.yaml                    ← プロジェクトルートのプロキシ
├── containers/
│   ├── compose.yaml               ← containers/ からのアクセス用
│   ├── .dockerignore              ← ビルド時の除外設定
│   ├── dev/
│   │   ├── Dockerfile             ← 開発・テスト用イメージ定義
│   │   ├── compose.yaml           ← 実際のサービス定義
│   │   └── README.md
│   ├── python3/
│   │   └── compose.yaml           ← PyPI パッケージ検証用
│   └── raspi/
│       └── Dockerfile             ← Raspberry Pi デプロイ用
└── （その他のプロジェクトファイル）
```

## クイックスタート

### 最初の一度：イメージをビルド

```bash
# プロジェクトルートで実行
docker compose build base
```

このコマンドで、`haniwers:latest` イメージが作成されます（初回は数分かかります）。

### テストを実行

```bash
# プロジェクトルートで実行
docker compose run --rm test
```

コンテナ内で pytest が実行され、すべてのテストが走ります。

### CLI をテスト

```bash
# Haniwers v0 のヘルプを表示
docker compose run --rm cli haniwers-v0 --help

# Haniwers v1 のポート一覧を表示
docker compose run --rm cli haniwers-v1 port list
```

### ドキュメントを生成

```bash
# ドキュメント生成
docker compose run --rm docs
```

## 実際の使用パターン

### パターン1: テストを実行してから開発

新機能を開発する前に、テストが通ることを確認：

```bash
# 1. イメージが最新か確認
docker compose build base

# 2. テスト実行
docker compose run --rm test

# 3. テスト結果を確認
# → エラーが出た場合は修正
```

### パターン2: コンテナ内で対話的にテスト

デバッグのため、コンテナ内で bash シェルを使用：

```bash
# コンテナ内で bash を起動
docker compose run --rm test bash

# コンテナ内で実行：
[container] # poetry run pytest tests/v1/unit/helpers/ -v
[container] # poetry run haniwers-v1 version --env
[container] # exit
```

**利点**: ホスト環境に影響を与えずにコマンドを試行できます。

### パターン3: ホストの変更をすぐにテスト

ソースコードを編集してから、コンテナでテスト：

```bash
# ホストでファイルを編集
vim src/haniwers/v1/helpers/parser.py

# コンテナでテスト実行
docker compose run --rm test tests/v1/unit/helpers/test_parser.py -v

# → コンテナは自動でホストの最新コードを読み込みます
```

**なぜ？** compose.yaml で以下のようにマウントしているため：

```yaml
volumes:
  - ../../src/haniwers:/workspace/haniwers  # ホストのコードをマウント
  - ../../tests:/workspace/tests            # テストをマウント
```

### パターン4: 特定のテストだけを実行

```bash
# 1つのテストファイルだけ実行
docker compose run --rm test tests/v1/unit/daq/device/test_connect.py

# 1つのテストクラスだけ実行
docker compose run --rm test tests/v1/unit/daq/device/test_connect.py::TestConnect

# 1つのテストメソッドだけ実行
docker compose run --rm test tests/v1/unit/daq/device/test_connect.py::TestConnect::test_connect_success
```

### パターン5: ドキュメントを確認

```bash
# ドキュメント生成
docker compose run --rm docs

# ブラウザで確認
# http://localhost:8000
```

## よくある課題と解決方法

### 課題1: イメージが古い

新しい依存関係を追加した場合、イメージを再ビルド：

```bash
# イメージをクリーンビルド
docker compose build base --no-cache
```

### 課題2: コンテナ内で permission denied

```bash
# エラー例：permission denied: /workspace/...

# 解決方法：コンテナ内で bash を実行して確認
docker compose run --rm test bash
[container] # ls -la /workspace/
```

### 課題3: ボリュームマウントが動作しない（macOS）

Docker Desktop の設定を確認：

1. **Preferences** → **Resources** → **File Sharing**
2. プロジェクトのディレクトリが追加されているか確認
3. 必要に応じて追加

### 課題4: コンテナが起動しない

```bash
# エラーログを確認
docker compose config   # 設定を確認
docker compose logs     # ログを表示
```

## 各サービスの詳細

### base（ビルド）

- **用途**: イメージビルド専用
- **自動起動**: なし（手動でビルド）
- **実行例**: `docker compose build base`

### test（テスト実行）

```bash
# 基本的なテスト実行
docker compose run --rm test

# 特定のテストスイート実行
docker compose run --rm test tests/v1/unit/

# カバレッジ付きテスト
docker compose run --rm test pytest --cov=haniwers

# 対話的なデバッグ
docker compose run --rm test bash
```

**マウント**:
- `src/haniwers/` → `/workspace/haniwers`
- `tests/` → `/workspace/tests`
- `data/` → `/workspace/data`

### cli（CLI テスト・シリアルポート）

```bash
# Haniwers v0 のヘルプを表示
docker compose run --rm cli haniwers-v0 --help

# Haniwers v1 のシリアルポート一覧を表示
docker compose run --rm cli haniwers-v1 port list

# Haniwers v1 のポートテスト
docker compose run --rm cli haniwers-v1 port test /dev/ttyUSB0

# バージョン確認
docker compose run --rm cli haniwers-v0 version

# 対話的にコマンドを実行
docker compose run --rm cli bash
[container] # haniwers-v1 daq --help
[container] # exit
```

**マウント**:

- `sandbox/` → `/workspace/sandbox`
- `/dev/` → `/dev/`（シリアルポートアクセス有効）

**特徴**:

- `poetry run` が自動的に実行される（entrypoint）
- ホストのシリアルポート（`/dev/ttyUSB*` など）にアクセス可能
- `privileged: true` でUSBデバイスアクセス権限を確保

### dev（開発環境）

```bash
# 開発環境で情報表示
docker compose run --rm dev

# コンテナ内で実行される内容：
# poetry run haniwers version --env
```

**マウント**:
- `src/haniwers/` → `/workspace/haniwers`
- `sandbox/` → `/workspace/sandbox`

### docs（ドキュメント生成）

```bash
# ドキュメント生成
docker compose run --rm docs

# 生成されたファイル：
# docs/_build/dirhtml/ に HTML が生成されます
```

**マウント**:
- `src/haniwers/` → `/workspace/haniwers`
- `docs/` → `/workspace/docs`

## Dockerfile について

`containers/dev/Dockerfile` はマルチステージビルドを使用：

```dockerfile
# Stage 1: Builder
FROM python:3.11-slim AS builder
RUN pip install poetry
# → Poetry で依存関係をインストール

# Stage 2: Runtime
FROM python:3.11-slim AS runtime
# → Builder の結果をコピー
# → ホストのコードをコピー
```

**なぜ?** イメージサイズを小さくしつつ、開発に必要なツールを含める

## 実践的なワークフロー例

### 新機能を追加する場合

```bash
# 1. 現在のテストが通ることを確認
docker compose run --rm test

# 2. 新しいコード/テストを作成（ホストのエディタで編集）
vim src/haniwers/v1/helpers/parser.py
vim tests/v1/unit/helpers/test_parser.py

# 3. 新しいテストだけ実行してみる
docker compose run --rm test tests/v1/unit/helpers/test_parser.py -v

# 4. すべてのテストが通ることを確認
docker compose run --rm test

# 5. コード品質をチェック
docker compose run --rm test bash
[container] # poetry run ruff format .
[container] # exit

# 6. コミット
git add ...
git commit -m "feat: add new feature"
```

### テストがデバッグできない場合

```bash
# 1. コンテナ内で bash を起動
docker compose run --rm test bash

# 2. コンテナ内で対話的にテスト
[container] # cd /workspace
[container] # poetry run pytest tests/... -v -s --pdb

# 3. デバッガーで問題を調査
# → exit で終了
```

### CI/CD パイプラインでも同じ環境を使用

```bash
# .gitlab-ci.yml で使用
image: python:3.11-slim

script:
  - poetry install
  - poetry run pytest
```

**重要**: ホストでの開発と CI/CD パイプラインで同じテスト環境を使用することで、「ローカルでは通るが CI では失敗」という問題を回避できます。

## トラブルシューティング

### Q: コンテナ内のファイルがホストに反映されない

A: コンテナ内で生成されたファイルは、**マウントされているディレクトリ**にのみホストに反映されます。

```yaml
# マウント設定の例
volumes:
  - ../../data:/workspace/data  # ホストの data/ にマウント
```

`data/` に生成されたファイルはホストで見えます。`/root/` などマウント外に生成されたファイルは見えません。

### Q: poetry.lock が最新でない

A: Dockerfile で依存関係がインストールされた時点の poetry.lock が使用されます。更新する場合：

```bash
# ホストで poetry.lock を更新
poetry update

# イメージをクリーンビルド
docker compose build base --no-cache

# テスト実行
docker compose run --rm test
```

### Q: Python バージョンを変更したい

A: Dockerfile を編集：

```dockerfile
# FROM python:3.11-slim  ← 3.11 を 3.12 に変更
FROM python:3.12-slim

# イメージをリビルド
docker compose build base --no-cache
```

## 参考資料

- [containers/README.md](../../containers/README.md) - Containers ディレクトリの概要
- [containers/dev/README.md](../../containers/dev/README.md) - 詳細な dev 環境設定
- [Docker 公式ドキュメント](https://docs.docker.com/)
- [Docker Compose 公式ドキュメント](https://docs.docker.com/compose/)

## 次のステップ

- [テストガイドライン](./testing.md) - テストの詳細な書き方
- [Mockerの使い方](./mocking.md) - デバイスなしでのテスト方法
- [アーキテクチャ](./architecture.md) - コードの設計理解
