Bahriya Tersane

Documentation / opensearch

OpenSearch

Installs OpenSearch, the search and analytics engine: a single node, or a cluster of nodes that each hold data and are eligible as cluster manager, with OpenSearch Dashboards as an option.

The chart provides:

  • a cluster of three nodes by default, with every index able to keep a replica on another node;
  • TLS on every connection, between the nodes and to the REST API, with a certificate authority of the cluster’s own, made with cert-manager;
  • authentication on every request, with a generated administrator password and none of the image’s demonstration users;
  • scheduled snapshots of every index to S3-compatible storage, and restore from them;
  • OpenSearch Dashboards, the web interface, on an optional Ingress;
  • a NetworkPolicy;
  • every Pod meeting the restricted Pod Security Standard, with nothing run as root or privileged.

Prerequisites

  • Kubernetes 1.33 or later.
  • vm.max_map_count of at least 262144 on every node that may run OpenSearch. OpenSearch refuses to start without it, and a Pod cannot set it without privilege, which the chart never asks for. The Bahriya Kubernetes Engine will set it on every node from release 2.5. On earlier releases of the Bahriya Kubernetes Engine and on other Kubernetes, set it on every node and keep it across reboots:

    echo 'vm.max_map_count = 262144' > /etc/sysctl.d/90-opensearch.conf
    sysctl --system
    
  • cert-manager, for the cluster’s certificates. It is included with the Bahriya Kubernetes Engine; on other Kubernetes, install it first.
  • A pull secret for 1x.ax named 1x-ax in the release’s namespace, created from an image token as described in the access documentation.
  • A StorageClass for the volumes: the cluster’s default, or one named in persistence.storageClass.

Installation

helm install logs tersane/opensearch \
  --namespace logs --create-namespace \
  --version <chart version>

A single node:

helm install logs tersane/opensearch --namespace logs --set replicas=1

The release’s resources are named <release>-opensearch (here logs-opensearch), or after the release alone if its name already contains opensearch. Check that every node is ready:

kubectl --namespace logs get pods -l app.kubernetes.io/instance=logs

Connecting

The REST API is served over HTTPS at https://<release>-opensearch:9200. Clients verify the server with ca.crt from the Secret <release>-opensearch-certs; the certificate names the Service in its short and namespaced forms, and any names in extraDnsNames.

The administrator is admin; its password is in the Secret <release>-opensearch, key admin-password:

kubectl --namespace logs get secret logs-opensearch \
  -o jsonpath='{.data.admin-password}' | base64 -d

Users and roles

The chart creates two users: admin, with every permission, and kibanaserver, which OpenSearch Dashboards connects as. The image’s demonstration users are not created. Further users, roles and role mappings are managed with the security REST API or in OpenSearch Dashboards, under Security, and are kept in the cluster:

curl --cacert ca.crt -u admin:<password> -X PUT \
  https://logs-opensearch:9200/_plugins/_security/api/internalusers/ingest \
  -H 'Content-Type: application/json' \
  -d '{"password":"<password>","backend_roles":["logstash"]}'

The two users’ passwords are set when the cluster is created. To use passwords of your own, create the Secret before installing and name it in auth.existingSecret, with the keys admin-password, admin-hash, dashboards-password and dashboards-hash, where each hash is the bcrypt hash of its password. A password changed later is changed with the security REST API, and the Secret updated to match, since the chart’s Secret is what Dashboards, the snapshot Job and helm test read.

High availability

With three nodes, the cluster keeps a majority of cluster managers while one node is down, and an index with number_of_replicas: 1 (OpenSearch’s default) keeps every shard. Nodes are spread across the cluster’s nodes by a preferred anti-affinity on affinity.topologyKey, and a PodDisruptionBudget lets a node drain take one OpenSearch node at a time. A change to the chart’s configuration restarts the nodes one at a time.

Snapshots

snapshots:
  enabled: true
  bucket: my-bucket
  basePath: opensearch/logs
  endpoint: s3.example.com        # empty for Amazon S3
  existingSecret: logs-s3         # keys access-key and secret-key
  schedule: "0 2 * * *"
  timezone: UTC
  maxAge: 30d

The key is kept in OpenSearch’s keystore on each node. After every install and upgrade, a Job registers the repository (snapshots.repository, s3-snapshots by default), checks that every node can write to it, and creates or replaces a snapshot schedule named after the release: a snapshot of every index and the cluster state on schedule, deleting those older than maxAge and always keeping the most recent. A changed key takes effect when the nodes restart.

To restore an index, delete or close it, and restore it from a snapshot:

curl --cacert ca.crt -u admin:<password> \
  https://logs-opensearch:9200/_snapshot/s3-snapshots/_all
curl --cacert ca.crt -u admin:<password> -X POST \
  'https://logs-opensearch:9200/_snapshot/s3-snapshots/<snapshot>/_restore?wait_for_completion=true' \
  -H 'Content-Type: application/json' -d '{"indices":"orders"}'

OpenSearch Dashboards

dashboards:
  enabled: true
  ingress:
    enabled: true
    host: search.example.com
    tlsSecret: search-example-com-tls

Dashboards connects to OpenSearch over HTTPS as kibanaserver, verifying the cluster’s certificate. Sign in with OpenSearch’s own users. TLS to the browser is terminated by the ingress controller; with a tlsSecret, Dashboards’ cookie is sent over HTTPS only.

Network policy

With networkPolicy.enabled (the default), the nodes admit:

  • the REST API on 9200 from networkPolicy.http.from, every source when it is empty;
  • always, the nodes themselves on 9200 and 9300, and the chart’s own clients (Dashboards, the snapshot Job and the test) on 9200.

Dashboards admits port 5601 from networkPolicy.dashboards.from, every source when it is empty.

Resources

The Java heap is half the memory limit, as OpenSearch recommends; the other half is left to the operating system’s file cache, which OpenSearch relies on for search. Give the memory limit in Mi or Gi. The init container runs OpenSearch’s keystore tool, with its own small heap, under initResources.

Certificates

The cluster’s certificate authority is valid for ten years, and the nodes’ certificate for one; cert-manager renews it thirty days before it expires. The nodes read the renewed certificate on their next restart, or at once when asked to reload it. The reload must be asked with the administrator’s certificate, from the Secret <release>-opensearch-admin-certs; the security plugin refuses it to the admin user’s password:

kubectl --namespace logs get secret logs-opensearch-admin-certs \
  -o jsonpath='{.data.tls\.crt}' | base64 -d > admin.crt
kubectl --namespace logs get secret logs-opensearch-admin-certs \
  -o jsonpath='{.data.tls\.key}' | base64 -d > admin.key
for layer in transport http; do
  curl --cacert ca.crt --cert admin.crt --key admin.key -X PUT \
    "https://logs-opensearch:9200/_plugins/_security/api/ssl/$layer/reloadcerts"
done

The same certificate is what the security plugin’s securityadmin tool presents. It is mounted in no Pod; keep the files you extract as you would the administrator’s password.

Pod Security

Every Pod the chart creates meets the Kubernetes restricted Pod Security Standard: it runs as the image’s opensearch user, cannot escalate its privileges, drops all Linux capabilities and runs under the container runtime’s default seccomp profile. Install it in a namespace that enforces restricted.

Testing

helm test logs --namespace logs

The test checks, as the administrator over HTTPS, that the cluster has every node and is not red, indexes a document in a new index, finds it with a search and deletes the index.

Uninstalling

helm uninstall logs --namespace logs

The volumes are kept; delete them to remove the data:

kubectl --namespace logs delete pvc -l app.kubernetes.io/instance=logs

Parameters

OpenSearch

Parameter Description Default
replicas Nodes: 1 is a single node 3
image.repository / image.tag OpenSearch with the S3 snapshot plugin 1x.ax/tersane/core/opensearch/opensearch / the release’s
imagePullSecrets Pull secrets of every Pod [{name: 1x-ax}]
config opensearch.yml settings added to the chart’s {}
auth.existingSecret A Secret with both users’ passwords and hashes generated
extraDnsNames Names the HTTPS certificate carries besides the Service’s []
clusterDomain The cluster’s DNS domain cluster.local

Storage, resources and scheduling

Parameter Description Default
persistence.size / .storageClass Volume size per node; StorageClass 20Gi / the default class
resources Requests and limits of each node; the heap is half the memory limit 500m/2Gi, limits 2000m/2Gi
initResources Requests and limits of the init container 100m/256Mi, limits 1000m/256Mi
jobResources Requests and limits of the snapshot Job and the test 10m/32Mi, limits 200m/128Mi
terminationGracePeriodSeconds The longest a node takes to stop 120
podDisruptionBudget.enabled / .maxUnavailable Nodes a drain may take at once true / 1
affinity.topologyKey / .podAntiAffinityType Spread of the nodes kubernetes.io/hostname / preferred
nodeSelector, tolerations, priorityClassName Placement of the nodes none

Snapshots

Parameter Description Default
snapshots.enabled Scheduled snapshots to S3 false
snapshots.bucket / .basePath Where snapshots are kept ""
snapshots.endpoint / .protocol / .region / .pathStyleAccess The storage "" / https / "" / true
snapshots.accessKey / .secretKey / .existingSecret The key, or a Secret with access-key and secret-key ""
snapshots.schedule / .timezone When a snapshot is taken 0 2 * * * / UTC
snapshots.maxAge How long snapshots are kept 30d
snapshots.repository The repository’s name s3-snapshots

Dashboards and network

Parameter Description Default
dashboards.enabled OpenSearch Dashboards false
dashboards.replicas / .resources Its replicas and resources 1 / 100m/512Mi, limits 1000m/1Gi
dashboards.image.repository / .tag Its image 1x.ax/tersane/core/opensearch/opensearch-dashboards / the release’s
dashboards.ingress.enabled / .host / .ingressClassName / .tlsSecret / .annotations Its Ingress false / "" / kong / "" / {}
networkPolicy.enabled Create the NetworkPolicies true
networkPolicy.http.from / .dashboards.from Sources admitted to 9200 and 5601 [] (every source)