feat(bot): 🚧 Start migrating to k8s.
This commit is contained in:
@@ -123,6 +123,7 @@ celerybeat.pid
|
|||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
|
.env.shared
|
||||||
.venv
|
.venv
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
|
|||||||
Vendored
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"conventionalCommits.scopes": ["bot", "db"]
|
"conventionalCommits.scopes": ["bot", "db"],
|
||||||
|
"yaml.schemas": {
|
||||||
|
"kubernetes://schema/apps/v1%40deployment": "file:///c%3A/Users/rafab/Documents/Repos/Payphone-1/k8s.yml"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,3 @@ RUN uv sync --no-dev
|
|||||||
RUN uv add uvloop
|
RUN uv add uvloop
|
||||||
|
|
||||||
ENTRYPOINT [ "/bin/bash" ]
|
ENTRYPOINT [ "/bin/bash" ]
|
||||||
CMD [ "run.sh" ]
|
|
||||||
|
|||||||
@@ -1 +1,8 @@
|
|||||||
# Payphone
|
# Payphone
|
||||||
|
|
||||||
|
Payphone is deployed using Kubernetes. To set up a local kubernetes cluster, you can use `minikube`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# To create a local cluster of 3 nodes:
|
||||||
|
minikube start --network-plugin=cni --cni=calico --nodes=3
|
||||||
|
```
|
||||||
@@ -10,6 +10,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- database
|
- database
|
||||||
- redis
|
- redis
|
||||||
|
command: run.sh
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: discord-bot
|
||||||
|
labels:
|
||||||
|
app: discord-bot
|
||||||
|
spec:
|
||||||
|
replicas: 11
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: discord-bot
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: discord-bot
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: bot
|
||||||
|
image: nekidev/payphone:latest
|
||||||
|
env:
|
||||||
|
- name: IS_DEBUG
|
||||||
|
value: "false"
|
||||||
|
- name: SHARD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: TOTAL_SHARDS
|
||||||
|
value: "11"
|
||||||
|
- name: REDIS_HOST
|
||||||
|
value: "redis"
|
||||||
|
- name: REDIS_PORT
|
||||||
|
value: "6379"
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
requests:
|
||||||
|
cpu: "250m"
|
||||||
|
memory: "256Mi"
|
||||||
|
restartPolicy: Always
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: citus-coordinator
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: citus
|
||||||
|
role: coordinator
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: citus
|
||||||
|
role: coordinator
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: coordinator
|
||||||
|
image: citusdata/citus:latest
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
value: "payphone"
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
value: "payphone"
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
value: "payphone"
|
||||||
|
volumeMounts:
|
||||||
|
- name: citus-coordinator-storage
|
||||||
|
mountPath: /var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "1"
|
||||||
|
limits:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "2"
|
||||||
|
volumes:
|
||||||
|
- name: citus-coordinator-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: citus-coordinator-pvc
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: citus-coordinator
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: citus
|
||||||
|
role: coordinator
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
targetPort: 5432
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: citus-coordinator-pvc
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi # Change this size as needed
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: citus-worker-pvc
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi # Change this size as needed
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: citus-worker
|
||||||
|
spec:
|
||||||
|
replicas: 2 # Increase as needed for more workers
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: citus
|
||||||
|
role: worker
|
||||||
|
serviceName: citus-worker-headless
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: citus
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: worker
|
||||||
|
image: citusdata/citus:latest
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
value: "payphone"
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
value: "payphone"
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
value: "payphone"
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "1"
|
||||||
|
limits:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "2"
|
||||||
|
volumeMounts:
|
||||||
|
- name: citus-worker-storage
|
||||||
|
mountPath: /var/lib/postgresql/data
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: citus-worker-storage
|
||||||
|
spec:
|
||||||
|
accessModes: [ "ReadWriteOnce" ]
|
||||||
|
storageClassName: "standard" # Update based on your storage class
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi # Size per worker
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: citus-worker-headless
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: citus
|
||||||
|
role: worker
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
targetPort: 5432
|
||||||
|
clusterIP: None # Defines the service as headless, allowing direct pod DNS entries
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: redis
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: redis
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: redis
|
||||||
|
image: redis:7.4.1-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 6379
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "1"
|
||||||
|
limits:
|
||||||
|
memory: "2Gi"
|
||||||
|
cpu: "1"
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: redis
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 6379
|
||||||
|
targetPort: 6379
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
45.134.39.59
|
||||||
|
2.56.246.33
|
||||||
+29
-21
@@ -28,9 +28,6 @@ from payphone.core import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
logging.setup()
|
|
||||||
|
|
||||||
|
|
||||||
class Payphone(commands.AutoShardedBot):
|
class Payphone(commands.AutoShardedBot):
|
||||||
shutdown: managers.ValueProxy[bool]
|
shutdown: managers.ValueProxy[bool]
|
||||||
"""A value proxy that connects with all other shard processes.
|
"""A value proxy that connects with all other shard processes.
|
||||||
@@ -74,11 +71,34 @@ class Payphone(commands.AutoShardedBot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def on_ready(self) -> None:
|
async def on_ready(self) -> None:
|
||||||
"""Runs when the bot has finished initializing.
|
"""Runs when the bot has finished initializing."""
|
||||||
"""
|
|
||||||
|
|
||||||
self.log.info("Ready! Logged in as %s.", self.user.display_name)
|
self.log.info("Ready! Logged in as %s.", self.user.display_name)
|
||||||
|
|
||||||
|
async def set_presence(self, shard_id: int) -> None:
|
||||||
|
"""Sets the presence of the bot back to online.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shard_id (int): The ID of the shard to set the status for.
|
||||||
|
"""
|
||||||
|
|
||||||
|
await self.change_presence(
|
||||||
|
activity=discord.CustomActivity(
|
||||||
|
name=f"Listening to p.help | Shard {shard_id + 1}"
|
||||||
|
),
|
||||||
|
status=discord.Status.online,
|
||||||
|
shard_id=shard_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def on_shard_connect(self, shard_id: int) -> None:
|
||||||
|
"""Runs when the shard connects.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shard_id (int): The ID of the shard that connected.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.log.info("Shard has connected.")
|
||||||
|
|
||||||
async def on_shard_ready(self, shard_id: int) -> None:
|
async def on_shard_ready(self, shard_id: int) -> None:
|
||||||
"""Sets the activity for the shard.
|
"""Sets the activity for the shard.
|
||||||
|
|
||||||
@@ -86,15 +106,9 @@ class Payphone(commands.AutoShardedBot):
|
|||||||
shard_id (int): The ID of the initialized shard.
|
shard_id (int): The ID of the initialized shard.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await self.change_presence(
|
self.log.info("Shard is ready.")
|
||||||
activity=discord.CustomActivity(
|
|
||||||
name=f"Listening to p.help | Shard {shard_id + 1}"
|
|
||||||
),
|
|
||||||
status=discord.Status.online,
|
|
||||||
shard_id=shard_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.log.info("Shard %s is ready!", shard_id)
|
await self.set_presence(shard_id)
|
||||||
|
|
||||||
async def on_shard_resumed(self, shard_id: int) -> None:
|
async def on_shard_resumed(self, shard_id: int) -> None:
|
||||||
"""Sets the activity for the shard.
|
"""Sets the activity for the shard.
|
||||||
@@ -103,15 +117,9 @@ class Payphone(commands.AutoShardedBot):
|
|||||||
shard_id (int): The ID of the resumed shard.
|
shard_id (int): The ID of the resumed shard.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await self.change_presence(
|
self.log.info("Shard has resumed.")
|
||||||
activity=discord.CustomActivity(
|
|
||||||
name=f"Listening to p.help | Shard {shard_id + 1}"
|
|
||||||
),
|
|
||||||
status=discord.Status.online,
|
|
||||||
shard_id=shard_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.log.info("Shard %s was resumed!", shard_id)
|
await self.set_presence(shard_id)
|
||||||
|
|
||||||
async def get_or_fetch_channel(
|
async def get_or_fetch_channel(
|
||||||
self, channel_id: int
|
self, channel_id: int
|
||||||
|
|||||||
+29
-10
@@ -3,17 +3,36 @@ import logging
|
|||||||
from rich import logging as rlogging
|
from rich import logging as rlogging
|
||||||
|
|
||||||
|
|
||||||
FORMAT = "%(message)s"
|
|
||||||
|
|
||||||
handler = rlogging.RichHandler(level=logging.INFO, rich_tracebacks=True)
|
|
||||||
|
|
||||||
formatter = logging.Formatter(FORMAT)
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
|
|
||||||
logger = logging.getLogger("payphone")
|
logger = logging.getLogger("payphone")
|
||||||
|
|
||||||
|
|
||||||
def setup():
|
def _get_handler(process_name: str, module: str | None = None) -> None:
|
||||||
"""Sets up pycord logging."""
|
"""Generates a handler for the given process name and module.
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG, handlers=[handler])
|
Args:
|
||||||
|
process_name (str): The process name.
|
||||||
|
module (str | None, optional): The module inside the process. Defaults to None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
fmt = f"[blue][{process_name}{f": [green]{module}[/green]" if module is not None else ""}][/blue] %(message)s"
|
||||||
|
formatter = logging.Formatter(fmt)
|
||||||
|
|
||||||
|
handler = rlogging.RichHandler(
|
||||||
|
level=logging.INFO, markup=True, rich_tracebacks=True
|
||||||
|
)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
def setup(process_name: str):
|
||||||
|
"""Sets up pycord logging.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
process_name (str): The name of the process.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG, handlers=[_get_handler(process_name)])
|
||||||
|
|
||||||
|
logging.getLogger("asyncio").addHandler(_get_handler(process_name, "asyncio"))
|
||||||
|
logging.getLogger("discord").addHandler(_get_handler(process_name, "discord"))
|
||||||
|
|||||||
@@ -117,9 +117,12 @@ async def loop() -> None:
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from payphone.db import setup
|
from payphone.db import setup
|
||||||
|
from payphone.core import logging
|
||||||
|
|
||||||
await setup.init()
|
await setup.init()
|
||||||
|
|
||||||
|
logging.setup("Flushing")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ requires-python = ">=3.12"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aerich==0.7.1",
|
"aerich==0.7.1",
|
||||||
"aiohttp>=3.10.10",
|
"aiohttp>=3.10.10",
|
||||||
"click>=8.1.7",
|
|
||||||
"colorthief>=0.2.1",
|
"colorthief>=0.2.1",
|
||||||
"httpx>=0.27.2",
|
"httpx>=0.27.2",
|
||||||
"orjson>=3.10.7",
|
"orjson>=3.10.7",
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ from datetime import datetime, timezone
|
|||||||
from payphone.core import logging
|
from payphone.core import logging
|
||||||
|
|
||||||
|
|
||||||
logging.setup()
|
|
||||||
|
|
||||||
log = logging.logger
|
log = logging.logger
|
||||||
|
|
||||||
|
|
||||||
@@ -28,13 +26,15 @@ def shard(
|
|||||||
|
|
||||||
from payphone.db import setup
|
from payphone.db import setup
|
||||||
from payphone.bot import bot
|
from payphone.bot import bot
|
||||||
from payphone.core import config
|
from payphone.core import config, logging
|
||||||
|
|
||||||
bot.shard_ids = [shard_id]
|
bot.shard_ids = [shard_id]
|
||||||
bot.shard_count = shard_count
|
bot.shard_count = shard_count
|
||||||
bot.shutdown = shutdown
|
bot.shutdown = shutdown
|
||||||
bot.scheduled_shutdown = scheduled_shutdown
|
bot.scheduled_shutdown = scheduled_shutdown
|
||||||
|
|
||||||
|
logging.setup(f"Shard {shard_id}")
|
||||||
|
|
||||||
log.info("Running shard %s with default loop.", shard_id)
|
log.info("Running shard %s with default loop.", shard_id)
|
||||||
|
|
||||||
bot.loop.set_debug(True)
|
bot.loop.set_debug(True)
|
||||||
@@ -84,6 +84,8 @@ if __name__ == "__main__":
|
|||||||
from payphone.db import setup, models
|
from payphone.db import setup, models
|
||||||
from payphone.core import config
|
from payphone.core import config
|
||||||
|
|
||||||
|
logging.setup("Payphone")
|
||||||
|
|
||||||
redis = aredis.Redis(host="localhost" if config.IS_DEBUG else "redis")
|
redis = aredis.Redis(host="localhost" if config.IS_DEBUG else "redis")
|
||||||
|
|
||||||
async def init():
|
async def init():
|
||||||
|
|||||||
@@ -392,7 +392,6 @@ source = { virtual = "." }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aerich" },
|
{ name = "aerich" },
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
{ name = "click" },
|
|
||||||
{ name = "colorthief" },
|
{ name = "colorthief" },
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
{ name = "orjson" },
|
{ name = "orjson" },
|
||||||
@@ -407,7 +406,6 @@ dependencies = [
|
|||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "aerich", specifier = "==0.7.1" },
|
{ name = "aerich", specifier = "==0.7.1" },
|
||||||
{ name = "aiohttp", specifier = ">=3.10.10" },
|
{ name = "aiohttp", specifier = ">=3.10.10" },
|
||||||
{ name = "click", specifier = ">=8.1.7" },
|
|
||||||
{ name = "colorthief", specifier = ">=0.2.1" },
|
{ name = "colorthief", specifier = ">=0.2.1" },
|
||||||
{ name = "httpx", specifier = ">=0.27.2" },
|
{ name = "httpx", specifier = ">=0.27.2" },
|
||||||
{ name = "orjson", specifier = ">=3.10.7" },
|
{ name = "orjson", specifier = ">=3.10.7" },
|
||||||
|
|||||||
Reference in New Issue
Block a user