Private
Public Access
1
0

Add Docker, Kubernetes configurations, and CI workflows for deployment. Integrate Gin server for API, WebSocket support, and static file hosting. Refactor WebSocket gateway to use Gin router.
Dynamic Branch Deploy / build-and-deploy (push) Has been cancelled

This commit is contained in:
Sebastian Unterschütz
2026-01-04 15:14:55 +01:00
parent 2fb19d314f
commit 16f683a360
13 changed files with 872 additions and 13 deletions
+35
View File
@@ -0,0 +1,35 @@
# Binaries
*.exe
*.exe~
*.dll
*.so
*.dylib
server
client
# Test files
*.test
*.out
# Development
.git/
.github/
.vscode/
.idea/
# Documentation
*.md
!README.md
# Temporary
tmp/
temp/
*.tmp
*.log
# OS
.DS_Store
Thumbs.db
# Dependencies (werden neu gebaut)
vendor/
+68
View File
@@ -0,0 +1,68 @@
name: Cleanup Environment
on: [delete]
jobs:
cleanup:
runs-on: ubuntu-latest
# Nur ausführen, wenn ein Branch gelöscht wurde (keine Tags)
if: github.event.ref_type == 'branch'
steps:
# 1. Variablen berechnen (MIT FIX FÜR REFS/HEADS & MAIN-CHECK)
- name: Prepare Variables
run: |
# Repo Name klein (z.B. "it232abschied")
REPO_LOWER=$(echo "${{ gitea.repository }}" | cut -d'/' -f2 | tr '[:upper:]' '[:lower:]')
# Branch Name aus Event (z.B. "refs/heads/feature-x")
RAW_REF="${{ github.event.ref }}"
# "refs/heads/" entfernen
BRANCH_CLEAN=${RAW_REF#refs/heads/}
# Kleinschreiben & Sonderzeichen
BRANCH_LOWER=$(echo "$BRANCH_CLEAN" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
# Logik synchron zum Deploy:
# Main/Master -> Namespace ist nur der Repo-Name
# Anderes -> Namespace ist Repo-Branch
if [ "$BRANCH_LOWER" = "main" ] || [ "$BRANCH_LOWER" = "master" ]; then
TARGET_NS="${REPO_LOWER}"
IS_MAIN="true"
else
TARGET_NS="${REPO_LOWER}-${BRANCH_LOWER}"
IS_MAIN="false"
fi
echo "DEBUG: Clean Branch: $BRANCH_LOWER"
echo "DEBUG: Target NS: $TARGET_NS"
echo "TARGET_NS=$TARGET_NS" >> $GITHUB_ENV
echo "IS_MAIN=$IS_MAIN" >> $GITHUB_ENV
# 2. Sicherheits-Check: Niemals Main/Master löschen!
# Wir prüfen jetzt die Variable IS_MAIN, statt den Namen hart zu codieren
- name: Protect Main
if: env.IS_MAIN == 'true'
run: |
echo "❌ ABBRUCH: Der Produktions-Namespace ${{ env.TARGET_NS }} darf nicht gelöscht werden!"
exit 1
# 3. Kubectl einrichten
- name: Setup Kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG }}" > $HOME/.kube/config
chmod 600 $HOME/.kube/config
# Der Trick für interne Kommunikation
sed -i 's|server: https://.*:6443|server: https://kubernetes.default.svc:443|g' $HOME/.kube/config
# 4. Namespace löschen
- name: Delete Namespace
run: |
echo "🗑️ Lösche Namespace: ${{ env.TARGET_NS }}"
# Wir löschen den Namespace ohne zu warten (async), das geht schneller
kubectl delete namespace ${{ env.TARGET_NS }} --ignore-not-found --wait=false
+128
View File
@@ -0,0 +1,128 @@
name: Dynamic Branch Deploy
on: [push]
env:
REGISTRY: git.zb-server.de
# WICHTIG: Deine echte Haupt-Domain
BASE_DOMAIN: escape-from-school.de
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# 1. Code auschecken
- name: Checkout Code
uses: actions/checkout@v3
# 2. Variablen vorbereiten (MIT HAUPT-DOMAIN LOGIK)
- name: Prepare Environment Variables
id: prep
run: |
# 1. Repo und Branch Namen säubern
# Voller Pfad für Docker Image (z.B. "user/escape-teacher")
FULL_IMAGE_PATH=$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]')
# Nur der Projektname für K8s (z.B. "escape-teacher")
REPO_LOWER=$(echo "$FULL_IMAGE_PATH" | cut -d'/' -f2)
# Branch Name säubern (Sonderzeichen zu Bindestrichen)
BRANCH_LOWER=$(echo "${{ gitea.ref_name }}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
# 2. Logik: Ist es der Haupt-Branch?
if [ "$BRANCH_LOWER" = "main" ] || [ "$BRANCH_LOWER" = "master" ]; then
# PRODUKTION:
# URL ist direkt die Domain (ohne Subdomain)
APP_URL="${{ env.BASE_DOMAIN }}"
# Namespace ist nur der Projektname (ohne Branch-Suffix)
TARGET_NS="${REPO_LOWER}"
echo "Mode: PRODUCTION (Root Domain)"
else
# ENTWICKLUNG:
# URL ist repo-branch.domain.de
APP_URL="${REPO_LOWER}-${BRANCH_LOWER}.${{ env.BASE_DOMAIN }}"
# Namespace ist repo-branch
TARGET_NS="${REPO_LOWER}-${BRANCH_LOWER}"
echo "Mode: DEVELOPMENT (Subdomain)"
fi
# Image Tag (Commit Hash)
IMAGE_TAG="${{ gitea.sha }}"
# Debug Ausgabe
echo "DEBUG: Branch: $BRANCH_LOWER"
echo "DEBUG: Namespace: $TARGET_NS"
echo "DEBUG: URL: $APP_URL"
# In Gitea Actions Environment schreiben
echo "FULL_IMAGE_PATH=$FULL_IMAGE_PATH" >> $GITHUB_ENV
echo "REPO_NAME=$REPO_LOWER" >> $GITHUB_ENV
echo "TARGET_NS=$TARGET_NS" >> $GITHUB_ENV
echo "APP_URL=$APP_URL" >> $GITHUB_ENV
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
# 3. Kaniko Build
- name: Build and Push with Kaniko
uses: aevea/action-kaniko@v0.12.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ gitea.actor }}
password: ${{ secrets.PACKAGE_TOKEN }}
image: ${{ env.FULL_IMAGE_PATH }}
tag: ${{ env.IMAGE_TAG }}
cache: true
extra_args: --skip-tls-verify-pull --insecure
# 4. Setup Kubectl (Interner Trick)
- name: Setup Kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG }}" > $HOME/.kube/config
chmod 600 $HOME/.kube/config
# Internal DNS Trick (für Kommunikation innerhalb des Clusters)
sed -i 's|server: https://.*:6443|server: https://kubernetes.default.svc:443|g' $HOME/.kube/config
# 5. Deploy to Kubernetes
- name: Deploy to Kubernetes
run: |
# Namespace erstellen (falls nicht existiert)
kubectl create namespace ${{ env.TARGET_NS }} --dry-run=client -o yaml | kubectl apply -f -
# Vollen Image Pfad bauen
FULL_IMAGE_URL="${{ env.REGISTRY }}/${{ env.FULL_IMAGE_PATH }}:${{ env.IMAGE_TAG }}"
# 1. Ingress anpassen (Hier wird die URL eingesetzt!)
sed -i "s|\${APP_URL}|${{ env.APP_URL }}|g" k8s/ingress.yaml
# 2. App Deployment anpassen (Image)
sed -i "s|\${IMAGE_NAME}|$FULL_IMAGE_URL|g" k8s/app.yaml
# Anwenden
echo "Deploying Resources to Namespace: ${{ env.TARGET_NS }}"
kubectl apply -f k8s/pvc.yaml -n ${{ env.TARGET_NS }}
kubectl apply -f k8s/redis.yaml -n ${{ env.TARGET_NS }}
kubectl apply -f k8s/app.yaml -n ${{ env.TARGET_NS }}
kubectl apply -f k8s/ingress.yaml -n ${{ env.TARGET_NS }}
# HPA (Autoscaling) nur für Main/Master Branch aktivieren
# Wir vergleichen den Namespace mit dem Repo-Namen
# Wenn Namespace == RepoName, dann sind wir im Main Branch
if [ "${{ env.TARGET_NS }}" == "${{ env.REPO_NAME }}" ]; then
echo "Main Branch detected: Applying HPA (Autoscaling)..."
kubectl apply -f k8s/hpa.yaml -n ${{ env.TARGET_NS }}
else
echo "Feature Branch: Skipping HPA."
# Optional: HPA löschen, falls es versehentlich da ist
kubectl delete hpa escape-game-hpa -n ${{ env.TARGET_NS }} --ignore-not-found
fi
# Force Update (damit das neue Image sicher geladen wird)
kubectl rollout restart deployment/escape-game -n ${{ env.TARGET_NS }}
# 6. Summary
- name: Summary
run: echo "🚀 Deployed successfully to https://${{ env.APP_URL }}"
-8
View File
@@ -51,14 +51,6 @@ Thumbs.db
.env .env
config.local.json config.local.json
# Generated assets (if you want to regenerate them)
# Uncomment if assets should be generated, not committed
# /cmd/client/web/assets/*.png
# /cmd/client/web/assets/assets.json
# Keep chunks but ignore generated ones if needed
# /cmd/client/web/assets/chunks/*.json
# Debug files # Debug files
debug debug
__debug_bin __debug_bin
+45
View File
@@ -0,0 +1,45 @@
# Stage 1: Builder
FROM golang:1.25.5-alpine AS builder
# Build-Dependencies
RUN apk add --no-cache git
WORKDIR /app
# Dependencies cachen
COPY go.mod go.sum ./
RUN go mod download
# Source Code kopieren
COPY . .
# Server binary bauen
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o server ./cmd/server
# WASM Client bauen
RUN GOOS=js GOARCH=wasm go build -o cmd/client/web/main.wasm ./cmd/client
# Stage 2: Production Image
FROM alpine:latest
RUN apk --no-cache add ca-certificates curl tzdata && \
addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
WORKDIR /app
# Binary und Web-Dateien vom Builder kopieren
COPY --from=builder /app/server .
COPY --from=builder /app/cmd/client/web ./cmd/client/web
# User wechseln
USER appuser
# Port für HTTP/WebSocket
EXPOSE 8080
# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CMD ["./server"]
+296
View File
@@ -0,0 +1,296 @@
# 🏃 Escape From Teacher
Ein Endless-Runner-Spiel entwickelt als Schulprojekt. Renne vor dem Lehrer davon, sammle Münzen, nutze Power-Ups und klettere an Wänden!
![License](https://img.shields.io/badge/license-Proprietary-red)
![Go Version](https://img.shields.io/badge/go-1.21+-blue.svg)
![Platform](https://img.shields.io/badge/platform-Web%20%7C%20Desktop-green)
## 📋 Inhaltsverzeichnis
- [Features](#-features)
- [Technologie-Stack](#-technologie-stack)
- [Installation](#-installation)
- [Spielmodi](#-spielmodi)
- [Entwicklung](#-entwicklung)
- [Architektur](#-architektur)
- [Tools](#-tools)
- [Lizenz](#-lizenz)
## ✨ Features
### Gameplay
- 🏃 **Endless Runner**: Unendliches Spiel mit prozedural generierten Levels
- 🧗 **Wall Climbing**: Klettere an Wänden hoch für neue Strategien
- 💰 **Coins & Power-Ups**: Sammle Münzen und nutze Power-Ups wie Double Jump und Godmode
- 🎯 **Hindernisse**: Weiche bewegenden Plattformen und Objekten aus
-**Dynamische Schwierigkeit**: Das Spiel wird mit der Zeit schneller
### Modi
- 👤 **Solo-Modus**: Spiele alleine und sammle Highscores
- 👥 **Coop-Modus**: Spiele mit Freunden im Team
- Team-Namen für Leaderboard
- Multiplizierter Score basierend auf lebenden Spielern
- Host-Controls für Lobby-Management
### Features
- 🏆 **Leaderboard**: Redis-basiertes globales Leaderboard mit Proof-Codes
- 🎮 **Cross-Platform**: Web (WASM) und Desktop (Native)
- 🎨 **Level Editor**: Erstelle eigene Levels mit dem integrierten Level Builder
- 🔊 **Audio**: Musik und Sound-Effekte mit Lautstärkeregelung
- 📱 **Mobile Support**: Touch-Controls für mobile Geräte
## 🛠 Technologie-Stack
### Backend
- **Go 1.25+**: Server und Game-Logik
- **NATS**: Message Broker für Echtzeit-Kommunikation (Legacy)
- **WebSocket**: Direkte WebSocket-Verbindungen für niedrige Latenz
- **Redis**: Persistentes Leaderboard mit Proof-Code-Validierung
### Frontend
- **Go + Ebiten**: Desktop-Client (Native)
- **Go + WASM**: Web-Client kompiliert zu WebAssembly
- **HTML/CSS/JavaScript**: Web-UI und Menüs
### Architektur
- **Client-Side Prediction**: Flüssiges Gameplay trotz Netzwerk-Latenz
- **Server-Authoritative**: Server validiert alle Spielaktionen
- **Deterministische Physik**: Gleiche Physik auf Client und Server
## 📦 Installation
### Voraussetzungen
```bash
# Go 1.25 oder höher
go version
# Redis für Leaderboard
redis-server --version
```
### Repository klonen
```bash
git clone https://git.zb-server.de/ZB-Server/EscapeFromTeacher.git
cd EscapeFromTeacher
```
### Assets generieren
Die Assets müssen aus den Raw-Dateien generiert werden:
```bash
# Asset-Builder starten (GUI)
go run ./cmd/builder
# Im GUI: Assets laden, anpassen und speichern
# Dies erstellt cmd/client/web/assets/assets.json und die PNG-Dateien
```
### Server starten
```bash
# Redis starten (Terminal 1)
redis-server
# Server starten (Terminal 2)
go run ./cmd/server
```
Server läuft auf `http://localhost:8080`
### Clients starten
#### Web-Client (WASM)
```bash
# WASM kompilieren
GOOS=js GOARCH=wasm go build -o cmd/client/web/main.wasm ./cmd/client
# Web-Server starten (z.B. mit Python)
cd cmd/client/web
python3 -m http.server 8000
# Browser öffnen: http://localhost:8000
```
#### Desktop-Client (Native)
```bash
go run ./cmd/client
```
## 🎮 Spielmodi
### Solo-Modus
- Starte direkt aus dem Hauptmenü
- Automatischer Room-Erstellen
- Score basiert auf zurückgelegter Distanz
- Eigener Highscore im Leaderboard
### Coop-Modus
1. **Raum erstellen** (Host):
- "RAUM ERSTELLEN" klicken
- Team-Namen festlegen (optional)
- Raum-Code mit Freunden teilen
2. **Raum beitreten**:
- Raum-Code eingeben
- "RAUM BEITRETEN" klicken
3. **Gameplay**:
- Host startet das Spiel wenn alle bereit sind
- Team-Score = Distanz × Anzahl lebender Spieler
- Beispiel: 100 Tiles mit 3 Spielern = 300 Punkte pro Spieler
### Steuerung
**Desktop:**
- `Leertaste` / `W` / `↑`: Springen
- `A` / `←`: Links bewegen
- `D` / `→`: Rechts bewegen
- `S` / `↓`: Schneller fallen
**Mobile:**
- Touch-Buttons auf dem Bildschirm
**Wall Climbing:**
- Laufe gegen eine Wand
- Halte Richtungstaste gedrückt zum Hochklettern
- Lasse los zum Herunterrutschen
## 🔧 Entwicklung
### Projekt-Struktur
```
EscapeFromTeacher/
├── cmd/
│ ├── server/ # Game Server
│ ├── client/ # Desktop & WASM Client
│ │ └── web/ # Web Assets & HTML
│ ├── builder/ # Asset Builder Tool
│ └── levelbuilder/ # Level Editor
├── pkg/
│ ├── game/ # Gemeinsame Game-Logik
│ ├── server/ # Server-spezifische Logik
│ └── config/ # Konfiguration
└── assets_raw/ # Rohe Asset-Dateien
```
### Neue Features entwickeln
1. **Server-Logik** in `pkg/server/`
2. **Game-Logik** (gemeinsam) in `pkg/game/`
3. **Client-Rendering** in `cmd/client/`
4. **Web-UI** in `cmd/client/web/`
### Build-Befehle
```bash
# Server
go build -o server ./cmd/server
# Desktop-Client
go build -o client ./cmd/client
# WASM-Client
GOOS=js GOARCH=wasm go build -o cmd/client/web/main.wasm ./cmd/client
# Level Builder
go build -o levelbuilder ./cmd/levelbuilder
```
## 🧰 Tools
### Level Builder
Erstelle eigene Levels mit dem visuellen Editor:
```bash
go run ./cmd/levelbuilder
```
**Features:**
- Drag & Drop von Assets
- Bewegende Plattformen konfigurieren
- Chunk-basiertes Level-Design
- JSON-Export in `cmd/client/web/assets/chunks/`
### Asset Builder
Konvertiere Raw-Assets zu optimierten Spiel-Assets:
```bash
go run ./cmd/builder
```
**Features:**
- PNG-Kompression und -Skalierung
- Hitbox-Editor
- Asset-Metadaten (Typ, Offsets, etc.)
- Generiert `assets.json`
## 📐 Architektur
### Client-Server Kommunikation
```
Client Server
| |
|------ WebSocket ------->|
| (Input Events) |
| |
|<----- Broadcast --------|
| (Game State) |
| |
|-- Score Submission ---->|
| |
|<--- Proof Code ---------|
```
### Game Loop
**Server (60 FPS):**
1. Verarbeite Inputs von allen Clients
2. Update Physik (Gravity, Kollision, etc.)
3. Update Map (Chunk spawning/despawning)
4. Broadcast Game State an alle Clients
**Client:**
1. Sende lokale Inputs an Server
2. Empfange Game State vom Server
3. Render Game State
4. (Optional) Client-Side Prediction für lokalen Spieler
### Score-System
**Solo:**
- Score = Zurückgelegte Distanz (in Tiles)
- Coins: +200 Punkte
**Coop:**
- Distanz-Score akkumuliert pro Tick
- Pro Tick: `Score += Anzahl lebender Spieler`
- Beispiel: 3 Spieler → +3 Punkte/Tick (180 Punkte/Sekunde)
- Coins werden geteilt: Alle Spieler bekommen +200
## 📄 Lizenz
Dies ist ein **Schulprojekt**. Kommerzielle Nutzung und Veränderung des Quellcodes sind ausdrücklich untersagt. Alle Rechte liegen bei den Urhebern.
**Projektleitung & Code:** Sebastian Unterschütz
**Musik & Sound Design:** Max E.
## 🙏 Credits
- **Ebiten**: Go 2D Game Engine
- **NATS**: Message Broker
- **Redis**: In-Memory Database
- **Go WASM**: WebAssembly Support
---
**Entwickelt als Abschlussprojekt für IT232 (2025/2026)**
+88
View File
@@ -0,0 +1,88 @@
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/nats-io/nats.go"
)
// SetupGinServer erstellt und konfiguriert den Gin-Server
func SetupGinServer(ec *nats.EncodedConn, port string) *gin.Engine {
// Production mode für bessere Performance
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
// Logging Middleware (bereits in gin.Default())
// Custom Logger für bessere Übersicht
r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return "🌐 " + param.TimeStamp.Format("2006-01-02 15:04:05") +
" | " + param.Method +
" | " + string(rune(param.StatusCode)) +
" | " + param.Latency.String() +
" | " + param.ClientIP +
" | " + param.Path + "\n"
}))
// Recovery Middleware
r.Use(gin.Recovery())
// Health Check Endpoint
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{
"status": "ok",
"service": "EscapeFromTeacher",
"version": "1.0.0",
})
})
// Metrics Endpoint (optional für Kubernetes)
r.GET("/metrics", func(c *gin.Context) {
mu.RLock()
roomCount := len(rooms)
playerCount := len(playerSessions)
mu.RUnlock()
c.JSON(200, gin.H{
"rooms": roomCount,
"players": playerCount,
})
})
// WebSocket Endpoint
r.GET("/ws", func(c *gin.Context) {
handleWebSocketGin(c.Writer, c.Request, ec)
})
// Static Files - Serve Web Client
r.Static("/assets", "./cmd/client/web/assets")
r.StaticFile("/", "./cmd/client/web/index.html")
r.StaticFile("/index.html", "./cmd/client/web/index.html")
r.StaticFile("/game.js", "./cmd/client/web/game.js")
r.StaticFile("/style.css", "./cmd/client/web/style.css")
r.StaticFile("/wasm_exec.js", "./cmd/client/web/wasm_exec.js")
r.StaticFile("/main.wasm", "./cmd/client/web/main.wasm")
r.StaticFile("/background.jpg", "./cmd/client/web/background.jpg")
// 404 Handler
r.NoRoute(func(c *gin.Context) {
c.JSON(404, gin.H{
"error": "Route not found",
})
})
log.Printf("🚀 Gin-Server konfiguriert auf Port %s", port)
log.Printf("📁 Statische Dateien: ./cmd/client/web/")
log.Printf("🌐 WebSocket Endpoint: /ws")
log.Printf("❤️ Health Check: /health")
return r
}
// handleWebSocketGin verwaltet WebSocket-Verbindungen über Gin
func handleWebSocketGin(w http.ResponseWriter, r *http.Request, ec *nats.EncodedConn) {
// Verwende die handleWebSocket Funktion aus websocket_gateway.go
handleWebSocket(w, r, ec)
}
+5 -5
View File
@@ -172,11 +172,11 @@ func main() {
log.Println("✅ Server bereit. Warte auf Spieler...") log.Println("✅ Server bereit. Warte auf Spieler...")
// 9. WEBSOCKET-GATEWAY STARTEN (für Browser-Clients) // 9. GIN-SERVER STARTEN (statische Dateien + WebSocket)
go StartWebSocketGateway("8080", ec) router := SetupGinServer(ec, "8080")
if err := router.Run(":8080"); err != nil {
// Block forever log.Fatal("❌ Gin-Server Fehler:", err)
select {} }
} }
func loadServerAssets(w *game.World) { func loadServerAssets(w *game.World) {
+72
View File
@@ -0,0 +1,72 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: escape-game
labels:
app: escape-game
spec:
replicas: 2
selector:
matchLabels:
app: escape-game
template:
metadata:
labels:
app: escape-game
spec:
containers:
- name: server
image: ${IMAGE_NAME}
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1000m"
volumeMounts:
- name: assets
mountPath: /root/cmd/client/web/assets
volumes:
- name: assets
persistentVolumeClaim:
claimName: game-assets-pvc
---
apiVersion: v1
kind: Service
metadata:
name: escape-game
labels:
app: escape-game
spec:
type: ClusterIP
selector:
app: escape-game
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600
+24
View File
@@ -0,0 +1,24 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: escape-game-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: escape-game
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
+25
View File
@@ -0,0 +1,25 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: game-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: web, websecure
traefik.ingress.kubernetes.io/router.middlewares: gitea-redirect-https@kubernetescrd
spec:
ingressClassName: traefik
tls:
- hosts:
- ${APP_URL}
secretName: game-tls-secret
rules:
- host: ${APP_URL}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: escape-game
port:
number: 80
+11
View File
@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: game-assets-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: longhorn
resources:
requests:
storage: 2Gi
+75
View File
@@ -0,0 +1,75 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
ports:
- port: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
initContainers:
- name: fix-permissions
image: busybox
command: ["sh", "-c", "chown -R 999:999 /data"]
volumeMounts:
- name: data
mountPath: /data
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
memory: "64Mi"
cpu: "50m" # 0.05 CPU Cores
limits:
memory: "256Mi"
cpu: "1000m" # 0.5 CPU Cores
livenessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 5
periodSeconds: 10
volumes:
- name: data
persistentVolumeClaim:
claimName: redis-data