添加qiming-rcoder模块

This commit is contained in:
Codex
2026-06-01 13:54:52 +08:00
parent 8092c4b1f8
commit 4b1a580132
539 changed files with 151650 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
# Patterns to ignore when building packages.
.DS_Store
.git/
.gitignore
.vscode/
.idea/
*.tmproj
*.bak
*.swp
*.orig
*~
# OWNERS file
OWNERS
# Helm
.helmignore

View File

@@ -0,0 +1,18 @@
apiVersion: v2
name: rcoder
description: RCoder AI 驱动开发平台 (ACP-based, Rust) —— 含 RCoder 主服务 + PostgreSQL + MinIO + JuiceFS StorageClass
type: application
version: 1.0.0
appVersion: "latest"
kubeVersion: ">=1.19.0-0"
keywords:
- rcoder
- acp
- ai
- agent
- juicefs
- rust
maintainers:
- name: RCoder Team
annotations:
category: AI

View File

@@ -0,0 +1,39 @@
RCoder {{ .Chart.AppVersion }} 已安装到 namespace "{{ .Release.Namespace }}" (release: {{ .Release.Name }}).
环境: {{ .Values.environment }}
副本数: {{ .Values.rcoder.replicas }}
NodePort: {{ .Values.rcoder.service.nodePort }}
StorageClass: {{ include "rcoder.storageClassName" . }}
1. 查看部署状态:
kubectl get pods -n {{ .Release.Namespace }}
kubectl get svc -n {{ .Release.Namespace }}
kubectl get pvc -n {{ .Release.Namespace }}
2. 等待就绪(首次部署 PostgreSQL + MinIO + JuiceFS mount 约需 2-5 分钟):
kubectl rollout status deploy/rcoder -n {{ .Release.Namespace }} --timeout=300s
3. 访问服务 (NodePort):
{{- if eq .Values.rcoder.service.type "NodePort" }}
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
curl http://${NODE_IP}:{{ .Values.rcoder.service.nodePort }}/health
{{- else }}
kubectl port-forward svc/rcoder {{ .Values.rcoder.service.port }}:{{ .Values.rcoder.service.port }} -n {{ .Release.Namespace }}
curl http://localhost:{{ .Values.rcoder.service.port }}/health
{{- end }}
4. 查看日志:
kubectl logs -n {{ .Release.Namespace }} -l app=rcoder -f
{{- if eq .Values.environment "prod" }}
⚠️ 生产环境安全提醒:
- credentials.postgresql.password / credentials.minio.rootPassword 不应使用默认值
- 建议使用 --set 动态注入或 SealedSecret / ExternalSecret 方案
- 当前密码: {{ .Values.credentials.postgresql.password }} (如为 CHANGE-ME-BEFORE-DEPLOY 请立即替换)
{{- end }}
升级: helm upgrade {{ .Release.Name }} k8s/helm/rcoder -f <values>
卸载: helm uninstall {{ .Release.Name }} --namespace {{ .Release.Namespace }}
注意StorageClass 与 ClusterRoleBinding 是集群级资源,会随 release 删除;
PostgreSQL / MinIO 的 PVC 由 reclaimPolicy 决定是否保留数据)

View File

@@ -0,0 +1,140 @@
{{/*
RCoder chart helper
*/}}
{{/* (Release + chart) */}}
{{- define "rcoder.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 -}}
{{/* Chart */}}
{{- define "rcoder.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/* () */}}
{{- define "rcoder.labels" -}}
helm.sh/chart: {{ include "rcoder.chart" . }}
app.kubernetes.io/name: rcoder
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
environment: {{ .Values.environment | quote }}
{{- end -}}
{{/* selector label (app=rcoder Kustomize , Service pod) */}}
{{- define "rcoder.selectorLabels" -}}
app: rcoder
component: rcoder-main
{{- end -}}
{{/* ============================================================
rcoder-own image -> {{ .Values.global.imageRegistry }}/{repo}:{tag}
third-party image -> {{ .Values.global.thirdPartyRegistry }}/{repo}:{tag}
( thirdPartyRegistry , repo)
============================================================ */}}
{{/* : dict {repository, tag, chartContext} */}}
{{- define "rcoder.ownImage" -}}
{{- $repo := .repository -}}
{{- $tag := .tag -}}
{{- $registry := .ctx.Values.global.imageRegistry -}}
{{- if empty $tag -}}{{- $tag = .ctx.Chart.AppVersion -}}{{- end -}}
{{- if $registry -}}
{{- printf "%s/%s:%s" $registry $repo $tag -}}
{{- else -}}
{{- printf "%s:%s" $repo $tag -}}
{{- end -}}
{{- end -}}
{{/* : dict {repository, tag, ctx} */}}
{{- define "rcoder.thirdPartyImage" -}}
{{- $repo := .repository -}}
{{- $tag := .tag -}}
{{- $registry := .ctx.Values.global.thirdPartyRegistry -}}
{{- if $registry -}}
{{- printf "%s/%s:%s" $registry $repo $tag -}}
{{- else -}}
{{- printf "%s:%s" $repo $tag -}}
{{- end -}}
{{- end -}}
{{/* : rcoder image */}}
{{- define "rcoder.image" -}}
{{- include "rcoder.ownImage" (dict "repository" .Values.rcoder.image.repository "tag" .Values.rcoder.image.tag "ctx" .) -}}
{{- end -}}
{{/* : agent-runner image ( rcoder env var 使) */}}
{{- define "rcoder.agentRunnerImage" -}}
{{- include "rcoder.ownImage" (dict "repository" .Values.agentRunner.image.repository "tag" .Values.agentRunner.image.tag "ctx" .) -}}
{{- end -}}
{{/* (config.yml docker_config.multi_image_config.global_defaults.registry_prefix 使) */}}
{{- define "rcoder.registryPrefix" -}}
{{- default "" .Values.global.imageRegistry -}}
{{- end -}}
{{/* ============================================================
StorageClass (, Release )
- juicefs.storageClass.name ->
- 退 "juicefs-sc-{release}"
============================================================ */}}
{{- define "rcoder.storageClassName" -}}
{{- if .Values.juicefs.storageClass.name -}}
{{- .Values.juicefs.storageClass.name -}}
{{- else -}}
{{- printf "juicefs-sc-%s" .Release.Name -}}
{{- end -}}
{{- end -}}
{{/* ============================================================
ClusterRoleBinding (, )
============================================================ */}}
{{- define "rcoder.clusterRoleBindingName" -}}
{{- printf "%s-pods-crb" .Release.Name -}}
{{- end -}}
{{/* ClusterRole release ClusterRole, */}}
{{- define "rcoder.clusterRoleName" -}}
rcoder-pods-clusterrole
{{- end -}}
{{/* imagePullSecrets */}}
{{- define "rcoder.imagePullSecrets" -}}
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- range .Values.global.imagePullSecrets }}
- name: {{ .name }}
{{- end }}
{{- end -}}
{{- end -}}
{{/* PostgreSQL service (namespace ) */}}
{{- define "rcoder.postgresqlHost" -}}
{{- printf "postgresql.%s.svc.cluster.local" .Release.Namespace -}}
{{- end -}}
{{/* MinIO service */}}
{{- define "rcoder.minioHost" -}}
{{- printf "minio-service.%s.svc.cluster.local" .Release.Namespace -}}
{{- end -}}
{{/* JuiceFS metaurl */}}
{{- define "rcoder.juicefsMetaurl" -}}
{{- printf "postgres://%s:%s@postgresql.%s:5432/juicefs" .Values.credentials.postgresql.user .Values.credentials.postgresql.password .Release.Namespace -}}
{{- end -}}
{{/* JuiceFS bucket URL */}}
{{- define "rcoder.juicefsBucketUrl" -}}
{{- printf "http://minio-service.%s:9000/%s" .Release.Namespace .Values.juicefs.bucket -}}
{{- end -}}

View File

@@ -0,0 +1,29 @@
{{- if .Values.rcoder.clusterRole.create }}
# 集群级 ClusterRole —— 多 release 共享同一个 (规则与 namespace 无关, 重复 apply 幂等)
# 与 Kustomize 并存时, 把 .Values.rcoder.clusterRole.create 置 false, 让 Kustomize 持有所有权
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "rcoder.clusterRoleName" . }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
annotations:
# 避免多 release 互相 helm uninstall 时删除共享 ClusterRole
"helm.sh/resource-policy": keep
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
- apiGroups: [""]
resources: ["pods/status"]
verbs: ["get"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "create", "delete"]
{{- end }}

View File

@@ -0,0 +1,15 @@
# 集群级 ClusterRoleBinding —— 每个 release 独立, 解决同集群多 release 冲突
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "rcoder.clusterRoleBindingName" . }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
subjects:
- kind: ServiceAccount
name: rcoder-pods-sa
namespace: {{ .Release.Namespace }}
roleRef:
kind: ClusterRole
name: {{ include "rcoder.clusterRoleName" . }}
apiGroup: rbac.authorization.k8s.io

View File

@@ -0,0 +1,70 @@
# ConfigMap: 渲染 config.yml —— 保持与 k8s/manifests/overlays 下 rcoder-configmap.yaml 结构对齐
apiVersion: v1
kind: ConfigMap
metadata:
name: rcoder-config
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
data:
config.yml: |
default_agent: {{ .Values.config.defaultAgent | quote }}
projects_dir: {{ .Values.config.projectsDir | quote }}
port: {{ .Values.config.port }}
cleanup_config:
enabled: {{ .Values.config.cleanup.enabled }}
idle_timeout_seconds: {{ .Values.config.cleanup.idleTimeoutSeconds }}
cleanup_interval_seconds: {{ .Values.config.cleanup.cleanupIntervalSeconds }}
docker_stop_timeout_seconds: {{ .Values.config.cleanup.dockerStopTimeoutSeconds }}
proxy_config:
listen_port: {{ .Values.config.proxy.listenPort }}
default_backend_port: {{ .Values.config.proxy.defaultBackendPort }}
backend_host: {{ .Values.config.proxy.backendHost | quote }}
port_param: {{ .Values.config.proxy.portParam | quote }}
health_check:
enabled: {{ .Values.config.proxy.healthCheck.enabled }}
interval_seconds: {{ .Values.config.proxy.healthCheck.intervalSeconds }}
timeout_seconds: {{ .Values.config.proxy.healthCheck.timeoutSeconds }}
healthy_threshold: {{ .Values.config.proxy.healthCheck.healthyThreshold }}
unhealthy_threshold: {{ .Values.config.proxy.healthCheck.unhealthyThreshold }}
docker_config:
multi_image_config:
global_defaults:
registry_prefix: {{ include "rcoder.registryPrefix" . | quote }}
services:
rcoder:
service_type: "RCoder"
arm64_image: {{ include "rcoder.image" . | quote }}
amd64_image: {{ include "rcoder.image" . | quote }}
default_image: {{ include "rcoder.image" . | quote }}
image_tag_prefix: "rcoder"
enabled: true
computer-agent-runner:
service_type: "ComputerAgentRunner"
arm64_image: {{ include "rcoder.agentRunnerImage" . | quote }}
amd64_image: {{ include "rcoder.agentRunnerImage" . | quote }}
default_image: {{ include "rcoder.agentRunnerImage" . | quote }}
image_tag_prefix: "rcoder-computer-agent-runner"
enabled: true
selection_strategy: "ServiceOnly"
cache_config:
enabled: {{ .Values.config.dockerConfig.cache.enabled }}
ttl_seconds: {{ .Values.config.dockerConfig.cache.ttlSeconds }}
max_entries: {{ .Values.config.dockerConfig.cache.maxEntries }}
network_base_name: {{ .Values.config.dockerConfig.networkBaseName | quote }}
network_mode: {{ .Values.config.dockerConfig.networkMode | quote }}
work_dir: {{ .Values.config.dockerConfig.workDir | quote }}
auto_cleanup: {{ .Values.config.dockerConfig.autoCleanup }}
container_ttl_seconds: {{ .Values.config.dockerConfig.containerTtlSeconds }}
api_key_auth:
enabled: {{ .Values.config.apiKeyAuth.enabled }}
api_key: {{ .Values.config.apiKeyAuth.apiKey | quote }}
kubernetes_config:
storage_class: {{ include "rcoder.storageClassName" . | quote }}

View File

@@ -0,0 +1,65 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: rcoder
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.rcoder.replicas }}
selector:
matchLabels:
{{- include "rcoder.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "rcoder.labels" . | nindent 8 }}
{{- include "rcoder.selectorLabels" . | nindent 8 }}
spec:
terminationGracePeriodSeconds: {{ .Values.rcoder.terminationGracePeriodSeconds }}
serviceAccountName: rcoder-pods-sa
{{- include "rcoder.imagePullSecrets" . | nindent 6 }}
containers:
- name: rcoder
image: {{ include "rcoder.image" . }}
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
command: ["/app/bin/rcoder"]
args: ["--port", "{{ .Values.rcoder.service.port }}"]
ports:
- containerPort: {{ .Values.rcoder.service.port }}
env:
- name: CONTAINER_RUNTIME
value: "kubernetes"
- name: RCODER_K8S_NAMESPACE
value: {{ .Release.Namespace | quote }}
- name: RUST_LOG
value: {{ .Values.rcoder.rustLog | quote }}
- name: RCODER_K8S_STORAGE_CLASS
value: {{ include "rcoder.storageClassName" . | quote }}
- name: RCODER_DOCKER_IMAGE
value: {{ include "rcoder.image" . | quote }}
- name: RCODER_DOCKER_IMAGE_COMPUTER
value: {{ include "rcoder.agentRunnerImage" . | quote }}
resources:
{{- toYaml .Values.rcoder.resources | nindent 12 }}
livenessProbe:
{{- toYaml .Values.rcoder.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.rcoder.readinessProbe | nindent 12 }}
volumeMounts:
- name: rcoder-config
mountPath: /app/config.yml
subPath: config.yml
- name: project-workspace
mountPath: /app/project_workspace
subPath: project_workspace
- name: project-workspace
mountPath: /app/computer-project-workspace
subPath: computer_workspace
volumes:
- name: rcoder-config
configMap:
name: rcoder-config
- name: project-workspace
persistentVolumeClaim:
claimName: rcoder-workspace

View File

@@ -0,0 +1,62 @@
{{- if .Values.rcoder.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: rcoder-network-policy
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
spec:
podSelector:
matchLabels:
app: rcoder
policyTypes:
- Ingress
- Egress
ingress:
# 同 namespace 入站流量
- from:
- podSelector: {}
ports:
- { protocol: TCP, port: {{ .Values.rcoder.service.port }} }
- { protocol: TCP, port: {{ .Values.config.proxy.listenPort }} }
egress:
# DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- { protocol: UDP, port: 53 }
# K8s API Server
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
kubernetes.io/service-name: kubernetes
ports:
- { protocol: TCP, port: 443 }
# PostgreSQL
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- { protocol: TCP, port: {{ .Values.postgresql.service.port }} }
# MinIO
- to:
- podSelector:
matchLabels:
app: minio
ports:
- { protocol: TCP, port: {{ .Values.minio.service.apiPort }} }
- { protocol: TCP, port: {{ .Values.minio.service.consolePort }} }
# 动态 agent pod (rcoder -> agent gRPC/HTTP)
- to:
- podSelector:
matchLabels:
managed-by: rcoder-runtime
ports:
- { protocol: TCP, port: 50051 }
- { protocol: TCP, port: 8086 }
{{- end }}

View File

@@ -0,0 +1,14 @@
{{- if .Values.rcoder.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: rcoder-pdb
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
spec:
maxUnavailable: {{ .Values.rcoder.pdb.maxUnavailable }}
selector:
matchLabels:
app: rcoder
{{- end }}

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: rcoder
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
spec:
type: {{ .Values.rcoder.service.type }}
# component=rcoder-main 把动态创建的 rcoder-agent / computer-agent-runner pod
# (它们也带 app=rcoder 标签) 排除在外, 避免 NodePort 把流量路由到不监听 8087 的 agent pod
selector:
{{- include "rcoder.selectorLabels" . | nindent 4 }}
ports:
- port: {{ .Values.rcoder.service.port }}
targetPort: {{ .Values.rcoder.service.port }}
{{- if eq .Values.rcoder.service.type "NodePort" }}
nodePort: {{ .Values.rcoder.service.nodePort }}
{{- end }}

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: rcoder-pods-sa
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Secret
metadata:
name: rcoder-credentials
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
type: Opaque
stringData:
POSTGRES_USER: {{ .Values.credentials.postgresql.user | quote }}
POSTGRES_PASSWORD: {{ .Values.credentials.postgresql.password | quote }}
MINIO_ROOT_USER: {{ .Values.credentials.minio.rootUser | quote }}
MINIO_ROOT_PASSWORD: {{ .Values.credentials.minio.rootPassword | quote }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rcoder-workspace
namespace: {{ .Release.Namespace }}
labels:
app: rcoder
{{- include "rcoder.labels" . | nindent 4 }}
spec:
# JuiceFS 原生支持 RWX, 多节点 rcoder pod 共享工作空间
accessModes: ["ReadWriteMany"]
storageClassName: {{ include "rcoder.storageClassName" . | quote }}
resources:
requests:
storage: {{ .Values.workspace.size | quote }}

View File

@@ -0,0 +1,26 @@
apiVersion: v1
kind: Secret
metadata:
name: juicefs-secret
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
type: Opaque
stringData:
# JuiceFS CE 必需字段 (小写键名)
name: "rcoder-juicefs"
metaurl: {{ include "rcoder.juicefsMetaurl" . | quote }}
storage: "minio"
bucket: {{ include "rcoder.juicefsBucketUrl" . | quote }}
access-key: {{ .Values.credentials.minio.rootUser | quote }}
secret-key: {{ .Values.credentials.minio.rootPassword | quote }}
# 兼容字段 (保留给脚本/日志使用)
METAURL: {{ include "rcoder.juicefsMetaurl" . | quote }}
MINIO_ACCESS_KEY: {{ .Values.credentials.minio.rootUser | quote }}
MINIO_SECRET_KEY: {{ .Values.credentials.minio.rootPassword | quote }}
MINIO_ADDRESS: {{ printf "minio-service.%s:%d" .Release.Namespace (int .Values.minio.service.apiPort) | quote }}
# 其他配置
APPEND_FSYNC: {{ .Values.juicefs.meta.appendFsync | quote }}
CACHE_SIZE: {{ .Values.juicefs.meta.cacheSize | quote }}

View File

@@ -0,0 +1,25 @@
{{- if .Values.juicefs.storageClass.create }}
# JuiceFS StorageClass —— 集群级资源, 每个 release 独立命名
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: {{ include "rcoder.storageClassName" . }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: csi.juicefs.com
parameters:
csi.storage.k8s.io/provisioner-secret-name: juicefs-secret
csi.storage.k8s.io/provisioner-secret-namespace: {{ .Release.Namespace }}
csi.storage.k8s.io/controller-expand-secret-name: juicefs-secret
csi.storage.k8s.io/controller-expand-secret-namespace: {{ .Release.Namespace }}
csi.storage.k8s.io/node-publish-secret-name: juicefs-secret
csi.storage.k8s.io/node-publish-secret-namespace: {{ .Release.Namespace }}
juicefs/controllerQuotaSet: "false"
juicefs/mountImage: {{ include "rcoder.thirdPartyImage" (dict "repository" .Values.juicefs.mountImage.repository "tag" .Values.juicefs.mountImage.tag "ctx" .) | quote }}
juicefs/mountOptions: {{ .Values.juicefs.storageClass.mountOptions | quote }}
reclaimPolicy: {{ .Values.juicefs.storageClass.reclaimPolicy }}
allowVolumeExpansion: true
volumeBindingMode: Immediate
{{- end }}

View File

@@ -0,0 +1,47 @@
apiVersion: batch/v1
kind: Job
metadata:
name: minio-init
namespace: {{ .Release.Namespace }}
labels:
{{- include "rcoder.labels" . | nindent 4 }}
annotations:
# 每次 helm upgrade 强制重新运行 (确保 bucket 一定存在, 即使 Job 已清理)
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "5"
helm.sh/hook-delete-policy: before-hook-creation
spec:
ttlSecondsAfterFinished: 300
template:
spec:
restartPolicy: OnFailure
{{- include "rcoder.imagePullSecrets" . | nindent 6 }}
containers:
- name: mc
image: {{ include "rcoder.thirdPartyImage" (dict "repository" .Values.minio.initImage.repository "tag" .Values.minio.initImage.tag "ctx" .) }}
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
command:
- /bin/sh
- -c
- |
echo "Waiting for MinIO to be ready..."
until mc alias set myminio http://minio:{{ .Values.minio.service.apiPort }} ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}; do
echo "MinIO not ready, retrying..."
sleep 2
done
echo "MinIO is ready, creating bucket..."
mc mb myminio/{{ .Values.minio.bucket }} --ignore-existing
mc ls myminio/
echo "MinIO initialization completed!"
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: rcoder-credentials
key: MINIO_ROOT_USER
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: rcoder-credentials
key: MINIO_ROOT_PASSWORD

View File

@@ -0,0 +1,41 @@
---
# Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: {{ .Release.Namespace }}
labels:
app: minio
{{- include "rcoder.labels" . | nindent 4 }}
spec:
clusterIP: None
selector:
app: minio
ports:
- name: api
port: {{ .Values.minio.service.apiPort }}
targetPort: {{ .Values.minio.service.apiPort }}
- name: console
port: {{ .Values.minio.service.consolePort }}
targetPort: {{ .Values.minio.service.consolePort }}
---
# ClusterIP Service for internal access
apiVersion: v1
kind: Service
metadata:
name: minio-service
namespace: {{ .Release.Namespace }}
labels:
app: minio
{{- include "rcoder.labels" . | nindent 4 }}
spec:
selector:
app: minio
ports:
- name: api
port: {{ .Values.minio.service.apiPort }}
targetPort: {{ .Values.minio.service.apiPort }}
- name: console
port: {{ .Values.minio.service.consolePort }}
targetPort: {{ .Values.minio.service.consolePort }}

View File

@@ -0,0 +1,83 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-pvc
namespace: {{ .Release.Namespace }}
labels:
app: minio
{{- include "rcoder.labels" . | nindent 4 }}
spec:
accessModes: ["ReadWriteOnce"]
{{- if .Values.minio.storage.storageClass }}
storageClassName: {{ .Values.minio.storage.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.minio.storage.size | quote }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
namespace: {{ .Release.Namespace }}
labels:
app: minio
{{- include "rcoder.labels" . | nindent 4 }}
spec:
serviceName: minio
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
{{- include "rcoder.labels" . | nindent 8 }}
spec:
{{- include "rcoder.imagePullSecrets" . | nindent 6 }}
containers:
- name: minio
image: {{ include "rcoder.thirdPartyImage" (dict "repository" .Values.minio.image.repository "tag" .Values.minio.image.tag "ctx" .) }}
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
ports:
- containerPort: {{ .Values.minio.service.apiPort }}
name: api
- containerPort: {{ .Values.minio.service.consolePort }}
name: console
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: rcoder-credentials
key: MINIO_ROOT_USER
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: rcoder-credentials
key: MINIO_ROOT_PASSWORD
args:
- server
- /data
- --console-address
- ":{{ .Values.minio.service.consolePort }}"
livenessProbe:
httpGet:
path: /minio/health/live
port: {{ .Values.minio.service.apiPort }}
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /minio/health/ready
port: {{ .Values.minio.service.apiPort }}
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: minio-data
mountPath: /data
volumes:
- name: minio-data
persistentVolumeClaim:
claimName: minio-pvc

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: postgresql
namespace: {{ .Release.Namespace }}
labels:
app: postgresql
{{- include "rcoder.labels" . | nindent 4 }}
spec:
selector:
app: postgresql
ports:
- name: postgres
port: {{ .Values.postgresql.service.port }}
targetPort: {{ .Values.postgresql.service.port }}

View File

@@ -0,0 +1,84 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-pvc
namespace: {{ .Release.Namespace }}
labels:
app: postgresql
{{- include "rcoder.labels" . | nindent 4 }}
spec:
accessModes: ["ReadWriteOnce"]
{{- if .Values.postgresql.storage.storageClass }}
storageClassName: {{ .Values.postgresql.storage.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.postgresql.storage.size | quote }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql
namespace: {{ .Release.Namespace }}
labels:
app: postgresql
{{- include "rcoder.labels" . | nindent 4 }}
spec:
serviceName: postgresql
replicas: 1
selector:
matchLabels:
app: postgresql
template:
metadata:
labels:
app: postgresql
{{- include "rcoder.labels" . | nindent 8 }}
spec:
{{- include "rcoder.imagePullSecrets" . | nindent 6 }}
initContainers:
- name: cleanup
image: {{ include "rcoder.thirdPartyImage" (dict "repository" .Values.postgresql.image.repository "tag" .Values.postgresql.image.tag "ctx" .) }}
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
command: ["rm", "-rf", "/var/lib/postgresql/data/lost+found"]
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
containers:
- name: postgresql
image: {{ include "rcoder.thirdPartyImage" (dict "repository" .Values.postgresql.image.repository "tag" .Values.postgresql.image.tag "ctx" .) }}
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
ports:
- containerPort: {{ .Values.postgresql.service.port }}
name: postgres
env:
- name: POSTGRES_DB
value: "juicefs"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: rcoder-credentials
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: rcoder-credentials
key: POSTGRES_PASSWORD
livenessProbe:
exec:
command: ["pg_isready", "-U", {{ .Values.credentials.postgresql.user | quote }}]
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["pg_isready", "-U", {{ .Values.credentials.postgresql.user | quote }}]
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgresql-pvc

View File

@@ -0,0 +1,25 @@
# ============================================================================
# Dev 环境覆盖
# 使用: helm install rcoder-dev k8s/helm/rcoder -f k8s/helm/rcoder/values-dev.yaml \
# --namespace nuwax-rcoder-dev --create-namespace
# ============================================================================
environment: dev
rcoder:
replicas: 1
service:
nodePort: 30080
juicefs:
storageClass:
name: juicefs-sc-dev
# Dev 默认值 —— 方便本地联调,生产部署前必须改
credentials:
postgresql:
user: juicefs
password: juicefs_password
minio:
rootUser: minioadmin
rootPassword: minioadmin

View File

@@ -0,0 +1,31 @@
# ============================================================================
# 离线部署覆盖
#
# 使用场景:政企内网,无公网访问,所有镜像已 re-tag 推送到客户私有 registry。
#
# 如果走 "ctr image import 到每个节点" 模式,节点 containerd 里会以原始 repo:tag
# 保存镜像,此时不需要改 global.imageRegistry —— 保留默认值即可,配合
# imagePullPolicy: IfNotPresent 直接用本地缓存。
#
# 如果走 "re-tag + push 到私有 registry" 模式,把下面两个字段覆盖为实际地址:
#
# global:
# imageRegistry: harbor.internal/rcoder # RCoder 自有镜像仓库
# thirdPartyRegistry: harbor.internal/thirdparty # 第三方镜像 (postgres/minio/juicedata)
#
# 使用:
# helm install rcoder k8s/helm/rcoder \
# -f k8s/helm/rcoder/values-dev.yaml \
# -f k8s/helm/rcoder/values-offline.yaml \
# --set global.imageRegistry=harbor.internal/rcoder \
# --set global.thirdPartyRegistry=harbor.internal/thirdparty
# ============================================================================
global:
# 示例值, 客户按需改
imageRegistry: harbor.internal/rcoder
thirdPartyRegistry: harbor.internal/thirdparty
imagePullPolicy: IfNotPresent
# 如果私有 registry 需要认证,在集群里创建 Secret 后填:
# imagePullSecrets: [{name: harbor-pull-secret}]
imagePullSecrets: []

View File

@@ -0,0 +1,30 @@
# ============================================================================
# Prod 环境覆盖
#
# ⚠️ 上线前必须替换 credentials.* 里的 CHANGE-ME-BEFORE-DEPLOY 占位符。
# 推荐做法:不把真实密码写进这里,改用 --set 或 SealedSecret/ExternalSecret。
#
# 使用: helm install rcoder-prod k8s/helm/rcoder -f k8s/helm/rcoder/values-prod.yaml \
# --namespace nuwax-rcoder-prod --create-namespace \
# --set credentials.postgresql.password=<real> \
# --set credentials.minio.rootPassword=<real>
# ============================================================================
environment: prod
rcoder:
replicas: 2
service:
nodePort: 30081
juicefs:
storageClass:
name: juicefs-sc-prod
credentials:
postgresql:
user: juicefs
password: CHANGE-ME-BEFORE-DEPLOY
minio:
rootUser: CHANGE-ME-BEFORE-DEPLOY
rootPassword: CHANGE-ME-BEFORE-DEPLOY

View File

@@ -0,0 +1,176 @@
# ============================================================================
# RCoder Helm values - 默认(共享)配置
#
# 使用方式:
# helm install rcoder-dev k8s/helm/rcoder -f k8s/helm/rcoder/values-dev.yaml
# helm install rcoder-prod k8s/helm/rcoder -f k8s/helm/rcoder/values-prod.yaml
#
# 离线部署叠加 values-offline.yaml 覆盖 global.imageRegistry。
# ============================================================================
global:
# 所有镜像的仓库前缀。离线部署时覆盖为 harbor.internal/rcoder 等私有地址。
# 应用到: rcoder / rcoder-agent-runner / postgres / minio / mc / juicedata/mount
# 说明: 为空时使用每个镜像的原始 repo非空时会被拼到 repo 前面形成 "{registry}/{repo}"。
imageRegistry: "nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev"
imagePullPolicy: IfNotPresent
imagePullSecrets: []
# 第三方镜像的 registry 前缀。离线/私有场景用于把 postgres / minio/minio / minio/mc /
# juicedata/mount 这些上游镜像指向客户私有仓库。为空时使用原始 repo。
thirdPartyRegistry: ""
# ----------------------------------------------------------------------------
# 应用部署
# ----------------------------------------------------------------------------
environment: dev # dev | prod —— 仅作为 label 和默认后缀
rcoder:
image:
repository: rcoder
tag: "" # 空时继承 .Chart.appVersion
replicas: 1
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 2Gi
cpu: 2000m
service:
type: NodePort
port: 8087
nodePort: 30080
terminationGracePeriodSeconds: 60
rustLog: info
livenessProbe:
httpGet: { path: /health, port: 8087 }
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet: { path: /health, port: 8087 }
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
# NetworkPolicy 是否启用 (cluster 不支持 NetworkPolicy 时置 false)
networkPolicy:
enabled: true
# PodDisruptionBudget
pdb:
enabled: true
maxUnavailable: 0
# 是否创建共享的 ClusterRole (rcoder-pods-clusterrole)
# - true (默认): chart 拥有该 ClusterRole
# - false: 当集群里已有其他方式创建的同名 ClusterRole (如 Kustomize 并存) 时置 false
clusterRole:
create: true
# 动态 agent pod 使用的镜像 (通过 env var 注入,不会直接创建 pod)
agentRunner:
image:
repository: rcoder-agent-runner
tag: "" # 空时继承 .Chart.appVersion
# ----------------------------------------------------------------------------
# 存储层
# ----------------------------------------------------------------------------
postgresql:
image:
repository: postgres
tag: 16-alpine
storage:
size: 10Gi
# 默认 local-path与 Kustomize base 保持一致, k3s 自带)。
# Longhorn / 离线集群可按需改为 longhorn / "" (空 => 集群默认)。
storageClass: local-path
service:
port: 5432
minio:
image:
repository: minio/minio
tag: latest
initImage:
repository: minio/mc
tag: latest
storage:
size: 30Gi
storageClass: local-path
service:
apiPort: 9000
consolePort: 9001
bucket: juicefs # 需与 juicefs.bucket 对应
juicefs:
mountImage:
repository: juicedata/mount
tag: ce-v1.3.1
# StorageClass 是集群级资源; Helm release 之间必须独立命名避免冲突
storageClass:
create: true
# 留空则自动使用 "juicefs-sc-{release}"
name: ""
reclaimPolicy: Retain
mountOptions: "cache-size=1024,allow-other"
# juicefs-secret 里会使用到, 决定 mount 时的元数据连接 / 对象存储地址
meta:
appendFsync: "1"
cacheSize: "1024"
# bucket URL 指向 namespace 内的 minio-service (自动拼装, 无需修改)
bucket: juicefs
# ----------------------------------------------------------------------------
# 凭据 (Secret rcoder-credentials)
# ----------------------------------------------------------------------------
credentials:
postgresql:
user: juicefs
password: CHANGE-ME
minio:
rootUser: minioadmin
rootPassword: minioadmin
# ----------------------------------------------------------------------------
# 工作空间 PVC (ReadWriteMany via JuiceFS)
# ----------------------------------------------------------------------------
workspace:
size: 50Gi
# ----------------------------------------------------------------------------
# RCoder 应用配置 (渲染为 ConfigMap 里的 config.yml)
# ----------------------------------------------------------------------------
config:
defaultAgent: Claude
projectsDir: /app/project_workspace
port: 8087
cleanup:
enabled: true
idleTimeoutSeconds: 600
cleanupIntervalSeconds: 300
dockerStopTimeoutSeconds: 30
proxy:
listenPort: 8088
defaultBackendPort: 8087
backendHost: 127.0.0.1
portParam: port
healthCheck:
enabled: true
intervalSeconds: 5
timeoutSeconds: 1
healthyThreshold: 2
unhealthyThreshold: 3
dockerConfig:
networkBaseName: agent-network
networkMode: bridge
workDir: /app
autoCleanup: true
containerTtlSeconds: 3600
cache:
enabled: true
ttlSeconds: 3600
maxEntries: 50
apiKeyAuth:
enabled: false
apiKey: YOUR_API_KEY_HERE