๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ”Ž Kubernetes

Elasticsearch Operator ๊ฐœ๋ฐœํ•˜๊ธฐ

by Seongpyo Hong 2020. 12. 18.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Operator SDK๋ฅผ ํ†ตํ•ด Elasticsearch Cluster๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” Elasticsearch Operator๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Operator๊ฐ€ ๋ฌด์—‡์ธ์ง€, Operator SDK์— ๋Œ€ํ•œ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์ด ๊ถ๊ธˆํ•˜์‹  ๋ถ„๊ป˜์„œ๋Š” ์ด์ „ ๊ธ€์„ ์ฐธ๊ณ ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Elasticsearch Cluster

๋จผ์ € Operator๋กœ ๊ด€๋ฆฌํ•  Elasticsearch Operator์— ๋Œ€ํ•œ ์ŠคํŽ™์„ ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Cluster ๊ตฌ์„ฑ์€ ์ œ๊ฐ€ ์ด์ „์— ์ž‘์„ฑํ–ˆ๋˜ Elasticsearch Helm Chart์˜ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • Master Node & Master Eligible Node
    ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ์™€ ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ๊ฐ€ ๋‹ค์šด๋์„ ๋•Œ ์Šน๊ฒฉ๋  ์ˆ˜ ์žˆ๋Š” ํ›„๋ณด ๋…ธ๋“œ์ž…๋‹ˆ๋‹ค.
  • Hot Data Node
    Elasticsearch์˜ Hot-Warm ์•„ํ‚คํ…์ณ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด Node์˜ ๋””์Šคํฌ ํƒ€์ž…์œผ๋กœ Data Node๋ฅผ ๋‚˜๋ˆ„์—ˆ์Šต๋‹ˆ๋‹ค. Hot Data Node๋Š” ๋น ๋ฅธ Disk I/O ์„ฑ๋Šฅ์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— SSD๋ฅผ ๊ฐ€์ง„ ๋…ธ๋“œ์—๋งŒ ๋ฐฐ์น˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • Warm Data Node
    Warm Data Node๋Š” Disk I/O ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๊ฒฉ์ด ๋น„์‹ผ SSD๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ HDD๋ฅผ ๊ฐ€์ง„ ๋…ธ๋“œ์—๋งŒ ๋ฐฐ์น˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • Coordinating Node
    ์š”์ฒญ ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ผ์šฐํŒ…ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ๋…ธ๋“œ์ž…๋‹ˆ๋‹ค.

  • Cerebro
    Elasticsearch Cluster ๋ฐ Index ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

  • Kibana
    Elasticsearch์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

Elasticsearch Cluster์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•˜์‹  ๋ถ„๋“ค์€ Elasticsearch ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ž์„ธํ•œ ๋…ธ๋“œ์˜ ์—ญํ• ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Elasticsearch Operator

CRD API

๋จผ์ € CRD๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Elasticsearch CRD๊ฐ€ ๊ฐ€์ง€๊ฒŒ ๋  ๋ฐ์ดํ„ฐ๋Š” api/v1alpha1/elasticsearch_types.go ์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

package v1alpha1

import (
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ElasticsearchStatus defines the observed state of Elasticsearch
type ElasticsearchStatus struct {
    // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
    // Important: Run "make" to regenerate code after modifying this file
    State   string `json:"state,omitempty"`
    Message string `json:"message,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// Elasticsearch is the Schema for the elasticsearches API
type Elasticsearch struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   ElasticsearchSpec   `json:"spec,omitempty"`
    Status ElasticsearchStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// ElasticsearchList contains a list of Elasticsearch
type ElasticsearchList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitempty"`
    Items           []Elasticsearch `json:"items"`
}

// ElasticsearchSpec defines the desired state of Elasticsearch
type ElasticsearchSpec struct {
    // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    // Important: Run "make" to regenerate code after modifying this file
    //Master Node Replicas
    MasterReplicas int32 `json:"master-replicas"`

    //Client Node Replicas
    ClientReplicas int32 `json:"client-replicas"`

    //Hot Data Node Replica
    HotDataReplicas int32 `json:"hot-data-replicas"`

    //Warm Data Node Replica
    WarmDataReplicas int32 `json:"warm-data-replicas"`

    //NodeSelector for the pod to be eligible to run on a node (ex. Hot-Warm Architecture)
    NodeSelector map[string]string `json:"nodeSelector,omitempty"`

    //Annotations
    Annotations map[string]string `json:"annotations,omitempty"`

    //Disk Size of Hot Data Node
    HotDataDiskSize string `json:"hot-data-volume"`

    //Disk Size of Warm Data Node
    WarmDataDiskSize string `json:"warm-data-volume"`

    //Elasticsearch Image
    ElasticsearchImage string `json:"elasticsearch-image"`

    //Elasticsearch Cluster Name
    ElasticsearchClusterName string `json:"elasticsearch-cluster-name"`
    //MasterJavaOpt
    MasterJavaOpts string `json:"master-javaOpts"`

    //ClientJavaOpt
    ClientJavaOpts string `json:"client-javaOpts"`

    //HotDataJavaOpt
    HotDataJavaOpts string `json:"hot-data-javaOpts"`

    //WarmDataJavaOpt
    WarmDataJavaOpts string `json:"warm-data-javaOpts"`

    //Cerebro
    Cerebro Cerebro `json:"cerebro"`

    //Kibana
    Kibana Kibana `json:"kibana"`

    //Curator
    Curator Curator `json:"curator"`
}

// Kibana properties (Optional)
type Kibana struct {
    // Defines the image to use for deploying kibana
    Image string `json:"image"`
}

// Cerebro properties (Optional)
type Cerebro struct {
    // Defines the image to use for deploying Cerebro
    Image string `json:"image"`
}

// Curator properties (Optional)
type Curator struct {
    // Defines the image to use for deploying Curator
    Image string `json:"image"`
}

func init() {
    SchemeBuilder.Register(&Elasticsearch{}, &ElasticsearchList{})
}

Elasticsearch Cluster์˜ Spec์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

  • Elasticsearch Image
  • Node์˜ Replica ์ˆ˜ (Master / Hot / Warm / Coordinating)
  • Data Node Disk ํฌ๊ธฐ (Hot / Warm)
  • Java Options (Master / Hot / Warm / Coordinating)
  • Cerebro Image
  • Kibana Image

์œ„์˜ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ์„ฑ๋œ CRD๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. (config/crd/bases ํด๋” ์•„๋ž˜ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.)

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.3.0
  creationTimestamp: null
  name: elasticsearches.sphong.com.my.domain
spec:
  group: sphong.com.my.domain
  names:
    kind: Elasticsearch
    listKind: ElasticsearchList
    plural: elasticsearches
    singular: elasticsearch
  scope: Namespaced
  subresources:
    status: {}
  validation:
    openAPIV3Schema:
      description: Elasticsearch is the Schema for the elasticsearches API
      properties:
        apiVersion:
          description: 'APIVersion defines the versioned schema of this representation
            of an object. Servers should convert recognized schemas to the latest
            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
          type: string
        kind:
          description: 'Kind is a string value representing the REST resource this
            object represents. Servers may infer this from the endpoint the client
            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
          type: string
        metadata:
          type: object
        spec:
          description: ElasticsearchSpec defines the desired state of Elasticsearch
          properties:
            annotations:
              additionalProperties:
                type: string
              description: Annotations
              type: object
            cerebro:
              description: Cerebro
              properties:
                image:
                  description: Defines the image to use for deploying Cerebro
                  type: string
              required:
              - image
              type: object
            client-javaOpts:
              description: ClientJavaOpt
              type: string
            client-replicas:
              description: Client Node Replicas
              format: int32
              type: integer
            curator:
              description: Curator
              properties:
                image:
                  description: Defines the image to use for deploying Curator
                  type: string
              required:
              - image
              type: object
            elasticsearch-cluster-name:
              description: Elasticsearch Cluster Name
              type: string
            elasticsearch-image:
              description: Elasticsearch Image
              type: string
            hot-data-javaOpts:
              description: HotDataJavaOpt
              type: string
            hot-data-replicas:
              description: Hot Data Node Replica
              format: int32
              type: integer
            hot-data-volume:
              description: Disk Size of Hot Data Node
              type: string
            kibana:
              description: Kibana
              properties:
                image:
                  description: Defines the image to use for deploying kibana
                  type: string
              required:
              - image
              type: object
            master-javaOpts:
              description: MasterJavaOpt
              type: string
            master-replicas:
              description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
                Important: Run "make" to regenerate code after modifying this file
                Master Node Replicas'
              format: int32
              type: integer
            nodeSelector:
              additionalProperties:
                type: string
              description: NodeSelector for the pod to be eligible to run on a node
                (ex. Hot-Warm Architecture)
              type: object
            warm-data-javaOpts:
              description: WarmDataJavaOpt
              type: string
            warm-data-replicas:
              description: Warm Data Node Replica
              format: int32
              type: integer
            warm-data-volume:
              description: Disk Size of Warm Data Node
              type: string
          required:
          - cerebro
          - client-javaOpts
          - client-replicas
          - curator
          - elasticsearch-cluster-name
          - elasticsearch-image
          - hot-data-javaOpts
          - hot-data-replicas
          - hot-data-volume
          - kibana
          - master-javaOpts
          - master-replicas
          - warm-data-javaOpts
          - warm-data-replicas
          - warm-data-volume
          type: object
        status:
          description: ElasticsearchStatus defines the observed state of Elasticsearch
          properties:
            message:
              type: string
            state:
              description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
                of cluster Important: Run "make" to regenerate code after modifying
                this file'
              type: string
          type: object
      type: object
  version: v1alpha1
  versions:
  - name: v1alpha1
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

Controller

๋‹ค์Œ์œผ๋กœ Controller์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € SetupWithManager ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

func (r *ElasticsearchReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&sphongcomv1alpha1.Elasticsearch{}).
        Owns(&v1.Deployment{}).
        Owns(&v1.StatefulSet{}).
        WithOptions(controller.Options{
            MaxConcurrentReconciles: 3,
        }).Complete(r)
}
  • ํŠœํ† ๋ฆฌ์–ผ๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ Elasticsearch Operator์—์„œ ๊ด€์ฐฐํ•  ๋ฆฌ์†Œ์Šค๊ฐ€ Deployment ์ด์™ธ์—๋„ StatefulSet๋„ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Owns(&v1.StatefulSet{}) ์„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ƒ์„ฑํ•œ Elasticsearch CR์— ๋Œ€ํ•ด Reconcile Loop๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ณณ์€ Reconcil ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

//Fetch the Elasticsearch Cluster
    elasticsearch := &sphongcomv1alpha1.Elasticsearch{}
    err := r.Get(ctx, req.NamespacedName, elasticsearch)
    if err != nil {
        if errors.IsNotFound(err) {
            // Case 1
            log.Info("Elasticsearch Resource Not Found!. Ignore since object must be deleted")
            return ctrl.Result{}, nil
        }
        // Case 2
        log.Error(err, "Failed to get Elasticsearch")
        return ctrl.Result{}, err
    }
  • Case 1์˜ ๊ฒฝ์šฐ๋Š” Request๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋Š”๋ฐ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ์ฆ‰, ์‚ญ์ œ๋œ ๊ฒฝ์šฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๊ธฐ์กด ์กด์žฌํ•˜๋˜ ๋ฆฌ์†Œ์Šค๋Š” ์ž๋™์œผ๋กœ GC๋˜๊ณ  ํ•„์š”ํ•˜๋‹ค๋ฉด ์ด ๋ถ€๋ถ„์— ๋ณ„๋„์˜ ์‚ญ์ œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์‚ญ์ œ ๊ฐ™์€ ๊ฒฝ์šฐ, ์ด๋ฒคํŠธ ํ์— ๋‹ค์‹œ Request๋ฅผ ๋„ฃ์ง€ ์•Š์œผ๋ฏ€๋กœ nil์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Case 2์˜ ๊ฒฝ์šฐ๋Š” Request๋ฅผ ์ •์ƒ์ ์œผ๋กœ ์ฝ์ง€ ๋ชปํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” Request๋ฅผ ์ด๋ฒคํŠธ ํ์— ๋‹ค์‹œ ๋„ฃ์–ด์ค˜์•ผ ํ•˜๋ฏ€๋กœ err์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ๋Š” Elasticsearch Cluster์— ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋“ค์ด ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

...

//Create Discovery Service
    foundMasterSvc := &v12.Service{}
    err = r.Get(ctx, types.NamespacedName, foundMasterSvc)

    if err != nil && errors.IsNotFound(err) {
        creationErr := r.createMasterService(elasticsearch)
        if creationErr != nil {
            return reconcile.Result{}, err
        }
    }

...
  • ๋งŒ์•ฝ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ƒˆ๋กœ์šด ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ƒ์„ฑ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉด request๋ฅผ ์ด๋ฒคํŠธ ํ์— ๋‹ค์‹œ ๋„ฃ์–ด์ค˜์•ผ ํ•˜๋ฏ€๋กœ creationErr๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ถ€๋ถ„์€ Spec๊ณผ Status๊ฐ€ ๋™์ผํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

//scaling status's size => spec's size
    hotDataSize := elasticsearch.Spec.HotDataReplicas
    if *foundHotData.Spec.Replicas != hotDataSize {
        foundHotData.Spec.Replicas = &hotDataSize
        err = r.Client.Update(context.TODO(), foundHotData)
        if err != nil {
            log.Error(err, "Failed to update Deployment's Size (Hot Data Node Replica)")
            return reconcile.Result{}, err
        }
    }

    warmDataSize := elasticsearch.Spec.WarmDataReplicas
    if *foundWarmData.Spec.Replicas != warmDataSize {
        foundWarmData.Spec.Replicas = &warmDataSize
        err = r.Client.Update(context.TODO(), foundWarmData)
        if err != nil {
            log.Error(err, "Failed to update Deployment's Size")
            return reconcile.Result{}, err
        }
    }
  • ์ œ๊ฐ€ ๋งŒ๋“  Elasticsearch Operator์—์„œ๋Š” Hot / Warm Data Node ์ˆ˜๋งŒ ๊ฒ€์‚ฌํ•˜์—ฌ Spec๊ณผ ๋™์ผํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋™์ผํ•˜์ง€ ์•Š๋‹ค๋ฉด Request๋ฅผ ์ด๋ฒคํŠธ ํ์— ๋‹ค์‹œ ๋„ฃ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ Hot/ Warm Data Node์˜ Status๋ฅผ ๊ฐ Pod์˜ ์ด๋ฆ„์œผ๋กœ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.

hotPodList := &v12.PodList{}
    hotListOpts := []client.ListOption{
        client.InNamespace(elasticsearch.Namespace),
        client.MatchingLabels(labelsForWarmData()),
    }
    if err = r.List(ctx, hotPodList, hotListOpts...); err != nil {
        log.Error(err, "Failed to list pods")
        return ctrl.Result{}, err
    }

    warmPodList := &v12.PodList{}
    warmListOpts := []client.ListOption{
        client.InNamespace(elasticsearch.Namespace),
        client.MatchingLabels(labelsForWarmData()),
    }
    if err = r.List(ctx, warmPodList, warmListOpts...); err != nil {
        log.Error(err, "Failed to list pods")
        return ctrl.Result{}, err
    }
    return ctrl.Result{}, nil

Deploy Operator

์ด๋ฒˆ์—๋Š” ๋กœ์ปฌ ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ์•„๋‹Œ ์™ธ๋ถ€ ํด๋Ÿฌ์Šคํ„ฐ์— operator๋ฅผ ๋ฐฐํฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ €๋Š” GKE๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋จผ์ €, ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ  GCS์— ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค.

> make docker-build IMG=<GCS Image> 
> make docker-push IMG=<GCS Image>

๋‹ค์Œ์œผ๋กœ kustomize์— namespace์— ๋Œ€ํ•œ ์„ค์ •์„ ๋ฐฐํฌํ•  namespace๋กœ ๋ณ€๊ฒฝํ•ด์ค๋‹ˆ๋‹ค. ์ €๋Š” default namespace์— ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

> cd config/default/ && kustomize edit set namespace "default" && cd ../..

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

> make deploy IMG=<GCS Image>

kubectl๋กœ ํ™•์ธํ•ด๋ณด๋ฉด elasticsearch operator๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

> kubectl get deployment
NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
elasticsearch-operator-controller-manager   1/1     1            1           1m

Create Elasticsearch CR

config/samples/ํด๋” ์•„๋ž˜์— ์กด์žฌํ•˜๋Š” CR์„ ์—…๋ฐ์ดํŠธ ํ•œ ํ›„ ์ ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ์›ํ•˜๋Š” ํด๋Ÿฌ์Šคํ„ฐ ์ŠคํŽ™์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

apiVersion: sphong.com.my.domain/v1alpha1
kind: Elasticsearch
metadata:
  name: elasticsearch-sample
  namespace: default
spec:
  cerebro:
    image: asia.gcr.io/--/cerebro:6.8.2
  client-javaOpts: ""
  client-replicas: 1
  curator:
    image: asia.gcr.io/--/cerebro:6.8.2
  elasticsearch-cluster-name: es-cluster
  elasticsearch-image: asia.gcr.io/--/elasticsearch-base:6.8.2
  hot-data-javaOpts: ""
  hot-data-replicas: 1
  hot-data-volume: 1Gi
  kibana:
    image: asia.gcr.io/--/kibana:6.8.2
  master-javaOpts: ""
  master-replicas: 3
  warm-data-javaOpts: ""
  warm-data-replicas: 1
  warm-data-volume: 1Gi

kubectl์„ ํ†ตํ•ด ํ•ด๋‹น CR์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

$ kubectl apply -f config/samples/elasticsearch-customresource.yaml

๋งˆ์ง€๋ง‰์œผ๋กœ ์ƒ์„ฑ๋œ Elasticsearch Cluster๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

$ kubectl get all
NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
elasticsearch-operator-controller-manager   1/1     1            1           8m
elasticsearch-master                        3/3     3            3           1m
elasticsearch-hot-data-0                    3/3     3            3           1m
...

์ด์ƒ์œผ๋กœ Elasticsearch Cluster๋ฅผ ๋ณด๋‹ค ์†์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ Hot / Warm Data Node ์ˆ˜์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๋Š” Elasticsearch Operator๋ฅผ ๊ฐœ๋ฐœํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ „์ฒด ์ฝ”๋“œ๋Š” Github์— ์กด์žฌํ•˜๋ฉฐ ๊ธ€์˜ ๋‚ด์šฉ ์ค‘ ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด๋‚˜ ๋” ์ข‹์€ ์˜๊ฒฌ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ํ”ผ๋“œ๋ฐฑ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. :)

์ฐธ๊ณ  ์ž๋ฃŒ


๋Œ“๊ธ€