ElasticSearch 索引藍綠部署

ES做爲線上服務的重要組件,索引重建時通常要求零停機。經過藍綠部署,咱們能實現安全的在ES集羣中進行流量瞬間無縫切換,實現零停機部署。php

部署設計

每次生產部署進行以下操做:安全

  1. 將模型的索引循環切換其別名指向至以下兩個實際索引
    • 實際索引_blue
    • 實際索引_green
  2. 刪除過期的實際索引
  3. 使用部署更新後的索引

REST API 流程

部署開始:
PUT /my_index_blue # 實際索引_藍 建立
|
V
1. 別名切換進行藍綠部署
2. DELETE /my_index_green  #刪除舊索引
|
V
GET /my_index/_search  #生產環境使用部署更新後的索引
|
V
新一輪部署:
PUT /my_index_green # 實際索引_綠 建立
|
V
1. 別名切換進行藍綠部署
2. DELETE /my_index_blue  #刪除舊索引
|
V
GET /my_index/_search  #生產環境使用部署更新後的索引
|
V
循環以上流程。。。

一個別名能夠指向多個索引,因此別名切換進行藍綠部署時,咱們要原子化操做「增長新別名 & 刪除舊別名」app

# 在一個原子操做中完成別名切換
POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index_blue", "alias": "my_index" }},
        { "add":    { "index": "my_index_green", "alias": "my_index" }}
    ]
}

Laravel Elasticquent實現

咱們約定每一個模型表獨佔一個INDEX空間設計

<?php

trait EsSearchable
{
    use Elasticquent\ElasticquentTrait;

    public static function getIndex(){
        return static::index;
    }

    public static function setIndex($index){
        return static::index = $index;
    }

    public static function getType(){
        return static::type;
    }

    public static function setType($type){
        return static::type = $type;
    }    
}

class Foo extends \App\Models\Model
{
    use EsSearchable;

    protected static $index = 'foo_index';
    protected static $type = 'foo_type';

    // protected $indexSettings = [];
    // protected $mappingProperties = [];
}

##########################################################################

$aliasIndex = Foo::getIndex();
$actualIndexes = [
    $aliasIndex . '_blue',
    $aliasIndex . '_green',
];
$fromIndex = \ES::indices()->existsAlias(['name'=>$aliasIndex, 'index'=>$actualIndexes[0]])
                        ? array_shift($actualIndexes) : array_pop($actualIndexes);
$toIndex = current($actualIndexes);

createIndexByModel($toIndex, Foo::class);
ModelDocsToIndex($toIndex, Foo::class);
switchIndexAlias($aliasIndex, $fromIndex, $toIndex);
deleteIndexByModel($fromIndex, Foo::class);


/**
 * 索引文檔至指定索引空間
 */
function ModelDocsToIndex($index, $modelCls){
    $originIndex = $modelCls::getIndex();

    $modelCls::setIndex($index);
    $modelCls::chunk(1000, function(Collection $collection){
        $collection->->addToIndex();
    });

    $modelCls::setIndex($originIndex);
}

/**
 * 藍綠部署切換:改變索引別名的指向
 */
function switchIndexAlias($aliasIndex, $fromIndex, $toIndex){
    $params = [
        'body' => [
            'actions' => [
                'remove' => ['index' => $fromIndex, 'alias' => $aliasIndex],
                'add' => ['index' => $toIndex, 'alias' => $aliasIndex],
            ]
        ]
    ];

    \ES::indices()->updateAliases($params);
}

/**
 * 新建索引(配置同指定模型)
 */
function createIndexByModel($index, $modelCls){
    $originIndex = $modelCls::getIndex();
    
    $modelCls::setIndex($index);
    $modelCls::createIndex();

    $modelCls::setIndex($originIndex);
}

/**
 * 刪除索引(配置同指定模型)
 */
function deleteIndexByModel($index, $modelCls){
    $originIndex = $modelCls::getIndex();
    
    $modelCls::setIndex($index);
    $modelCls::deleteIndex();

    $modelCls::setIndex($originIndex);
}
相關文章
相關標籤/搜索