initial argo commit
This commit is contained in:
@@ -132,3 +132,5 @@ dist
|
|||||||
|
|
||||||
__pycache__/
|
__pycache__/
|
||||||
.venv/
|
.venv/
|
||||||
|
|
||||||
|
*.xml
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: AppProject
|
||||||
|
metadata: { name: ai-stack, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
destinations: [{ namespace: ai, server: https://kubernetes.default.svc }]
|
||||||
|
sourceRepos: ["*"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: ai-stack, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://git.ion606.com/ion606/ollama-plus
|
||||||
|
targetRevision: main
|
||||||
|
path: apps/children
|
||||||
|
directory: { recurse: true }
|
||||||
|
syncPolicy:
|
||||||
|
automated: { prune: true, selfHeal: true }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: browser, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://your.git/repo.git
|
||||||
|
targetRevision: main
|
||||||
|
path: manifests/browser
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: coderunner, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://git.ion606.com/ion606/ollama-plus
|
||||||
|
targetRevision: main
|
||||||
|
path: manifests/coderunner
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: ollama, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://helm.otwld.com
|
||||||
|
chart: ollama
|
||||||
|
targetRevision: "*"
|
||||||
|
helm:
|
||||||
|
values: |
|
||||||
|
service: { type: ClusterIP }
|
||||||
|
# add gpu values later if your node has one
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: open-webui, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://helm.openwebui.com
|
||||||
|
chart: open-webui
|
||||||
|
targetRevision: "*"
|
||||||
|
helm:
|
||||||
|
values: |
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 5Gi
|
||||||
|
service: { type: ClusterIP }
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
hosts:
|
||||||
|
- host: openwebui.local
|
||||||
|
paths: [{ path: "/", pathType: Prefix }]
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: postgresql, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://charts.bitnami.com/bitnami
|
||||||
|
chart: postgresql
|
||||||
|
targetRevision: "*"
|
||||||
|
helm:
|
||||||
|
values: |
|
||||||
|
architecture: replication
|
||||||
|
auth:
|
||||||
|
username: openwebui
|
||||||
|
password: openwebui-pass
|
||||||
|
database: openwebui_db
|
||||||
|
primary:
|
||||||
|
persistence: { enabled: true, size: 5Gi }
|
||||||
|
readReplicas:
|
||||||
|
replicaCount: 1
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: rag-server, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://git.ion606.com/ion606/ollama-plus
|
||||||
|
targetRevision: main
|
||||||
|
path: manifests/rag-server
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: searxng, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://charts.kubito.dev
|
||||||
|
chart: searxng
|
||||||
|
targetRevision: "*"
|
||||||
|
helm:
|
||||||
|
values: |
|
||||||
|
service: { type: ClusterIP }
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata: { name: tools, namespace: argocd }
|
||||||
|
spec:
|
||||||
|
project: ai-stack
|
||||||
|
destination: { server: https://kubernetes.default.svc, namespace: ai }
|
||||||
|
source:
|
||||||
|
repoURL: https://git.ion606.com/ion606/ollama-plus
|
||||||
|
targetRevision: main
|
||||||
|
path: manifests/tools
|
||||||
|
syncPolicy: { automated: { prune: true, selfHeal: true } }
|
||||||
+2
-2
@@ -30,8 +30,8 @@ RUN mkdir -p /opt/web-ui/tmp /data && chown -R appuser:appuser /opt/web-ui /data
|
|||||||
|
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
# copy default env
|
# # copy default env
|
||||||
COPY .env .env
|
# COPY .env .env
|
||||||
|
|
||||||
EXPOSE 7788
|
EXPOSE 7788
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --retries=5 \
|
HEALTHCHECK --interval=30s --timeout=5s --retries=5 \
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"@kubernetes/client-node": "^1.3.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.3.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@jsep-plugin/assignment": ["@jsep-plugin/assignment@1.3.0", "", { "peerDependencies": { "jsep": "^0.4.0||^1.0.0" } }, "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ=="],
|
||||||
|
|
||||||
|
"@jsep-plugin/regex": ["@jsep-plugin/regex@1.0.4", "", { "peerDependencies": { "jsep": "^0.4.0||^1.0.0" } }, "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg=="],
|
||||||
|
|
||||||
|
"@kubernetes/client-node": ["@kubernetes/client-node@1.3.0", "", { "dependencies": { "@types/js-yaml": "^4.0.1", "@types/node": "^22.0.0", "@types/node-fetch": "^2.6.9", "@types/stream-buffers": "^3.0.3", "form-data": "^4.0.0", "hpagent": "^1.2.0", "isomorphic-ws": "^5.0.0", "js-yaml": "^4.1.0", "jsonpath-plus": "^10.3.0", "node-fetch": "^2.6.9", "openid-client": "^6.1.3", "rfc4648": "^1.3.0", "socks-proxy-agent": "^8.0.4", "stream-buffers": "^3.0.2", "tar-fs": "^3.0.8", "ws": "^8.18.2" } }, "sha512-IE0yrIpOT97YS5fg2QpzmPzm8Wmcdf4ueWMn+FiJSI3jgTTQT1u+LUhoYpdfhdHAVxdrNsaBg2C0UXSnOgMoCQ=="],
|
||||||
|
|
||||||
|
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@24.3.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g=="],
|
||||||
|
|
||||||
|
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
|
||||||
|
|
||||||
|
"@types/stream-buffers": ["@types/stream-buffers@3.0.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw=="],
|
||||||
|
|
||||||
|
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
|
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||||
|
|
||||||
|
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||||
|
|
||||||
|
"b4a": ["b4a@1.7.1", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-ZovbrBV0g6JxK5cGUF1Suby1vLfKjv4RWi8IxoaO/Mon8BDD9I21RxjHFtgQ+kskJqLAVyQZly3uMBui+vhc8Q=="],
|
||||||
|
|
||||||
|
"bare-events": ["bare-events@2.6.1", "", {}, "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g=="],
|
||||||
|
|
||||||
|
"bare-fs": ["bare-fs@4.4.4", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-Q8yxM1eLhJfuM7KXVP3zjhBvtMJCYRByoTT+wHXjpdMELv0xICFJX+1w4c7csa+WZEOsq4ItJ4RGwvzid6m/dw=="],
|
||||||
|
|
||||||
|
"bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="],
|
||||||
|
|
||||||
|
"bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="],
|
||||||
|
|
||||||
|
"bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="],
|
||||||
|
|
||||||
|
"bare-url": ["bare-url@2.2.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA=="],
|
||||||
|
|
||||||
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
||||||
|
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
|
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||||
|
|
||||||
|
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||||
|
|
||||||
|
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||||
|
|
||||||
|
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||||
|
|
||||||
|
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||||
|
|
||||||
|
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||||
|
|
||||||
|
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||||
|
|
||||||
|
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
|
||||||
|
|
||||||
|
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
|
||||||
|
|
||||||
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
||||||
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
|
|
||||||
|
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||||
|
|
||||||
|
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||||
|
|
||||||
|
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||||
|
|
||||||
|
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||||
|
|
||||||
|
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||||
|
|
||||||
|
"hpagent": ["hpagent@1.2.0", "", {}, "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA=="],
|
||||||
|
|
||||||
|
"ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="],
|
||||||
|
|
||||||
|
"isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="],
|
||||||
|
|
||||||
|
"jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="],
|
||||||
|
|
||||||
|
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||||
|
|
||||||
|
"jsep": ["jsep@1.4.0", "", {}, "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw=="],
|
||||||
|
|
||||||
|
"jsonpath-plus": ["jsonpath-plus@10.3.0", "", { "dependencies": { "@jsep-plugin/assignment": "^1.3.0", "@jsep-plugin/regex": "^1.0.4", "jsep": "^1.4.0" }, "bin": { "jsonpath": "bin/jsonpath-cli.js", "jsonpath-plus": "bin/jsonpath-cli.js" } }, "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA=="],
|
||||||
|
|
||||||
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||||
|
|
||||||
|
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
|
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||||
|
|
||||||
|
"oauth4webapi": ["oauth4webapi@3.8.1", "", {}, "sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA=="],
|
||||||
|
|
||||||
|
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||||
|
|
||||||
|
"openid-client": ["openid-client@6.8.0", "", { "dependencies": { "jose": "^6.1.0", "oauth4webapi": "^3.8.1" } }, "sha512-oG1d1nAVhIIE+JSjLS+7E9wY1QOJpZltkzlJdbZ7kEn7Hp3hqur2TEeQ8gLOHoHkhbRAGZJKoOnEQcLOQJuIyg=="],
|
||||||
|
|
||||||
|
"pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
|
||||||
|
|
||||||
|
"rfc4648": ["rfc4648@1.5.4", "", {}, "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg=="],
|
||||||
|
|
||||||
|
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||||
|
|
||||||
|
"socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
|
||||||
|
|
||||||
|
"socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
||||||
|
|
||||||
|
"stream-buffers": ["stream-buffers@3.0.3", "", {}, "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw=="],
|
||||||
|
|
||||||
|
"streamx": ["streamx@2.22.1", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA=="],
|
||||||
|
|
||||||
|
"tar-fs": ["tar-fs@3.1.0", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w=="],
|
||||||
|
|
||||||
|
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||||
|
|
||||||
|
"text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
|
||||||
|
|
||||||
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||||
|
|
||||||
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
||||||
|
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||||
|
|
||||||
|
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
|
"@kubernetes/client-node/@types/node": ["@types/node@22.18.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw=="],
|
||||||
|
|
||||||
|
"@kubernetes/client-node/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
+101
-47
@@ -1,5 +1,6 @@
|
|||||||
import http from "node:http";
|
import http from "node:http";
|
||||||
import { spawn } from "node:child_process";
|
import * as k8s from "@kubernetes/client-node";
|
||||||
|
|
||||||
|
|
||||||
const PORT = Number(process.env.PORT || 8787);
|
const PORT = Number(process.env.PORT || 8787);
|
||||||
const HOST = "0.0.0.0";
|
const HOST = "0.0.0.0";
|
||||||
@@ -41,8 +42,14 @@ type fileType = {
|
|||||||
content: string
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// docker binary (or set DOCKER_BIN=podman)
|
const NS = process.env.NAMESPACE || "ai";
|
||||||
const DOCKER_BIN = process.env.DOCKER_BIN || "docker";
|
|
||||||
|
const kc = new k8s.KubeConfig();
|
||||||
|
kc.loadFromDefault(); // in-cluster uses serviceaccount
|
||||||
|
|
||||||
|
const batch = kc.makeApiClient(k8s.BatchV1Api),
|
||||||
|
core = kc.makeApiClient(k8s.CoreV1Api);
|
||||||
|
|
||||||
|
|
||||||
// basic openapi for open webui
|
// basic openapi for open webui
|
||||||
const OPENAPI = {
|
const OPENAPI = {
|
||||||
@@ -123,7 +130,7 @@ const OPENAPI = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function sendJson(res, status, obj) {
|
function sendJson(res: any, status: number, obj: any) {
|
||||||
const body = JSON.stringify(obj);
|
const body = JSON.stringify(obj);
|
||||||
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
||||||
res.end(body);
|
res.end(body);
|
||||||
@@ -175,63 +182,110 @@ async function ensureImage(spec: langObj) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForJobPod(core: k8s.CoreV1Api, jobName: string): Promise<string> {
|
||||||
|
const labelSelector = `job-name=${jobName}`;
|
||||||
|
for (; ;) {
|
||||||
|
const pods = await core.listNamespacedPod({ namespace: NS, labelSelector });
|
||||||
|
const pod = pods.items.find((p) => p.status?.phase === "Running" || p.status?.phase === "Succeeded" || p.status?.phase === "Failed");
|
||||||
|
if (pod?.metadata?.name) return pod.metadata.name;
|
||||||
|
await new Promise((r) => setTimeout(r, 400));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function waitForCompletionAndLogs(core: k8s.CoreV1Api, podName: string): Promise<{ status: string; stdout: string; stderr: string; }> {
|
||||||
|
for (; ;) {
|
||||||
|
const readReq = {
|
||||||
|
name: podName,
|
||||||
|
namespace: NS
|
||||||
|
},
|
||||||
|
p = await core.readNamespacedPod(readReq),
|
||||||
|
phase = p.status?.phase ?? "Pending";
|
||||||
|
|
||||||
|
if (phase === "Succeeded" || phase === "Failed") {
|
||||||
|
const logs = await core.readNamespacedPodLog(readReq);
|
||||||
|
// stderr is not separated by the api; you can split by stream if needed
|
||||||
|
return { status: phase, stdout: logs, stderr: "" };
|
||||||
|
};
|
||||||
|
await new Promise((r) => setTimeout(r, 500));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
async function runInContainer({ language, code, args = [], files = [] }: {
|
async function runInContainer({ language, code, args = [], files = [] }: {
|
||||||
language: string,
|
language: string, code: string, args: string[], files: fileType[]
|
||||||
code: string,
|
|
||||||
args: string[],
|
|
||||||
files: fileType[]
|
|
||||||
}) {
|
}) {
|
||||||
if (!LANGS[language]) throw new Error(`language not allowed: ${language}`);
|
if (!(language in LANGS)) throw new Error(`language not allowed: ${language}`);
|
||||||
const spec = LANGS[language];
|
const spec = LANGS[language as keyof typeof LANGS];
|
||||||
|
|
||||||
await ensureImage(spec);
|
// build the same shell script you already use
|
||||||
|
|
||||||
// build the Docker args
|
|
||||||
const dockerArgs = [
|
|
||||||
"run", "--rm",
|
|
||||||
"--network=none", "--read-only",
|
|
||||||
"--pids-limit=256",
|
|
||||||
"--cpus=1", "--memory=512m",
|
|
||||||
"--cap-drop=ALL", "--security-opt", "no-new-privileges",
|
|
||||||
"--tmpfs", "/work:rw,exec,size=64m",
|
|
||||||
"-w", "/work",
|
|
||||||
"--pull=never",
|
|
||||||
spec.image
|
|
||||||
];
|
|
||||||
|
|
||||||
// inside the container, write the files and run code
|
|
||||||
const script = [
|
const script = [
|
||||||
// write the main file using base64
|
|
||||||
`echo ${JSON.stringify(Buffer.from(code, "utf8").toString("base64"))} | base64 -d > ${spec.filename}`,
|
`echo ${JSON.stringify(Buffer.from(code, "utf8").toString("base64"))} | base64 -d > ${spec.filename}`,
|
||||||
// write any extra files using base64
|
|
||||||
...files.flatMap((f) => [
|
...files.flatMap((f) => [
|
||||||
`mkdir -p "$(dirname "${f.path}")"`,
|
`mkdir -p "$(dirname "${f.path}")"`,
|
||||||
`echo ${JSON.stringify(Buffer.from(f.content, "utf8").toString("base64"))} | base64 -d > "${f.path}"`,
|
`echo ${JSON.stringify(Buffer.from(f.content, "utf8").toString("base64"))} | base64 -d > "${f.path}"`,
|
||||||
]),
|
]),
|
||||||
// run it
|
|
||||||
`${spec.run.join(' ')} ${args.map((a) => JSON.stringify(a)).join(' ')}`
|
`${spec.run.join(' ')} ${args.map((a) => JSON.stringify(a)).join(' ')}`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
dockerArgs.push("sh", "-lc", script);
|
const uid = crypto.randomUUID().slice(0, 8);
|
||||||
|
const jobName = `coderun-${uid}`;
|
||||||
|
|
||||||
const result = await new Promise((resolve) => {
|
// create a short-lived Job with tight security and resource caps
|
||||||
const child = spawn(DOCKER_BIN, dockerArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
const job: k8s.V1Job = {
|
||||||
let stdout = "", stderr = "";
|
apiVersion: "batch/v1",
|
||||||
|
kind: "Job",
|
||||||
|
metadata: { name: jobName, namespace: NS },
|
||||||
|
spec: {
|
||||||
|
ttlSecondsAfterFinished: 300, // auto-clean once done
|
||||||
|
backoffLimit: 0, // no retries
|
||||||
|
activeDeadlineSeconds: 25, // mirrors your 25s timeout
|
||||||
|
template: {
|
||||||
|
metadata: { labels: { app: "coderunner-task" } },
|
||||||
|
spec: {
|
||||||
|
restartPolicy: "Never",
|
||||||
|
securityContext: {
|
||||||
|
runAsNonRoot: true,
|
||||||
|
seccompProfile: { type: "RuntimeDefault" }
|
||||||
|
},
|
||||||
|
containers: [{
|
||||||
|
name: "task",
|
||||||
|
image: spec.image,
|
||||||
|
command: ["sh", "-lc", script],
|
||||||
|
resources: {
|
||||||
|
requests: { cpu: "1", memory: "512Mi" },
|
||||||
|
limits: { cpu: "1", memory: "512Mi" }
|
||||||
|
},
|
||||||
|
securityContext: {
|
||||||
|
allowPrivilegeEscalation: false,
|
||||||
|
readOnlyRootFilesystem: true,
|
||||||
|
capabilities: { drop: ["ALL"] }
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
await batch.createNamespacedJob({
|
||||||
child.kill("SIGKILL");
|
namespace: NS,
|
||||||
resolve({ stdout, stderr: stderr + "\n[killed: timeout]", exitCode: 137, timedOut: true });
|
body: job
|
||||||
}, 25_000);
|
|
||||||
|
|
||||||
child.stdout.on("data", (d) => { stdout += d.toString(); });
|
|
||||||
child.stderr.on("data", (d) => { stderr += d.toString(); });
|
|
||||||
child.on("close", (code) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
resolve({ stdout, stderr, exitCode: code ?? 1, timedOut: false });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
// wait for pod to complete, then get logs
|
||||||
|
const podName = await waitForJobPod(core, jobName);
|
||||||
|
const { status, stdout, stderr } = await waitForCompletionAndLogs(core, podName);
|
||||||
|
|
||||||
|
// delete job for hygiene (ttl also cleans it eventually)
|
||||||
|
try {
|
||||||
|
await batch.deleteNamespacedJob({ namespace: NS, propagationPolicy: "Background", name: jobName });
|
||||||
|
} catch (err) { console.error(err); };
|
||||||
|
|
||||||
|
return {
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
exitCode: status === "Succeeded" ? 0 : 1,
|
||||||
|
timedOut: status === "Failed"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = http.createServer(async (req, res) => {
|
const server = http.createServer(async (req, res) => {
|
||||||
@@ -257,7 +311,7 @@ const server = http.createServer(async (req, res) => {
|
|||||||
const payload = JSON.parse(body || "{}");
|
const payload = JSON.parse(body || "{}");
|
||||||
const out = await runInContainer(payload);
|
const out = await runInContainer(payload);
|
||||||
sendJson(res, 200, out);
|
sendJson(res, 200, out);
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
sendJson(res, 400, { error: String(e?.message || e) });
|
sendJson(res, 400, { error: String(e?.message || e) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -266,7 +320,7 @@ const server = http.createServer(async (req, res) => {
|
|||||||
|
|
||||||
res.writeHead(404);
|
res.writeHead(404);
|
||||||
res.end("not found");
|
res.end("not found");
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
sendJson(res, 500, { error: String(e?.message || e) });
|
sendJson(res, 500, { error: String(e?.message || e) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Generated
-29
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "coderunner",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^24.3.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/node": {
|
|
||||||
"version": "24.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz",
|
|
||||||
"integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"undici-types": "~7.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
|
||||||
"version": "7.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
|
||||||
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.3.1"
|
"@types/node": "^24.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@kubernetes/client-node": "^1.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata: { name: browser, namespace: ai }
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector: { matchLabels: { app: browser } }
|
||||||
|
template:
|
||||||
|
metadata: { labels: { app: browser } }
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: browser
|
||||||
|
image: docker.io/ion606/browser:latest
|
||||||
|
ports: [{ containerPort: 7788 }]
|
||||||
|
env:
|
||||||
|
- { name: WEBUI_IP, value: "0.0.0.0" }
|
||||||
|
- { name: WEBUI_PORT, value: "7788" }
|
||||||
|
resources:
|
||||||
|
requests: { cpu: "250m", memory: "256Mi" }
|
||||||
|
limits: { cpu: "1", memory: "1Gi" } # hard cap
|
||||||
|
readinessProbe:
|
||||||
|
{
|
||||||
|
httpGet: { path: "/", port: 7788 },
|
||||||
|
initialDelaySeconds: 5,
|
||||||
|
periodSeconds: 10,
|
||||||
|
}
|
||||||
|
livenessProbe:
|
||||||
|
{
|
||||||
|
httpGet: { path: "/", port: 7788 },
|
||||||
|
initialDelaySeconds: 15,
|
||||||
|
periodSeconds: 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata: { name: browser, namespace: ai }
|
||||||
|
spec:
|
||||||
|
selector: { app: browser }
|
||||||
|
ports: [{ name: http, port: 7788, targetPort: 7788 }]
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata: { name: coderunner, namespace: ai }
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector: { matchLabels: { app: coderunner } }
|
||||||
|
template:
|
||||||
|
metadata: { labels: { app: coderunner } }
|
||||||
|
spec:
|
||||||
|
serviceAccountName: coderunner-sa
|
||||||
|
containers:
|
||||||
|
- name: coderunner
|
||||||
|
image: docker.io/ion606/coderunner:latest
|
||||||
|
ports: [{ containerPort: 8787 }]
|
||||||
|
env:
|
||||||
|
- { name: PORT, value: "8787" }
|
||||||
|
- {
|
||||||
|
name: NAMESPACE,
|
||||||
|
valueFrom:
|
||||||
|
{ fieldRef: { fieldPath: metadata.namespace } },
|
||||||
|
}
|
||||||
|
readinessProbe:
|
||||||
|
{
|
||||||
|
httpGet: { path: "/openapi.json", port: 8787 },
|
||||||
|
initialDelaySeconds: 5,
|
||||||
|
periodSeconds: 10,
|
||||||
|
}
|
||||||
|
livenessProbe:
|
||||||
|
{
|
||||||
|
httpGet: { path: "/openapi.json", port: 8787 },
|
||||||
|
initialDelaySeconds: 15,
|
||||||
|
periodSeconds: 20,
|
||||||
|
}
|
||||||
|
resources:
|
||||||
|
requests: { cpu: "100m", memory: "128Mi" }
|
||||||
|
limits: { cpu: "500m", memory: "512Mi" }
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata: { name: coderunner, namespace: ai }
|
||||||
|
spec:
|
||||||
|
selector: { app: coderunner }
|
||||||
|
ports: [{ name: http, port: 8787, targetPort: 8787 }]
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata: { name: coderunner-sa, namespace: ai }
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata: { name: coderunner-job-role, namespace: ai }
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["batch"]
|
||||||
|
resources: ["jobs"]
|
||||||
|
verbs: ["create", "get", "list", "watch", "delete"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods", "pods/log"]
|
||||||
|
verbs: ["get", "list", "watch", "delete"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata: { name: coderunner-job-rb, namespace: ai }
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: coderunner-sa
|
||||||
|
namespace: ai
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: coderunner-job-role
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata: { name: default-deny-all, namespace: ai }
|
||||||
|
spec:
|
||||||
|
podSelector: {}
|
||||||
|
policyTypes: ["Ingress", "Egress"]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata: { name: rag-server, namespace: ai }
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector: { matchLabels: { app: rag-server } }
|
||||||
|
template:
|
||||||
|
metadata: { labels: { app: rag-server } }
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: rag-server
|
||||||
|
image: docker.io/ion606/rag-server:latest
|
||||||
|
ports: [{ containerPort: 8788 }]
|
||||||
|
env:
|
||||||
|
- { name: PORT, value: "8788" }
|
||||||
|
- {
|
||||||
|
name: OLLAMA_BASE,
|
||||||
|
value: "http://ollama.ai.svc.cluster.local:11434",
|
||||||
|
}
|
||||||
|
- { name: OLLAMA_CHAT_MODEL, value: "llama3.1" }
|
||||||
|
- { name: OLLAMA_EMBED_MODEL, value: "nomic-embed-text" }
|
||||||
|
readinessProbe:
|
||||||
|
{ httpGet: { path: "/openapi.json", port: 8788 } }
|
||||||
|
livenessProbe:
|
||||||
|
{
|
||||||
|
httpGet: { path: "/", port: 8788 },
|
||||||
|
initialDelaySeconds: 10,
|
||||||
|
}
|
||||||
|
resources:
|
||||||
|
requests: { cpu: "200m", memory: "256Mi" }
|
||||||
|
limits: { cpu: "1", memory: "1Gi" }
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata: { name: rag-server, namespace: ai }
|
||||||
|
spec:
|
||||||
|
selector: { app: rag-server }
|
||||||
|
ports: [{ name: http, port: 8788, targetPort: 8788 }]
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata: { name: tools, namespace: ai }
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector: { matchLabels: { app: tools } }
|
||||||
|
template:
|
||||||
|
metadata: { labels: { app: tools } }
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: tools
|
||||||
|
image: docker.io/ion606/tools:latest
|
||||||
|
ports: [{ containerPort: 1331 }]
|
||||||
|
env:
|
||||||
|
- { name: HOST, value: "0.0.0.0" }
|
||||||
|
- { name: PORT, value: "1331" }
|
||||||
|
- { name: ROKU_IP, value: "192.0.2.10" }
|
||||||
|
readinessProbe:
|
||||||
|
{ httpGet: { path: "/roku/openapi.json", port: 1331 } }
|
||||||
|
livenessProbe:
|
||||||
|
{
|
||||||
|
httpGet: { path: "/roku/openapi.json", port: 1331 },
|
||||||
|
initialDelaySeconds: 10,
|
||||||
|
}
|
||||||
|
resources:
|
||||||
|
requests: { cpu: "100m", memory: "128Mi" }
|
||||||
|
limits: { cpu: "500m", memory: "512Mi" }
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata: { name: tools, namespace: ai }
|
||||||
|
spec:
|
||||||
|
selector: { app: tools }
|
||||||
|
ports: [{ name: http, port: 1331, targetPort: 1331 }]
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# browser
|
||||||
|
docker build -t ion606/browser:latest ./browser;
|
||||||
|
docker push ion606/browser:latest;
|
||||||
|
|
||||||
|
# coderunner
|
||||||
|
docker build -t ion606/coderunner:latest ./coderunner;
|
||||||
|
docker push ion606/coderunner:latest;
|
||||||
|
|
||||||
|
# rag-server
|
||||||
|
docker build -t ion606/rag-server:latest ./rag-server;
|
||||||
|
docker push ion606/rag-server:latest;
|
||||||
|
|
||||||
|
# tools
|
||||||
|
docker build -t ion606/tools:latest ./tools;
|
||||||
|
docker push ion606/tools:latest;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail;
|
||||||
|
|
||||||
|
# cluster + ingress addons (nginx + ingress-dns)
|
||||||
|
# https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/
|
||||||
|
# https://minikube.sigs.k8s.io/docs/handbook/addons/ingress-dns/
|
||||||
|
minikube start --driver=docker || true;
|
||||||
|
minikube addons enable ingress;
|
||||||
|
minikube addons enable ingress-dns;
|
||||||
|
|
||||||
|
# namespaces
|
||||||
|
kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f -;
|
||||||
|
kubectl create namespace ai --dry-run=client -o yaml | kubectl apply -f -;
|
||||||
|
|
||||||
|
# install argo cd (stable manifest)
|
||||||
|
# https://argo-cd.readthedocs.io/en/stable/operator-manual/installation/
|
||||||
|
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml;
|
||||||
|
|
||||||
|
# WAIT for argocd core components to be ready enough to accept apps (slow piece of-)
|
||||||
|
kubectl rollout status deploy/argocd-server -n argocd --timeout=180s || true;
|
||||||
|
kubectl rollout status deploy/argocd-repo-server -n argocd --timeout=180s || true;
|
||||||
|
kubectl rollout status deploy/argocd-application-controller -n argocd --timeout=180s || true;
|
||||||
|
|
||||||
|
# bootstrap this repo
|
||||||
|
# NOTE: creates the child Applications in apps/children/*
|
||||||
|
kubectl apply -n argocd -f apps/0-project-and-root.yaml;
|
||||||
|
|
||||||
|
# port-forward argocd ui
|
||||||
|
echo "";
|
||||||
|
echo "argocd initial admin password (username 'admin'):";
|
||||||
|
kubectl -n argocd get secret argocd-initial-admin-secret \
|
||||||
|
-o jsonpath='{.data.password}' | base64 -d; echo "";
|
||||||
|
echo "";
|
||||||
|
echo "port-forwarding argocd ui to https://localhost:8443 (ctrl+c to stop) ...";
|
||||||
|
kubectl -n argocd port-forward svc/argocd-server 8443:443;
|
||||||
Reference in New Issue
Block a user