This is a quick guide on how to deploy the Elastic stack on GKE using Storage Classes, Headless Services, and Stateful Sets. I will provide walk-throughs for building some aspects of the cluster through the Google Cloud Shell (command line) as well as the Google Cloud Platform (GCP) console. This cluster is meant to help readers learn how to deploy capabilities to GKE and is not representative individual requirements for a production environment.
SSD Storage Class
GKE deployments provide a standard storage class, which does not provide sufficient IOPS to sustain an Elasticsearch node deployment. For this reason, we will create a new SSD storage class to handle the increase IOPS for hot data storage. Take a look at the Storage Options documentation for detail comparisons between pd-standard & pd-ssd.
1.Create a new storage.yaml file.
> nano storage.yaml
2.Paste the following template into nano (be mindful of the zone) and save/close nano via
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: ssd provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd zone: us-central1-a
3.Apply the storage class using the following command:
> kubectl apply -f storage.yaml
Elastic Stack Services
In order for our Elasticsearch nodes nodes to discover each other, we need to create a service that helps identify nodes within the Kubernetes cluster.
1.Create a new service.yaml file
> nano service.yaml
2.Paste the following template into nano and save/close via
# Services will allow our Elasticsearch nodes discover themselves properly apiVersion: v1 kind: Service metadata: name: es-nodes labels: service: elasticsearch spec: clusterIP: None ports: - port: 9200 name: external - port: 9300 name: internal selector: service: elasticsearch --- # Kibana service that will help group scaled instances for future load balancing apiVersion: v1 kind: Service metadata: name: kibana labels: service: kibana spec: clusterIP: None ports: - port: 5601 name: external - port: 9300 name: internal selector: service: kibana
3.Apply the service definition using the following command:
> kubectl apply -f service.yaml
Deploying The Stack
Finally we can get to the good stuff of actually deploying our Elasticsearch cluster and Kibana! Brace yourself for a large cluster StatefulSet down below, though I promise it will be a fun one!
1.Create a new cluster.yaml file.
> nano cluster.yaml
2.Paste the following template into nano and save/close nano via
# Elasticsearch node deployment and configuration apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: elasticsearch labels: service: elasticsearch spec: serviceName: es-nodes replicas: 3 selector: matchLabels: service: elasticsearch template: metadata: labels: service: elasticsearch spec: terminationGracePeriodSeconds: 300 # Utilize Init containers to set ulimit, vm-max-map-count, and volume permissions initContainers: - name: ulimit image: busybox command: - sh - -c - ulimit -n 65536 securityContext: privileged: true - name: vm-max-map-count image: busybox command: - sysctl - -w - vm.max_map_count=262144 securityContext: privileged: true - name: volume-permission image: busybox command: - sh - -c - chown -R 1000:1000 /usr/share/elasticsearch/data securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data # Containers defined for Elasticsearch nodes containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch:6.3.0 ports: - containerPort: 9200 name: http - containerPort: 9300 name: tcp resources: requests: memory: 400Mi limits: memory: 1Gi env: - name: cluster.name value: elasticsearch-cluster - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.zen.ping.unicast.hosts # Note that we are using "es-nodes" as defined in our service value: "elasticsearch-0.es-nodes.default.svc.cluster.local,elasticsearch-1.es-nodes.default.svc.cluster.local,elasticsearch-2.es-nodes.default.svc.cluster.local" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m -XX:-AssumeMP" volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteOnce storageClassName: ssd resources: requests: storage: 10Gi --- # Kibana node deployment apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: kibana labels: service: kibana spec: serviceName: kibana replicas: 1 selector: matchLabels: service: kibana template: metadata: labels: service: kibana spec: containers: - name: kibana image: docker.elastic.co/kibana/kibana:6.3.0 resources: # need more cpu upon initialization, therefore burstable class limits: cpu: 1000m requests: cpu: 100m env: - name: ELASTICSEARCH_URL value: http://elasticsearch-0.es-nodes.default.svc.cluster.local:9200 ports: - containerPort: 5601 name: ui protocol: TCP
3.Apply the storage class using the following command:
> kubectl apply -f cluster.yaml
Setup Kibana Access
For the sake of this tutorial, we will setup a load balancer to our Kibana instance with an external facing IP address. We will also configure to redirect port 80 to 5601 via our load balancer. Since we only spun up a single Kibana instances, we can setup our connectivity relatively easily.
- Navigate to Workloads.
- Click the workload named Kibana.
- Scroll down until you see all the managed pods, click kibana-0.
- Now click on the Expose menu option.
- Set the target port to 5601 and Service Type to Load Balancer, click Expose
- Navigate to the Service menu and you should see your newly created load balancer along with a public endpoint *note it is unsecured.
Nice job you now have a three node instance of Elasticsearch & Kibana running on GKE!