diff --git a/apolo/configmaps/configmap-coredns.yaml b/apolo/configmaps/configmap-coredns.yaml index 8b94610..f4e5657 100644 --- a/apolo/configmaps/configmap-coredns.yaml +++ b/apolo/configmaps/configmap-coredns.yaml @@ -18,6 +18,13 @@ data: 192.168.200.13 muc.chat.apolo.c2et.net 192.168.200.12 streaming.apolo.c2et.net 192.168.200.14 meeting.apolo.c2et.net + + # === ARGOS (videovigilancia) === + 192.168.200.15 mqtt.argos.interna + 192.168.200.16 mediamtx.argos.interna + 192.168.200.10 s3.argos.interna + 192.168.200.10 minio.argos.interna + 192.168.200.10 panel.argos.c2et.net fallthrough } forward . /etc/resolv.conf diff --git a/argos/certs/certificate-argos-panel.yaml b/argos/certs/certificate-argos-panel.yaml new file mode 100644 index 0000000..002bfee --- /dev/null +++ b/argos/certs/certificate-argos-panel.yaml @@ -0,0 +1,12 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: panel-argos-cert + namespace: argos-core +spec: + secretName: panel-argos-tls + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer + dnsNames: + - panel.argos.c2et.net diff --git a/argos/configmaps/configmap-mediamtx.yaml b/argos/configmaps/configmap-mediamtx.yaml new file mode 100644 index 0000000..3285b67 --- /dev/null +++ b/argos/configmaps/configmap-mediamtx.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mediamtx-config + namespace: argos-core +data: + mediamtx.yml: | + logLevel: info + rtsp: yes + rtmp: no + hls: no + webrtc: yes + paths: + all: + sourceOnDemand: yes diff --git a/argos/configmaps/configmap-mosquitto.yaml b/argos/configmaps/configmap-mosquitto.yaml new file mode 100644 index 0000000..9e55849 --- /dev/null +++ b/argos/configmaps/configmap-mosquitto.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mosquitto-conf + namespace: argos-core +data: + mosquitto.conf: | + listener 1883 0.0.0.0 + allow_anonymous true diff --git a/argos/configmaps/configmap-orchestrator.yaml b/argos/configmaps/configmap-orchestrator.yaml new file mode 100644 index 0000000..76f0d7f --- /dev/null +++ b/argos/configmaps/configmap-orchestrator.yaml @@ -0,0 +1,156 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argos-orchestrator-config + namespace: argos-core +data: + settings.yaml: | + mqtt: + host: mqtt.argos.interna + port: 1883 + topic: frigate/events + client_id: argos-core + minio: + bucket: argos + region: us-east-1 + edges: + - name: rpi01 + base_url: http://10.20.0.10:5000 # URL del Frigate del edge (VPN). 5000 por defecto. + api_token: "" # si usas auth, ponlo aquí + cameras: + - name: cam1 + rtsp_main: rtsp://10.20.0.10:8554/cam1_main + path_mtx: cam1 # para abrir live en MediaMTX: /?path=cam1 + app.py: | + import os, json, time, datetime, sqlite3, subprocess, yaml, requests + from pathlib import Path + from urllib.parse import urljoin + import paho.mqtt.client as mqtt + from minio import Minio + + CFG_PATH="/app/settings.yaml" + DB_PATH="/data/argos.db" + TMP="/tmp" + with open(CFG_PATH,"r") as f: + CFG=yaml.safe_load(f) + + # Map quick-lookup: camera -> (edge, cfg) + CAM_MAP={} + for e in CFG.get("edges", []): + for c in e.get("cameras", []): + CAM_MAP[c["name"]]=(e, c) + + # MinIO + mc=Minio(os.getenv("MINIO_ENDPOINT","s3.argos.interna"), + access_key=os.getenv("MINIO_ACCESS_KEY"), + secret_key=os.getenv("MINIO_SECRET_KEY"), + secure=os.getenv("MINIO_SECURE","false").lower()=="true") + BUCKET=CFG["minio"]["bucket"] + if not mc.bucket_exists(BUCKET): mc.make_bucket(BUCKET) + + # DB + Path("/data").mkdir(parents=True, exist_ok=True) + con=sqlite3.connect(DB_PATH, check_same_thread=False) + cur=con.cursor() + cur.execute("""CREATE TABLE IF NOT EXISTS events( + id TEXT PRIMARY KEY, ts INTEGER, edge TEXT, camera TEXT, label TEXT, + s3url TEXT, thumb_s3 TEXT + )""") + con.commit() + + def upload_file(local_path, key, content_type): + mc.fput_object(BUCKET, key, local_path, content_type=content_type) + return f"s3://{BUCKET}/{key}" + + def fetch_frigate_clip(edge_cfg, ev_id): + """ Descarga el clip nativo de Frigate si existe """ + base=edge_cfg["base_url"] + url=urljoin(base, f"/api/events/{ev_id}/clip") + headers={} + if edge_cfg.get("api_token"): + headers["Authorization"]=f"Bearer {edge_cfg['api_token']}" + r=requests.get(url, headers=headers, stream=True, timeout=30) + if r.status_code!=200: return None + tmp=f"{TMP}/{ev_id}.mp4" + with open(tmp,"wb") as f: + for chunk in r.iter_content(1<<20): + if chunk: f.write(chunk) + return tmp + + def fetch_frigate_thumb(edge_cfg, ev_id): + base=edge_cfg["base_url"] + url=urljoin(base, f"/api/events/{ev_id}/thumbnail.jpg") + headers={} + if edge_cfg.get("api_token"): + headers["Authorization"]=f"Bearer {edge_cfg['api_token']}" + r=requests.get(url, headers=headers, timeout=10) + if r.status_code!=200: return None + tmp=f"{TMP}/{ev_id}.jpg" + with open(tmp,"wb") as f: f.write(r.content) + return tmp + + def record_rtsp(rtsp_url, seconds=30): + tmp=f"{TMP}/rtsp_{int(time.time())}.mp4" + cmd=["ffmpeg","-nostdin","-y","-rtsp_transport","tcp","-i",rtsp_url,"-t",str(seconds),"-c","copy",tmp] + try: + subprocess.run(cmd, check=True) + return tmp + except Exception as e: + print("FFmpeg fallback failed:", e) + return None + + def on_message(client, userdata, msg): + try: + payload=json.loads(msg.payload.decode("utf-8")) + except Exception as e: + print("Bad JSON", e); return + + ev_type=payload.get("type") + after=payload.get("after") or {} + ev_id=after.get("id") or payload.get("id") + cam=after.get("camera") or payload.get("camera") + label=after.get("label") or payload.get("label","") + + if ev_type!="new" or not cam or not ev_id: return + if cam not in CAM_MAP: + print("Unknown camera:", cam); return + + edge_cfg, cam_cfg = CAM_MAP[cam] + ts=int(time.time()) + print(f"[ARGOS] {ev_id} {cam} → try Frigate clip") + path_local = fetch_frigate_clip(edge_cfg, ev_id) + if not path_local: + print(f"[ARGOS] {ev_id} no native clip, fallback RTSP") + path_local = record_rtsp(cam_cfg["rtsp_main"], seconds=30) + if not path_local: + print(f"[ARGOS] {ev_id} failed recording"); return + + # upload clip + date=datetime.datetime.utcfromtimestamp(ts) + key=f"{cam}/{date.year:04d}/{date.month:02d}/{date.day:02d}/{ts}_{ev_id}.mp4" + s3url=upload_file(path_local, key, "video/mp4") + try: os.remove(path_local) + except: pass + + # thumbnail + thumb_local = fetch_frigate_thumb(edge_cfg, ev_id) + thumb_s3=None + if thumb_local: + tkey=f"{cam}/thumbs/{ts}_{ev_id}.jpg" + thumb_s3=upload_file(thumb_local, tkey, "image/jpeg") + try: os.remove(thumb_local) + except: pass + + cur.execute("INSERT OR REPLACE INTO events(id, ts, edge, camera, label, s3url, thumb_s3) VALUES (?,?,?,?,?,?,?)", + (ev_id, ts, edge_cfg["name"], cam, label, s3url, thumb_s3)) + con.commit() + print(f"[ARGOS] stored {s3url}") + + def main(): + m=mqtt.Client(client_id=CFG["mqtt"]["client_id"], clean_session=True) + m.connect(CFG["mqtt"]["host"], CFG["mqtt"]["port"], keepalive=60) + m.subscribe(CFG["mqtt"]["topic"]) + m.on_message=on_message + m.loop_forever() + + if __name__=="__main__": main() diff --git a/argos/configmaps/configmap-panel.yaml b/argos/configmaps/configmap-panel.yaml new file mode 100644 index 0000000..4404c50 --- /dev/null +++ b/argos/configmaps/configmap-panel.yaml @@ -0,0 +1,87 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argos-panel-config + namespace: argos-core +data: + app.py: | + import os, sqlite3, time + from fastapi import FastAPI, HTTPException + from fastapi.responses import HTMLResponse + from minio import Minio + from urllib.parse import urlparse + + DB="/data/argos.db" + mc=Minio(os.getenv("MINIO_ENDPOINT","s3.argos.interna"), + access_key=os.getenv("MINIO_ACCESS_KEY"), + secret_key=os.getenv("MINIO_SECRET_KEY"), + secure=os.getenv("MINIO_SECURE","false").lower()=="true") + app=FastAPI() + + def rows(limit=100, camera=None, since=None): + q="SELECT id, ts, edge, camera, label, s3url, thumb_s3 FROM events" + cond=[]; args=[] + if camera: cond.append("camera=?"); args.append(camera) + if since: cond.append("ts>=?"); args.append(int(since)) + if cond: q+=" WHERE "+ " AND ".join(cond) + q+=" ORDER BY ts DESC LIMIT ?"; args.append(limit) + con=sqlite3.connect(DB); cur=con.cursor() + cur.execute(q, tuple(args)); r=cur.fetchall(); con.close() + return r + + @app.get("/api/events") + def api_events(limit:int=100, camera:str=None, since:int=None): + return [dict(id=i, ts=t, edge=e, camera=c, label=l or "", s3url=s, thumb=th or "") + for (i,t,e,c,l,s,th) in rows(limit,camera,since)] + + @app.get("/api/url/{event_id}") + def presign(event_id: str, expires: int = 600): + con=sqlite3.connect(DB); cur=con.cursor() + cur.execute("SELECT s3url FROM events WHERE id=?", (event_id,)) + row=cur.fetchone(); con.close() + if not row: raise HTTPException(404, "Not found") + s3url=row[0]; p=urlparse(s3url); b=p.netloc; k=p.path.lstrip("/") + return {"url": mc.presigned_get_object(b, k, expires=expires)} + + @app.get("/", response_class=HTMLResponse) + def index(): + return """ + ARGOS Panel + +

ARGOS – Alarmas

+
+
+ + """ diff --git a/argos/deployments/deploy-mediamtx.yaml b/argos/deployments/deploy-mediamtx.yaml new file mode 100644 index 0000000..a92fd19 --- /dev/null +++ b/argos/deployments/deploy-mediamtx.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mediamtx + namespace: argos-core +spec: + replicas: 1 + selector: { matchLabels: { app: mediamtx } } + template: + metadata: { labels: { app: mediamtx } } + spec: + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + containers: + - name: mediamtx + image: bluenviron/mediamtx:1.14.0 + command: ["/bin/sh","-c"] + args: + - | + set -e + ulimit -n 1048576 + exec mediamtx /config/mediamtx.yml + volumeMounts: + - name: cfg + mountPath: /config + ports: + - containerPort: 8554 # RTSP + - containerPort: 8189 # SRT + - containerPort: 8889 # WHIP + - containerPort: 8880 # HTTP/API + volumes: + - name: cfg + configMap: + name: mediamtx-config diff --git a/argos/deployments/deploy-minio.yaml b/argos/deployments/deploy-minio.yaml new file mode 100644 index 0000000..09a4f70 --- /dev/null +++ b/argos/deployments/deploy-minio.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: minio + namespace: argos-core +spec: + replicas: 1 + selector: { matchLabels: { app: minio } } + template: + metadata: { labels: { app: minio } } + spec: + containers: + - name: minio + image: quay.io/minio/minio:latest + args: ["server", "/data", "--console-address", ":9001"] + envFrom: + - secretRef: { name: minio-creds } + ports: + - containerPort: 9000 + - containerPort: 9001 + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: minio-data diff --git a/argos/deployments/deploy-mosquitto.yaml b/argos/deployments/deploy-mosquitto.yaml new file mode 100644 index 0000000..a82eb87 --- /dev/null +++ b/argos/deployments/deploy-mosquitto.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mosquitto + namespace: argos-core +spec: + replicas: 1 + selector: { matchLabels: { app: mosquitto } } + template: + metadata: { labels: { app: mosquitto } } + spec: + containers: + - name: mosquitto + image: harbor.c2et.net/library/eclipse-mosquitto:latest + ports: + - containerPort: 1883 + volumeMounts: + - name: cfg + mountPath: /mosquitto/config + volumes: + - name: cfg + configMap: + name: mosquitto-conf + items: + - key: mosquitto.conf + path: mosquitto.conf diff --git a/argos/deployments/deploy-orchestrator.yaml b/argos/deployments/deploy-orchestrator.yaml new file mode 100644 index 0000000..0b8df8f --- /dev/null +++ b/argos/deployments/deploy-orchestrator.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argos-orchestrator + namespace: argos-core +spec: + replicas: 1 + selector: { matchLabels: { app: argos-orchestrator } } + template: + metadata: { labels: { app: argos-orchestrator } } + spec: + containers: + - name: orchestrator + image: harbor.c2et.net/library/python:3.13.7-slim-bookworm + command: ["/bin/sh","-c"] + args: + - | + set -e + apt-get update && apt-get install -y --no-install-recommends ffmpeg curl && rm -rf /var/lib/apt/lists/* + pip install paho-mqtt minio pyyaml requests + python /app/app.py + envFrom: + - secretRef: { name: argos-orchestrator-secret } + volumeMounts: + - { name: cfg, mountPath: /app } + - { name: data, mountPath: /data } + volumes: + - name: cfg + configMap: + name: argos-orchestrator-config + items: + - { key: app.py, path: app.py } + - { key: settings.yaml, path: settings.yaml } + - name: data + emptyDir: {} diff --git a/argos/deployments/deploy-panel.yaml b/argos/deployments/deploy-panel.yaml new file mode 100644 index 0000000..9da5194 --- /dev/null +++ b/argos/deployments/deploy-panel.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argos-panel + namespace: argos-core +spec: + replicas: 1 + selector: { matchLabels: { app: argos-panel } } + template: + metadata: { labels: { app: argos-panel } } + spec: + containers: + - name: panel + image: docker.io/library/python:3.13.7-slim-bookworm + command: ["/bin/sh","-c"] + args: + - | + set -e + pip install fastapi uvicorn minio + uvicorn app:app --host 0.0.0.0 --port 8000 + envFrom: + - secretRef: { name: argos-panel-secret } + volumeMounts: + - { name: app, mountPath: /app } + - { name: data, mountPath: /data } + ports: + - containerPort: 8000 + volumes: + - name: app + configMap: + name: argos-panel-config + items: [ { key: app.py, path: app.py } ] + - name: data + emptyDir: {} diff --git a/argos/ingress/ingress-minio.yaml b/argos/ingress/ingress-minio.yaml new file mode 100644 index 0000000..c7365a2 --- /dev/null +++ b/argos/ingress/ingress-minio.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: minio-ingress + namespace: argos-core +spec: + ingressClassName: nginx + rules: + - host: s3.argos.interna + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: minio + port: { number: 9000 } + - host: minio.argos.interna + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: minio + port: { number: 9001 } diff --git a/argos/ingress/ingress-panel.yaml b/argos/ingress/ingress-panel.yaml new file mode 100644 index 0000000..d7095e2 --- /dev/null +++ b/argos/ingress/ingress-panel.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: argos-panel-internal + namespace: argos-core +spec: + ingressClassName: nginx-internal + rules: + - host: panel.argos.c2et.net # mismo FQDN + http: + paths: + - path: / + pathType: Prefix + backend: + service: { name: argos-panel, port: { number: 80 } } + tls: + - hosts: ["panel.argos.c2et.net"] + secretName: panel-argos-tls diff --git a/argos/kustomization.yaml b/argos/kustomization.yaml new file mode 100644 index 0000000..ce3752f --- /dev/null +++ b/argos/kustomization.yaml @@ -0,0 +1,48 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: argos-core + +commonLabels: + app.kubernetes.io/part-of: argos + app.kubernetes.io/managed-by: kustomize + +resources: + # Namespace y políticas + - namespace.yaml + - policies/network-policy.yaml + + # ConfigMaps + - configmaps/configmap-mediamtx.yaml + - configmaps/configmap-mosquitto.yaml + - configmaps/configmap-orchestrator.yaml + - configmaps/configmap-panel.yaml + + # Secrets + - secrets/secret-minio.yaml + - secrets/secret-orchestrator.yaml + - secrets/secret-panel.yaml + + # Storage + - pvc/pvc-minio.yaml + + # Deployments + - deployments/deploy-mediamtx.yaml + - deployments/deploy-mosquitto.yaml + - deployments/deploy-orchestrator.yaml + - deployments/deploy-panel.yaml + - deployments/deploy-minio.yaml + + # Services + - services/argos-panel.yaml + - services/svc-mediamtx-tcp.yaml + - services/svc-mediamtx-udp.yaml + - services/svc-minio.yaml + - services/svc-mosquitto.yaml + + # Ingress + - ingress/ingress-minio.yaml + - ingress/ingress-panel.yaml + + # Certificados (si usas ACME en el externo y compartes el secret) + - certs/certificate-argos-panel.yaml diff --git a/argos/namespace.yaml b/argos/namespace.yaml new file mode 100644 index 0000000..09836f7 --- /dev/null +++ b/argos/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: argos-core diff --git a/argos/policies/network-policy.yaml b/argos/policies/network-policy.yaml new file mode 100644 index 0000000..fcf2dae --- /dev/null +++ b/argos/policies/network-policy.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-from-wg-and-200 + namespace: argos-core +spec: + podSelector: {} + policyTypes: [Ingress] + ingress: + - from: + - ipBlock: { cidr: 192.168.254.0/24 } # WireGuard peers + - ipBlock: { cidr: 192.168.200.0/24 } # red 200 (acceso interno/admin) diff --git a/argos/pvc/pvc-minio.yaml b/argos/pvc/pvc-minio.yaml new file mode 100644 index 0000000..702c9d0 --- /dev/null +++ b/argos/pvc/pvc-minio.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: minio-data + namespace: argos-core +spec: + storageClassName: ceph-rbd + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Ti # ajusta capacidad diff --git a/argos/secrets/secret-harbor-cred.yaml b/argos/secrets/secret-harbor-cred.yaml new file mode 100644 index 0000000..775414c --- /dev/null +++ b/argos/secrets/secret-harbor-cred.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + .dockerconfigjson: eyJhdXRocyI6eyJoYXJib3IuYzJldC5jb20iOnsidXNlcm5hbWUiOiJ4YXZvciIsInBhc3N3b3JkIjoiTUBuYWJvMjAyNSIsImVtYWlsIjoibm8tcmVwbHlAYzJldC5jb20iLCJhdXRoIjoiZUdGMmIzSTZUVUJ1WVdKdk1qQXlOUT09In19fQ== +kind: Secret +metadata: + creationTimestamp: null + name: harbor-cred + namespace: apolo +type: kubernetes.io/dockerconfigjson diff --git a/argos/secrets/secret-minio.yaml b/argos/secrets/secret-minio.yaml new file mode 100644 index 0000000..1fdc2d4 --- /dev/null +++ b/argos/secrets/secret-minio.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: minio-creds + namespace: argos-core +type: Opaque +stringData: + MINIO_ROOT_USER: admin + MINIO_ROOT_PASSWORD: adminadmin123 diff --git a/argos/secrets/secret-orchestrator.yaml b/argos/secrets/secret-orchestrator.yaml new file mode 100644 index 0000000..bf16a20 --- /dev/null +++ b/argos/secrets/secret-orchestrator.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: argos-orchestrator-secret + namespace: argos-core +type: Opaque +stringData: + MINIO_ENDPOINT: minio.argos-core.svc.cluster.local:9000 + MINIO_ACCESS_KEY: admin + MINIO_SECRET_KEY: adminadmin123 + MINIO_SECURE: "false" diff --git a/argos/secrets/secret-panel.yaml b/argos/secrets/secret-panel.yaml new file mode 100644 index 0000000..984e984 --- /dev/null +++ b/argos/secrets/secret-panel.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: argos-panel-secret + namespace: argos-core +type: Opaque +stringData: + MINIO_ENDPOINT: minio.argos-core.svc.cluster.local:9000 + MINIO_ACCESS_KEY: admin + MINIO_SECRET_KEY: adminadmin123 + MINIO_SECURE: "false" diff --git a/argos/services/argos-panel.yaml b/argos/services/argos-panel.yaml new file mode 100644 index 0000000..cc101e9 --- /dev/null +++ b/argos/services/argos-panel.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: argos-panel + namespace: argos-core +spec: + type: ClusterIP + selector: { app: argos-panel } + ports: + - { name: http, port: 80, targetPort: 8000 } diff --git a/argos/services/svc-mediamtx-tcp.yaml b/argos/services/svc-mediamtx-tcp.yaml new file mode 100644 index 0000000..3283b6c --- /dev/null +++ b/argos/services/svc-mediamtx-tcp.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: mediamtx-tcp + namespace: argos-core + annotations: + metallb.universe.tf/allow-shared-ip: "mediamtx-addr" +spec: + type: LoadBalancer + loadBalancerIP: 192.168.200.16 + selector: { app: mediamtx } + ports: + - { name: rtsp, port: 8554, targetPort: 8554, protocol: TCP } + - { name: http, port: 8880, targetPort: 8880, protocol: TCP } + - { name: whip, port: 8889, targetPort: 8889, protocol: TCP } diff --git a/argos/services/svc-mediamtx-udp.yaml b/argos/services/svc-mediamtx-udp.yaml new file mode 100644 index 0000000..db0953b --- /dev/null +++ b/argos/services/svc-mediamtx-udp.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: mediamtx-udp + namespace: argos-core + annotations: + metallb.universe.tf/allow-shared-ip: "mediamtx-addr" +spec: + type: LoadBalancer + loadBalancerIP: 192.168.200.16 + selector: { app: mediamtx } + ports: + - { name: srt, port: 8189, targetPort: 8189, protocol: UDP } diff --git a/argos/services/svc-minio.yaml b/argos/services/svc-minio.yaml new file mode 100644 index 0000000..29aa80c --- /dev/null +++ b/argos/services/svc-minio.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: minio + namespace: argos-core +spec: + type: ClusterIP + selector: { app: minio } + ports: + - { name: api, port: 9000, targetPort: 9000, protocol: TCP } + - { name: console, port: 9001, targetPort: 9001, protocol: TCP } diff --git a/argos/services/svc-mosquitto.yaml b/argos/services/svc-mosquitto.yaml new file mode 100644 index 0000000..72424c2 --- /dev/null +++ b/argos/services/svc-mosquitto.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mosquitto + namespace: argos-core +spec: + type: LoadBalancer + loadBalancerIP: 192.168.200.15 + selector: { app: mosquitto } + ports: + - name: mqtt + port: 1883 + targetPort: 1883 + protocol: TCP diff --git a/gitea/copypod.yaml b/gitea/copypod.yaml new file mode 100644 index 0000000..33952be --- /dev/null +++ b/gitea/copypod.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: { name: loader, namespace: gitea } +spec: + restartPolicy: Never + containers: + - name: sh + image: alpine:latest + command: ["sh","-c","sleep 36000"] + volumeMounts: + - { name: gitea, mountPath: /mnt/gitea } + - { name: mysql, mountPath: /mnt/mysql } + volumes: + - name: gitea + persistentVolumeClaim: { claimName: gitea-data } + - name: mysql + persistentVolumeClaim: { claimName: gitea-db } diff --git a/gitea/deployments/gitea-db.yaml b/gitea/deployments/gitea-db.yaml new file mode 100644 index 0000000..e7659af --- /dev/null +++ b/gitea/deployments/gitea-db.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gitea-db + namespace: gitea +spec: + replicas: 1 + selector: + matchLabels: + app: gitea-db + template: + metadata: + labels: + app: gitea-db + spec: + containers: + - name: mysql + image: mysql:8 + env: + - name: MYSQL_ROOT_PASSWORD + value: gitea123 + - name: MYSQL_DATABASE + value: gitea + - name: MYSQL_USER + value: gitea + - name: MYSQL_PASSWORD + value: gitea123 + ports: + - containerPort: 3306 + volumeMounts: + - name: gitea-db + mountPath: /var/lib/mysql + volumes: + - name: gitea-db + persistentVolumeClaim: + claimName: gitea-db diff --git a/gitea/deployments/gitea.yaml b/gitea/deployments/gitea.yaml new file mode 100644 index 0000000..327d56e --- /dev/null +++ b/gitea/deployments/gitea.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gitea + namespace: gitea +spec: + replicas: 1 + selector: + matchLabels: + app: gitea + template: + metadata: + labels: + app: gitea + spec: + containers: + - name: gitea + image: gitea/gitea:latest + ports: + - containerPort: 3000 + env: + - name: USER_UID + value: "1000" + - name: USER_GID + value: "1000" + - name: GITEA__database__DB_TYPE + value: "mysql" + - name: GITEA__database__HOST + value: "gitea-db:3306" + - name: GITEA__database__NAME + value: "gitea" + - name: GITEA__database__USER + value: "gitea" + - name: GITEA__database__PASSWD + value: "gitea123" + volumeMounts: + - name: gitea-data + mountPath: /data + volumes: + - name: gitea-data + persistentVolumeClaim: + claimName: gitea-data diff --git a/gitea/ingress/ingress.yaml b/gitea/ingress/ingress.yaml new file mode 100644 index 0000000..480f421 --- /dev/null +++ b/gitea/ingress/ingress.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: gitea + namespace: gitea + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "false" +# nginx.ingress.kubernetes.io/proxy-body-size: "20m" + nginx.ingress.kubernetes.io/force-ssl-redirect: "false" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" +spec: + ingressClassName: nginx + tls: + - hosts: + - git.c2et.net + secretName: gitea-tls + rules: + - host: git.c2et.net + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gitea + port: + number: 3000 diff --git a/gitea/kustomization.yaml b/gitea/kustomization.yaml new file mode 100644 index 0000000..d55b114 --- /dev/null +++ b/gitea/kustomization.yaml @@ -0,0 +1,9 @@ +resources: + - namespace.yaml + - pvc/gitea-data.yaml + - pvc/gitea-db.yaml + - deployments/gitea.yaml + - deployments/gitea-db.yaml + - services/gitea.yaml + - services/gitea-db.yaml + - ingress/ingress.yaml diff --git a/gitea/namespace.yaml b/gitea/namespace.yaml new file mode 100644 index 0000000..4d0f469 --- /dev/null +++ b/gitea/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: gitea \ No newline at end of file diff --git a/gitea/pvc/gitea-data.yaml b/gitea/pvc/gitea-data.yaml new file mode 100644 index 0000000..fbc63e3 --- /dev/null +++ b/gitea/pvc/gitea-data.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: gitea-data + namespace: gitea +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + storageClassName: ceph-rbd diff --git a/gitea/pvc/gitea-db.yaml b/gitea/pvc/gitea-db.yaml new file mode 100644 index 0000000..c466035 --- /dev/null +++ b/gitea/pvc/gitea-db.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: gitea-db + namespace: gitea +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi + storageClassName: ceph-rbd diff --git a/gitea/readme.md b/gitea/readme.md new file mode 100644 index 0000000..5146b02 --- /dev/null +++ b/gitea/readme.md @@ -0,0 +1,12 @@ +# Manifiestos para Gitea +Este repositorio contiene los manifiestos necesarios para desplegar Gitea, un servidor Git ligero, en el namespace gitea. + +## Despliegue + + kubectl apply -f namespace.yaml + kubectl apply -f pvc/gitea-data.yaml + kubectl apply -f pvc/gitea-db.yaml + kubectl apply -f deployments/gitea-db.yaml + kubectl apply -f services/gitea-db.yaml + kubectl apply -f deployments/gitea.yaml + kubectl apply -f services/gitea.yaml \ No newline at end of file diff --git a/gitea/services/gitea-db.yaml b/gitea/services/gitea-db.yaml new file mode 100644 index 0000000..d486c98 --- /dev/null +++ b/gitea/services/gitea-db.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: gitea-db + namespace: gitea +spec: + type: ClusterIP + selector: + app: gitea-db + ports: + - port: 3306 diff --git a/gitea/services/gitea.yaml b/gitea/services/gitea.yaml new file mode 100644 index 0000000..e5c111d --- /dev/null +++ b/gitea/services/gitea.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: gitea + namespace: gitea +spec: + type: NodePort + selector: + app: gitea + ports: + - name: http + port: 3000 + targetPort: 3000 + nodePort: 30300 diff --git a/harbor/values.yaml b/harbor/values.yaml new file mode 100644 index 0000000..cd35e2e --- /dev/null +++ b/harbor/values.yaml @@ -0,0 +1,56 @@ +expose: + type: ingress + tls: + enabled: true + certSource: auto + ingress: + ingressClassName: nginx + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + core: harbor.c2et.net + notary: notary.harbor.c2et.net + +externalURL: https://harbor.c2et.net + +persistence: + enabled: true + resourcePolicy: "keep" + persistentVolumeClaim: + registry: + storageClass: "ceph-rbd" + accessMode: ReadWriteOnce + chartmuseum: + storageClass: "ceph-rbd" + accessMode: ReadWriteOnce + jobservice: + storageClass: "ceph-rbd" + accessMode: ReadWriteOnce + database: + storageClass: "ceph-rbd" + accessMode: ReadWriteOnce + redis: + storageClass: "ceph-rbd" + accessMode: ReadWriteOnce + trivy: + storageClass: "ceph-rbd" + accessMode: ReadWriteOnce + +harborAdminPassword: Pozuelo12345 + +portal: + replicaCount: 1 + +core: + replicaCount: 1 + +registry: + replicaCount: 1 + +database: + type: internal + internal: + password: "root123" + +redis: + type: internal diff --git a/kubectl b/kubectl new file mode 100644 index 0000000..e69de29 diff --git a/rook/cluster/ceph-cluster-inicial.yaml b/rook/cluster/ceph-cluster-inicial.yaml index ad68c86..9616319 100644 --- a/rook/cluster/ceph-cluster-inicial.yaml +++ b/rook/cluster/ceph-cluster-inicial.yaml @@ -15,6 +15,8 @@ spec: - "192.168.4.0/24" cluster: - "192.168.4.0/24" + mgr: + count: 2 mon: count: 3 @@ -34,6 +36,16 @@ spec: operator: In values: ["site-a","site-b"] + mgr: + nodeAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + preference: + matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: ["srvfkvm01","srvfkvm04"] + storage: useAllNodes: false useAllDevices: false