Skip to content

Commit ae3f858

Browse files
committed
Updates for k8s deployment
1 parent 846ecb8 commit ae3f858

File tree

9 files changed

+268
-42
lines changed

9 files changed

+268
-42
lines changed

DEPLOYMENT.md

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ This document describes the complete deployment setup for Pyrom (ROM24 Python MU
66

77
Pyrom now has a complete Kubernetes deployment setup with:
88
- **Docker containerization** using Python 3.12 and UV package manager
9-
- **Three environments**: dev, staging, and prod
9+
- **Four environments**: local (Tilt), dev, staging, and prod
1010
- **Persistent storage** for player data, world data, and system files
1111
- **ArgoCD GitOps** integration for automated deployments
1212
- **TCP ingress** routing for telnet access
13+
- **Tilt** for local development with live reload
1314

1415
## File Structure
1516

1617
```
1718
rom24/
1819
├── Dockerfile # Docker image definition
1920
├── .dockerignore # Docker build exclusions
21+
├── Tiltfile # Tilt configuration for local dev
2022
├── Makefile # Build and deployment commands
2123
├── k8s/
2224
│ ├── README.md # Kubernetes deployment documentation
@@ -28,6 +30,11 @@ rom24/
2830
│ │ ├── pyrom-pvc.yaml # Persistent volume claims
2931
│ │ └── pyrom-configmap.yaml # Configuration
3032
│ ├── overlays/
33+
│ │ ├── local/ # Local development (Tilt)
34+
│ │ │ ├── namespace.yaml
35+
│ │ │ ├── kustomization.yaml
36+
│ │ │ ├── pyrom-pvc-patch.yaml
37+
│ │ │ └── pyrom-configmap.yaml
3138
│ │ ├── dev/ # Development environment
3239
│ │ │ ├── namespace.yaml
3340
│ │ │ ├── kustomization.yaml
@@ -51,14 +58,51 @@ rom24/
5158

5259
## Environments
5360

54-
| Environment | Namespace | Telnet Port | Hostname | Log Level |
55-
|-------------|----------------|-------------|-------------------------------|-----------|
56-
| Production | pyrom-prod | 1337 | pyrom.bubtaylor.com | WARNING |
57-
| Staging | pyrom-staging | 1338 | pyrom-staging.bubtaylor.com | INFO |
58-
| Development | pyrom-dev | 1339 | pyrom-dev.bubtaylor.com | DEBUG |
61+
| Environment | Namespace | Telnet Port | Hostname | Log Level | Storage Class |
62+
|-------------|----------------|-------------|-------------------------------|-----------|---------------|
63+
| **Local** | pyrom-local | 1337 | localhost | DEBUG | hostpath |
64+
| Production | pyrom-prod | 1337 | pyrom.bubtaylor.com | WARNING | longhorn |
65+
| Staging | pyrom-staging | 1338 | pyrom-staging.bubtaylor.com | INFO | longhorn |
66+
| Development | pyrom-dev | 1339 | pyrom-dev.bubtaylor.com | DEBUG | longhorn |
5967

6068
## Quick Start
6169

70+
### Local Development with Tilt (Recommended for Development)
71+
72+
[Tilt](https://tilt.dev/) provides live reload and automatic rebuilds for local development.
73+
74+
**Prerequisites:**
75+
- Docker Desktop with Kubernetes enabled
76+
- [Tilt installed](https://docs.tilt.dev/install.html)
77+
78+
**Start local development:**
79+
80+
```bash
81+
cd rom24
82+
tilt up
83+
```
84+
85+
This will:
86+
- Build the Docker image
87+
- Deploy to `pyrom-local` namespace
88+
- Set up port forwarding to `localhost:1337`
89+
- Watch for file changes and automatically rebuild/restart
90+
- Provide a web UI at http://localhost:10350
91+
92+
**Connect to your local MUD:**
93+
94+
```bash
95+
telnet localhost 1337
96+
```
97+
98+
**Stop Tilt:**
99+
100+
```bash
101+
tilt down
102+
```
103+
104+
### Remote Deployments
105+
62106
### 1. Build and Push Docker Image
63107

64108
```bash

TCP_INGRESS_SETUP.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# TCP Ingress Setup for Pyrom
2+
3+
## Problem
4+
5+
Pyrom uses **telnet (TCP protocol)**, not HTTP. The standard HTTP Ingress won't work for telnet connections. We need to configure ingress-nginx to route TCP traffic.
6+
7+
## Solution
8+
9+
### 1. Apply the TCP Services ConfigMap
10+
11+
The ConfigMap tells ingress-nginx which TCP ports to route to which services:
12+
13+
```bash
14+
# Apply via ArgoCD (recommended)
15+
kubectl apply -f argocd/ingress-nginx-tcp-config.yaml
16+
17+
# Or apply directly
18+
kubectl apply -f k8s/ingress-nginx/tcp-services-configmap.yaml
19+
```
20+
21+
Verify it's applied:
22+
23+
```bash
24+
kubectl get configmap tcp-services -n ingress-nginx -o yaml
25+
```
26+
27+
You should see:
28+
29+
```yaml
30+
data:
31+
"1337": "pyrom-prod/pyrom-service:1337"
32+
"1338": "pyrom-staging/pyrom-service:1337"
33+
"1339": "pyrom-dev/pyrom-service:1337"
34+
```
35+
36+
### 2. Configure ingress-nginx Controller to Expose TCP Ports
37+
38+
The ingress-nginx controller Service needs to expose these TCP ports. You have two options:
39+
40+
#### Option A: Patch the existing ingress-nginx controller Service
41+
42+
```bash
43+
kubectl patch service ingress-nginx-controller -n ingress-nginx --type='json' -p='[
44+
{"op": "add", "path": "/spec/ports/-", "value": {"name": "pyrom-prod", "port": 1337, "protocol": "TCP", "targetPort": 1337}},
45+
{"op": "add", "path": "/spec/ports/-", "value": {"name": "pyrom-staging", "port": 1338, "protocol": "TCP", "targetPort": 1338}},
46+
{"op": "add", "path": "/spec/ports/-", "value": {"name": "pyrom-dev", "port": 1339, "protocol": "TCP", "targetPort": 1339}}
47+
]'
48+
```
49+
50+
#### Option B: Edit the ingress-nginx Helm values (if using Helm)
51+
52+
If you installed ingress-nginx via Helm, update the values:
53+
54+
```yaml
55+
tcp:
56+
1337: "pyrom-prod/pyrom-service:1337"
57+
1338: "pyrom-staging/pyrom-service:1337"
58+
1339: "pyrom-dev/pyrom-service:1337"
59+
```
60+
61+
Then upgrade:
62+
63+
```bash
64+
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
65+
-n ingress-nginx \
66+
-f ingress-nginx-values.yaml
67+
```
68+
69+
### 3. Verify the Setup
70+
71+
Check that the ingress-nginx controller Service has the TCP ports:
72+
73+
```bash
74+
kubectl get svc ingress-nginx-controller -n ingress-nginx -o yaml | grep -A 5 "1337\|1338\|1339"
75+
```
76+
77+
You should see ports 1337, 1338, and 1339 listed.
78+
79+
### 4. Test the Connection
80+
81+
```bash
82+
# Production
83+
telnet pyrom.bubtaylor.com 1337
84+
85+
# Staging
86+
telnet pyrom-staging.bubtaylor.com 1338
87+
88+
# Dev
89+
telnet pyrom-dev.bubtaylor.com 1339
90+
```
91+
92+
## Important Notes
93+
94+
1. **The HTTP Ingress resource** (`pyrom-ingress.yaml`) is **NOT used for telnet connections**. It's only there if you want to serve HTTP content on port 80.
95+
96+
2. **TCP routing is configured via**:
97+
- ConfigMap: `tcp-services` in `ingress-nginx` namespace
98+
- Service: `ingress-nginx-controller` must expose the TCP ports
99+
100+
3. **DNS must point to the ingress-nginx controller's external IP** for the hostnames to work.
101+
102+
## Troubleshooting
103+
104+
### Check if ConfigMap is applied
105+
106+
```bash
107+
kubectl describe configmap tcp-services -n ingress-nginx
108+
```
109+
110+
### Check if ingress-nginx controller is using the ConfigMap
111+
112+
```bash
113+
kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml | grep tcp-services
114+
```
115+
116+
You should see a reference to the `tcp-services` ConfigMap.
117+
118+
### Check ingress-nginx controller logs
119+
120+
```bash
121+
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | grep -i tcp
122+
```
123+
124+
### Check if ports are exposed on the Service
125+
126+
```bash
127+
kubectl get svc ingress-nginx-controller -n ingress-nginx
128+
```
129+
130+
Look for ports 1337, 1338, 1339 in the PORT(S) column.
131+

Tiltfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Tiltfile for pyrom local development
2+
allow_k8s_contexts('docker-desktop')
3+
4+
# Build pyrom Docker image with live updates
5+
docker_build('bubthegreat/pyrom',
6+
context='./',
7+
dockerfile='Dockerfile',
8+
live_update=[
9+
sync('./src/', '/pyrom/src/'),
10+
# Restart the server when Python files change
11+
run('pkill -f rom24 || true', trigger=['./src/rom24/']),
12+
],
13+
ignore=[
14+
'./.git/',
15+
'./k8s/',
16+
'./argocd/',
17+
]
18+
)
19+
20+
# Load the Kubernetes manifests using kustomize local overlay
21+
k8s_yaml(kustomize('k8s/overlays/local'))
22+
23+
# Port forward the pyrom deployment
24+
k8s_resource(workload='pyrom-deployment', port_forwards=1337)
25+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
namespace: pyrom-local
5+
6+
resources:
7+
- namespace.yaml
8+
- ../../base
9+
10+
commonLabels:
11+
environment: local
12+
13+
patchesStrategicMerge:
14+
- pyrom-configmap.yaml
15+
- pyrom-pvc-patch.yaml
16+

k8s/overlays/local/namespace.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: pyrom-local
5+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: pyrom-config
5+
data:
6+
PYROM_PORT: "1337"
7+
PYROM_LOG_LEVEL: "DEBUG"
8+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: v1
2+
kind: PersistentVolumeClaim
3+
metadata:
4+
name: pyrom-player-pvc
5+
spec:
6+
storageClassName: hostpath
7+
---
8+
apiVersion: v1
9+
kind: PersistentVolumeClaim
10+
metadata:
11+
name: pyrom-world-pvc
12+
spec:
13+
storageClassName: hostpath
14+
---
15+
apiVersion: v1
16+
kind: PersistentVolumeClaim
17+
metadata:
18+
name: pyrom-system-pvc
19+
spec:
20+
storageClassName: hostpath
21+

src/rom24/instance.py

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -206,43 +206,16 @@ def from_json(data):
206206

207207

208208
def save():
209-
os.makedirs(settings.INSTANCE_DIR, 0o755, True)
210-
filename = os.path.join(settings.INSTANCE_DIR, "list.json")
211-
tmp_dict = {}
212-
for i in global_instances:
213-
if i in players:
214-
pass
215-
else:
216-
tmp_dict[i] = [
217-
global_instances[i].__module__,
218-
global_instances[i].__class__.__name__,
219-
]
220-
with open(filename, "w") as fp:
221-
json.dump(
222-
{"max_instance_id": max_instance_id, "data": tmp_dict},
223-
fp,
224-
default=to_json,
225-
indent=4,
226-
sort_keys=True,
227-
)
228-
229-
for i in areas:
230-
areas[i].save(force=True)
231-
for i in rooms:
232-
rooms[i].save(force=True)
233-
for i in npcs:
234-
npcs[i].save(force=True)
209+
# Only persist players - areas, rooms, NPCs, and items are ephemeral
210+
# This significantly speeds up save times and we're okay with losing
211+
# intermediate mob state on crashes/reboots
212+
213+
# Note: We skip saving the instance list JSON since we're not persisting
214+
# non-player instances anymore
215+
216+
# Only save players
235217
for i in players:
236218
players[i].save(force=True)
237-
for i in items:
238-
it = items[i]
239-
if it.in_living is not None:
240-
continue
241-
if it.in_room is not None:
242-
continue
243-
if it.in_item is not None:
244-
continue
245-
it.save(force=True)
246219

247220

248221
def load():

src/rom24/miniboa/telnet.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,10 @@ def socket_recv(self):
332332
## Did they close the connection?
333333
size = len(data)
334334
if size == 0:
335-
logger.debug("No data recieved, client closed connection")
335+
# Suppress logging for health check probes (connections that close within 1 second)
336+
connection_duration = time.time() - self.connect_time
337+
if connection_duration > 1.0:
338+
logger.debug("No data recieved, client closed connection")
336339
raise ConnectionLost()
337340

338341
## Update some trackers

0 commit comments

Comments
 (0)