클러스터 운영 중 일부 데이터 노드들의 디스크 수명이 다 되어 교체가 필요한 상황. (현재 하나의 노드에 디스크 3대를 꽂아 사용중이다.)

평소와 같이 교체하려던 중 난감한 상황에 맞닥뜨렸다.

 

디스크 수급 문제로 인해 기존 디스크의 1/2 사이즈인 디스크로밖에 교체를 할 수 없다!

...

Elasticsearch는 모든 노드가 동일한 양의 디스크를 가지고 있다고 가정하므로 각 노드의 디스크 용량이 다를 시 문제가 발생할 수 있다.

교체가 필요한 노드는 총 32대의 데이터 노드 중 9대...

 

이 문제를 해결할 수 있는 방법을 강구해보았다.

 

 

1. shard allocation 옵션 적용

참고: https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-cluster.html#disk-based-shard-allocation

 

cluster.routing.allocation.disk.watermark.low, high 옵션을 조정해 노드의 디스크 사용량이 초과될 경우 적재된 샤드의 노드 이동 및 신규 샤드 할당 제한한다.

덧붙여 cluster.routing.allocation 아래의 옵션을 조정해 샤드 리밸런싱 및 복구 수 제한을 두어 또 샤드의 이동으로 인해 발생할 수 있는 부하를 최대한 줄인다.

 

가장 간단하고 빠르게 적용할 수 있는 방법이다.

그러나!

아무리 줄이고 제한을 둔다고 해도 샤드 리밸런싱 자체가 노드와 클러스터에 큰 부하를 일으킬 수 있는 작업이고, 더욱이 대용량 데이터를 다룰 경우 그 부담이 매우 크다.

 

따라서 위 방법은 사용하지 않는 것으로 결정.

 

 

 

2. shard allocation filtering 적용

참고: https://www.elastic.co/guide/en/elasticsearch/reference/current/shard-allocation-filtering.html#shard-allocation-filtering

 

shard allocation filtering을 사용하면 특정 인덱스의 샤드 할당 위치를 제어할 수 있다.

기존 디스크를 사용하는 25대의 노드와 절반 사이즈의 디스크를 사용하는 9대의 노드를 두고 현재 적재되고 있는 인덱스 별로 디스크 노드 타입을 지정해 분리 적재할 수 있다.

따라서 클러스터의 디스크 용량을 효율적으로 관리할 수도 있고 데이터 일관성을 유지할 수 있어 관리하기도 편하다.

 

해볼만한 방법이다.

그러나...

 

무엇보다 유연성이 제한된다는 점이 마음에 걸렸다.

각 인덱스들이 전체 데이터 노드에 고루 분산되지 않고 나뉘어 저장될 경우 샤드의 리밸런싱에 제한이 있을 뿐 아니라 일부 노드에 부하가 집중될 수 있는 가능성이 커진다.

특정 인덱스의 데이터가 급증하거나 쿼리량이 급증할 경우, 혹은 노드가 다운될 경우 등의 상황에 전체 데이터 노드가 부하를 나눠 받는게 아니라 디스크 타입 별 노드 그룹에 영향을 미치니 말이다.

또한 데이터 특성 상 중요도가 높고 자주 사용되는 인덱스 타입들은 사이즈가 작아 구성 상 9대의 노드에만 분배할 수 있었기에 더욱 부담스러웠다.

 

 

3. Hot - Warm 아키텍쳐 적용

참고: https://www.elastic.co/kr/blog/implementing-hot-warm-cold-in-elasticsearch-with-index-lifecycle-management

 

인덱스의 사용빈도(즉, disk의 I/O 빈도)에 따라 tier를 나눠서 보관하는 방법.

자주 사용되는 인덱스는 성능이 좋은 Hot 노드에 저장하여 사용하고, 간혈적으로 사용하는 인덱스는 비교적 성능이 낮은 Warm 노드에 저장하여 구성할 수 있다.

현재 운영중인 클러스터에 적용한다면 총 32대의 노드 중 25대의 기존 디스크를 가진 노드들을 Hot 노드로, 나머지 9대의 노드를 Warm 노드로 구성한 뒤 Hot노드로 적재된 인덱스들 중 자주 사용하지 않는 오래된 인덱스들은 Warm노드로 이동시키는 정책을 설정할 수 있다.

 

가장 합리적인 방법이라고 생각했다.

샤드 필터링과 마찬가지로 디스크 사용량을 효율적으로 관리할 수 있는데다가 

무엇보다 인덱스 타입 별로 구분하는 것이 아닌 최신 데이터와 오래된 데이터를 분리하는 것이기 때문에 부하 또한 골고루 분산될 수 있기 때문이다.

더욱이 현재 적재되는 인덱스들은 daily 단위로 관리되고 있기 때문에 이동시킬 데이터를 구분하기도 쉬웠고 해당 정책을 비교적 간단히 적용할 수 있었다.

 

한가지 마음에 걸리는 건 hot노드에서 warm 노드로 인덱스가 이동할 때 클러스터에 부하가 있지 않을까 했던 것.

그러나 전체 인덱스 대비 1/8 규모의 인덱스만 이동하고 데이터 적재량과 사용량이 아주 드문 새벽 시간대에 이동 작업을 진행하므로 크게 무리는 없겠다는 판단이 섰고 진행했다.

 

적용 과정

1. elasticsearch.yml 파일을 이용해 노드 속성 warm노드로 변경 (노드 재시작 필요)

node.attr.box_type: warm
  • v7.10 이후 node.roles에 data_hot, data_warm 등 속성 지정이 가능하나 현재 노드에 box_type 속성으로 지정되어있어 해당 옵션으로 적용

 

2. ILM에 warm phase 추가

PUT _ilm/policy/index-policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "8d",
        "actions": {
          "allocate": {
            "require": {
              "box_type": "warm"
            }
          }
        }
      },
      "delete": {
        "min_age": "10d",
        "actions": {
          "delete": {
            "delete_searchable_snapshot": true
          }
        }
      }
    }
  }
}
  • hot노드에 적재된 인덱스들이 8일이 지나면 warm 노드로 이동하며 10일이 지나면 클러스터에서 제거된다.
  • 각 노드 타입 별 디스크 사이즈와 인덱스 별 사이즈를 계산하여 적절한 이동 주기를 계산한 후 적용하였다.

 

 

3. 인덱스 템플릿에 ILM 추가

PUT _template/index_template
{
  "index_patterns": ["test-*"], 
  "settings": {
    "index.lifecycle.name": "index-policy"
  }
}
  • 이미 인덱스 템플릿을 이용해 인덱스들을 관리하고 있다면 해당 과정은 적용하지 않아도 된다 (ilm정책 자동 업데이트)
  • 롤오버 작업이 포함된 ILM정책의 경우 템플릿에 rollover_alias 추가 및 앨리어스에 is_write_index 설정이 필요하다

 

4. 적재된 인덱스에 ilm 적용해 warm노드 임의 이동 (필요 시)

PUT test-2024-03-10/_settings
{
  "index.lifecycle.name": "index-policy" 
}
  • 기존 인덱스에 ILM이 적용되지 않았을 경우 적용한다.

 

(인덱스 warm노드 임의 이동)

PUT test-2024-03-10/_settings
{
  "settings": {
    "index.routing.allocation.require.box_type": "warm"
  }
}
  • ILM을 적용하지 않고 임의로 warm노드로 이동할 수도 있다. (이동만 한다. 삭제는 알아서...)

 

결과

 

전체 노드의 disk 사용량을 60% 이하로 유지하며 클러스터 부하 없이 안정적으로 유지 중이다!

 

 

간단 회고

내키진 않지만 플랫폼에서 권장하지 않는 구성을 해야만 할 때가 있다.

이번 이슈의 경우 그런 어려운 상황을 극복함과 동시에 현재 운영중인 클러스터의 안정성을 더 높였다는 성취감이 있어 더욱 뿌듯했다. (그래프가 생각했던 것 보다 더 이쁘기도 하다 ^_^)

'Elasticsearch' 카테고리의 다른 글

nested list field size 집계  (1) 2023.12.23
keyword list 필드 string으로 합치기  (1) 2023.12.23
NGram analyzer  (0) 2023.12.14
Elasticsearh Enrich processor  (1) 2023.10.28
ElasticSearch(oss) vs OpenSearch  (0) 2023.10.28

검색 엔진은 전문화된 NoSQL의 유형

 

NoSQL 

ACID 제공하지 않음 (SQL보다 덜 제한적인 일관성 모델을 이용하는 데이터의 저장 및 검색 매커니즘 제공)

SQL 보다 더 좋은 Consistency, Partition Tolerlance, Availity 제공

 

 

※ CAP 이론

현존하는 거의 모든(혹은 모든) 데이터 저장하는 매체들은 Consistency, Partition Tolerlance, Availity 세 가지 중 두 가지만 추구 가능

 

SQL: Consistency 추구하는 데이터베이스

검색 엔진: Availity, Partition Tolerance를 축으로 하는 서비스 

- 하나의 문서 변경 시 색인 변경에 시간이 걸리고 그 사이 Consistency 추구되지 않을 수 있음

 

 

 

 

'검색 엔진' 카테고리의 다른 글

SQL vs 검색 엔진  (0) 2023.12.23

Scale, Speed, Usefulness

 

SQL vs 검색 엔진

SQL 검색 엔진
ACID 보장 (Atomic - 원자성, Consistent - 일관성, Isolated - 격리성, Durable - 지속성)
- 트랜젝션을 통해 데이터의 유효성 보장
ACID 미보장
결과가 Stale할 수 있고(최신 데이터 미반영) 없는 결과 나타날 수도 있으며 데이터가 바뀌어도 반영되는데 시간 소요될 수 있음
B-Tree, Linear Scan 사용해 데이터 사이즈 커질수록 속도 감소
- O(n), O(log n)
데이터 사이즈에 상관없이 균일 결과 출력
- O(1)
Realtime data(실시간 데이터) 제공
- 저장된 데이터 바로 확인 가능
데이터 추가, 인덱싱, 서빙에 상대적으로 꽤 큰 시간 소요
고급 검색 결과, 랭킹 기술 도입 어려움 고급 검색(text manipulation, audio image processing 등) 적용 가능
- 무제한 Advanced Search Feature 추가 가능
서빙 트래픽 증가할수록 서빙 스피드가 기하급수적으로 감소
- 사용자 선형적으로 증가 시 서비스의 cost 기하급수적으로 증가 
트래픽 늘어나더라도 부하 속도 일정적으로 유지 가능
- 트래픽에 매우 Scalable - 트래픽이 선형적으로 증가하더라도 서빙 시스템에 영향 미치지 않음

 

'검색 엔진' 카테고리의 다른 글

NoSQL vs 검색 엔진  (0) 2023.12.23

list 형태로 적재된 nested field의 전체 size 구하기

 

POST products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "bool": {
            "must": [
              {
                "nested": {
                  "path": "products",
                  "query": {
                    "exists": {
                      "field": "products"
                    }
                  }
                }
              }
            ] 
          }
        }
      ]
    }
  },
  "aggs": {
    "agg_hour": {
      "date_histogram": {
        "field": "time",
        "calendar_interval": "1h",
        "time_zone": "Asia/Seoul",
        "min_doc_count": 1
      },
      "aggs": {
        "products_agg": {
          "sum": {
            "script": {
              "inline": "params._source.products.size()"
            }
          }
        }
      }
    }
  }
}

 

products 인덱스에 nested list 형태로 적재되어있는 products 필드의 길이를 계산하여 시간대 별로 합산하여 집계

  • query 옵션으로 products 필드가 존재하는 문서만 집계 대상에 포함
  • aggs 옵션에 sum 메트릭 이용하여 products의 size의 합 집계
  • date_histogram 이용해 시간대 별 집계 적용

 

 

 

 

 

'Elasticsearch' 카테고리의 다른 글

Elasticsearch 디스크 불균등 이슈 해결하기  (0) 2024.03.10
keyword list 필드 string으로 합치기  (1) 2023.12.23
NGram analyzer  (0) 2023.12.14
Elasticsearh Enrich processor  (1) 2023.10.28
ElasticSearch(oss) vs OpenSearch  (0) 2023.10.28

keyword list형태의 필드를 하나의 string 으로 합쳐 재색인

  • reindex 및 script 사용

 

 

POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "keywords"
  },
  "dest": {
    "index": "keywords_to_string"
  },
  "script": {
    "source": """
       if (ctx._source.kwd != null) {
          StringBuilder combinedValues = new StringBuilder();
          for (item in ctx._source.kwd) {
            combinedValues.append(item);
            combinedValues.append(" ");
          }
          
          ctx._source.str = combinedValues.toString().trim();
          ctx._source.remove('kwd');
        }
      """,
    "lang": "painless"
  }
}

keywords 인덱스에 keyword list 형태로 적재되어있는 kwd 필드를 하나의 string으로 합쳐서 keywords_to_string 인덱스의 str 필드로 적재

  • script 옵션 이용해 string 생성 코드 작성
  • 스페이스(" ")로 구분자 적용
  • 기존 kwd 필드 제거 필요할 경우 remove 이용해 제거

'Elasticsearch' 카테고리의 다른 글

Elasticsearch 디스크 불균등 이슈 해결하기  (0) 2024.03.10
nested list field size 집계  (1) 2023.12.23
NGram analyzer  (0) 2023.12.14
Elasticsearh Enrich processor  (1) 2023.10.28
ElasticSearch(oss) vs OpenSearch  (0) 2023.10.28

NGram 

Elasticsearch에서는 빠른 검색을 위해 검색에 사용될 텀들을 미리 분리해 역인덱스(inverted index)에 저장함.

텀이 아닌 단어의 일부만 가지고 검색이 필요할 경우 검색 텀의 일부만 미리 분리해서 저장할 수 있는데, 이렇게 단어의 일부를 나눈 부위를 NGram이라고 함 (unigram - 1글자, bigram - 2글자 등) ("type": "nGram")

 

ex. "spring" 이라는 단어를 bigram으로 처리할 경우 "sp", "pr", "ri", "in", "ng" 총 5개의 토큰이 추출되며, ngram 토큰 필터 사용 시 2글자로 추출된 텀들이 모두 검색 토큰으로 저장됨 -> "pr" 검색 시 spring이 포함된 도큐먼트들 매치

 

Edge NGram 

 텀 앞쪽의 ngram 만 저장하기 위해서는 Edge NGram 토큰필터를 이용 ("type": "edgeNGram")

 

ex. edgeNGram의 옵션을 "min_gram": 1, "max_gram": 4 으로 설정하고 "spring" 분석 시 "s", "sp", "spr", "spri" 토큰 생성

 

Shingle

문자가 아닌 단어 단위로 구성된 묶음 ("type": "shingle" )

 

ex. "spring blooms bright flowers"를 Shingle 토큰 필터를 적용해 2단어씩 분리할 경우 "spring blooms", "bloom bright", "bright flowers" 3개의 shingle 생성

 

 

 

 

NGram Analyzer 적용

ex.

"컴퓨터프로그래밍_노래방카페_휴대폰어플_커피숍카페" 문자열을 "_" 구분자로 나눈 후 ngram 토큰 필터 적용해 분석하기

 

PUT ngram_test
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "ngram_analyzer": {
            "type": "custom",
            "tokenizer": "underscore_tokenizer",
            "filter": "bigram_filter"
          }
        },
        "filter": {
          "bigram_filter": {
            "type": "ngram",
            "min_gram": 2,
            "max_gram": 2
          }
        }, 
        "tokenizer": {
          "underscore_tokenizer": {
            "type": "pattern",
            "pattern": "_"
          }
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "ngram_analyzer"
      }
    }
  }
}
  • ngram_analyzer: underscore_tokenizer와 bigram_filter로 구성
    • underscore_tokenizer: "_"를 구분자로 토큰 추출
    • bigram_filter: 토큰들을 2글자씩 추출해 검색 토큰으로 저장

 

분석 및 결과

GET ngram_test/_analyze
{
  "analyzer": "ngram_analyzer",
  "text": "컴퓨터프로그래밍_노래방카페_휴대폰어플_커피숍카페"
}


{
  "tokens": [
    {
      "token": "컴퓨",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "퓨터",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "터프",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "프로",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "로그",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "그래",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "래밍",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "노래",
      "start_offset": 9,
      "end_offset": 14,
      "type": "word",
      "position": 1
    },
    {
      "token": "래방",
      "start_offset": 9,
      "end_offset": 14,
      "type": "word",
      "position": 1
    },
    {
      "token": "방카",
      "start_offset": 9,
      "end_offset": 14,
      "type": "word",
      "position": 1
    },
    {
      "token": "카페",
      "start_offset": 9,
      "end_offset": 14,
      "type": "word",
      "position": 1
    },
    {
      "token": "휴대",
      "start_offset": 15,
      "end_offset": 20,
      "type": "word",
      "position": 2
    },
    {
      "token": "대폰",
      "start_offset": 15,
      "end_offset": 20,
      "type": "word",
      "position": 2
    },
    {
      "token": "폰어",
      "start_offset": 15,
      "end_offset": 20,
      "type": "word",
      "position": 2
    },
    {
      "token": "어플",
      "start_offset": 15,
      "end_offset": 20,
      "type": "word",
      "position": 2
    },
    {
      "token": "커피",
      "start_offset": 21,
      "end_offset": 26,
      "type": "word",
      "position": 3
    },
    {
      "token": "피숍",
      "start_offset": 21,
      "end_offset": 26,
      "type": "word",
      "position": 3
    },
    {
      "token": "숍카",
      "start_offset": 21,
      "end_offset": 26,
      "type": "word",
      "position": 3
    },
    {
      "token": "카페",
      "start_offset": 21,
      "end_offset": 26,
      "type": "word",
      "position": 3
    }
  ]
}

 

 

검색 예제 및 결과

GET ngram_test/_search
{
  "query": {
    "match": {
      "text": "노래"
    }
  }
}



{
  ...
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.4249156,
    "hits": [
      {
        "_index": "ngram_test",
        "_id": "2m8UaIwBamOI6MTqQUbR",
        "_score": 0.4249156,
        "_source": {
          "text": "컴퓨터프로그래밍_노래방카페_휴대폰어플_커피숍카페"
        }
      }
    ]
  }
}

 

 

 

 

 

wildcard 쿼리와의 비교

 

Elasticsearch는 RDBMS의 LIKE 검색 처럼 사용하는 wildcard 쿼리나 regexp (정규식) 쿼리도 지원을 하지만, 이런 쿼리들은 메모리 소모가 많고 느리기 때문에 Elasticsearch의 장점을 활용하지 못함

 

wildcard 쿼리는 term level 쿼리이기 때문에 inverted index의 term(token) 목록 중 쿼리에서 질의한 keyword 검색

  • token기준으로 wildcard 에서 못찾는 document 검색 가능
  • ngram이 반응 속도 더 빠름
  • token 의 갯수가 많아지기 때문에 inverted index 사이즈 증가

모니터링 시스템에서 Pull 또는 Push 동작 방식 비교 및 선택을 위한 리서치

 

 

1.  모니터링 시스템 유형

다양한 관점에서 여러 범주로 나눌 수 있는 수백 가지가 넘는 모니터링 시스템 존재

  • 모니터링 대상: 범용(대부분의 모니터링 대상에 적합한 일반적인 모니터링 방법), 전용(Java JMX 시스템, CPU 고온 보호 등 특정 기능에 맞게 사용자 지정)
  • 데이터 수집 방법: Pull(Prometheus, SNMP, JMX), Push(CollectD, Zabbix, InfluxDB)
  • 배포 모드: Coupled(결합형, 모니터링 시스템과 함께 배포), standalone(독립형, 단일 인스턴스 배포), distributed(분산형, 수평적 확장 가능), SaaS(배포가 필요 없음, 많은 상용 회사에서 제공).
  • 데이터 수집 방법: 인터페이스 유형(일부 API를 통해서만 데이터 수집), DSL(PromQL 및 GraphQL 등 포함), SQL(표준 SQL 등).
  • 상업적 속성: 오픈 소스 및 무료(ex: Prometheus 및 InfluxDB standalone 버전), 오픈 소스 상용 유형(ex: InfluxDB 클러스터 버전, Elastic Search X-Pack), closed-source 상용 유형(예: DataDog, Splunk 및 AWS Cloud Watch).
 
 

 

2. Pull or Push?

데이터 수집 방법에 따른 동작 방식 

Pull

모니터링이 필요한 오브젝트에 원격으로 접근해 지표를 능동적으로 획득. 모니터링이 필요한 개체에 원격으로 엑세스할 수 있는 기능 필요

 

Push

모니터링이 필요한 오브젝트가 적극적으로 지표 Push

 

 

모니터링 시스템의 구축 및 선택을 위해서는 이 두 가지 방법의 장단점을 미리 이해하고 구현에 적합한 계획을 선택해야 함

-> 모니터링 시스템의 안정성을 보장하기 위한 후속 유지 관리 비용과 배포 및 O&M 비용 증가 방지

 

 

 

3. Pull과 Push 방식 Overview

 

 

 

4.  원리 및 아키텍처 비교

 

Pull Model Architecture

Pull 모델 데이터 수집의 핵심: Prometheus와 같은 모니터링 백엔드와 함께 배포되는 Pull 모듈

  1. Service discovery system에 연결할 수 있는 기능이 있어야 함 (host service discovery(일반적으로 회사의 CMDB 시스템에 의존), application service discovery(ex: Consul), PaaS service discovery(예: Kubernetes))
  2. 일반적으로 공통 프로토콜을 사용하여 원격 끝(remote end)에서 데이터 가져오며, pull interval, timeout interval, metric filtering, rename 및 간단한 프로세스 기능 제공
  3. application-end SDK는 풀링 기능을 제공하기 위해 고정 포트에 대한 수신 대기 기능 지원
  4. 다양한 미들웨어 및 기타 시스템이 Pull 프로토콜과 호환되지 않으므로, 시스템의 메트릭 가져오기를 지원하고 표준 Pull 인터페이스를 제공하기 위해 Exporter에 해당하는 에이전트 개발 필요

 

Push Model Architecture

비교적 간단한 Push 모델

  1. Push Agent는 다양한 모니터링 대상의 메트릭 데이터 가져와 서버로 푸시하는 기능 지원. 모니터링 시스템과 결합된 방식으로 배포하거나 별도로 배포 가능
  2. ConfigCenter(옵션)는 모니터링 대상, 수집 간격, 메트릭 필터링, 메트릭 처리 및 원격 대상과 같은 중앙 집중식 동적 구성 기능 제공
  3. application-end SDK는 모니터링 백엔드 또는 로컬 에이전트로 데이터 전송 지원(일반적으로 로컬 에이전트는 일련의 백엔드 인터페이스도 구현)

 

요약

Pull 모델의 배포 방식은 미들웨어 및 기타 시스템을 모니터링하기에는 너무 복잡하고 유지 관리 비용 높음

Push 모드는 상대적으로 편리하지만, Metrics 포트를 제공하거나 사전에 배포를 푸시하는데 드는 애플리케이션의 비용 거의 동일

 

 

 

5. Pull's Distributed Solution

 

 

확장성 측면에서 Push 데이터 수집은 기본적으로 분산되어 있고 모니터링 백엔드 기능이 지원되는 경우 제한 없이 수평적으로 확장 가능하나, Pull 확장성 방식은 더 번거로움

  1. Pull 모듈은 모니터링 백엔드에서 분리되어 있으며, Pull은 에이전트로서 별도 배포
  2. Pull 에이전트는 분산 협업을 실현해야 하며, 이를 위한 가장 간단한 방법은 샤딩(ex: service discovery system에서 모니터링되는 머신의 목록을 가져오고, 머신에서 해시 모듈로 작업을 수행한 다음, 샤딩을 통해 어떤 에이전트가 Pull을 담당할지 결정)
  3. Pull 에이전트를 관리하기 위한 configuration center 추가

 

이 분산 방식에 대한 문제점

  1. 단일 지점 병목 현상이 여전히 존재하며 모든 에이전트가 service discovery module 요청 필요
  2. 에이전트 확장 후 모니터링 대상 변경되어 데이터 중복 혹은 누락 가능
 

 

 

6.  모니터링 기능 비교

Monitoring Target Survivability

생존 가능성은 모니터링에 필요한 가장 기본적인 첫 번째 작업

 

Pull

목표 생존 가능성(target survivability) 모니터링이 비교적 간단

Pull 의 중앙에서 목표 지표의 요청 여부 확인 가능하며, 장애 발생 시 네트워크 시간 초과, 피어 연결 거부와 같은 간단한 오류에 대한 알림 수신

 

Push

푸시 모드는 더 번거로움

애플리케이션이 report하지 않을 시 애플리케이션 고장, 네트워크 문제 또는 애플리케이션이 다른 노드로 마이그레이션 가능성 있음

Pull 모듈은 실시간으로 service discovery와 필터 상호 작용을 수행할 수 있지만 Push 모드는 이를 수행할 수 없어 서버가 service discovery와 상호 작용한 후에야만 구체적인 장애 원인 파악 가능

 

Data Completeness Calculation

데이터 완전성의 개념은 대규모 모니터링 시스템에서 매우 중요

(ex:  1,000개의 사본이 있는 트랜잭션 애플리케이션의 QPS 모니터링 시 이 지표를 1,000개의 데이터와 결합 필요. 데이터가 완전하지 않은 경우, QPS가 2% 감소할 때 알람을 트리거하도록 구성되었다고 가정할 때, 20개 이상의 복사본에서 보고된 데이터가 네트워크 변동으로 인해 몇 초 지연되면 잘못된 알람 트리거됨. 따라서 알람을 구성할 때 데이터 완전성 고려 필요)

 

데이터 완전성 계산은 service discovery 모듈에 따라 달라짐

 

Pull

Pull 방식은 데이터를 한 라운드씩 차례로 가져오는 방식이므로, 한 라운드가 끝나면 데이터가 완성

일부 Pull이 실패하더라도 불완전한 데이터의 비율 파악 가능

 

Push

Push 모드에서는 각 에이전트와 애플리케이션이 능동적으로 푸시 수행

각 클라이언트의 푸시 간격과 네트워크 지연 시간은 달라 과거 상황에 따라 서버가 데이터 완전성을 계산해야 하므로 막대한 비용이 발생

 

 

Short Lifecycle/Serverless Application Monitoring

실제 시나리오에서는 short-lifecycle/serverless 애플리케이션이 많으며, 특히 비용 측면에서 많은 수의 작업, 탄력적 인스턴스, 서버리스 애플리케이션 사용 (ex: 렌더링 작업이 도착한 후 탄력적 컴퓨팅 인스턴스가 시작되고 완료되면 즉시 해제. 머신 러닝 학습 작업, 이벤트 기반 서버리스 워크플로우, 정기적으로 실행되는 작업(리소스 정리, 용량 확인, 보안 스캔 등))

 

수명 주기가 짧은 애플리케이션을 관리하기 위해 순수 Pull 시스템은 애플리케이션의 사전 예방적 푸시를 수락한 다음 모니터링 시스템에 풀 포트를 제공하는 중간 계층(예: Prometheus Push Gateway)을 제공하나, 이는 추가적인 중간 계층의 관리 및 O&M 비용으로 이어짐

이 모드는 푸시를 시뮬레이션하는 Pull을 통해 이루어지기 때문에 보고 지연 시간이 길어지고, 즉시 사라지는 이러한 메트릭을 제때 정리해야 함

 

 

Flexibility and Coupling

Flexibility(유연성) 측면에서는 Pull 모드가 다소 유리

Pull 모듈에서 원하는 지표를 구성하고 지표에 대해 간단한 계산과 보조 처리를 할 수 있으나

Push SDK/에이전트에서도 이러한 파라미터를 구성할 수 있고 구성 센터의 도움으로 구성 관리가 간단

Coupling(결합성) 측면에서 Pull모델은 백엔드 간의 결합도가 낮고 백엔드가 이해할 수 있는 인터페이스만 제공하면 됨

어떤 백엔드가 연결되어 있고 백엔드에 어떤 지표가 필요한지 알 필요 없이 분업이 명확합

애플리케이션 개발자는 애플리케이션의 지표만 노출하면 되고, SRE는 이러한 지표 확인 가능 

Push 모델은 결합 정도가 더 높음

애플리케이션이 백엔드 주소와 인증 정보를 구성해야 함

그러나 로컬 push 에이전트에 의존하면 애플리케이션은 로컬 주소만 푸시하면 되므로 큰 비용이 들지 않음

 

 

 

7.  Resource 와 O&M 비용

Resource 비용

전체 비용 측면에서는 두 접근 방식 간에 약간의 차이가 있지만 비용 분포 고려 시

  • Pull 모드의 비용은 주로 모니터링 시스템 단에 집중. 애플리케이션 단에서는 비용 적음
  • Push 모드의 핵심 소비는 푸시 에이전트 단에 집중.  모니터링 시스템 단에서의 비용은 Pull보다 훨씬 적음

O&M 비용

Pull모드의 비용이 더 높음

  • Pull 모드의 O&M을 담당하는 구성 요소에는 다양한 exporters, service discovery, Pull Agent, 모니터링 백엔드 포함
  •  Push 모드는 Push 에이전트, 모니터링 백엔드, configuration center(선택 사항, 일반적으로 모니터링 백엔드와 함께 배포 완료됨)에 대한 O&M만 수행

 

한 가지 주의할 점 - Pull 모드에서는 서버가 클라이언트에 대한 요청을 능동적으로 시작하기 때문에 네트워크에서 애플리케이션 측의 클러스터 간 연결 및 네트워크 보호 ACL을 고려해야 함. Push 모드에 비해 네트워크 연결은 간단하며, 서버가 각 노드가 액세스할 수 있는 도메인 이름/VIP만 제공하면 됨

 

 

 

8.  Pull or Push 선택

Pull 패턴의 대표적인 오픈소스 솔루션: Prometheus 패밀리 솔루션. (Prometheus 확장성이 제한적이기 때문에 패밀리라고 지칭. Prometheus에는 Thanos, VicoriaMetrics, Cortex 등 기술 커뮤니티에 분산된 솔루션이 많이 있음)

Push 패턴의 대표적 솔루션: InfluxDB의 TICK(Telegraf, InfluxDB, Chronograf, Kapacitor)

 

두 솔루션 모두 장단점 존재

클라우드 네이티브라는 배경에서 Prometheus는 CNCF와 Kubernetes의 인기와 함께 번성하기 시작하여 이미 많은 오픈 소스 소프트웨어가 Prometheus 모드에서 Pull 포트를 제공

그러나 많은 시스템이 설계 초기부터 Pull 포트를 제공하는 데 어려움을 겪어왔습니다. 이러한 시스템을 모니터링하는 데는 Push 에이전트 방식이 더 적합

 

사내 실제 시나리오를 기반으로 Pull, Push 방식 선택 필요

ex: 사내 네트워크 클러스터가 매우 복잡하거나 수명주기가 짧은 애플리케이션 사용 시 Push 선택. 모바일 애플리케이션의 경우 Push만 사용 가능. 시스템 자체는 service discovery위해 Consul을 사용하고 Pull 포트 export해 쉽게 구현 가능

 

회사 내 모니터링 시스템의 경우 Pull과 Push 기능을 모두 갖춘 것이 최적의 솔루션

  1. 호스트, 프로세스, 미들웨어 모니터링은 Push 에이전트 사용
  2. Kubernetes 및 기타 직접 노출(exposed)된 Pull 포트는 Pull 모드를 사용
  3. 애플리케이션은 실제 시나리오에 따라 Pull 또는 Push 선택

'기타' 카테고리의 다른 글

Airflow  (0) 2023.11.24

kubectl로 custom resource definition(crd) 제거 시 동작 안될 때

 

증상

helm 차트 삭제 시 특정 crd 계속 남아있음

kubectl delete 명령어로 특정 crd 제거해도 동작 안함

kubectl delete crd flinksessionjobs.flink.apache.org

 

 

원인

finalizer를 가진 custom resource가 "deadlock" 상태에 빠짐

 

** finalizer:  

CRD의 수명 주기를 관리하는 중요한 개념 중 하나.

kubernetes 오브젝트의 삭제 과정에서 수행되는 특별한 작업 정의.

삭제되기 전에 수행되어야 하는 추가적인 작업이나 정리 과정을 finalizer를 통해 지정.

 

 

해결

kubectl patch 명령어를 이용하여 해당 CRD의 finalizer 제거

kubectl patch crd/flinksessionjobs.flink.apache.org -p '{"metadata":{"finalizers":[]}}' --type=merge 
customresourcedefinition.apiextensions.k8s.io/flinksessionjobs.flink.apache.org patched (no change)


kubectl delete crd flinksessionjobs.flink.apache.org
warning: deleting cluster-scoped resources, not scoped to the provided namespace
customresourcedefinition.apiextensions.k8s.io "flinksessionjobs.flink.apache.org" deleted

 

 

제거 후 helm 차트 제거 시 정상 동작

'Kubernetes' 카테고리의 다른 글

K8S Jenkins 구축  (1) 2023.11.11

+ Recent posts