Elasticsearch Phenix Operator is a kubernetes operator to manage elasticsearch Indices and Templates lifecycle.
Supported Elasticsearch versions are:
- Elasticsearch 8+
- Elasticsearch 7+
- Elasticsearch 6+
See the Quickstart to get started with Elasticsearch Phenix Operator.
- Features
- Kubernetes Domain, Group and Kinds
- Quick Start
- Creating a kubernetes cluster
- Install prerequisites
- Creating an elasticsearch cluster
- Install Elasticsearch Phenix Operator
- Creating a secret for connection URL
- Creating an elasticindex
- Creating an elastictemplate
- Get created objects and debugging
- Deleting elasticindex, elastictemplate with annotation
- Architecture
- Operator arguments
- Release artifacts
- Validations
- Mutation
- Add new kind to Elasticsearch Phenix Operator
- Manage Elasticsearch indices and templates lifecycle: create, update and delete
- Create new indices/templates, or manage existing indices/templates. In case of existing indices/templates, the
ElasticIndex/ElasticTemplateobject definition should be compatible with existingindex/template, otherwise you will get a kubernetes object created withErrorstatus - One instance of the operator can manage indices and templates on different elasticsearch servers
- Elasticsearch server URI is provided from a secret when you create ElasticIndex and ElasticTemplate objects
- Manage indices and templates uniqueness inside kubernetes
- A ValidatingWebhook is implemented to validate ElasticIndex and ElasticTemplate objects
Domain: carrefour.com
Group: elastic
Kinds: two kinds are available
ElasticIndex: manage elasticsearch indices lifecyclecreate,updateanddeleteElasticTemplate: manage elasticsearch templates lifecyclecreate,updateanddelete
You can use kind to run a kubernetes cluster in your machine. For more information: https://kind.sigs.k8s.io/docs/user/quick-start/
Create a cluster:
kind create cluster --image=kindest/node:v1.17.0
Cert-manager is needed to handle TLS certificate for admission webhook servers. You need cert-manager version v1.0.0 or above. For more information: https://github.com/jetstack/cert-manager/
To install cert-manager:
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.1/cert-manager.yaml
You should wait until the cert-manager becomes in running state:
kubectl wait --for=condition=Ready --timeout=-1s --all pods -n cert-manager
You can use ECK (Elastic Cloud on Kubernetes) to create an elasticsearch cluster in kubernetes. For more information: https://www.elastic.co/guide/en/cloud-on-k8s/master/k8s-overview.html
To install ECK:
kubectl apply -f https://download.elastic.co/downloads/eck/1.3.0/all-in-one.yaml
You should wait until the ECK operator becomes in running state:
kubectl wait --for=condition=Ready --timeout=-1s --all pods -n elastic-system
You can create a single node Elasticsearch cluster:
cat <<EOF | kubectl apply -n elastic-system -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: elastic-system
spec:
version: 7.9.2
nodeSets:
- name: default
count: 1
config:
node.master: true
node.data: true
node.ingest: true
node.store.allow_mmap: false
xpack.security.authc:
anonymous:
username: anonymous_user
roles: superuser
authz_exception: false
http:
service:
spec:
type: ClusterIP
tls:
selfSignedCertificate:
disabled: true
subjectAltNames:
- dns: localhost,127.0.0.1
EOF
You should wait until the elasticsearch cluster becomes in running state:
kubectl wait --for=condition=Ready --timeout=-1s --all pods -n elastic-system
To install Elasticsearch Phenix Operator (EPO):
kubectl apply -f https://raw.githubusercontent.com/Carrefour-Group/elastic-phenix-operator/v1.2.0/manifests/epo-all-in-one.yaml
You should wait until Elasticsearch Phenix Operator becomes in running state:
kubectl wait --for=condition=Ready --timeout=-1s --all pods -n elastic-phenix-operator-system
To access logs for deployment:
kubectl logs deployment/elastic-phenix-operator-controller-manager -c manager -n elastic-phenix-operator-system
You can find samples located at config/samples.
Before creating an ElasticIndex or an ElasticTemplate, you should create a secret containing elasticsearch uri that respects this pattern: <scheme>://<user>:<password>@<hostname>:<port> e.g. http://localhost:9200, https://elastic:pass@myshost:9200
cat <<EOF | kubectl apply -n elastic-phenix-operator-system -f -
apiVersion: v1
kind: Secret
metadata:
name: elasticsearch-cluster-secret
namespace: elastic-phenix-operator-system
type: Opaque
stringData:
uri: http://elastic-system-es-http.elastic-system.svc:9200
EOF
When creating an ElasticIndex, you should reference the elasticsearch server URI from the secret created before:
/!\ Secret should be in the same namespace, otherwise you will get an error /!\
cat <<EOF | kubectl apply -n elastic-phenix-operator-system -f -
apiVersion: elastic.carrefour.com/v1alpha1
kind: ElasticIndex
metadata:
name: product-index
namespace: elastic-phenix-operator-system
spec:
indexName: product
elasticURI:
secretKeyRef:
name: elasticsearch-cluster-secret
key: uri
numberOfShards: 6
numberOfReplicas: 1
model: |-
{
"settings": {
"index.codec": "best_compression"
},
"mappings": {
"_source": {
"enabled": true
},
"dynamic": false,
"properties": {
"barcode": {
"type": "keyword",
"index": true
},
"description": {
"type": "text",
"index": true
}
}
}
}
EOF
When creating an ElasticTemplate, you should reference the elasticsearch server URI from the secret created before:
/!\ Secret should be in the same namespace, otherwise you will get an error /!\
cat <<EOF | kubectl apply -n elastic-phenix-operator-system -f -
apiVersion: elastic.carrefour.com/v1alpha1
kind: ElasticTemplate
metadata:
name: invoice-template
namespace: elastic-phenix-operator-system
spec:
templateName: invoice
elasticURI:
secretKeyRef:
name: elasticsearch-cluster-secret
key: uri
numberOfShards: 5
numberOfReplicas: 1
order: 1
model: |-
{
"index_patterns": ["invoice*"],
"settings": {
"index.codec": "best_compression"
},
"mappings": {
"_source": {
"enabled": true
},
"properties": {
"key": {
"type": "keyword",
"index": true
},
"content": {
"type": "text",
"index": true
}
}
}
}
EOF
To get created object, you can use kubectl cli:
> kubectl get elasticindex -n elastic-phenix-operator-system
NAME INDEX_NAME SHARDS REPLICAS STATUS AGE
product-index product 6 1 Created 24m
city-index city 4 3 Error 21m
> kubectl get elastictemplate -n elastic-phenix-operator-system
NAME TEMPLATE_NAME SHARDS REPLICAS STATUS AGE
invoice-template invoice 5 3 Created 9m
You can also check indices and templates in elasticsearch cluster:
kubectl exec -it pod/elastic-system-es-default-0 -n elastic-system -- curl "localhost:9200/_cat/indices/product?v"
kubectl exec -it pod/elastic-system-es-default-0 -n elastic-system -- curl "localhost:9200/_cat/templates/invoice?v"
The STATUS column indicates whether index/template was created successfully in elasticsearch server. Possible values:
Created: whenindex/templatewas created successfully in elasticsearch serverError,Retry: when error has occurred during creating or updating anelasticindex/elastictemplate
When you have an elasticindex/elastictemplate with Error or Retry status, use kubectl describe to get more details:
> kubectl describe elasticindex/city-index -n elastic-phenix-operator-system
Name: city-index
Namespace: elastic-phenix-operator-system
Annotations: API Version: elastic.carrefour.com/v1alpha1
Kind: ElasticIndex
Metadata:
...
Spec:
...
Status:
Http Code Status: 400
Message: [400 Bad Request] {"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"Root mapping definition has unsupported parameters: [dynamicc : false]"}],"type":"mapper_parsing_exception","reason":"Failed to parse mapping: Root mapping definition has unsupported parameters: [dynamicc : false]","caused_by":{"type":"mapper_parsing_exception","reason":"Root mapping definition has unsupported parameters: [dynamicc : false]"}},"status":400}
Status: Error
When you delete an ElasticIndex/ElasticTemplate kubernetes object, the index/template in elasticsearch cluster will remain existing.
kubectl delete elastictemplate/invoice-template -n elastic-phenix-operator-system
kubectl delete elasticindex/product-index -n elastic-phenix-operator-system
If you want to delete the index/template in elasticsearch cluster too, you should add the annotation carrefour.com/delete-in-cluster=true to your kubernetes object.
kubectl annotate elastictemplate/invoice-template carrefour.com/delete-in-cluster=true -n elastic-phenix-operator-system
kubectl annotate elasticindex/product-index carrefour.com/delete-in-cluster=true -n elastic-phenix-operator-system
Now, when you delete your ElasticIndex/ElasticTemplate kubernetes object, elasticsearch index/template will be deleted too from elasticsearch cluster.
/!\ For indices deletion, you will lose indices data in elasticsearch cluster /!\
You can customise Elasticsearch Phenix Operator behavior using these manager arguments:
namespaces: create a cache on namespaces and watch only these namespace (defaults to all namespaces)namespaces-regex-filter: watch all namespaces and filter before reconciliation process (defaults to no filter applied)
When releasing Elasticsearch Phenix Operator, two artifacts are generated:
- a docker image containing
elastic-phenix-operatormanager embeddingElasticIndexandElasticTemplatecontrollers. All docker images are published in docker hub: https://hub.docker.com/r/carrefourphx/elastic-phenix-operator - an all-in-one kubernetes manifest file located at
manifest/epo-all-in-one.yamlthat defines all kubernetes objects needed to install and run theElasticsearch Phenix Operator:CustoResourceDefinition,Namespace,Deployment,Service,MutatingWebhookConfiguration,ValidatingWebhookConfiguration,Role,ClusterRole,RoleBinding,ClusterRoleBinding,Certificate
ElasticIndex and ElasticTemplate kubernetes objects creation goes through two steps of validation: syntactic validation and semantic validation
A syntactic validation is defined in CustomResourceDefinition (section openAPIV3Schema).
These rules are defined:
indexNameandtemplateNamefields are mandatory, and value should respect regex^[a-z0-9-_\.]+$numberOfShardsfield is mandatory, and value should be between 1 and 500numberOfReplicasfield is mandatory, and value should be between 1 and 3modelfield is mandatoryelasticURIfield is mandatory
A semantic validation is defined in a kubernetes ValidatingWebhook.
Multiple rules are implemented for different actions: create, update or delete
modelfield content is a valid jsonElasticIndex modeljson root content contains at mostaliases,mappings,settingsElasticTemplate modeljson root content contains at mostaliases,mappings,settings,index_patterns,versionElasticTemplatemodel field contains the mandatory fieldindex_patternselasticURIsecret should exist on the sameElasticIndex/ElasticTemplatenamespaceelasticURIsecret should respect this pattern:<scheme>://<user>:<password>@<hostname>:<port>e.g.http://localhost:9200,https://elastic:pass@myshost:9200- manage index and template uniqueness: you cannot create the same elasticsearch index/template (
indexName/templateNamefield) on different kubernetesElasticIndex/ElasticTemplateobjects when you specify the same elasticsearchhost:portinelasticURIsecret
ElasticIndex: you cannot update
indexNamefieldnumberOfShardsfieldmodelsettings (onlynumberOfReplicasupdate is allowed)
ElasticTemplate: you cannot update
templateNamefieldmodelfield if new model content is not a valid json
For both ElasticIndex/ElasticTemplate when updating elasticURI secret:
- it should exist on the same
ElasticIndex/ElasticTemplatenamespace - it should respect this pattern:
<scheme>://<user>:<password>@<hostname>:<port>e.g.http://localhost:9200,https://elastic:pass@myshost:9200 - you cannot update elasticsearch
host:port, onlyuserand/orpasswordcan be updated inelasticURIcontent
elasticURIsecret should exists on the sameElasticIndex/ElasticTemplatenamespace
A MutatingWebhook is implemented to initialize numberOfShards and numberOfReplicas settings fields, from fields numberOfShards and numberOfReplicas of an ElasticIndex/ElasticTemplate.
If user has defined numberOfShards or/and numberOfReplicas in settings in model field, these values will be overridden by numberOfShards and numberOfReplicas fields in the ElasticIndex/ElasticTemplate defintion.
For this ElasticIndex defintion:
apiVersion: elastic.carrefour.com/v1alpha1
kind: ElasticIndex
metadata:
name: product-index
spec:
indexName: product
elasticURI:
secretKeyRef:
name: elasticsearch-cluster-secret
key: uri
numberOfShards: 6
numberOfReplicas: 1
model: |-
{
"settings": {
"numberOfReplicas": 3
},
"mappings": {
"_source": {
"enabled": true
},
"dynamic": false,
"properties": {
"barcode": {
"type": "keyword",
"index": true
},
"description": {
"type": "text",
"index": true
}
}
}
}
=> The result of mutation step will be:
apiVersion: elastic.carrefour.com/v1alpha1
kind: ElasticIndex
metadata:
name: product-index
spec:
indexName: product
elasticURI:
secretKeyRef:
name: elasticsearch-cluster-secret
key: uri
numberOfShards: 6
numberOfReplicas: 1
model: |-
{
"settings": {
"numberOfReplicas": 1
"numberOfShards": 6
},
"mappings": {
"_source": {
"enabled": true
},
"dynamic": false,
"properties": {
"barcode": {
"type": "keyword",
"index": true
},
"description": {
"type": "text",
"index": true
}
}
}
}
This operator was generated using kubebuilder 2.3.1. For more details about kubebuiler: https://book.kubebuilder.io/
Let's say that you want to add a new Kind to manage elasticsearch pipelines: ElasticPipeline
You should run these commands:
kubebuilder create api --group elastic --version v1alpha1 --kind ElasticPipeline
kubebuilder create webhook --group elastic --version v1alpha1 --kind ElasticPipeline --defaulting

