Este guia descreve como migrar dados de um cluster Elasticsearch autogerenciado (hospedado no ECS) para uma instância do Alibaba Cloud Elasticsearch por meio da Reindex API. Essa API permite que o cluster de destino extraia dados de um cluster de origem remoto.
Contexto e guia de seleção
A Reindex API é ideal para migrar índices específicos ou volumes de dados pequenos a médios. Escolha a ferramenta adequada conforme a arquitetura da instância e o tamanho dos dados:
|
Volume de dados / Requisito |
Método recomendado |
|
Volume pequeno a médio |
Reindex API (Este guia) |
|
Grande volume (> 100 GB) |
|
|
Filtragem ou transformação de dados |
Migrate self-managed Elasticsearch data by using Alibaba Cloud Logstash |
Pré-requisitos
-
Conectividade de rede:
A instância ECS (Elasticsearch autogerenciado) e o cluster do Alibaba Cloud Elasticsearch devem estar na mesma Virtual Private Cloud (VPC).
Grupo de segurança: O grupo de segurança do ECS deve permitir tráfego de entrada na porta 9200 proveniente dos endereços IP dos nós do Alibaba Cloud Elasticsearch (disponíveis no console do Kibana).
-
Verificação da arquitetura do cluster: Consulte Basic Information > Control Architecture Type do cluster Alibaba Cloud Elasticsearch para identificar a arquitetura:
Basic Control Architecture (v2): Implantada na sua VPC. O acesso é direto. Siga o procedimento deste guia.
Cloud-native Control Architecture (v3): Implantada em uma VPC de service. Use o PrivateLink para estabelecer a conexão de rede. Consulte Migrate self-managed Elasticsearch data via a private connection.
Estabilidade: Interrompa a gravação de dados no cluster de origem durante a migração para garantir consistência total dos dados.
Observações de uso
Instâncias criadas antes de outubro de 2020 (arquitetura v2) não executam reindexação entre clusters diretamente com instâncias criadas após essa data (arquitetura v3) sem proxy ou PrivateLink. Se as arquiteturas forem diferentes, use o Logstash como intermediário.
Ao acessar um Elasticsearch autogerenciado ou um cluster do Alibaba Cloud Elasticsearch por nome de domínio, não use URL que inclua path, como
http://host:port/path.
Procedimento
Etapa 1: Estabelecer conectividade de rede (apenas para arquitetura v3)
Se o cluster Alibaba Cloud Elasticsearch usar a arquitetura de controle cloud-native (v3), crie uma conexão privada com o PrivateLink. Para mais informações, consulte Configure a PrivateLink connection.
Etapa 2: Criar índice de destino
Crie o índice no cluster Alibaba Cloud Elasticsearch de destino com os mesmos mappings e configurações da origem.
Script em lote (Python 2.7): Use este script para replicar as estruturas de índice (mappings/shards) do cluster antigo para o novo.
Neste exemplo, as réplicas são definidas como 0 para acelerar a migração.
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# File name: indiceCreate.py
import sys
import base64
import time
import httplib
import json
## The host of the self-managed Elasticsearch cluster.
oldClusterHost = "old-cluster.com"
## The username for the self-managed Elasticsearch cluster. This can be empty.
oldClusterUserName = "old-username"
## The password for the self-managed Elasticsearch cluster. This can be empty.
oldClusterPassword = "old-password"
## The host of the Alibaba Cloud Elasticsearch cluster. You can obtain it from the Basic Information page of the Alibaba Cloud Elasticsearch instance.
newClusterHost = "new-cluster.com"
## The username for the Alibaba Cloud Elasticsearch cluster.
newClusterUser = "elastic"
## The password for the Alibaba Cloud Elasticsearch cluster.
newClusterPassword = "new-password"
DEFAULT_REPLICAS = 0
def httpRequest(method, host, endpoint, params="", username="", password=""):
conn = httplib.HTTPConnection(host)
headers = {}
if (username != "") :
'Hello {name}, your age is {age} !'.format(name = 'Tom', age = '20')
base64string = base64.encodestring('{username}:{password}'.format(username = username, password = password)).replace('\n', '')
headers["Authorization"] = "Basic %s" % base64string;
if "GET" == method:
headers["Content-Type"] = "application/x-www-form-urlencoded"
conn.request(method=method, url=endpoint, headers=headers)
else :
headers["Content-Type"] = "application/json"
conn.request(method=method, url=endpoint, body=params, headers=headers)
response = conn.getresponse()
res = response.read()
return res
def httpGet(host, endpoint, username="", password=""):
return httpRequest("GET", host, endpoint, "", username, password)
def httpPost(host, endpoint, params, username="", password=""):
return httpRequest("POST", host, endpoint, params, username, password)
def httpPut(host, endpoint, params, username="", password=""):
return httpRequest("PUT", host, endpoint, params, username, password)
def getIndices(host, username="", password=""):
endpoint = "/_cat/indices"
indicesResult = httpGet(oldClusterHost, endpoint, oldClusterUserName, oldClusterPassword)
indicesList = indicesResult.split("\n")
indexList = []
for indices in indicesList:
if (indices.find("open") > 0):
indexList.append(indices.split()[2])
return indexList
def getSettings(index, host, username="", password=""):
endpoint = "/" + index + "/_settings"
indexSettings = httpGet(host, endpoint, username, password)
print index + " The original settings are as follows:\n" + indexSettings
settingsDict = json.loads(indexSettings)
## By default, the number of shards is the same as that of the index in the self-managed Elasticsearch cluster.
number_of_shards = settingsDict[index]["settings"]["index"]["number_of_shards"]
## By default, the number of replicas is 0.
number_of_replicas = DEFAULT_REPLICAS
newSetting = "\"settings\": {\"number_of_shards\": %s, \"number_of_replicas\": %s}" % (number_of_shards, number_of_replicas)
return newSetting
def getMapping(index, host, username="", password=""):
endpoint = "/" + index + "/_mapping"
indexMapping = httpGet(host, endpoint, username, password)
print index + " The original mapping is as follows:\n" + indexMapping
mappingDict = json.loads(indexMapping)
mappings = json.dumps(mappingDict[index]["mappings"])
newMapping = "\"mappings\" : " + mappings
return newMapping
def createIndexStatement(oldIndexName):
settingStr = getSettings(oldIndexName, oldClusterHost, oldClusterUserName, oldClusterPassword)
mappingStr = getMapping(oldIndexName, oldClusterHost, oldClusterUserName, oldClusterPassword)
createstatement = "{\n" + str(settingStr) + ",\n" + str(mappingStr) + "\n}"
return createstatement
def createIndex(oldIndexName, newIndexName=""):
if (newIndexName == "") :
newIndexName = oldIndexName
createstatement = createIndexStatement(oldIndexName)
print "The settings and mapping for the new index " + newIndexName + " are as follows:\n" + createstatement
endpoint = "/" + newIndexName
createResult = httpPut(newClusterHost, endpoint, createstatement, newClusterUser, newClusterPassword)
print "Result of creating the new index " + newIndexName + ": " + createResult
## main
indexList = getIndices(oldClusterHost, oldClusterUserName, oldClusterPassword)
systemIndex = []
for index in indexList:
if (index.startswith(".")):
systemIndex.append(index)
else :
createIndex(index, index)
if (len(systemIndex) > 0) :
for index in systemIndex:
print index + " might be a system index and will not be re-created. Handle it separately if needed."
Etapa 3: Configurar a lista de permissões de reindex remoto
O Alibaba Cloud Elasticsearch exige uma lista de permissões para permitir comunicação remota.
Faça login no console do Alibaba Cloud Elasticsearch.
No painel de navegação à esquerda, selecione Elasticsearch Clusters.
-
Acesse o cluster de destino.
Na barra de navegação superior, selecione o grupo de recursos e a região do cluster.
Na página Elasticsearch Clusters, localize o cluster e clique em no respectivo ID.
No painel de navegação à esquerda, selecione .
Na seção YML File Configuration, clique em Modify Configuration à direita.
-
No painel YML File Configuration, modifique Other Configurations para definir a lista de permissões de reindex. Para mais informações, consulte Configure YML parameters.
-
Arquitetura v2: Especifique uma combinação de host e porta. Exemplo:
reindex.remote.whitelist: ["10.0.xx.xx:9200","10.0.xx.xx:9200","10.0.xx.xx:9200","10.15.xx.xx:9200","10.15.xx.xx:9200","10.15.xx.xx:9200"] -
Arquitetura v3: Especifique a combinação do nome de domínio do endpoint e da porta correspondentes à instância. Exemplo:
ep-bp1hfkx7coy8lvu4****-cn-hangzhou-i.epsrv-bp1zczi0fgoc5qtv****.cn-hangzhou.privatelink.aliyuncs.com:9200
-
Salve e reinicie o cluster.
Etapa 4: Migrar dados
Selecione o script adequado ao seu cenário de dados.
Cenário A: Migração simples (poucos dados)
Execute este script uma vez para cada índice.
#!/bin/bash
# file:reindex.sh
indexName="Your index name"
newClusterUser="Username for the Alibaba Cloud Elasticsearch cluster"
newClusterPass="Password for the Alibaba Cloud Elasticsearch cluster"
newClusterHost="Host of the Alibaba Cloud Elasticsearch cluster"
oldClusterUser="Username for the self-managed Elasticsearch cluster"
oldClusterPass="Password for the self-managed Elasticsearch cluster"
# The host of the self-managed Elasticsearch cluster must be in the format of [scheme]://[host]:[port], for example, http://10.37.*.*:9200.
oldClusterHost="Host of the self-managed Elasticsearch cluster"
curl -u ${newClusterUser}:${newClusterPass} -XPOST "http://${newClusterHost}/_reindex?pretty" -H "Content-Type: application/json" -d'{
"source": {
"remote": {
"host": "'${oldClusterHost}'",
"username": "'${oldClusterUser}'",
"password": "'${oldClusterPass}'"
},
"index": "'${indexName}'",
"query": {
"match_all": {}
}
},
"dest": {
"index": "'${indexName}'"
}
}'
Cenário B: Migração incremental (grande volume de dados com timestamp)
Se houver um campo update_time, use este script de loop para migrar dados em blocos. Isso minimiza o tempo de inatividade ao permitir a sincronização do delta (novas alterações) após a movimentação inicial em massa.
#!/bin/bash
# file: circleReindex.sh
# CONTROLLING STARTUP:
# This is a script for remote reindexing. Requirements:
# 1. The index has been created in the Alibaba Cloud Elasticsearch cluster, or the cluster supports automatic creation and dynamic mapping.
# 2. An IP address whitelist must be configured in the YML file of the Alibaba Cloud Elasticsearch cluster, for example, reindex.remote.whitelist: 172.16.**.**:9200.
# 3. The host must be in the format of [scheme]://[host]:[port].
USAGE="Usage: sh circleReindex.sh <count>
count: The number of executions. A negative number indicates a loop for incremental execution. A positive number indicates a one-time or multiple executions.
Example:
sh circleReindex.sh 1
sh circleReindex.sh 5
sh circleReindex.sh -1"
indexName="Your index name"
newClusterUser="Username for the Alibaba Cloud Elasticsearch cluster"
newClusterPass="Password for the Alibaba Cloud Elasticsearch cluster"
oldClusterUser="Username for the self-managed Elasticsearch cluster"
oldClusterPass="Password for the self-managed Elasticsearch cluster"
## http://myescluster.com
newClusterHost="Host of the Alibaba Cloud Elasticsearch cluster"
# The host of the self-managed Elasticsearch cluster must be in the format of [scheme]://[host]:[port], for example, http://10.37.*.*:9200.
oldClusterHost="Host of the self-managed Elasticsearch cluster"
timeField="Update time field"
reindexTimes=0
lastTimestamp=0
curTimestamp=`date +%s`
hasError=false
function reIndexOP() {
reindexTimes=$[${reindexTimes} + 1]
curTimestamp=`date +%s`
ret=`curl -u ${newClusterUser}:${newClusterPass} -XPOST "${newClusterHost}/_reindex?pretty" -H "Content-Type: application/json" -d '{
"source": {
"remote": {
"host": "'${oldClusterHost}'",
"username": "'${oldClusterUser}'",
"password": "'${oldClusterPass}'"
},
"index": "'${indexName}'",
"query": {
"range" : {
"'${timeField}'" : {
"gte" : '${lastTimestamp}',
"lt" : '${curTimestamp}'
}
}
}
},
"dest": {
"index": "'${indexName}'"
}
}'`
lastTimestamp=${curTimestamp}
echo "The ${reindexTimes}th reindex. The update deadline for this execution is ${lastTimestamp}. Result: ${ret}"
if [[ ${ret} == *error* ]]; then
hasError=true
echo "An exception occurred during this execution. Subsequent operations are interrupted. Please check."
fi
}
function start() {
## If the number is negative, the loop runs continuously.
if [[ $1 -lt 0 ]]; then
while :
do
reIndexOP
done
elif [[ $1 -gt 0 ]]; then
k=0
while [[ k -lt $1 ]] && [[ ${hasError} == false ]]; do
reIndexOP
let ++k
done
fi
}
## main
if [ $# -lt 1 ]; then
echo "$USAGE"
exit 1
fi
echo "Start the reindex operation for the index ${indexName}."
start $1
echo "A total of ${reindexTimes} reindex operations were performed."
Cenário C: Migração incremental (grande volume de dados sem timestamp)
Modifique o código do service upstream para adicionar um campo update_time. Após adicionar o campo, migre primeiro os dados históricos. Em seguida, use o método de migração via scroll descrito em Scenario B: Incremental migration (Large data with timestamp).
#!/bin/bash
# file:miss.sh
indexName="Your index name"
newClusterUser="Username for the Alibaba Cloud Elasticsearch cluster"
newClusterPass="Password for the Alibaba Cloud Elasticsearch cluster"
newClusterHost="Host of the Alibaba Cloud Elasticsearch cluster"
oldClusterUser="Username for the self-managed Elasticsearch cluster"
oldClusterPass="Password for the self-managed Elasticsearch cluster"
# The host of the self-managed Elasticsearch cluster must be in the format of [scheme]://[host]:[port], for example, http://10.37.*.*:9200
oldClusterHost="Host of the self-managed Elasticsearch cluster"
timeField="updatetime"
curl -u ${newClusterUser}:${newClusterPass} -XPOST "http://${newClusterHost}/_reindex?pretty" -H "Content-Type: application/json" -d '{
"source": {
"remote": {
"host": "'${oldClusterHost}'",
"username": "'${oldClusterUser}'",
"password": "'${oldClusterPass}'"
},
"index": "'${indexName}'",
"query": {
"bool": {
"must_not": {
"exists": {
"field": "'${timeField}'"
}
}
}
}
},
"dest": {
"index": "'${indexName}'"
}
}'
Perguntas frequentes e solução de problemas
-
P: Ao executar o comando curl, recebo a mensagem de erro
{"error":"Content-Type header [application/x-www-form-urlencoded] is not supported","status":406}.R: Adicione
-H "Content-Type: application/json"ao comando curl e tente novamente.// Get information about all indexes in the self-managed Elasticsearch cluster. If you do not have permissions, you can remove the "-u user:pass" parameter. oldClusterHost is the host of the self-managed Elasticsearch cluster. Replace it with your actual host. curl -u user:pass -XGET http://oldClusterHost/_cat/indices | awk '{print $3}' // Based on the returned index list, obtain the settings and mapping of the user index to be migrated. Replace indexName with the name of the user index you want to query. curl -u user:pass -XGET http://oldClusterHost/indexName/_settings,_mapping?pretty=true // Based on the obtained _settings and _mapping information of the corresponding index, create the corresponding index in the Alibaba Cloud Elasticsearch cluster. You can set the number of replicas to 0 to speed up data synchronization. After the data migration is complete, reset the number of replicas to 1. // newClusterHost is the host of the Alibaba Cloud Elasticsearch cluster, testindex is the name of the created index, and testtype is the type of the corresponding index. curl -u user:pass -XPUT http://<newClusterHost>/<testindex> -d '{ "testindex" : { "settings" : { "number_of_shards" : "5", // Assume that the number of shards for the corresponding index in the self-managed Elasticsearch cluster is 5. "number_of_replicas" : "0" // Set the number of replicas for the index to 0. } }, "mappings" : { // Assume that the mappings for the corresponding index in the self-managed Elasticsearch cluster are configured as follows. "testtype" : { "properties" : { "uid" : { "type" : "long" }, "name" : { "type" : "text" }, "create_time" : { "type" : "long" } } } } } }' -
P: Como acelerar a migração?
R:
Desative as réplicas: Defina
number_of_replicas: 0no índice de destino antes de iniciar.Desative o refresh: Configure
refresh_interval: -1no índice de destino.-
Fatiamento: Use o parâmetro
slicesna Reindex API para paralelizar o processo:POST _reindex?slices=5&refreshPara mais informações, consulte reindex API.