セッション Pod がブート失敗で Failed になると reaper が即削除し kubectl logs で 死因を追えなかった。「開く」で 502 になるだけで原因不明だった。 - session.reaper.terminatedRetentionSeconds(既定 900): 終了済み Pod を finishedAt からこの秒数は回収しない。manager 回収ループと reaper CronJob 両方が尊重 - GET /api/sessions/<id>/log: セッション Pod のログを text で返す(pods/log get のみ、追加権限なし) - UI: 「ログ」ボタン、状態の色分け、Failed 行に reason/exitCode/message 併記、 Running 以外は「開く」非表示 - session-entrypoint.sh: URL スキームと identity 型の不一致を警告。 git clone を --progress ... 2>&1 にして git/ssh の出力を Pod ログへ - helm lint / template 検証済み
262 lines
11 KiB
YAML
262 lines
11 KiB
YAML
{{- $sessNs := include "claude-code.sessionsNamespace" . -}}
|
|
{{- $ccVersion := .Values.session.claudeCodeVersion | default .Chart.AppVersion -}}
|
|
{{- /* --- git identities を manager が解釈できる JSON へ変換 --- */ -}}
|
|
{{- $idents := dict -}}
|
|
{{- range $name, $id := .Values.git.identities -}}
|
|
{{- $entry := dict "type" $id.type "secret" (include "claude-code.gitIdentitySecretName" (dict "ctx" $ "name" $name)) -}}
|
|
{{- if eq $id.type "ssh" -}}
|
|
{{- $_ := set $entry "keyPrivateKey" ($id.existingSecretKeyPrivateKey | default "id") -}}
|
|
{{- $_ := set $entry "keyKnownHosts" ($id.existingSecretKeyKnownHosts | default "known_hosts") -}}
|
|
{{- else if eq $id.type "https-token" -}}
|
|
{{- $_ := set $entry "keyToken" ($id.existingSecretKeyToken | default "token") -}}
|
|
{{- $_ := set $entry "username" ($id.username | default "x-access-token") -}}
|
|
{{- end -}}
|
|
{{- $_ := set $idents $name $entry -}}
|
|
{{- end -}}
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ include "claude-code.fullname" . }}
|
|
namespace: {{ .Release.Namespace }}
|
|
labels:
|
|
{{- include "claude-code.labels" . | nindent 4 }}
|
|
app.kubernetes.io/component: control-plane
|
|
spec:
|
|
replicas: {{ .Values.gateway.replicaCount }}
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxUnavailable: 1
|
|
maxSurge: 1
|
|
selector:
|
|
matchLabels:
|
|
{{- include "claude-code.selectorLabels" . | nindent 6 }}
|
|
app.kubernetes.io/component: control-plane
|
|
template:
|
|
metadata:
|
|
labels:
|
|
{{- include "claude-code.selectorLabels" . | nindent 8 }}
|
|
app.kubernetes.io/component: control-plane
|
|
annotations:
|
|
checksum/manager: {{ .Files.Get "files/manager.py" | sha256sum }}
|
|
checksum/gateway: {{ print (.Files.Get "files/gateway.conf.template") (.Files.Get "files/gateway-entrypoint.sh") | sha256sum }}
|
|
checksum/session: {{ .Files.Get "files/session-entrypoint.sh" | sha256sum }}
|
|
{{- with .Values.commonAnnotations }}
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
spec:
|
|
serviceAccountName: {{ include "claude-code.managerServiceAccountName" . }}
|
|
automountServiceAccountToken: true
|
|
{{- with .Values.imagePullSecrets }}
|
|
imagePullSecrets:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
securityContext:
|
|
{{- include "claude-code.podSecurityContext" . | nindent 8 }}
|
|
containers:
|
|
# ---- gateway: 受付・認証・WebSocket プロキシ ----
|
|
- name: gateway
|
|
image: "{{ .Values.image.gateway.registry }}/{{ .Values.image.gateway.repository }}:{{ .Values.image.gateway.tag }}"
|
|
imagePullPolicy: {{ .Values.image.gateway.pullPolicy }}
|
|
command: ["/bin/sh", "/etc/gateway/entrypoint.sh"]
|
|
env:
|
|
- name: GATEWAY_PORT
|
|
value: {{ .Values.gateway.port | quote }}
|
|
- name: MANAGER_PORT
|
|
value: "8081"
|
|
- name: TTYD_PORT
|
|
value: {{ .Values.session.ttydPort | quote }}
|
|
- name: SESSIONS_NS
|
|
value: {{ $sessNs | quote }}
|
|
- name: AUTH_MODE
|
|
value: {{ .Values.auth.mode | quote }}
|
|
{{- if eq .Values.auth.mode "forward" }}
|
|
- name: AUTH_FORWARD_URL
|
|
value: {{ required "auth.mode=forward には auth.forward.url が必須です" .Values.auth.forward.url | quote }}
|
|
{{- end }}
|
|
- name: NGINX_ERROR_LEVEL
|
|
value: {{ eq .Values.manager.logLevel "debug" | ternary "info" "warn" | quote }}
|
|
ports:
|
|
- name: http
|
|
containerPort: {{ .Values.gateway.port }}
|
|
securityContext:
|
|
{{- include "claude-code.containerSecurityContext" . | nindent 12 }}
|
|
readinessProbe:
|
|
httpGet: { path: /healthz, port: http }
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet: { path: /healthz, port: http }
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 20
|
|
resources:
|
|
{{- toYaml .Values.gateway.resources | nindent 12 }}
|
|
volumeMounts:
|
|
- name: gateway-config
|
|
mountPath: /etc/gateway
|
|
readOnly: true
|
|
- name: nginx-tmp
|
|
mountPath: /tmp/nginx
|
|
{{- if eq .Values.auth.mode "basic" }}
|
|
- name: gateway-secret
|
|
mountPath: /etc/gateway-secret
|
|
readOnly: true
|
|
{{- end }}
|
|
# ---- manager: ライフサイクル API / UI / 回収ループ ----
|
|
- name: manager
|
|
image: "{{ .Values.image.manager.registry }}/{{ .Values.image.manager.repository }}:{{ .Values.image.manager.tag }}"
|
|
imagePullPolicy: {{ .Values.image.manager.pullPolicy }}
|
|
command: ["python3", "-u", "/app/manager.py"]
|
|
env:
|
|
- name: LISTEN_PORT
|
|
value: "8081"
|
|
- name: PYTHONDONTWRITEBYTECODE
|
|
value: "1"
|
|
- name: LOG_LEVEL
|
|
value: {{ .Values.manager.logLevel | quote }}
|
|
- name: FULLNAME
|
|
value: {{ include "claude-code.fullname" . | quote }}
|
|
- name: SESSIONS_NAMESPACE
|
|
value: {{ $sessNs | quote }}
|
|
- name: SESSION_SERVICE_ACCOUNT
|
|
value: {{ include "claude-code.sessionServiceAccountName" . | quote }}
|
|
- name: SESSION_IMAGE
|
|
value: "{{ .Values.image.session.registry }}/{{ .Values.image.session.repository }}:{{ .Values.image.session.tag }}"
|
|
- name: SESSION_IMAGE_PULL_POLICY
|
|
value: {{ .Values.image.session.pullPolicy | quote }}
|
|
{{- with .Values.imagePullSecrets }}
|
|
- name: SESSION_IMAGE_PULL_SECRETS_JSON
|
|
value: {{ toJson . | quote }}
|
|
{{- end }}
|
|
- name: SESSION_BOOTSTRAP_CONFIGMAP
|
|
value: {{ include "claude-code.fullname" . }}-session
|
|
- name: ANTHROPIC_SECRET
|
|
value: {{ include "claude-code.anthropicSecretName" . | quote }}
|
|
- name: ANTHROPIC_AUTH_MODE
|
|
value: {{ .Values.anthropic.authMode | quote }}
|
|
- name: ANTHROPIC_CREDENTIALS_KEY
|
|
value: {{ .Values.anthropic.existingSecretKeyCredentials | default ".credentials.json" | quote }}
|
|
- name: GIT_IDENTITIES_JSON
|
|
value: {{ $idents | toJson | quote }}
|
|
- name: GIT_ALLOWED_HOSTS
|
|
value: {{ join "," .Values.git.allowedHosts | quote }}
|
|
- name: GIT_ALLOW_ARBITRARY
|
|
value: {{ .Values.git.allowArbitraryRepos | quote }}
|
|
- name: GIT_DEFAULT_AUTHOR_NAME
|
|
value: {{ .Values.git.defaultAuthorName | quote }}
|
|
- name: GIT_DEFAULT_AUTHOR_EMAIL
|
|
value: {{ .Values.git.defaultAuthorEmail | quote }}
|
|
- name: IDLE_TIMEOUT_SECONDS
|
|
value: {{ .Values.session.idleTimeoutSeconds | quote }}
|
|
- name: MAX_LIFETIME_SECONDS
|
|
value: {{ .Values.session.maxLifetimeSeconds | quote }}
|
|
- name: REAP_INTERVAL_SECONDS
|
|
value: {{ .Values.session.reaper.intervalSeconds | quote }}
|
|
- name: TERMINATED_RETENTION_SECONDS
|
|
value: {{ .Values.session.reaper.terminatedRetentionSeconds | quote }}
|
|
- name: MAX_SESSIONS
|
|
value: {{ .Values.session.maxSessions | quote }}
|
|
- name: TTYD_PORT
|
|
value: {{ .Values.session.ttydPort | quote }}
|
|
- name: SESSION_RESOURCES_JSON
|
|
value: {{ toJson .Values.session.resources | quote }}
|
|
- name: WORKSPACE_DIR
|
|
value: {{ .Values.session.workspaceDir | quote }}
|
|
- name: AUTOSTART_CLAUDE
|
|
value: {{ .Values.session.autoStartClaude | quote }}
|
|
- name: VERIFY_COMMAND
|
|
value: {{ .Values.session.verifyCommand | quote }}
|
|
- name: AUTOPUSH_JSON
|
|
value: {{ toJson .Values.session.autoPush | quote }}
|
|
- name: EXTRA_APK_PACKAGES
|
|
value: {{ join " " .Values.session.extraApkPackages | quote }}
|
|
- name: CLAUDE_CODE_VERSION
|
|
value: {{ $ccVersion | quote }}
|
|
{{- with .Values.anthropic.extraEnv }}
|
|
- name: ANTHROPIC_EXTRA_ENV_JSON
|
|
value: {{ toJson . | quote }}
|
|
{{- end }}
|
|
{{- with .Values.session.podLabels }}
|
|
- name: SESSION_POD_LABELS_JSON
|
|
value: {{ toJson . | quote }}
|
|
{{- end }}
|
|
{{- with .Values.session.podAnnotations }}
|
|
- name: SESSION_POD_ANNOTATIONS_JSON
|
|
value: {{ toJson . | quote }}
|
|
{{- end }}
|
|
{{- with .Values.session.nodeSelector }}
|
|
- name: SESSION_NODE_SELECTOR_JSON
|
|
value: {{ toJson . | quote }}
|
|
{{- end }}
|
|
{{- with .Values.session.tolerations }}
|
|
- name: SESSION_TOLERATIONS_JSON
|
|
value: {{ toJson . | quote }}
|
|
{{- end }}
|
|
{{- with .Values.session.priorityClassName }}
|
|
- name: SESSION_PRIORITY_CLASS
|
|
value: {{ . | quote }}
|
|
{{- end }}
|
|
- name: AUTH_MODE
|
|
value: {{ .Values.auth.mode | quote }}
|
|
{{- if eq .Values.auth.mode "token" }}
|
|
- name: AUTH_TOKEN_FILE
|
|
value: /etc/manager-secret/token
|
|
{{- end }}
|
|
ports:
|
|
- name: manager
|
|
containerPort: 8081
|
|
securityContext:
|
|
{{- include "claude-code.containerSecurityContext" . | nindent 12 }}
|
|
readinessProbe:
|
|
httpGet: { path: /healthz, port: manager }
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet: { path: /healthz, port: manager }
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 20
|
|
resources:
|
|
{{- toYaml .Values.manager.resources | nindent 12 }}
|
|
volumeMounts:
|
|
- name: manager-app
|
|
mountPath: /app
|
|
readOnly: true
|
|
- name: manager-tmp
|
|
mountPath: /tmp
|
|
{{- if eq .Values.auth.mode "token" }}
|
|
- name: manager-secret
|
|
mountPath: /etc/manager-secret
|
|
readOnly: true
|
|
{{- end }}
|
|
volumes:
|
|
- name: gateway-config
|
|
configMap:
|
|
name: {{ include "claude-code.fullname" . }}-gateway
|
|
defaultMode: 0555
|
|
- name: nginx-tmp
|
|
emptyDir:
|
|
medium: Memory
|
|
sizeLimit: 16Mi
|
|
- name: manager-app
|
|
configMap:
|
|
name: {{ include "claude-code.fullname" . }}-manager
|
|
defaultMode: 0555
|
|
- name: manager-tmp
|
|
emptyDir:
|
|
medium: Memory
|
|
sizeLimit: 16Mi
|
|
{{- if eq .Values.auth.mode "token" }}
|
|
- name: manager-secret
|
|
secret:
|
|
secretName: {{ include "claude-code.authSecretName" . }}
|
|
items:
|
|
- key: {{ .Values.auth.token.existingSecretKey | default "token" }}
|
|
path: token
|
|
{{- end }}
|
|
{{- if eq .Values.auth.mode "basic" }}
|
|
- name: gateway-secret
|
|
secret:
|
|
secretName: {{ include "claude-code.authSecretName" . }}
|
|
items:
|
|
- key: {{ .Values.auth.basic.existingSecretKey | default "htpasswd" }}
|
|
path: htpasswd
|
|
{{- end }}
|