feat: CI/CD pipeline + Learning/Medicine/Travel services
Some checks failed
Build and Deploy GooSeek / build-backend (push) Failing after 1m4s
Build and Deploy GooSeek / build-webui (push) Failing after 1m2s
Build and Deploy GooSeek / deploy (push) Has been skipped

- Add Gitea Actions workflow for automated build & deploy
- Add K8s manifests: webui, travel-svc, medicine-svc, sandbox-svc
- Update kustomization for localhost:5000 registry
- Add ingress for gooseek.ru and api.gooseek.ru
- Learning cabinet with onboarding, courses, sandbox integration
- Medicine service with symptom analysis and doctor matching
- Travel service with itinerary planning
- Server setup scripts (NVIDIA/CUDA, K3s, Gitea runner)

Made-with: Cursor
This commit is contained in:
home
2026-03-02 20:25:44 +03:00
parent 08bd41e75c
commit ab48a0632b
92 changed files with 15562 additions and 2198 deletions

View File

@@ -16,6 +16,27 @@ data:
COLLECTION_SVC_URL: "http://collection-svc:3025"
FILE_SVC_URL: "http://file-svc:3026"
THREAD_SVC_URL: "http://thread-svc:3027"
LEARNING_SVC_URL: "http://learning-svc:3034"
MEDICINE_SVC_URL: "http://medicine-svc:3037"
SANDBOX_SVC_URL: "http://sandbox-svc:3036"
OPENSANDBOX_URL: "http://opensandbox-server:8080"
AUTH_SVC_URL: "http://auth-svc:3050"
TRAVEL_SVC_URL: "http://travel-svc:3035"
ADMIN_SVC_URL: "http://admin-svc:3040"
DEFAULT_LLM_MODEL: "${DEFAULT_LLM_MODEL}"
DEFAULT_LLM_PROVIDER: "${DEFAULT_LLM_PROVIDER}"
TIMEWEB_API_BASE_URL: "${TIMEWEB_API_BASE_URL}"
TIMEWEB_AGENT_ACCESS_ID: "${TIMEWEB_AGENT_ACCESS_ID}"
TRAVELPAYOUTS_TOKEN: "${TRAVELPAYOUTS_TOKEN}"
NEXT_PUBLIC_ENABLED_ROUTES: "${NEXT_PUBLIC_ENABLED_ROUTES}"
NEXT_PUBLIC_TWOGIS_API_KEY: "${NEXT_PUBLIC_TWOGIS_API_KEY}"
S3_ENDPOINT: "${S3_ENDPOINT}"
S3_ACCESS_KEY: "${S3_ACCESS_KEY}"
S3_SECRET_KEY: "${S3_SECRET_KEY}"
S3_BUCKET: "${S3_BUCKET}"
S3_USE_SSL: "${S3_USE_SSL}"
S3_REGION: "${S3_REGION}"
S3_PUBLIC_URL: "${S3_PUBLIC_URL}"
---
apiVersion: v1
kind: Secret
@@ -28,5 +49,6 @@ stringData:
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}"
GEMINI_API_KEY: "${GEMINI_API_KEY}"
JWT_SECRET: "${JWT_SECRET}"
TIMEWEB_API_KEY: "${TIMEWEB_API_KEY}"
POSTGRES_USER: "gooseek"
POSTGRES_PASSWORD: "gooseek"

View File

@@ -3,9 +3,24 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKEND_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
ROOT_DIR="$(cd "$BACKEND_DIR/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
echo "=== GooSeek Go Backend K8s Deployment ==="
REGISTRY="localhost:5000"
IMAGE_TAG="${IMAGE_TAG:-latest}"
echo "=== GooSeek K8s Deployment ==="
echo "Backend dir: $BACKEND_DIR"
echo "Registry: $REGISTRY"
echo "Tag: $IMAGE_TAG"
# Load .env
if [ -f "$ENV_FILE" ]; then
echo "Loading env from $ENV_FILE"
set -a
source "$ENV_FILE"
set +a
fi
# Check kubectl
if ! command -v kubectl &> /dev/null; then
@@ -13,17 +28,40 @@ if ! command -v kubectl &> /dev/null; then
exit 1
fi
# Build Docker image
# Build and push backend image
echo ""
echo "=== Building Docker image ==="
echo "=== Building Go backend image ==="
cd "$BACKEND_DIR"
docker build -f deploy/docker/Dockerfile.all -t gooseek/backend:latest .
docker build -f deploy/docker/Dockerfile.all \
-t "$REGISTRY/gooseek/backend:$IMAGE_TAG" \
-t "$REGISTRY/gooseek/backend:latest" \
.
# Load to k3s (if using k3s)
if command -v k3s &> /dev/null; then
echo ""
echo "=== Loading image to k3s ==="
docker save gooseek/backend:latest | sudo k3s ctr images import -
echo "=== Pushing backend to registry ==="
docker push "$REGISTRY/gooseek/backend:$IMAGE_TAG"
docker push "$REGISTRY/gooseek/backend:latest"
# Build and push webui image
echo ""
echo "=== Building webui image ==="
docker build \
-f "$BACKEND_DIR/webui/Dockerfile" \
--build-arg "NEXT_PUBLIC_ENABLED_ROUTES=${NEXT_PUBLIC_ENABLED_ROUTES:-}" \
--build-arg "NEXT_PUBLIC_TWOGIS_API_KEY=${NEXT_PUBLIC_TWOGIS_API_KEY:-}" \
-t "$REGISTRY/gooseek/webui:$IMAGE_TAG" \
-t "$REGISTRY/gooseek/webui:latest" \
"$BACKEND_DIR/webui"
echo "=== Pushing webui to registry ==="
docker push "$REGISTRY/gooseek/webui:$IMAGE_TAG"
docker push "$REGISTRY/gooseek/webui:latest"
# Generate configmap/secrets from .env via envsubst
echo ""
echo "=== Generating K8s manifests from .env ==="
if command -v envsubst &> /dev/null && [ -f "$ENV_FILE" ]; then
envsubst < "$SCRIPT_DIR/configmap.yaml" > "$SCRIPT_DIR/_generated_configmap.yaml"
kubectl apply -f "$SCRIPT_DIR/_generated_configmap.yaml" -n gooseek
fi
# Apply kustomization
@@ -32,20 +70,31 @@ echo "=== Applying K8s manifests ==="
cd "$SCRIPT_DIR"
kubectl apply -k .
# Rolling restart to pull new images
echo ""
echo "=== Rolling restart deployments ==="
kubectl -n gooseek rollout restart deployment/api-gateway
kubectl -n gooseek rollout restart deployment/webui
kubectl -n gooseek rollout restart deployment/chat-svc
kubectl -n gooseek rollout restart deployment/agent-svc
kubectl -n gooseek rollout restart deployment/discover-svc
kubectl -n gooseek rollout restart deployment/search-svc
kubectl -n gooseek rollout restart deployment/learning-svc
kubectl -n gooseek rollout restart deployment/medicine-svc
kubectl -n gooseek rollout restart deployment/travel-svc
kubectl -n gooseek rollout restart deployment/sandbox-svc
# Wait for rollout
echo ""
echo "=== Waiting for deployments ==="
kubectl -n gooseek rollout status deployment/api-gateway --timeout=120s || true
echo "=== Waiting for rollouts ==="
kubectl -n gooseek rollout status deployment/api-gateway --timeout=180s || true
kubectl -n gooseek rollout status deployment/chat-svc --timeout=120s || true
kubectl -n gooseek rollout status deployment/agent-svc --timeout=120s || true
kubectl -n gooseek rollout status deployment/discover-svc --timeout=120s || true
kubectl -n gooseek rollout status deployment/search-svc --timeout=120s || true
kubectl -n gooseek rollout status deployment/redis --timeout=60s || true
# Show status
echo ""
echo "=== Deployment Status ==="
kubectl -n gooseek get pods
kubectl -n gooseek get pods -o wide
echo ""
kubectl -n gooseek get svc
echo ""
@@ -53,4 +102,5 @@ kubectl -n gooseek get ingress
echo ""
echo "=== Done ==="
echo "API Gateway: http://localhost:3015 (NodePort) or via Ingress"
echo "API: https://api.gooseek.ru"
echo "Web: https://gooseek.ru"

View File

@@ -0,0 +1,44 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: gitea
spec:
replicas: 1
selector:
matchLabels:
app: gitea
template:
metadata:
labels:
app: gitea
spec:
containers:
- name: gitea
image: gitea/gitea:1.22
ports:
- containerPort: 3000
name: http
- containerPort: 22
name: ssh
volumeMounts:
- name: data
mountPath: /data
env:
- name: GITEA__database__DB_TYPE
value: sqlite3
- name: GITEA__server__DOMAIN
value: git.gooseek.ru
- name: GITEA__server__ROOT_URL
value: https://git.gooseek.ru/
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: gitea-data

View File

@@ -4,7 +4,7 @@ metadata:
name: gooseek-ingress
namespace: gooseek
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
@@ -14,9 +14,20 @@ spec:
ingressClassName: nginx
tls:
- hosts:
- gooseek.ru
- api.gooseek.ru
secretName: gooseek-tls
rules:
- host: gooseek.ru
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webui
port:
number: 3000
- host: api.gooseek.ru
http:
paths:
@@ -27,25 +38,3 @@ spec:
name: api-gateway
port:
number: 3015
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gooseek-ingress-local
namespace: gooseek
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 3015

View File

@@ -9,6 +9,7 @@ resources:
- postgres.yaml
- redis.yaml
- api-gateway.yaml
- webui.yaml
- chat-svc.yaml
- agent-svc.yaml
- search-svc.yaml
@@ -18,6 +19,11 @@ resources:
- collection-svc.yaml
- file-svc.yaml
- thread-svc.yaml
- learning-svc.yaml
- medicine-svc.yaml
- travel-svc.yaml
- sandbox-svc.yaml
- opensandbox.yaml
- ingress.yaml
commonLabels:
@@ -26,4 +32,8 @@ commonLabels:
images:
- name: gooseek/backend
newName: localhost:5000/gooseek/backend
newTag: latest
- name: gooseek/webui
newName: localhost:5000/gooseek/webui
newTag: latest

View File

@@ -0,0 +1,68 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: learning-svc
namespace: gooseek
labels:
app: learning-svc
app.kubernetes.io/name: learning-svc
app.kubernetes.io/part-of: gooseek
spec:
replicas: 2
selector:
matchLabels:
app: learning-svc
template:
metadata:
labels:
app: learning-svc
spec:
containers:
- name: learning-svc
image: gooseek/backend:latest
env:
- name: SERVICE
value: "learning-svc"
- name: PORT
value: "3034"
envFrom:
- configMapRef:
name: gooseek-config
- secretRef:
name: gooseek-secrets
ports:
- containerPort: 3034
name: http
livenessProbe:
httpGet:
path: /health
port: 3034
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /health
port: 3034
initialDelaySeconds: 10
periodSeconds: 15
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: learning-svc
namespace: gooseek
spec:
type: ClusterIP
selector:
app: learning-svc
ports:
- port: 3034
targetPort: 3034
name: http

View File

@@ -0,0 +1,70 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: medicine-svc
namespace: gooseek
labels:
app: medicine-svc
app.kubernetes.io/name: medicine-svc
app.kubernetes.io/part-of: gooseek
spec:
replicas: 2
selector:
matchLabels:
app: medicine-svc
template:
metadata:
labels:
app: medicine-svc
spec:
containers:
- name: medicine-svc
image: gooseek/backend:latest
env:
- name: SERVICE
value: "medicine-svc"
- name: PORT
value: "3037"
- name: LLM_PROVIDER
value: "timeweb"
envFrom:
- configMapRef:
name: gooseek-config
- secretRef:
name: gooseek-secrets
ports:
- containerPort: 3037
name: http
livenessProbe:
httpGet:
path: /health
port: 3037
initialDelaySeconds: 10
periodSeconds: 20
readinessProbe:
httpGet:
path: /health
port: 3037
initialDelaySeconds: 5
periodSeconds: 15
resources:
requests:
cpu: 150m
memory: 192Mi
limits:
cpu: 700m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: medicine-svc
namespace: gooseek
spec:
type: ClusterIP
selector:
app: medicine-svc
ports:
- port: 3037
targetPort: 3037
name: http

View File

@@ -0,0 +1,165 @@
apiVersion: v1
kind: Namespace
metadata:
name: gooseek-sandbox
labels:
app.kubernetes.io/part-of: gooseek
purpose: user-sandboxes
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: opensandbox-sa
namespace: gooseek
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: opensandbox-role
namespace: gooseek-sandbox
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec", "pods/log"]
verbs: ["create", "get", "list", "watch", "delete"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create", "get", "list", "watch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: opensandbox-binding
namespace: gooseek-sandbox
subjects:
- kind: ServiceAccount
name: opensandbox-sa
namespace: gooseek
roleRef:
kind: Role
name: opensandbox-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: sandbox-quota
namespace: gooseek-sandbox
spec:
hard:
requests.cpu: "8"
requests.memory: "16Gi"
limits.cpu: "16"
limits.memory: "32Gi"
pods: "50"
---
apiVersion: v1
kind: LimitRange
metadata:
name: sandbox-limits
namespace: gooseek-sandbox
spec:
limits:
- default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "2"
memory: "2Gi"
type: Container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: opensandbox-server
namespace: gooseek
labels:
app: opensandbox-server
app.kubernetes.io/name: opensandbox-server
app.kubernetes.io/part-of: gooseek
spec:
replicas: 1
selector:
matchLabels:
app: opensandbox-server
template:
metadata:
labels:
app: opensandbox-server
spec:
serviceAccountName: opensandbox-sa
containers:
- name: opensandbox
image: registry.cn-hangzhou.aliyuncs.com/open_sandbox/server:v1.0.1
ports:
- containerPort: 8080
name: http
env:
- name: SANDBOX_NAMESPACE
value: "gooseek-sandbox"
- name: SANDBOX_DEFAULT_TIMEOUT
value: "30m"
- name: SANDBOX_MAX_CONCURRENT
value: "20"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
---
apiVersion: v1
kind: Service
metadata:
name: opensandbox-server
namespace: gooseek
spec:
type: ClusterIP
selector:
app: opensandbox-server
ports:
- port: 8080
targetPort: 8080
name: http
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: sandbox-isolation
namespace: gooseek-sandbox
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
app.kubernetes.io/part-of: gooseek
egress:
- to:
- namespaceSelector:
matchLabels:
app.kubernetes.io/part-of: gooseek
- to: []
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 80

View File

@@ -0,0 +1,78 @@
apiVersion: v1
kind: Secret
metadata:
name: registry-auth
namespace: gooseek
type: Opaque
stringData:
htpasswd: |
admin:$2y$05$A6oxuQhSjFObdjDsbjiWee.FJ62XQrc6BhLfzCMofY.9A/qQ050v6
---
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-config
namespace: gooseek
data:
config.yml: |
version: 0.1
log:
level: info
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
auth:
htpasswd:
realm: GooSeek Registry
path: /auth/htpasswd
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry
namespace: gooseek
spec:
replicas: 1
selector:
matchLabels:
app: registry
template:
metadata:
labels:
app: registry
spec:
containers:
- name: registry
image: registry:2
ports:
- containerPort: 5000
volumeMounts:
- name: registry-data
mountPath: /var/lib/registry
- name: registry-config
mountPath: /etc/docker/registry
- name: registry-auth
mountPath: /auth
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
volumes:
- name: registry-data
persistentVolumeClaim:
claimName: registry-pvc
- name: registry-config
configMap:
name: registry-config
- name: registry-auth
secret:
secretName: registry-auth

View File

@@ -0,0 +1,70 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: sandbox-svc
namespace: gooseek
labels:
app: sandbox-svc
app.kubernetes.io/name: sandbox-svc
app.kubernetes.io/part-of: gooseek
spec:
replicas: 1
selector:
matchLabels:
app: sandbox-svc
template:
metadata:
labels:
app: sandbox-svc
spec:
containers:
- name: sandbox-svc
image: gooseek/backend:latest
env:
- name: SERVICE
value: "sandbox-svc"
- name: PORT
value: "3036"
- name: OPENSANDBOX_URL
value: "http://opensandbox-server:8080"
envFrom:
- configMapRef:
name: gooseek-config
- secretRef:
name: gooseek-secrets
ports:
- containerPort: 3036
name: http
livenessProbe:
httpGet:
path: /health
port: 3036
initialDelaySeconds: 10
periodSeconds: 20
readinessProbe:
httpGet:
path: /health
port: 3036
initialDelaySeconds: 5
periodSeconds: 15
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: sandbox-svc
namespace: gooseek
spec:
type: ClusterIP
selector:
app: sandbox-svc
ports:
- port: 3036
targetPort: 3036
name: http

View File

@@ -0,0 +1,68 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: travel-svc
namespace: gooseek
labels:
app: travel-svc
app.kubernetes.io/name: travel-svc
app.kubernetes.io/part-of: gooseek
spec:
replicas: 2
selector:
matchLabels:
app: travel-svc
template:
metadata:
labels:
app: travel-svc
spec:
containers:
- name: travel-svc
image: gooseek/backend:latest
env:
- name: SERVICE
value: "travel-svc"
- name: PORT
value: "3035"
envFrom:
- configMapRef:
name: gooseek-config
- secretRef:
name: gooseek-secrets
ports:
- containerPort: 3035
name: http
livenessProbe:
httpGet:
path: /health
port: 3035
initialDelaySeconds: 10
periodSeconds: 20
readinessProbe:
httpGet:
path: /health
port: 3035
initialDelaySeconds: 5
periodSeconds: 15
resources:
requests:
cpu: 150m
memory: 192Mi
limits:
cpu: 700m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: travel-svc
namespace: gooseek
spec:
type: ClusterIP
selector:
app: travel-svc
ports:
- port: 3035
targetPort: 3035
name: http

View File

@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: webui
namespace: gooseek
labels:
app: webui
app.kubernetes.io/name: webui
app.kubernetes.io/part-of: gooseek
spec:
replicas: 2
selector:
matchLabels:
app: webui
template:
metadata:
labels:
app: webui
spec:
containers:
- name: webui
image: gooseek/webui:latest
ports:
- containerPort: 3000
name: http
envFrom:
- configMapRef:
name: gooseek-config
- secretRef:
name: gooseek-secrets
livenessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: webui
namespace: gooseek
spec:
type: ClusterIP
selector:
app: webui
ports:
- port: 3000
targetPort: 3000
name: http