
네트워크 사용과 프로그램 반입이 자유롭지 않은 환경에서 데이터를 이관해야 할 일이 생겼다. 단기로 급하게 지원을 나온 상태라 내부 히스토리도 잘 모르는 상황이었다. 나중에 운영으로 데이터를 이관해야할 땐 Apache nifi 반입 신청을 하겠지만 지금 당장은 쓸 수없는 것 처럼 보였기에, ELK Stack을 쓸 방법을 생각해 봤다.
1. Elasticsaerch data 디렉토리를 통째로 복사
2. Logstash를 이용한 복사
3. Elasticsaerch Snapshot 일 이용한 복원 (restore)
| Elasticsearch | Kibana | Logstash |
|---|---|---|
| 6.7.x | 6.7.x | 5.6.x-6.8.x |
| 7.10.x | 7.10.x | 6.8.x-7.17.x |
깨끗한 elasticsearch cluster 1대에서 1개의 노드로 테스트 진행
$ curl -XDELETE localhost:${elasticsearchPort}/_all
🧩 사용 → input ES-Cluster / ouput ES-Cluster
1. 가장 간단한 방법:
Elasticsearch 데이터 디렉토리를 통째로 복사하여 새로운 클러스터로 데이터를 옮길 수 있음.
2. Index ID 값 보존:
데이터를 통째로 복사하기 때문에 Index ID 값이 기존과 동일하게 유지됨.
3. 시간 소요:
elasticsearch-x.x.x/data 디렉토리를 묶고 푸는 과정에서 처리 속도가 느려질 수 있음.
4. 버전 호환성 문제:
Elasticsearch 버전 간 차이로 인해 복사된 데이터가 제대로 작동하지 않을 가능성이 있음.
5. 다운타임 발생:
Elasticsearch 인스턴스를 중지한 상태에서 복사를 진행해야 하므로 클러스터에 다운타임이 발생함.
6. 분산 환경에서의 제약:
다수의 노드로 구성된 분산 환경에서는 데이터 일관성 및 복잡성 문제로 인해 이 방법을 사용하기 어려움.
7. 안전한 복원 보장 어려움:
복사한 데이터가 새로운 클러스터에서 안정적으로 작동한다는 보장이 없음.
elastic 공식에서 권장하지 않는 방법이다. 가볍게 사용할 테스트 서버를 만들 때, 그리고 이관 될 elastic과 버전이 같을만 써본 방법이다.
elasticsearch-x.x.x/data 폴더를 tar.gz로 묶어서 가져오기. elasticsearch-x.x.x/data 폴더에 부은 후 재시작.remote → local
| els 6.7.0 | 실행 문제없음 |
|---|---|
| kibana 6.7.0 | 실행 문제없음 |
하위 버전 (ELS 7.10.2 → ELS 6.7.0) : not supported
remote → local
| els 6.7.0 | not supported |
|---|---|
| kibana 6.7.0 | not supported |
remote → local
| els 7.10.2 | 실행 문제 없음 |
|---|---|
| kibana 6.7.0 | 버전 호환 ❌ |
| kibana 7.10.2 | 매핑 이슈 🔥 |
▼ 매핑 이슈
최상위 수준 또는 일부 하위 필드에서 dynamic: strict 를 사용하면 mappings 정의에 맞지 않는 문서를 거부하며 사전 정의된 매핑 준수를 강제하는 이슈가 발생합니다. 해결 방법은 하기와 같습니다.
kibana_* 인덱스의 매핑을 변경하기 위해 하기의 단계를 수행합니다.
$ curl -XDELETE localhost:${elasticsearchPort}/.kibana* 명령어 수행.🔗 Discuss Kibana: mapping set to strict, ...
🔗 Elastic 공식 : 너무 많은 필드 수! Elasticsearch에서 매핑 폭발을 방지하는 3가지 방법
🧩 사용 → input ES-Cluster / ouput ES-Cluster / Logstash
a. 네트워크 환경 : input / output Plugin
b. 네트워크 차단된 환경 : Elasticsearch 데이터를 JSON 형식으로 덤프
1. CPU 성능 및 구성 설정 의존:
처리 속도가 CPU 성능과 Logstash 설정(configuration)에 크게 좌우됨.
2. Index ID 변경:
_bulk API를 사용하기 때문에 새로운 Index ID 값이 생성됨.
3. 버전 호환성 문제:
Elasticsearch 버전 간 호환성 문제 발생 가능.
4. 다운타임 최소화:
Elasticsearch를 가동한 상태에서 데이터 전송이 가능하므로 다운타임이 최소화됨.
5. 클러스터링 설정:
복사 대상 클러스터가 기존과 비슷한 수준의 클러스터링 설정을 갖추고 있어야 함.
PUT _template/shard1replica0
{
"order" : 0,
"index_patterns" : [
".openquery*",
"tr*"
],
"settings" : {
"index" : {
"number_of_shards" : "1",
"number_of_replicas" : "0"
}
},
"mappings" : { },
"aliases" : { }
}
input {
elasticsearch {
hosts => ["${input_ES_path}"]
index => "*,-.kibana_1,-.task" # 가져올 인덱스 명. 시스템 인덱스를 제외한 모든 일반 인덱스.
scroll => "10m" # 데이터를 검색할 때 사용할 스크롤 컨텍스트의 유지 시간.
size => 1000 # 한 번에 가져올 문서 수.
codec => "json" # Elasticsearch 데이터를 JSON 형식으로 처리.
docinfo => true
}
}
output {
elasticsearch {
hosts => ["${output_ES_path}"]
index => "%{[@metadata][_index]}" # 문서를 저장할 인덱스, 메타데이터를 참조하여 원본 인덱스를 유지.
}
stdout { codec => rubydebug { metadata => true } } # 디버깅 용도.
index => "*,-.kibana_1,-.task"size : 기본적으로 1000 으로 시작하고, 시스템 성능을 모니터링하며 사이즈를 늘려주세요.$ logstash-x.x.x/bin>logstash -f ../config/migration.conf --config.reload.automatic
--config.reload.automatic 플래그하기의 오류를 접할 시,
[warning][migrations] Another Kibana instance appears to be migrating the index. Waiting for that migration to complete. If no other Kibana instance is attempting migrations, you can get past this message by deleting index .kibana_1 and restarting Kibana.
🔥 Error 및 이슈
▼ logsatash 버전 호환성 이슈
Encountered a retryable error (will retry with exponential backoff)
{:code=>400, url=>"http://[IP]:[port]/_template/logstash/_bulk"}
elasticsearch ouput plugin 사용 시, es 쿼리가 정상적이지 않아 요청이 실패 되면서 logstash가 지속적으로 재시도를 하게 되는 현상. logstash 와 es 버전이 안맞을 때 발생.
▼ bulk insert 시 컨텐츠 길이 이슈
Encountered a retryable error (will retry with exponential backoff)
{:code=>400, :url=>"http://[IP:port]/_bulk", :content_length=>37171}
/config/elasticsearch.yml에 http:.max_content_length 설정이 있는데 설정된 용량보다 초과된 메시지를 보낼 때 나타난다.
메시지 용량 사이즈를 늘려줘야한다. default 값은 100mb이며, 설정 가능한 최대 값은 2gb이다. 2gb 이상의 값을 설정하면 하기와 같은 로그가 찍히면서 변경 값이 적용되지 않는다.
[WARN][http.netty ] [Magdalena] maxContentLength[2.9gb] set to high value, resetting it to [100mb]
▼ 실행 중 ES가 다운되는 이슈
[ERROR][logstash.outputs.elasticsearch][main][85b115c56ef75d7e7a4b5e780ec36b6cd437e86ab6333be9c55e5590f8dd33d4] Attempted to send a bulk request to elasticsearch' but Elasticsearch appears to be unreachable or down! {:error_message=>"Elasticsearch Unreachable: [http://localhost:7550/][Manticore::SocketException] Connection reset", :class=>"LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError", :will_retry_in_seconds=>2}
.kibana* 인덱스들을 migration 하는 과정에서 버전성이 호환되지 않아 꺼지는 것으로 파악됩니다. logstash-x.x.x/config/migration.conf 파일의 input Plugin 설정에서 .kibana* 관련 인덱스 exclude 를 빼먹지 않았는지 확인해 주세요.
▼ 실행 중 네트워크 연결이 다운되는 경우 : 🧩 Redis 활용
Error: Connection refused - Failed to open TCP connection to 192.168.0.4:xxxx (Connection refused - connect(2) for "192.168.0.4" port xxxx)
네트워크 꺼짐으로 인해 logstash 혹은 elasticsearch에 문제가 생길 경우 데이터 loss 또는 데이터 duplicate 가 발생할 수 있습니다.
임시 저장소인 🧩Redis 를 활용하여 데이터 손실이 발생하지 않도록 전송할 데이터를 일시적으로 혹은 일정 기간 동안 보관했다가 logstash와 elasticsearch가 정상화 되었을 때 다시 전달할 수 있도록 만들 수 있습니다.
🔗 기술블로그 : logstash tcp로 데이터 전송 시 손실없는 데이터 전달 흐름 설계
logstash-x.x.x/config/migration_out.conf 파일 생성input {
elasticsearch {
hosts => ["${input_ES_path}"]
# 시스템 인덱스를 제외한 모든 일반 인덱스
index => "*,-.kibana_1,-.tasks"
scroll => "10m"
size => 1000
codec => "json"
docinfo => true
}
}
filter {
mutate {
# 사용하지 않는 불필요한 필드 제거
remove_field => ["@version", "@timestamp", "host", "path"]
# 동적 인덱스명 사용을 위한 index 필드 추가
add_field => { "index" => "%{[@metadata][_index]}" }
}
}
output {
file {
codec => "json_lines"
path => "${yout_path/elasticsearch_output.json}" # json 파일 저장 경로
}
stdout { codec => rubydebug { metadata => true } }
}
$ logstash-x.x.x/bin>logstash -f ../config/migration_out.conf --config.reload.automatic --log.level debug
logstash-x.x.x/config/migration_in.conf 파일 생성input {
file {
codec => "json"
path => "${yout_path/elasticsearch_output.json}"
start_position => "beginning"
}
}
filter {
# _source 안의 id 값을 metadata의 _id로 설정
# _source 안의 index 값을 metadata의 _index로 추가
# 불필요한 필드 제거
mutate {
copy => {"id" => "[@metadata][_id]"}
copy => {"index" => "[@metadata][_index]"}
remove_field => ["@version", "@timestamp", "host", "path", "index"]
}
}
output {
elasticsearch {
hosts => ["${output_ES_path}"]
index => "%{[@metadata][_index]}" # metadata 를 통해 index명 동적 설정
document_id => "%{[@metadata][_id]}" # metadata 의 _id 값을 document_id로 사용
}
stdout { codec => rubydebug { metadata => true } }
}
input
codec : 입력받은 데이터가 message 필드 안에 json 스트링으로 보여집니다. key-value 로 보기 위해서는 input 형식이 json 타입이라는 것을 명시해 주어야 합니다.filter
ruby : bulkAPI 를 통해 이관되므로, 고유 id 값이 새로 생성됩니다. 고유 id 값을 원본과 동일하게 만들기위해 _source 내부의 id 값을 사용하여 고유 _id 값으로 변환하는 로직입니다.$ logstash-x.x.x/bin>logstash -f ../config/migration_in.conf --config.reload.automatic --log.level debug
🔥이슈
▼ 메모리 부족으로 인한 데이터 누락
메모리가 부족하여 logstash 실행 중 elasticsearch가 다운 (7) 되며, 일부 데이터 개수가 맞지 않거나 원하는 결과가 추출되지 않는 상황이 발생합니다.
# elasticsearch
[INFO ][o.e.i.IndexingMemoryController] [yongin-node-1] now throttling indexing for shard [[.openquery-monitor-indices-20230626][0]]: segment writing can't keep up
[INFO ][o.e.i.IndexingMemoryController] [yongin-node-1] stop throttling indexing for shard [[.openquery-monitor-indices-20230629][0]]
[INFO ][o.e.m.j.JvmGcMonitorService] [yongin-node-1] [gc][3918] overhead, spent [346ms] collecting in the last [1s]
PUT /_settings
{
"index.blocks.read_only_allow_delete": null
}
# elasticsearch-x.x.x/config/jvm.options
-Xms4g
-Xmx4g
👻 테스트
.kibana_ 인덱스 제외하고 원본 총 114개 / 복사본 총 100개Logstash의 Splitting: 데이터를 청크로 분할하여 용량을 줄이고 각각의 청크를 별도의 Bulk API 요청으로 전송.
일시적인 인덱싱: 먼저 100개의 문서를 인덱싱하고, 그 다음에 나머지 15개의 문서를 따로 처리.
🧩 사용 → input ES-Cluster / ouput ES-Cluster
1. 공식 권장 방법:
Elasticsearch에서 권장하는 안전한 데이터 복사 및 복원 방법.
2. Index ID 변경:
restore를 사용하는 복원 과정에서 새로운 Index ID 값이 생성됨.
3. 데이터 무결성 보장:
Snapshot은 데이터를 백업하고 복원하는 과정을 철저히 관리하여 데이터 무결성을 보장함.
4. 다운타임 최소화:
Elasticsearch를 가동한 상태에서 Snapshot 및 복원을 진행할 수 있어 다운타임을 최소화할 수 있음.
5. 버전 호환성 확인 필요:
Snapshot 및 복원 과정에서 Elasticsearch 버전 간 호환성을 사전에 확인해야 함.
6. 대규모 데이터 지원:
대규모 데이터 마이그레이션에 적합하며, 분산 환경에서도 안정적으로 작동.
🔗 기술블로그 : elasticsearch-backup-and-restore
🔗 기술블로그 : Elasticsearch Backup/Restore
🔗 기술블로그 : elastic 백업 (스냅샷)
a. 복사 대상 ES Cluster 작업 : Backup
# elasticsearch-x.x.x/config/elasticsearch.yml
path.repo: ["/home/yongin/appl/elastic/elasticsearch-6.7.0/data_backup/snapshot"]
PUT _snapshot/2024_05_31
{
"type": "fs",
"settings": {
"location": "C:\\elasticsearch-6.7.0\\data_backup\\snapshot"
}
}
GET_snapshot
PUT _snapshot/2024_05_31/snapshot_1
GET /_snapshot/2024_05_31/snapshot_1/_status
GET _snapshot/2024_05_31/_all?pretty
tar.gz 로 압축 b. 전달될 ES Cluster 작업 : Restore
# elasticsearch-x.x.x/config/elasticsearch.yml
path.repo: C:\\elasticsearch-6.7.0\\data_backup\\snapshot
PUT _snapshot/2024_05_31
{
"type": "fs",
"settings": {
"location": "C:\\elasticsearch-6.7.0\\data_backup\\snapshot"
}
}
저장했던 tar.gz 파일을 snapshot 저장 경로에 옮기고 풀기
현재 노드를 close 하기
curl -X POST "localhost:xxxx/_all/_close"
POST /_snapshot/2024_05_31/snapshot_1/_restore
{
"indices": "-.kibana*",
"index_settings":{
"index.number_of_replicas":0
},
"ignore_index_settings":[
"index.refresh_interval"
]
}
GET _cat/indices?pretty&s=i
.)로 시작하는 인덱스 이름이 숨겨진 인덱스와 시스템 인덱스에만 예약되도록 변경 됨🔗 Elastic 공식: Migrationg data
🔗 기술블로그 : Elastic Cloud의 클러스트럴 스냅샷과 리스토어 기능을 이용해서 마이그레이션 하는 방법
🔗 기술블로그 : Elasticsearch 3TB의 인덱스를 reindex 하는 방법
🔗 기술블로그 : 배달의민족 광고데이터 이관기
🔗 기술블로그 : Elasticsearch에서 데이터를 백업하고 복원하는 방안