--- # OpenSearch StatefulSet for lexical (BM25) search # Deployed alongside pgvector for hybrid semantic+lexical search # JWT realm configured for Authentik integration apiVersion: v1 kind: Namespace metadata: name: poimen labels: pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/audit: baseline pod-security.kubernetes.io/warn: baseline --- # OpenSearch Service (Headless for StatefulSet discovery) apiVersion: v1 kind: Service metadata: name: opensearch namespace: poimen labels: app.kubernetes.io/name: opensearch spec: clusterIP: None # Headless selector: app.kubernetes.io/name: opensearch ports: - name: http port: 9200 targetPort: 9200 - name: transport port: 9300 targetPort: 9300 --- # OpenSearch Service (Internal for queries from Memory Service) apiVersion: v1 kind: Service metadata: name: opensearch-internal namespace: poimen labels: app.kubernetes.io/name: opensearch spec: type: ClusterIP selector: app.kubernetes.io/name: opensearch ports: - name: http port: 9200 targetPort: 9200 --- # ConfigMap: OpenSearch configuration with JWT realm apiVersion: v1 kind: ConfigMap metadata: name: opensearch-config namespace: poimen data: opensearch.yml: | # Cluster settings cluster.name: poimen-memory node.name: ${HOSTNAME} cluster.initial_master_nodes: opensearch-0 discovery.seed_hosts: opensearch-0.opensearch.poimen.svc.cluster.local # Network network.host: 0.0.0.0 http.port: 9200 transport.port: 9300 # Security Plugin disabled (internal-only, JWT auth via Memory Service) plugins.security.disabled: "true" # Memory indices.memory.index_buffer_size: 30% log4j2.properties: | status = warn appender.console.type = Console appender.console.name = console appender.console.layout.type = PatternLayout appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n rootLogger.level = info rootLogger.appenderRef.console.ref = console internal_users.yml: | admin: hash: "$2y$12$K/SpwjtB.wW8u3/52l.f2OPST9/PgBkqquzi.Oi8KfRMfsKkCq3GO" reserved: true backend_roles: - "admin" - "all_access" roles_mapping.yml: | all_access: reserved: false backend_roles: - "*" hosts: - "*" --- # StatefulSet: OpenSearch (2 replicas for HA cluster) apiVersion: apps/v1 kind: StatefulSet metadata: name: opensearch namespace: poimen labels: app.kubernetes.io/name: opensearch spec: serviceName: opensearch replicas: 1 selector: matchLabels: app.kubernetes.io/name: opensearch template: metadata: labels: app.kubernetes.io/name: opensearch spec: serviceAccountName: opensearch hostNetwork: false initContainers: - name: sysctl image: busybox:1.28 command: - sysctl - -w - vm.max_map_count=262144 securityContext: privileged: true containers: - name: opensearch image: opensearchproject/opensearch:2.11.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 9200 - name: transport containerPort: 9300 env: - name: HOSTNAME valueFrom: fieldRef: fieldPath: metadata.name - name: CLUSTER_NAME value: "poimen-memory" - name: OPENSEARCH_JAVA_OPTS value: "-Xms1g -Xmx1g" - name: DISABLE_SECURITY_PLUGIN value: "true" # Volume mounts volumeMounts: - name: opensearch-data mountPath: /usr/share/opensearch/data - name: opensearch-config mountPath: /usr/share/opensearch/config/opensearch.yml subPath: opensearch.yml - name: opensearch-logs mountPath: /usr/share/opensearch/logs # Resource limits resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m" # Liveness probe livenessProbe: httpGet: path: /_cluster/health port: 9200 initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 # Readiness probe readinessProbe: httpGet: path: /_cluster/health?local=true port: 9200 initialDelaySeconds: 30 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2 # Security context securityContext: runAsUser: 1000 # Volumes volumes: - name: opensearch-config configMap: name: opensearch-config - name: opensearch-logs emptyDir: {} # PVC template for data persistence volumeClaimTemplates: - metadata: name: opensearch-data spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 30Gi --- # ServiceAccount for OpenSearch apiVersion: v1 kind: ServiceAccount metadata: name: opensearch namespace: poimen --- # NetworkPolicy: Only Memory Service can access OpenSearch apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: opensearch-access namespace: poimen spec: podSelector: matchLabels: app.kubernetes.io/name: opensearch policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app.kubernetes.io/name: poimen-memory - podSelector: matchLabels: app.kubernetes.io/name: opensearch-dashboards ports: - protocol: TCP port: 9200 --- # OpenSearch Dashboards: UI for monitoring, querying, and managing OpenSearch apiVersion: v1 kind: Service metadata: name: opensearch-dashboards namespace: poimen labels: app.kubernetes.io/name: opensearch-dashboards spec: type: ClusterIP selector: app.kubernetes.io/name: opensearch-dashboards ports: - name: http port: 5601 targetPort: 5601 --- # ConfigMap: OpenSearch Dashboards configuration apiVersion: v1 kind: ConfigMap metadata: name: opensearch-dashboards-config namespace: poimen data: opensearch_dashboards.yml: | # OpenSearch Dashboards configuration server.name: opensearch-dashboards server.host: "0.0.0.0" server.port: 5601 # OpenSearch connection opensearch.hosts: ["http://opensearch-internal.poimen.svc.cluster.local:9200"] opensearch.username: "admin" opensearch.password: "admin" opensearch.ssl.verificationMode: none # Logging logging.appenders.default.type: console logging.appenders.default.layout.type: pattern logging.appenders.default.layout.pattern: "[%date][%level][%logger] %message" logging.root.appenders: [default] logging.root.level: info --- # Deployment: OpenSearch Dashboards apiVersion: apps/v1 kind: Deployment metadata: name: opensearch-dashboards namespace: poimen labels: app.kubernetes.io/name: opensearch-dashboards spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: opensearch-dashboards template: metadata: labels: app.kubernetes.io/name: opensearch-dashboards spec: serviceAccountName: opensearch-dashboards containers: - name: opensearch-dashboards image: opensearchproject/opensearch-dashboards:2.11.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 5601 env: - name: OPENSEARCH_HOSTS value: "http://opensearch-internal.poimen.svc.cluster.local:9200" - name: OPENSEARCH_USERNAME value: "admin" - name: OPENSEARCH_PASSWORD valueFrom: secretKeyRef: name: opensearch-dashboards-secret key: password # Volume mounts volumeMounts: - name: opensearch-dashboards-config mountPath: /usr/share/opensearch-dashboards/config/opensearch_dashboards.yml subPath: opensearch_dashboards.yml # Resource limits resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" # Liveness probe livenessProbe: httpGet: path: / port: 5601 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 # Readiness probe readinessProbe: httpGet: path: / port: 5601 initialDelaySeconds: 15 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2 # Security context securityContext: runAsNonRoot: true runAsUser: 1000 capabilities: drop: - ALL # Volumes volumes: - name: opensearch-dashboards-config configMap: name: opensearch-dashboards-config --- # Secret: OpenSearch Dashboards password apiVersion: v1 kind: Secret metadata: name: opensearch-dashboards-secret namespace: poimen type: Opaque stringData: password: "admin" # ⚠️ Change in production --- # ServiceAccount for OpenSearch Dashboards apiVersion: v1 kind: ServiceAccount metadata: name: opensearch-dashboards namespace: poimen --- # Secret for OpenSearch Admin Password apiVersion: v1 kind: Secret metadata: name: opensearch-secrets namespace: poimen type: Opaque stringData: admin-password: "OpenSearch@Admin123!"