- templates/service.yaml: service.type 既定を LoadBalancer に。annotations (kube-vip の VIP 指定等) / loadBalancerIP / loadBalancerClass / loadBalancerSourceRanges / externalTrafficPolicy / nodePort / labels を追加。 Ingress 機能は変更なし(LoadBalancer と併用可) - values.yaml / values.schema.json: service ブロックを拡張 - templates/NOTES.txt: LoadBalancer の EXTERNAL-IP 案内を追加 - examples/loadbalancer-oauth.values.yaml: Ingress 無効 + LB + source range 制限 + サブスクリプション認証 (authMode: oauth) の構成例を追加 - README: 「Claude 認証(API 課金なし / サブスクリプション)」「管理画面への アクセス(LoadBalancer / Ingress)」セクションを追加。claude setup-token → anthropic.authMode: oauth(CLAUDE_CODE_OAUTH_TOKEN 注入)でローカル実行と同一挙動 - helm lint / template 検証済み
498 lines
31 KiB
Markdown
498 lines
31 KiB
Markdown
# claude-code
|
||
|
||
**Alpine ベースの軽量コンテナで Claude Code をリモート実行する Helm チャート。**
|
||
|
||
ブラウザ(PC / スマートフォン)からアクセスし、**セッションごとに独立した使い捨て Pod** の中で
|
||
Git リポジトリ(GitHub / Gitea など互換 Git 全般)をチェックアウト → 編集 → 検証 → プッシュします。
|
||
一定時間アイドルなセッションは Pod ごと自動破棄され、リソースを占有し続けません。
|
||
Raspberry Pi などの ARM ベアメタル Kubernetes を主な対象にしています。
|
||
|
||
> **設計目標** — Claude Code は本来ローカルで動かすものだが、これをクラスタにデプロイすることで、
|
||
> Git 管理された複数プロジェクトを PC からもスマホからも横断的に、安全に触れるようにする。
|
||
|
||
- Gitea リポジトリ: `ssh://git@192.168.9.65/helmchart/claude-code`
|
||
- Helm リポジトリ: `https://git.cafepieters.com/api/packages/helmchart/helm`
|
||
- セッションイメージ: `git.cafepieters.com/helmchart/claude-code/session:<appVersion>`
|
||
|
||
---
|
||
|
||
## 目次
|
||
|
||
- [アーキテクチャ](#アーキテクチャ)
|
||
- [セッションのライフサイクル](#セッションのライフサイクル)
|
||
- [セキュリティモデル](#セキュリティモデル)
|
||
- [Claude 認証(API 課金なし / サブスクリプション)](#claude-認証api-課金なし--サブスクリプション)
|
||
- [管理画面へのアクセス(LoadBalancer / Ingress)](#管理画面へのアクセスloadbalancer--ingress)
|
||
- [インストール](#インストール)
|
||
- [使い方](#使い方)
|
||
- [values リファレンス](#values-リファレンス)
|
||
- [image](#image)
|
||
- [gateway / manager](#gateway--manager)
|
||
- [sessionsNamespace](#sessionsnamespace)
|
||
- [auth — アクセス認証](#auth--アクセス認証)
|
||
- [anthropic — Claude Code 認証情報](#anthropic--claude-code-認証情報)
|
||
- [git — 接続先と認証アイデンティティ](#git--接続先と認証アイデンティティ)
|
||
- [session — 使い捨てセッション](#session--使い捨てセッション)
|
||
- [networkPolicy](#networkpolicy)
|
||
- [service / ingress](#ingress--service)
|
||
- [セッションイメージ](#セッションイメージ)
|
||
- [運用](#運用)
|
||
- [トラブルシューティング](#トラブルシューティング)
|
||
- [リリースフロー](#リリースフロー)
|
||
- [バージョン履歴](#バージョン履歴)
|
||
|
||
---
|
||
|
||
## アーキテクチャ
|
||
|
||
```
|
||
┌──────────── リリース NS ────────────┐
|
||
ブラウザ (PC/スマホ) │ Deployment: claude-code │
|
||
│ HTTPS/WSS │ ┌──────────┐ ┌───────────────┐ │
|
||
▼ │ │ gateway │──▶│ manager │ │
|
||
Ingress / LB ──────────▶│ │ (nginx) │ │ (python stdlib)│ │
|
||
│ │ 認証 │ │ - Web UI │ │
|
||
│ │ WS proxy │ │ - Session API │ │
|
||
│ └────┬─────┘ │ - 回収ループ │ │
|
||
│ │ └───────┬────────┘ │
|
||
│ │ ttyd(WS) │ K8s API │
|
||
└───────┼─────────────────┼───────────┘
|
||
▼ ▼ (Role: pods/services)
|
||
┌───────── セッション NS (restricted PSA) ──────────┐
|
||
│ Pod claude-sess-ab12cd34ef Pod claude-sess-… │
|
||
│ ┌───────────────────────┐ │
|
||
│ │ alpine + node + git │ git clone │
|
||
│ │ + ttyd + claude-code │ → claude (ttyd) │
|
||
│ │ 非root / roFS / netpol│ → claude-verify/push │
|
||
│ └───────────────────────┘ アイドルで自壊 │
|
||
└────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| コンポーネント | イメージ | 役割 |
|
||
|---|---|---|
|
||
| **gateway** | `nginxinc/nginx-unprivileged:*-alpine` | 単一入口。`auth.mode` に応じた認証、`/s/<id>/` をセッション Pod の `ttyd` へ WebSocket リバースプロキシ、リクエスト毎にアクティビティ時刻を更新 |
|
||
| **manager** | `python:3.x-alpine`(標準ライブラリのみ・追加 pip 不要) | モバイル対応 Web UI、セッション作成/一覧/破棄 API、アイドル/寿命の回収ループ。in-cluster ServiceAccount で Kubernetes API を直接操作 |
|
||
| **session** | `git.cafepieters.com/helmchart/claude-code/session`(既定)または任意の node:alpine 系 | セッションごとに作られる **使い捨て Pod**。`git clone` → `claude` を `ttyd` でブラウザに提供 |
|
||
| **reaper** | manager と同じイメージ(`--reap-once`) | manager 障害時の保険となる孤児掃除 CronJob |
|
||
|
||
manager / gateway / session-entrypoint の実体は `files/` にあり、`.Files.Get` で ConfigMap 化されます。
|
||
|
||
## セッションのライフサイクル
|
||
|
||
1. **作成** — UI か `POST /api/sessions` に `repo` / `ref` / `identity` を渡す。manager は
|
||
- リポジトリ URL の形式と **Git ホスト allow-list**(`git.allowedHosts`)を検証
|
||
- 同時セッション数(`session.maxSessions`)を確認
|
||
- セッション NS に Pod(`claude-sess-<10hex>`)と Service を作成
|
||
2. **ブート**(`files/session-entrypoint.sh`, 非 root)
|
||
- マウントされた SSH 鍵 / HTTPS トークンで git 認証を構成(`known_hosts` 必須、TOFU 無効)
|
||
- リポジトリを `session.workspaceDir` に `clone`(`--filter=blob:none`)
|
||
- `claude` が無ければ `$HOME` に非 root で npm インストール
|
||
- `ttyd` を起動(`session.autoStartClaude: true` なら `claude` を自動起動)
|
||
3. **利用** — `/s/<id>/` 経由でブラウザ内ターミナルに接続。`claude-verify` / `claude-push` が使える
|
||
4. **破棄** — 次のいずれかで Pod ごと消滅(Service も回収)
|
||
- **アイドル**: `ttyd` への接続が `session.idleTimeoutSeconds` 秒間ゼロ → Pod 内ウォッチドッグが自壊
|
||
- **アイドル(保険)**: manager の回収ループが `last-activity` アノテーションを見て削除
|
||
- **最大寿命**: `session.maxLifetimeSeconds`(Pod の `activeDeadlineSeconds`)到達で強制終了
|
||
- **手動**: UI の「破棄」/ `DELETE /api/sessions/<id>`
|
||
- **孤児掃除**: 予備 CronJob(`session.reaper.cronjob`)
|
||
|
||
## セキュリティモデル
|
||
|
||
| 層 | 対策 |
|
||
|---|---|
|
||
| **入口** | すべて gateway 経由。`auth.mode`= `token`(既定)/ `basic` / `forward`(外部 SSO へ委譲)/ `none`。WebSocket は Cookie トークンで認証(ブラウザは WS に Authorization ヘッダを付けられないため) |
|
||
| **制御プレーン RBAC** | manager はセッション NS の `pods` / `services` のみ操作する **Role**(ClusterRole 不使用)。他 NS・他リソースには触れない |
|
||
| **セッション Pod** | `automountServiceAccountToken: false`(**API サーバー到達不可**)、`runAsNonRoot` / `readOnlyRootFilesystem` / `capabilities.drop: [ALL]` / `seccompProfile: RuntimeDefault`。書き込み可能なのは emptyDir(`/workspace`, `$HOME`, `/tmp`)のみ |
|
||
| **ネットワーク** | NetworkPolicy でセッションの ingress は gateway からのみ。egress は DNS + HTTPS(443, git/Anthropic) + SSH(22)。`networkPolicy.blockedEgressCIDRs`(既定でクラウドメタデータ IP `169.254.169.254/32`)を egress の `except` に指定。自クラスタの Pod/Service CIDR を追加すると内部 SSRF も遮断できる |
|
||
| **Git 到達先** | `git.allowedHosts` の allow-list。既定は allow-list 外を拒否(`allowArbitraryRepos: true` で解除) |
|
||
| **認証情報** | `ANTHROPIC_API_KEY` / SSH 鍵 / トークンは Kubernetes Secret。`values.yaml` に平文で書かず `--set` / `--set-file` / `existingSecret` で注入。git HTTPS トークンはセッション内で `credential.helper store` の 0600 ファイルに限定し、プロセス環境に残さない |
|
||
| **PodSecurity** | セッション NS に `pod-security.kubernetes.io/enforce: restricted` を付与(`sessionsNamespace.create: true` 時) |
|
||
| **ブラスト半径** | `ResourceQuota`(Pod / Service 数)+ `LimitRange`(コンテナ CPU/メモリ上限)+ `session.maxSessions` |
|
||
| **自動 push** | 既定 **無効**。有効時もデフォルト/保護ブランチ(`autoPush.protectedBranches`)への push と `--force` は不可。`branchPrefix` 配下の作業ブランチにのみ push |
|
||
|
||
> **注意** — `values.yaml` に API キーや秘密鍵を書いてコミットしないでください。`helm get values` /
|
||
> CI ログ / Git 履歴に残ります。`existingSecret` か CI からの `--set` を使ってください。
|
||
|
||
## Claude 認証(API 課金なし / サブスクリプション)
|
||
|
||
このチャートは **Claude の API(従量課金)を使わずに**、ローカルの Claude Code と同じ
|
||
**サブスクリプション認証(Pro / Max プラン)** で動かせます。セッション内の `claude` の
|
||
挙動・機能はローカル実行と同一です(同じ CLI・同じログイン方式)。
|
||
|
||
1. 手元の PC(Claude にログイン済み)で長期トークンを発行:
|
||
|
||
```bash
|
||
claude setup-token
|
||
```
|
||
|
||
ブラウザで承認すると `CLAUDE_CODE_OAUTH_TOKEN` として使える長期トークンが表示されます。
|
||
|
||
2. それをチャートに渡す(`authMode: oauth`):
|
||
|
||
```bash
|
||
helm upgrade --install claude-code cafepieters/claude-code -n claude-code --create-namespace \
|
||
-f examples/loadbalancer-oauth.values.yaml \
|
||
--set anthropic.oauthToken="<発行されたトークン>"
|
||
```
|
||
|
||
Secret にしておく場合:
|
||
|
||
```bash
|
||
kubectl -n claude-code-sessions create secret generic claude-oauth \
|
||
--from-literal=CLAUDE_CODE_OAUTH_TOKEN="<トークン>"
|
||
# values: anthropic.existingSecret=claude-oauth
|
||
```
|
||
|
||
| 方式 | values | 認証情報 | 課金 |
|
||
|---|---|---|---|
|
||
| **サブスクリプション(推奨)** | `anthropic.authMode: oauth` | `anthropic.oauthToken`(`claude setup-token`) | Claude プランの利用枠を消費(API 課金なし) |
|
||
| API キー | `anthropic.authMode: apiKey` | `anthropic.apiKey`(`sk-ant-...`) | API 従量課金 |
|
||
|
||
- トークンは **セッション NS の Secret** に保存され、セッション Pod へ環境変数
|
||
`CLAUDE_CODE_OAUTH_TOKEN` として注入されます(`values.yaml` に平文で書かない)。
|
||
- 利用枠はローカルの Claude Code と共有です。複数セッションを同時に走らせると
|
||
プランのレート上限に早く達する点に注意してください。
|
||
- トークンには有効期限があります。失効したら `claude setup-token` で再発行し
|
||
`helm upgrade --set anthropic.oauthToken=...`(または Secret 更新 + Pod 再起動)します。
|
||
|
||
## 管理画面へのアクセス(LoadBalancer / Ingress)
|
||
|
||
管理画面(gateway)への到達経路は 2 つあり、**併用できます**。既定は LoadBalancer です。
|
||
|
||
### A. LoadBalancer でローカルネットワークの IP を使う(既定・Ingress 不要)
|
||
|
||
`service.type: LoadBalancer`(既定)にすると、クラスタの LB 実装
|
||
(**kube-vip** / MetalLB / Cilium LB-IPAM など)がローカルネットワークの IP を払い出します。
|
||
その IP をブラウザで開くだけでアクセスできます。ドメインも Ingress も TLS 終端も不要です。
|
||
|
||
```yaml
|
||
ingress:
|
||
enabled: false # Ingress は張らない
|
||
service:
|
||
type: LoadBalancer
|
||
port: 80
|
||
annotations:
|
||
kube-vip.io/loadbalancerIPs: "192.168.9.50" # 払い出す VIP(空きアドレス)
|
||
kube-vip.io/vipHost: "cpi11"
|
||
loadBalancerSourceRanges: # 接続元をローカルセグメントに限定(第一の防御線)
|
||
- 192.168.9.0/24
|
||
externalTrafficPolicy: Local # 実クライアント IP を保持
|
||
```
|
||
|
||
```bash
|
||
kubectl -n claude-code get svc claude-code # EXTERNAL-IP を確認
|
||
# → http://<EXTERNAL-IP>/ をブラウザで開く
|
||
```
|
||
|
||
| キー | 説明 |
|
||
|---|---|
|
||
| `service.type` | `LoadBalancer`(既定)/ `ClusterIP` / `NodePort` |
|
||
| `service.port` | 待受ポート(既定 `80`) |
|
||
| `service.annotations` | LB 実装へのヒント(kube-vip の VIP 指定、MetalLB のプール指定など) |
|
||
| `service.loadBalancerIP` | 払い出す IP を直接指定(annotations 指定を推奨) |
|
||
| `service.loadBalancerSourceRanges` | 接続を許可する CIDR。**Ingress を張らない構成では必ず設定して接続元を絞る** |
|
||
| `service.loadBalancerClass` | 複数 LB 実装がある場合の指定 |
|
||
| `service.externalTrafficPolicy` | `Local` で実クライアント IP を保持 |
|
||
| `service.nodePort` | `type: NodePort` 時の固定ポート |
|
||
|
||
> **セキュリティ** — LoadBalancer は L3/L4 でネットワークに口を開けます。`loadBalancerSourceRanges`
|
||
> で接続元を絞り、`auth.mode` は `token`(既定)以上にしてください。VIP セグメントは
|
||
> ルーターの DHCP 範囲と重複させないこと。
|
||
|
||
### B. Ingress でドメインアクセス(従来どおり維持)
|
||
|
||
`ingress.enabled: true` で Ingress を作成します。A と同時に有効化しても構いません。
|
||
WebSocket 用に `proxy-read-timeout` / `proxy-send-timeout` を既定 `3600` に設定します
|
||
(`ingress.annotations` で上書き可)。TLS は `ingress.tls` で設定します。
|
||
|
||
```yaml
|
||
ingress:
|
||
enabled: true
|
||
className: nginx
|
||
hosts:
|
||
- host: claude-code.example.com
|
||
paths: [{ path: /, pathType: Prefix }]
|
||
tls:
|
||
- secretName: claude-code-tls
|
||
hosts: [claude-code.example.com]
|
||
```
|
||
|
||
## インストール
|
||
|
||
### 前提
|
||
|
||
- Kubernetes 1.25+(PodSecurity admission)、NetworkPolicy 対応 CNI(Calico / Cilium など)
|
||
- 管理画面の公開方法に応じて **どちらか**:
|
||
- LoadBalancer 実装(kube-vip / MetalLB / Cilium LB-IPAM など) … 既定
|
||
- Ingress コントローラー … `ingress.enabled: true` の場合
|
||
- どちらも無い場合は `service.type: ClusterIP` + `kubectl port-forward` で利用可
|
||
- Claude の認証情報 … `claude setup-token` の OAuth トークン(推奨)または `ANTHROPIC_API_KEY`
|
||
|
||
### Helm リポジトリから
|
||
|
||
```bash
|
||
helm repo add cafepieters https://git.cafepieters.com/api/packages/helmchart/helm
|
||
helm repo update
|
||
helm upgrade --install claude-code cafepieters/claude-code \
|
||
-n claude-code --create-namespace \
|
||
-f my.values.yaml \
|
||
--set anthropic.apiKey="$ANTHROPIC_API_KEY"
|
||
```
|
||
|
||
### ローカル LB の IP + サブスクリプション認証(API 不要・推奨構成)
|
||
|
||
```bash
|
||
claude setup-token # 手元の PC で長期 OAuth トークンを発行
|
||
|
||
helm upgrade --install claude-code cafepieters/claude-code \
|
||
-n claude-code --create-namespace \
|
||
-f examples/loadbalancer-oauth.values.yaml \
|
||
--set anthropic.oauthToken="<発行されたトークン>" \
|
||
--set-file git.identities.gitea-ssh.sshPrivateKey=P:/Claude/.ssh/id_claude \
|
||
--set-file git.identities.gitea-ssh.knownHosts=./known_hosts.gitea
|
||
```
|
||
|
||
`examples/loadbalancer-oauth.values.yaml` は Ingress 無効・`service.type: LoadBalancer` +
|
||
kube-vip の VIP 指定 + `loadBalancerSourceRanges` によるローカルセグメント限定の構成です。
|
||
|
||
### ローカルチャートから(GitHub 公開リポジトリだけ触る最小構成)
|
||
|
||
```bash
|
||
helm upgrade --install claude-code ./claude-code \
|
||
-n claude-code --create-namespace \
|
||
-f examples/minimal.values.yaml \
|
||
--set anthropic.apiKey="$ANTHROPIC_API_KEY"
|
||
```
|
||
|
||
### Gitea(SSH) + GitHub(HTTPS) 両対応
|
||
|
||
```bash
|
||
helm upgrade --install claude-code ./claude-code \
|
||
-n claude-code --create-namespace \
|
||
-f examples/gitea-and-github.values.yaml \
|
||
--set anthropic.apiKey="$ANTHROPIC_API_KEY" \
|
||
--set-file git.identities.gitea-ssh.sshPrivateKey=P:/Claude/.ssh/id_claude \
|
||
--set-file git.identities.gitea-ssh.knownHosts=./known_hosts.gitea \
|
||
--set git.identities.github-https.token="$GITHUB_PAT"
|
||
```
|
||
|
||
`known_hosts` は事前に取得します:
|
||
|
||
```bash
|
||
ssh-keyscan 192.168.9.65 > known_hosts.gitea
|
||
```
|
||
|
||
## 使い方
|
||
|
||
1. 管理画面を開く:
|
||
- **LoadBalancer(既定)**: `kubectl -n claude-code get svc claude-code` の `EXTERNAL-IP` を
|
||
ブラウザで開く(`http://<IP>/`)
|
||
- **Ingress**: 設定したドメイン(`https://claude-code.example.com/`)
|
||
- **どちらも無効**: `kubectl -n claude-code port-forward svc/claude-code 8080:80` → `http://localhost:8080/`
|
||
|
||
2. `auth.mode: token` の場合、トークンを取得して UI に貼り付け(または `?token=` で開く):
|
||
|
||
```bash
|
||
kubectl -n claude-code get secret claude-code-auth \
|
||
-o jsonpath='{.data.token}' | base64 -d ; echo
|
||
```
|
||
|
||
3. UI で **リポジトリ URL** / **ブランチ** / **認証 identity** を指定して「セッション作成」。
|
||
4. 一覧の「開く」でブラウザ内ターミナル(Claude Code)に接続。
|
||
5. セッション内の補助コマンド:
|
||
- `claude-verify` … `session.verifyCommand`(例 `npm test`)を実行
|
||
- `claude-push` … 検証 → `claude/<branch>` へ push(保護ブランチ / `--force` 不可)
|
||
6. 無操作 `session.idleTimeoutSeconds` 秒でセッション(Pod)は自動的に消えます。
|
||
|
||
## values リファレンス
|
||
|
||
### image
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `image.gateway.*` | `nginxinc/nginx-unprivileged:1.29.1-alpine` | 受付 nginx(非 root 版) |
|
||
| `image.manager.*` | `library/python:3.13-alpine` | 制御プレーン(標準ライブラリのみ) |
|
||
| `image.session.*` | `library/node:22-alpine` | セッション。既定はブート時に `apk`/`npm` を実行するため初回起動が遅い。[セッションイメージ](#セッションイメージ)参照 |
|
||
| `imagePullSecrets` | `[]` | プライベートレジストリ用 |
|
||
|
||
### gateway / manager
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `gateway.replicaCount` | `1` | 制御プレーン Deployment のレプリカ数 |
|
||
| `gateway.port` | `8080` | gateway コンテナ待受ポート(Service / Ingress バックエンド) |
|
||
| `manager.logLevel` | `info` | `debug` / `info` / `warning` / `error` |
|
||
| `gateway.resources` / `manager.resources` | 控えめ | それぞれの resources |
|
||
|
||
### sessionsNamespace
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `sessionsNamespace.name` | `claude-code-sessions` | セッション Pod を隔離する NS(空 = リリース NS) |
|
||
| `sessionsNamespace.create` | `true` | この NS を作成し PodSecurity ラベルを付与するか。既存 NS を使うなら `false` にして手動でラベル付与 |
|
||
| `sessionsNamespace.podSecurityLevel` | `restricted` | 付与する PodSecurity `enforce` レベル |
|
||
| `sessionsNamespace.resourceQuota.enabled` | `true` | セッション NS の `ResourceQuota`(既定は Pod/Service 数の抑制のみ。compute 集計上限は `hard` で明示) |
|
||
| `sessionsNamespace.resourceQuota.hard` | `{}` | 指定するとそのまま `spec.hard` |
|
||
| `sessionsNamespace.limitRange.*` | max 4CPU/4Gi | セッション NS のコンテナ既定値・上限(`LimitRange`) |
|
||
|
||
### auth — アクセス認証
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `auth.mode` | `token` | `token` / `basic` / `forward` / `none` |
|
||
| `auth.token.value` | `""` | 空なら install 時に自動生成(`lookup` で再 install 間も安定)。運用では `existingSecret` 推奨 |
|
||
| `auth.token.existingSecret` / `existingSecretKey` | `""` / `token` | 既存 Secret からトークンを読む |
|
||
| `auth.basic.users` | `[]` | `htpasswd -nbB user pass` の出力を 1 行以上 |
|
||
| `auth.basic.existingSecret` / `existingSecretKey` | `""` / `htpasswd` | 既存 htpasswd Secret |
|
||
| `auth.forward.url` | `""` | `auth_request` の転送先(例: oauth2-proxy の `/oauth2/auth`) |
|
||
| `auth.forward.signInUrl` | `""` | 未認証時のサインイン URL(任意) |
|
||
|
||
`token` モードでは UI のログインフォームで一度トークンを入力すると、`HttpOnly` Cookie に保存され
|
||
以降の `/s/<id>/`(WebSocket 含む)に自動的に付与されます。
|
||
|
||
### anthropic — Claude Code 認証情報
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `anthropic.authMode` | `apiKey` | `apiKey`(`ANTHROPIC_API_KEY`)/ `oauth`(`CLAUDE_CODE_OAUTH_TOKEN`) |
|
||
| `anthropic.apiKey` / `anthropic.oauthToken` | `""` | `--set` / `--set-file` で注入 |
|
||
| `anthropic.existingSecret` | `""` | 既存 Secret を使う(キー名は `existingSecretKeyApiKey` / `existingSecretKeyOauthToken`) |
|
||
| `anthropic.extraEnv` | `{}` | 全セッションへ渡す追加 env(例 `ANTHROPIC_BASE_URL`、Bedrock/Vertex 切替など) |
|
||
|
||
Secret は **セッション NS** に作成され、セッション Pod へ `envFrom` で注入されます。
|
||
|
||
### git — 接続先と認証アイデンティティ
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `git.allowedHosts` | GitHub/GitLab/Gitea | clone を許可するホスト allow-list |
|
||
| `git.allowArbitraryRepos` | `false` | allow-list を無効化し任意 URL を許可(非推奨) |
|
||
| `git.defaultAuthorName` / `Email` | `Claude` / `claude@cafepieters.com` | コミット author 既定 |
|
||
| `git.identities.<name>.type` | — | `ssh` / `https-token` |
|
||
| `…ssh.sshPrivateKey` / `knownHosts` | — | PEM 秘密鍵 / `ssh-keyscan` 出力(**`knownHosts` 必須**) |
|
||
| `…https-token.username` / `token` | `x-access-token` / — | HTTPS ベーシック(GitHub PAT は username 任意) |
|
||
| `…existingSecret` ほか | — | 既存 Secret 参照(キー名は `existingSecretKey*` で調整) |
|
||
|
||
GitHub でも Gitea でも GitLab でも、SSH か HTTPS トークンで認証できる Git ホストなら同じ仕組みで扱えます。
|
||
セッション作成時に identity 名を選ぶと、その Secret だけがそのセッション Pod にマウントされます。
|
||
|
||
### session — 使い捨てセッション
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `session.autoStartClaude` | `true` | ブート後に `claude` を自動起動(`false` はログインシェル) |
|
||
| `session.workspaceDir` | `/workspace` | clone 先 |
|
||
| `session.ttydPort` | `7681` | `ttyd` 待受(Pod 内のみ) |
|
||
| `session.extraApkPackages` | `[]` | 追加 apk(事前ビルドイメージなど root 権限がある場合のみ有効) |
|
||
| `session.claudeCodeVersion` | `""` | インストールする `@anthropic-ai/claude-code` バージョン(空 = チャート `appVersion`) |
|
||
| `session.resources` | 100m/256Mi 〜 2CPU/2Gi/4Gi(eph) | セッション Pod の resources |
|
||
| `session.idleTimeoutSeconds` | `1800` | 無通信での破棄しきい値 |
|
||
| `session.maxLifetimeSeconds` | `43200` | Pod の `activeDeadlineSeconds` |
|
||
| `session.reaper.intervalSeconds` | `60` | manager 回収ループ間隔 |
|
||
| `session.reaper.cronjob.enabled` / `schedule` | `true` / `*/15 * * * *` | 予備の孤児掃除 CronJob |
|
||
| `session.maxSessions` | `10` | 同時セッション数上限(manager が enforce) |
|
||
| `session.verifyCommand` | `""` | `claude-verify` / `claude-push` が実行するコマンド |
|
||
| `session.autoPush.enabled` | `false` | `claude-push` を許可するか |
|
||
| `session.autoPush.branchPrefix` | `claude/` | push 先ブランチのプレフィックス |
|
||
| `session.autoPush.protectedBranches` | `[main, master, develop, release]` | push 禁止ブランチ |
|
||
| `session.podLabels` / `podAnnotations` / `nodeSelector` / `tolerations` / `priorityClassName` | — | セッション Pod へのメタデータ / スケジューリング |
|
||
|
||
### networkPolicy
|
||
|
||
| キー | 既定 | 説明 |
|
||
|---|---|---|
|
||
| `networkPolicy.enabled` | `true` | NetworkPolicy を作成するか(CNI が対応必須) |
|
||
| `networkPolicy.dnsNamespaceSelector` | `kube-system` | DNS を許可する NS セレクタ |
|
||
| `networkPolicy.allowInternetHttps` | `true` | セッションの 443/tcp 外向きを許可(git HTTPS + Anthropic API) |
|
||
| `networkPolicy.allowSshGit` | `true` | セッションの 22/tcp 外向きを許可(SSH git) |
|
||
| `networkPolicy.blockedEgressCIDRs` | `[169.254.169.254/32]` | egress `except`。自クラスタの Pod/Service CIDR を足すと内部 SSRF を遮断 |
|
||
| `networkPolicy.sessionEgressExtra` | `[]` | 追加許可(社内ミラー / プロキシ)。`{cidr, ports[]}` |
|
||
|
||
完全にインターネットを遮断したい場合は `allowInternetHttps: false` にして egress プロキシ
|
||
(例: 許可リスト付き Squid)を `sessionEgressExtra` で通してください。
|
||
|
||
### ingress / service
|
||
|
||
→ [管理画面へのアクセス(LoadBalancer / Ingress)](#管理画面へのアクセスloadbalancer--ingress) を参照。
|
||
`service.type` の既定は `LoadBalancer`、`ingress.enabled` の既定は `false`(両者は併用可)。
|
||
|
||
## セッションイメージ
|
||
|
||
既定の `image.session` は CI(`session-image.yaml`)がビルドする **事前ビルドイメージ**
|
||
`git.cafepieters.com/helmchart/claude-code/session:<appVersion>` です。
|
||
`git` / `openssh-client` / `ttyd` / `bash` / `node` / `@anthropic-ai/claude-code` を同梱し、
|
||
非 root(uid 10001)で動作するため、`restricted` PodSecurity / `readOnlyRootFilesystem` 下でも
|
||
確実に起動し、コールドスタートも数秒です。
|
||
|
||
`session-image.yaml` は `files/Dockerfile.session` 等の変更 push、または
|
||
`image-update-and-release.yaml` による `Chart.yaml` 更新で発火し、`linux/amd64,linux/arm64` の
|
||
2 アーキテクチャでビルドして Gitea Container Registry へ push します。手動ビルドは:
|
||
|
||
```bash
|
||
docker buildx build --platform linux/amd64,linux/arm64 \
|
||
--build-arg CLAUDE_CODE_VERSION=2.1.259 \
|
||
-f files/Dockerfile.session \
|
||
-t git.cafepieters.com/helmchart/claude-code/session:2.1.259 --push .
|
||
```
|
||
|
||
### 事前ビルドを使わない場合
|
||
|
||
`image.session` に素の `node:22-alpine` などを指定することもできます。この場合ブート
|
||
スクリプトが `$HOME` に非 root で `@anthropic-ai/claude-code` を npm インストールします
|
||
(初回数十秒)。ただし **`ttyd` は非 root では `apk add` できない** ため、`ttyd` を含まない
|
||
イメージでは起動に失敗します。`ttyd` 入りの独自イメージを用意するか、事前ビルドイメージを
|
||
使ってください。
|
||
|
||
## 運用
|
||
|
||
- **セッション一覧**: `kubectl -n <sessionsNamespace> get pods -l app.kubernetes.io/component=session`
|
||
- **セッションのログ**: `kubectl -n <sessionsNamespace> logs claude-sess-<id>`
|
||
- **強制破棄**: UI から、または `kubectl -n <sessionsNamespace> delete pod,svc claude-sess-<id>`
|
||
- **回収の確認**: manager のログに `reaping session <id> (idle|max-lifetime|phase=...)`
|
||
- **トークンのローテーション**: `auth.token.existingSecret` の値を更新し、Deployment を再起動
|
||
- **アップグレード**: `helm upgrade`。`files/*` の変更は checksum アノテーションで自動ローリング
|
||
|
||
## トラブルシューティング
|
||
|
||
| 症状 | 原因と対処 |
|
||
|---|---|
|
||
| UI が 502 / セッションを開くと 502 | セッション Pod がまだ Ready でない(初回は `npm i` に時間)。数十秒待つ。恒久対策は事前ビルドイメージ |
|
||
| セッション作成が `許可されていない Git ホスト` | `git.allowedHosts` に対象ホストを追加(IP と FQDN は別物) |
|
||
| clone が `Host key verification failed` | identity の `knownHosts` が対象ホストの鍵と不一致。`ssh-keyscan <host>` で取り直す |
|
||
| clone が `terminated ... could not read Username` | HTTPS の場合 `git.identities.<name>` 未指定 or token 無効。SSH の場合は identity の type/秘密鍵を確認 |
|
||
| WebSocket がすぐ切れる | Ingress の `proxy-read-timeout` を延長。`auth.mode: token` で Cookie 未設定なら一度 UI からログイン |
|
||
| セッションが破棄されない | manager の RBAC / ログを確認。予備 CronJob(`session.reaper.cronjob`)が有効か |
|
||
| `apk` エラーがログに出る | 既定イメージ + `readOnlyRootFilesystem` の制約。事前ビルドイメージを使う |
|
||
| manager が全リクエストを 401 | `auth.mode: token` でトークンが空。`auth.token.value` か `existingSecret` を設定 |
|
||
|
||
## リリースフロー
|
||
|
||
`Chart.yaml` の `version` / `appVersion` は **Claude Code CLI(`@anthropic-ai/claude-code` npm 最新版)と同一**。
|
||
|
||
- **`helm-release.yaml`** — `main` push 時に Gitea Package Registry へチャートを publish(全リポジトリ共通)。
|
||
- **`image-update-and-release.yaml`** — 毎週月曜 3:00 JST。
|
||
- npm の Claude Code 最新版に追従。**主アプリ更新時のみ** `Chart.yaml` 更新 → セッションイメージ
|
||
multi-arch ビルド/push → Git タグ `v<ver>` → Gitea リリース → チャート publish。
|
||
- `nginx` / `python` の Alpine タグのみ更新の場合は `values.yaml` を更新してコミットするだけ
|
||
(**リリースなし**)。
|
||
- **手動リリース(臨時)** — バージョン末尾にアルファベットを付与(`2.1.259` → `2.1.259-a`)。
|
||
対応する `CHANGELOG-<version>.md` を追加する。
|
||
|
||
必要な Gitea Secrets: `GITEA_TOKEN` / `REGISTRY_USER` / `REGISTRY_TOKEN`。
|
||
|
||
### ワークフロー一覧
|
||
|
||
| ファイル | トリガー | 動作 |
|
||
|---|---|---|
|
||
| `helm-release.yaml` | `main` への push / 手動 | `helm lint`。`Chart.yaml` の version が未リリース(Gitea リリース・Package Registry 双方に無い)なら タグ + Gitea リリース + チャート publish。リリース済みなら lint のみ |
|
||
| `image-update-and-release.yaml` | 毎週月曜 3:00 JST / 手動 | npm の `@anthropic-ai/claude-code` 最新版と base image(nginx / python)の最新 Alpine タグを確認。Claude Code の更新があれば `values.yaml` / `Chart.yaml` / README 履歴を更新してコミット → タグ + リリース + publish。base image のみの更新は `values.yaml` を更新してコミットのみ(リリースなし) |
|
||
| `session-image.yaml` | `files/Dockerfile.session` ほかの変更 / 手動 | セッションイメージ(`.../claude-code/session`)を `linux/amd64,linux/arm64` でビルドし、`Chart.yaml` の `appVersion` タグ + `latest` で Gitea Container Registry へ push |
|
||
|
||
## バージョン履歴
|
||
|
||
| バージョン | Claude Code | 変更内容 |
|
||
|---|---|---|
|
||
| 2.1.263-a | 2.1.263 | Service を `LoadBalancer` 対応(kube-vip 等でローカル LB の IP から管理画面へアクセス、`loadBalancerSourceRanges` で接続元制限、既定を `LoadBalancer` に変更)。Ingress は従来どおり併用可。API を使わないサブスクリプション認証(`claude setup-token` → `authMode: oauth`)の手順を README に追記 |
|
||
| 2.1.263 | 2.1.263 | 自動更新 |
|
||
| 2.1.259 | 2.1.259 | 初版。gateway + manager + 使い捨て session Pod + reaper、GitHub/Gitea 対応、アイドル自動破棄、NetworkPolicy / RBAC 最小化 / restricted PodSecurity |
|