From 8c489009c238aa6d56354f5ef873addea1f4fd5c Mon Sep 17 00:00:00 2001 From: Jason Anderson Date: Mon, 24 Feb 2020 15:36:46 -0600 Subject: [PATCH] [elasticsearch] Add migration for Kibana 6.x index Elasticsearch 6.x dropped support for mapping types[1], which by default the Kibana index used. This means that when deploying ELK 6.x, the Kibana index must be migrated to the new schema to preserve dashboards and visualizations. There is a process defined[2], which involves creating a new index with the specified schema, then reindexing the old index's data into the new index, then doing a rename/delete. This adds support for that workflow via Ansible. It takes place after the ES container is restarted after an upgrade, so there will be a (short) period of time where the Kibana index is not migrated. During this time, Kibana still loads, but presents the user with a status screen informing that the index needs migration. [1]: https://www.elastic.co/guide/en/elasticsearch/reference/6.x/removal-of-types.html [2]: https://www.elastic.co/guide/en/kibana/6.x/migrating-6.0-index.html Implements: blueprint elasticsearch-kibana-version-upgrade Depends-On: https://review.opendev.org/709624 Change-Id: I4550629e2113f3da7f1cecfeab0d5fe0d899dae8 --- .../roles/kibana/files/kibana-6-index.json | 264 ++++++++++++++++++ .../kibana/tasks/migrate-kibana-index.yml | 111 ++++++++ ansible/roles/kibana/tasks/upgrade.yml | 2 + 3 files changed, 377 insertions(+) create mode 100644 ansible/roles/kibana/files/kibana-6-index.json create mode 100644 ansible/roles/kibana/tasks/migrate-kibana-index.yml diff --git a/ansible/roles/kibana/files/kibana-6-index.json b/ansible/roles/kibana/files/kibana-6-index.json new file mode 100644 index 0000000000..08e61bb0d1 --- /dev/null +++ b/ansible/roles/kibana/files/kibana-6-index.json @@ -0,0 +1,264 @@ +{ + "settings" : { + "number_of_shards" : 1, + "index.mapper.dynamic": false + }, + "mappings" : { + "doc": { + "properties": { + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "config": { + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchId": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + } + } + } + } +} diff --git a/ansible/roles/kibana/tasks/migrate-kibana-index.yml b/ansible/roles/kibana/tasks/migrate-kibana-index.yml new file mode 100644 index 0000000000..694a5a8d96 --- /dev/null +++ b/ansible/roles/kibana/tasks/migrate-kibana-index.yml @@ -0,0 +1,111 @@ +--- +- name: Set fact for Elasticsearch URL + set_fact: + elasticsearch_url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}" + +- name: Wait for Elasticsearch + become: true + kolla_toolbox: + module_name: uri + module_args: + url: "{{ elasticsearch_url }}" + delegate_to: "{{ groups['elasticsearch'][0] }}" + run_once: true + retries: 10 + delay: 5 + register: result + until: ('status' in result) and result.status == 200 + +- name: Check state of migration + become: true + kolla_toolbox: + module_name: uri + module_args: + url: "{{ elasticsearch_url }}/.kibana/_mappings/doc" + status_code: [200, 404] + delegate_to: "{{ groups['elasticsearch'][0] }}" + run_once: true + register: kibana_6_index + +# The official procedure for migrating the Kibana index: +# https://www.elastic.co/guide/en/kibana/6.x/migrating-6.0-index.html +- name: Migrate Kibana index to 6.x + block: + - name: Set .kibana index to read-only + become: true + kolla_toolbox: + module_name: uri + module_args: + url: "{{ elasticsearch_url }}/.kibana/_settings" + method: PUT + status_code: 200 + return_content: yes + body: | + { + "index.blocks.write": true + } + body_format: json + delegate_to: "{{ groups['elasticsearch'][0] }}" + run_once: true + + - name: Create .kibana-6 index + become: true + kolla_toolbox: + module_name: uri + module_args: + url: "{{ elasticsearch_url }}/.kibana-6" + method: PUT + status_code: 200 + return_content: yes + body: "{{ lookup('file', 'kibana-6-index.json') }}" + body_format: json + delegate_to: "{{ groups['elasticsearch'][0] }}" + run_once: true + + - name: Reindex .kibana into .kibana-6 + become: true + kolla_toolbox: + module_name: uri + module_args: + url: "{{ elasticsearch_url }}/_reindex" + method: POST + status_code: 200 + return_content: yes + body: | + { + "source": { + "index": ".kibana" + }, + "dest": { + "index": ".kibana-6" + }, + "script": { + "inline": "ctx._source = [ ctx._type : ctx._source ]; ctx._source.type = ctx._type; ctx._id = ctx._type + \":\" + ctx._id; ctx._type = \"doc\"; ", + "lang": "painless" + } + } + body_format: json + delegate_to: "{{ groups['elasticsearch'][0] }}" + run_once: true + + - name: Alias .kibana-6 to .kibana and remove legacy .kibana index + become: true + kolla_toolbox: + module_name: uri + module_args: + url: "{{ elasticsearch_url }}/_aliases" + method: POST + status_code: 200 + return_content: yes + body: | + { + "actions" : [ + { "add": { "index": ".kibana-6", "alias": ".kibana" } }, + { "remove_index": { "index": ".kibana" } } + ] + } + body_format: json + delegate_to: "{{ groups['elasticsearch'][0] }}" + run_once: true + + when: ('status' in kibana_6_index) and kibana_6_index.status != 200 diff --git a/ansible/roles/kibana/tasks/upgrade.yml b/ansible/roles/kibana/tasks/upgrade.yml index 375dcad19b..4192da0fe5 100644 --- a/ansible/roles/kibana/tasks/upgrade.yml +++ b/ansible/roles/kibana/tasks/upgrade.yml @@ -1,5 +1,7 @@ --- - include_tasks: config.yml +- include_tasks: migrate-kibana-index.yml + - name: Flush handlers meta: flush_handlers