Adding fabsi n8n

This commit is contained in:
Moritz Graf 2025-11-16 12:17:57 +01:00
parent 5251eb6185
commit ebeaa2154f
4 changed files with 145 additions and 119 deletions

View File

@ -728,3 +728,13 @@ uvx garth login
#take output, put in in garth_tkomen.txt
kubectl create secret generic garth-token-secret --from-file=GARTH_TOKEN=./garth_token.txt -n n8n
```
## n8n-fabi
```sh
kubectl create ns n8n-fabi
helm upgrade --cleanup-on-fail --install fabi-n8n \
oci://8gears.container-registry.com/library/n8n \
--namespace n8n-fabi --values n8n-fabi/n8n-fabi.secret.yml --version 1.0.15
```

View File

@ -0,0 +1,110 @@
#small deployment with nodeport for local testing or small deployments
image:
repository: n8nio/n8n
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: "stable"
main:
config:
generic:
timezone: Europe/Berlin
n8n:
editor_base_url: https://n8n-fabi.moritzgraf.de
webhook_url: https://n8n-fabi.moritzgraf.de
extra:
node_modules:
- axios
node:
function_allow_builtin: '*'
function_allow_external: '*'
db:
type: postgresdb
postgresdb:
host: db-rw
user: n8n
# password: password is read from cnpg db-app secretKeyRef
# Moritz: Assuming the db-app secret is created by cnpg operator
pool:
size: 10
ssl:
enabled: true
reject_Unauthorized: true
ca_file: "/home/ssl/certs/postgresql/ca.crt"
secret:
n8n:
encryption_key: "ephikoaloVeev7xaiz5sheig9ieZaNgeihaCaiTh5ahqua5Aelanu8eicooy"
extraEnv:
DB_POSTGRESDB_PASSWORD:
valueFrom:
secretKeyRef:
name: db-app
key: password
# Mount the CNPG CA Cert into N8N container
extraVolumeMounts:
- name: db-ca-cert
mountPath: /home/ssl/certs/postgresql
readOnly: true
extraVolumes:
- name: db-ca-cert
secret:
secretName: db-ca
items:
- key: ca.crt
path: ca.crt
resources:
limits:
memory: 2048Mi
requests:
memory: 512Mi
service:
type: NodePort
port: 5678
ingress:
# Enable ingress for home assistant
enabled: true
className: "nginx"
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
hosts:
- host: n8n-fabi.moritzgraf.de
paths:
- /
tls:
- hosts:
- "n8n-fabi.moritzgraf.de"
secretName: n8n-fabi-moritzgraf-de
# cnpg DB cluster request
extraManifests:
- apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: db
spec:
instances: 1
bootstrap:
initdb:
database: n8n
owner: n8n
postgresql:
parameters:
shared_buffers: "64MB"
resources:
requests:
memory: "512Mi"
limits:
memory: "512Mi"
storage:
size: 1Gi

View File

@ -34,48 +34,39 @@ spec:
- name: garth-mcp-server
# Use a Python image version >= 3.13 as requested.
image: python:3.13-slim
workingDir: /app
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
# This command now installs dependencies and directly executes the mounted script.
command: ["/bin/sh", "-c"]
args:
- |
set -e
echo "----> Installing Python dependencies..."
pip install flask garth garth-mcp-server
echo "----> Dependencies installed."
echo "----> Starting Flask server from mounted script."
exec python /app/wrapper.py
args: [
"pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir uv mcp-proxy && \
echo '--- Setup complete, starting server ---' && \
mcp-proxy --host=0.0.0.0 --port=8080 --pass-environment uvx garth-mcp-server"
]
ports:
- containerPort: 5000
- containerPort: 8080
name: http
# Mount the wrapper.py script from the ConfigMap into the container.
volumeMounts:
- name: wrapper-script-volume
mountPath: /app/wrapper.py
subPath: wrapper.py
# Inject the Garmin token securely from the Kubernetes Secret.
envFrom:
- secretRef:
name: garth-token-secret
# Health probes for Kubernetes to manage the pod's lifecycle.
livenessProbe:
httpGet:
path: /healthz
port: 5000
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /healthz
port: 5000
# Increased delay to allow for dependency installation.
initialDelaySeconds: 60
periodSeconds: 10
# Define the volume that will be populated by the ConfigMap.
volumes:
- name: wrapper-script-volume
configMap:
name: garth-wrapper-script
# # Health probes for Kubernetes to manage the pod's lifecycle.
# livenessProbe:
# tcpSocket:
# port: 8080
# initialDelaySeconds: 15
# periodSeconds: 20
# readinessProbe:
# tcpSocket:
# port: 8080
# initialDelaySeconds: 60
# periodSeconds: 10
---
# --- 3. Service to expose the Deployment ---
# This creates a stable internal endpoint for the server.

View File

@ -1,85 +0,0 @@
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: garth-wrapper-script
namespace: n8n
data:
wrapper.py: |
# wrapper.py
import subprocess
import logging
from flask import Flask, request, Response
# Configure basic logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Initialize the Flask application
app = Flask(__name__)
# Define the main endpoint that n8n will call. It only accepts POST requests.
@app.route('/mcp', methods=['POST'])
def mcp_wrapper():
"""
This function receives a POST request from n8n, executes the garth-mcp-server
as a subprocess, pipes the request body to the subprocess's stdin,
captures its stdout, and returns it as the HTTP response.
"""
# Retrieve the raw binary data from the incoming request body.
request_data = request.get_data()
logging.info(f"Received request with {len(request_data)} bytes of data.")
# Define the command to execute the garth-mcp-server.
# 'uvx' is the recommended runner for this package.
command = ['uvx', 'garth-mcp-server']
try:
# Execute the command as a subprocess.
# - input: The data to be sent to the subprocess's stdin.
# - capture_output=True: Captures stdout and stderr.
# - check=True: Raises a CalledProcessError if the command returns a non-zero exit code.
# - timeout: Sets a 60-second timeout to prevent hanging processes.
result = subprocess.run(
command,
input=request_data,
capture_output=True,
check=True,
timeout=60
)
# If the command was successful, log the success and return the captured stdout.
# The content type is set to text/plain to ensure proper handling by clients.
logging.info(f"Subprocess executed successfully. Returning {len(result.stdout)} bytes of stdout.")
return Response(result.stdout, mimetype='text/plain', status=200)
except subprocess.CalledProcessError as e:
# If the subprocess returns a non-zero exit code, it indicates an error.
# Log the error, including the captured stderr for debugging.
error_message = e.stderr.decode('utf-8', errors='ignore')
logging.error(f"Subprocess failed with exit code {e.returncode}. Stderr: {error_message}")
return Response(f"Error executing garth-mcp-server: {error_message}", mimetype='text/plain', status=500)
except subprocess.TimeoutExpired:
# If the subprocess takes longer than the specified timeout.
logging.error("Subprocess timed out after 60 seconds.")
return Response("Error: garth-mcp-server process timed out.", mimetype='text/plain', status=504)
except Exception as e:
# Catch any other unexpected exceptions.
logging.error(f"An unexpected error occurred: {str(e)}")
return Response(f"An unexpected server error occurred: {str(e)}", mimetype='text/plain', status=500)
# Define a simple health check endpoint for Kubernetes liveness and readiness probes.
@app.route('/healthz', methods=['GET'])
def health_check():
"""
A simple endpoint that returns a 200 OK response, indicating the
Flask server is running and responsive.
"""
return Response("OK", status=200)
# Main execution block to run the Flask development server.
# host='0.0.0.0' makes the server accessible from outside the container.
# port=5000 is the port the server will listen on.
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)