--- apiVersion: v1 kind: Namespace metadata: name: opscart-system --- apiVersion: v1 kind: ServiceAccount metadata: name: opscart-watcher namespace: opscart-system --- # Read-only access to all resources the analyzers inspect. # nodes + pods + namespaces — nodepool_costs.go, resources.go, security.go, waste.go # deployments etc. — deployment_costs.go (breakdown=deployment mode) # jobs / cronjobs — waste.go (stale batch workloads) # networkpolicies — network.go (unprotected namespace detection) # persistentvolumeclaims — waste.go (unbound PVC detection) # horizontalpodautoscalers — waste.go # endpointslices — waste.go, scanner/enhanced.go (service backend readiness) apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: opscart-watcher rules: - apiGroups: [""] resources: - nodes - pods - namespaces - services - endpoints - persistentvolumeclaims - replicationcontrollers - events verbs: ["get", "list"] - apiGroups: ["apps"] resources: ["deployments", "statefulsets", "daemonsets", "replicasets"] verbs: ["get", "list"] - apiGroups: ["batch"] resources: ["jobs", "cronjobs"] verbs: ["get", "list"] - apiGroups: ["autoscaling"] resources: ["horizontalpodautoscalers"] verbs: ["get", "list"] - apiGroups: ["networking.k8s.io"] resources: ["networkpolicies", "ingresses"] verbs: ["get", "list"] - apiGroups: ["discovery.k8s.io"] resources: ["endpointslices"] verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: opscart-watcher roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: opscart-watcher subjects: - kind: ServiceAccount name: opscart-watcher namespace: opscart-system --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: opscart-data namespace: opscart-system spec: accessModes: - ReadWriteOnce # storageClassName is intentionally omitted so the cluster's default # StorageClass is used (e.g. minikube's "standard", or a cloud # provider's default such as "gp2" / "managed-csi"). If your cluster # has no default StorageClass, or you need a specific one, set it # explicitly here, e.g.: # storageClassName: standard resources: requests: storage: 1Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: opscart-watcher namespace: opscart-system labels: app: opscart-watcher spec: replicas: 1 # opscart-data is ReadWriteOnce, so it can only be mounted by one pod # at a time. A RollingUpdate would start the new pod (and try to mount # the volume) before terminating the old one, and the new pod would # hang waiting for a mount the old pod still holds. Recreate tears down # the old pod first, freeing the volume before the replacement starts. # Only correct for single-replica deployments backed by RWO storage. strategy: type: Recreate selector: matchLabels: app: opscart-watcher template: metadata: labels: app: opscart-watcher spec: serviceAccountName: opscart-watcher # In-cluster: client-go auto-detects the service account token and # cluster CA at /var/run/secrets/kubernetes.io/serviceaccount/ # No kubeconfig flag needed. containers: - name: dashboard image: ghcr.io/opscart/opscart-dashboard:v1.7.0 imagePullPolicy: Always args: - "--port=8080" - "--breakdown=deployment" env: - name: OPSCART_DB_PATH value: /data/opscart.db # Resolved incidents/events older than this are pruned each scan # cycle; 0 = keep forever. - name: OPSCART_RETENTION_DAYS value: "90" ports: - name: http containerPort: 8080 protocol: TCP resources: requests: cpu: 100m memory: 64Mi limits: cpu: 500m memory: 256Mi volumeMounts: - name: opscart-data mountPath: /data readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 10 failureThreshold: 3 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 20 periodSeconds: 30 failureThreshold: 3 volumes: - name: opscart-data persistentVolumeClaim: claimName: opscart-data securityContext: runAsNonRoot: true runAsUser: 65534 # nobody — matches USER in Dockerfile runAsGroup: 65534 seccompProfile: type: RuntimeDefault --- apiVersion: v1 kind: Service metadata: name: opscart-watcher namespace: opscart-system labels: app: opscart-watcher spec: type: ClusterIP selector: app: opscart-watcher ports: - name: http port: 80 targetPort: 8080 protocol: TCP