k8s/aux: add cert-manager longhorn dashboard forge dev-tools and shadowsocks
- cert-manager ClusterIssuers (LetsEncrypt + homelab-ca) - Longhorn storage dashboard - Portainer dashboard config - Forgejo git service - Claude terminal remote access - Shadowsocks tunnel for remote access
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: claude-terminal
|
||||
description: Persistent Claude CLI terminal running in tmux with web access via gotty
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "1.0"
|
||||
@@ -0,0 +1,31 @@
|
||||
FROM --platform=linux/amd64 ubuntu:24.04
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
tmux \
|
||||
curl \
|
||||
git \
|
||||
build-essential \
|
||||
nodejs \
|
||||
npm \
|
||||
bash \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install gotty (web terminal access)
|
||||
RUN curl -sL https://github.com/sorenisanerd/gotty/releases/download/v1.5.0/gotty_linux_amd64.tar.gz | \
|
||||
tar xz -C /usr/local/bin && chmod +x /usr/local/bin/gotty
|
||||
|
||||
# Install Claude CLI
|
||||
RUN npm install -g claude-code-cli 2>&1 || echo "Note: Claude CLI will be available after NPM package is published"
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
# Create persistent storage dir
|
||||
RUN mkdir -p /root/.claude /root/.config /root/.cache
|
||||
|
||||
# Entrypoint: start tmux session and gotty
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,49 @@
|
||||
# Claude Terminal — Persistent Remote Dev Environment
|
||||
|
||||
Runs Claude CLI in a persistent tmux session with web-based terminal access via gotty.
|
||||
|
||||
## Building the Image
|
||||
|
||||
Build for `linux/amd64`:
|
||||
|
||||
```bash
|
||||
cd homelab
|
||||
docker buildx build --platform linux/amd64 \
|
||||
-t forgejo.riotpiao.homelab.com/rock/claude-terminal:latest \
|
||||
-f k8s/dev-tools/Dockerfile \
|
||||
k8s/dev-tools
|
||||
|
||||
# Log in to Forgejo registry
|
||||
docker login forgejo.riotpiao.homelab.com \
|
||||
--username ci-bot \
|
||||
--password "$(talos get cluster/iam/agents/ci-bot --key token)"
|
||||
|
||||
# Push
|
||||
docker push forgejo.riotpiao.homelab.com/rock/claude-terminal:latest
|
||||
```
|
||||
|
||||
Or use the provided build script:
|
||||
|
||||
```bash
|
||||
./k8s/dev-tools/build.sh
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Update `values.yaml` if needed, then deploy via helmfile:
|
||||
|
||||
```bash
|
||||
helmfile apply -l name=claude-terminal
|
||||
```
|
||||
|
||||
Access the terminal at: **https://claude.riotpiao.homelab.com**
|
||||
|
||||
## Persistent Storage
|
||||
|
||||
- All Claude configuration stored in `/root/.claude` (persistent PVC, 10Gi Longhorn)
|
||||
- Survives pod restarts and node reboots
|
||||
- Accessible immediately after reconnecting
|
||||
|
||||
## SSH Access (Optional)
|
||||
|
||||
To add SSH access, extend the Dockerfile to include openssh-server and mount the PVC as home directory.
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
REGISTRY="forgejo.riotpiao.homelab.com"
|
||||
IMAGE_NAME="rock/claude-terminal"
|
||||
TAG="latest"
|
||||
FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
||||
|
||||
echo "🔨 Building Claude Terminal image for linux/amd64..."
|
||||
docker buildx build --platform linux/amd64 \
|
||||
-t "${FULL_IMAGE}" \
|
||||
-f Dockerfile \
|
||||
. || { echo "❌ Build failed"; exit 1; }
|
||||
|
||||
echo "🔓 Logging in to Forgejo registry..."
|
||||
REGISTRY_TOKEN=$(talos get cluster/iam/agents/ci-bot --key token)
|
||||
echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \
|
||||
--username ci-bot \
|
||||
--password-stdin || { echo "❌ Login failed"; exit 1; }
|
||||
|
||||
echo "📤 Pushing image to registry..."
|
||||
docker push "${FULL_IMAGE}" || { echo "❌ Push failed"; exit 1; }
|
||||
|
||||
echo "✅ Successfully pushed ${FULL_IMAGE}"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Start tmux server in background
|
||||
tmux new-session -d -s claude -c /root "bash"
|
||||
|
||||
# Give tmux a moment to stabilize
|
||||
sleep 1
|
||||
|
||||
# Start gotty serving the tmux session
|
||||
# -w: allow write (make terminal interactive)
|
||||
# -p 8080: listen on port 8080
|
||||
exec gotty -p 8080 -w tmux attach-session -t claude
|
||||
@@ -0,0 +1,49 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "claude-terminal.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "claude-terminal.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "claude-terminal.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "claude-terminal.labels" -}}
|
||||
helm.sh/chart: {{ include "claude-terminal.chart" . }}
|
||||
{{ include "claude-terminal.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "claude-terminal.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "claude-terminal.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,57 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "claude-terminal.fullname" . }}
|
||||
labels:
|
||||
{{- include "claude-terminal.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "claude-terminal.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "claude-terminal.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: claude-terminal
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: claude-storage
|
||||
mountPath: {{ .Values.persistence.mountPath }}
|
||||
volumes:
|
||||
- name: claude-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "claude-terminal.fullname" . }}-pvc
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,41 @@
|
||||
{{- if .Values.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "claude-terminal.fullname" . }}
|
||||
labels:
|
||||
{{- include "claude-terminal.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
pathType: {{ .pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "claude-terminal.fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
{{- if .Values.persistence.enabled }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "claude-terminal.fullname" . }}-pvc
|
||||
labels:
|
||||
{{- include "claude-terminal.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: {{ .Values.persistence.storageClass }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "claude-terminal.fullname" . }}
|
||||
labels:
|
||||
{{- include "claude-terminal.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "claude-terminal.selectorLabels" . | nindent 4 }}
|
||||
@@ -0,0 +1,45 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: localhost:5000/claude-terminal
|
||||
pullPolicy: IfNotPresent
|
||||
tag: latest
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: homelab-ca
|
||||
hosts:
|
||||
- host: claude.riotpiao.homelab.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: claude-terminal-tls
|
||||
hosts:
|
||||
- claude.riotpiao.homelab.com
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: longhorn
|
||||
size: 10Gi
|
||||
mountPath: /root/.claude
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
Reference in New Issue
Block a user