AnsiblePlaybookでElasticStackを自動的に更新する
公開: 2022-03-11何千ものデバイスで構成されるネットワークのログ分析は、Elastic Stackを集中型のログソリューションとして使用することを決定する前は、複雑で時間のかかる退屈な作業でした。 それは非常に賢明な決断であることが証明されました。 すべてのログを検索する場所が1つしかないだけでなく、検索結果がほぼ瞬時に得られ、分析とトラブルシューティングに非常に役立つ強力な視覚化と、ネットワークの便利な概要を示す美しいダッシュボードが得られます。
Elastic Stackは常に新しい驚くべき機能をリリースしており、非常に活発な開発ペースを維持しており、毎月2つの新しいリリースを提供することがよくあります。 新しい機能や改善点を利用できるように、常に環境を最新の状態に保つのが好きです。 また、バグやセキュリティの問題から解放されます。 しかし、これには常に環境を更新する必要があります。
ElasticのWebサイトには、製品のアップグレードプロセスなど、明確で詳細なドキュメントがありますが、手動でアップグレードすることは、特にElasticsearchクラスターでは複雑な作業です。 関係するステップはたくさんあり、非常に特定の順序に従う必要があります。 そのため、私はずっと前に、AnsiblePlaybooksを使用してプロセス全体を自動化することにしました。
このAnsibleチュートリアルでは、ElasticStackのインストールを自動アップグレードするために開発された一連のAnsiblePlaybookについて説明します。
ElasticStackとは何ですか
以前はELKスタックと呼ばれていたElasticStackは、オープンソース企業ElasticのElasticsearch、Logstash、Kibanaで構成されており、データのインデックス作成、検索、分析のための強力なプラットフォームを提供します。 幅広い用途に使用できます。 ロギングとセキュリティ分析からアプリケーションパフォーマンス管理とサイト検索まで。
Elasticsearchはスタックの中核です。 これは、大量の保存データに対してもほぼリアルタイムの検索結果を提供できる分散型検索および分析エンジンです。
Logstashは、さまざまなソース(私が書いている50の公式入力プラグイン)からデータを取得または受信し、それを解析、フィルタリング、変換して、1つ以上の可能な出力に送信する処理パイプラインです。 私たちの場合、Elasticsearch出力プラグインに関心があります。
Kibanaは、ユーザーおよび操作のフロントエンドです。 データを視覚化、検索、ナビゲートし、データに関する驚くべき洞察を提供するダッシュボードを作成できます。
Ansibleとは
Ansibleは、システムの構成、ソフトウェアの展開またはアップグレード、および複雑なITタスクの調整に使用できるIT自動化プラットフォームです。 その主な目標は、シンプルさと使いやすさです。 Ansibleの私のお気に入りの機能は、エージェントレスであるということです。つまり、管理したいホストやデバイスに追加のソフトウェアをインストールして管理する必要がありません。 Ansible自動化の機能を使用して、ElasticStackを自動アップグレードします。
免責事項と注意の言葉
ここで共有するプレイブックは、公式製品のドキュメントに記載されている手順に基づいています。 同じメジャーバージョンのアップグレードにのみ使用することを目的としています。 例: 5.x
→ 5.y
または6.x
→ 6.y
ここで、 x
> y
。 メジャーバージョン間のアップグレードには追加の手順が必要になることが多く、それらのプレイブックはそのような場合には機能しません。
とにかく、アップグレードにプレイブックを使用する前に、リリースノート、特に重大な変更のセクションを常に読んでください。 プレイブックで実行されるタスクを理解していることを確認し、アップグレード手順を常にチェックして、重要な変更がないことを確認してください。
そうは言っても、Elasticsearchのバージョン2.2以降、これらのプレイブック(または以前のバージョン)を問題なく使用しています。 当時、私は製品ごとに完全に別々のプレイブックを持っていました。彼らが知っているのと同じバージョン番号を共有していなかったからです。
そうは言っても、私はこの記事に含まれる情報の使用について一切責任を負いません。
私たちの架空の環境
プレイブックが実行される環境は、6台のCentOS7サーバーで構成されます。
- 1xLogstashサーバー
- 1 x Kibana Server
- 4xElasticsearchノード
ご使用の環境に異なるサーバー数があるかどうかは関係ありません。 それに応じてインベントリファイルに反映するだけで、プレイブックは問題なく実行されます。 ただし、RHELベースのディストリビューションを使用していない場合は、ディストリビューション固有のいくつかのタスク(主にパッケージマネージャーのもの)を変更するための演習として残しておきます。
在庫
Ansibleには、プレイブックを実行する必要があるホストを知るためのインベントリが必要です。 4つの想像上のシナリオでは、次のインベントリファイルを使用します。
[logstash] server01 ansible_host=10.0.0.1 [kibana] server02 ansible_host=10.0.0.2 [elasticsearch] server03 ansible_host=10.0.0.3 server04 ansible_host=10.0.0.4 server05 ansible_host=10.0.0.5 server06 ansible_host=10.0.0.6
Ansibleインベントリファイルでは、 [section]
はホストのグループを表します。 私たちのインベントリには、 kibana
、 logstash
、 elasticsearch
の3つのホストグループがあります。 プレイブックではグループ名のみを使用していることに気付くでしょう。 つまり、グループが正しい限り、プレイブックが実行されるので、インベントリ内のホストの数は関係ありません。
アップグレードプロセス
アップグレードプロセスは、次の手順で構成されます。
1)パッケージを事前にダウンロードする
2)Logstashのアップグレード
3)Elasticsearchクラスターのローリングアップグレード
4)Kibanaのアップグレード
主な目標は、ダウンタイムを最小限に抑えることです。 ほとんどの場合、ユーザーは気付かないでしょう。 Kibanaが数秒間利用できない場合があります。 それは私には受け入れられます。
Main AnsiblePlaybook
アップグレードプロセスは、さまざまなプレイブックのセットで構成されています。 Ansibleのimport_playbook機能を使用して、すべてのプレイブックを1つのメインプレイブックファイルに整理します。このファイルを呼び出して、プロセス全体を処理できます。
- name: pre-download import_playbook: pre-download.yml - name: logstash-upgrade import_playbook: logstash-upgrade.yml - name: elasticsearch-rolling-upgrade import_playbook: elasticsearch-rolling-upgrade.yml - name: kibana-upgrade import_playbook: kibana-upgrade.yml
かなりシンプルです。 これは、プレイブックの実行を1つの特定の順序で整理する方法にすぎません。
それでは、上記のAnsibleプレイブックの例をどのように使用するかを考えてみましょう。 後でどのように実装するかを説明しますが、これはバージョン6.5.4にアップグレードするために実行するコマンドです。
$ ansible-playbook -i inventory -e elk_version=6.5.4 main.yml
パッケージを事前にダウンロードする
その最初のステップは実際にはオプションです。 私がこれを使用する理由は、アップグレードする前に実行中のサービスを停止するのが一般的に良い習慣であると考えるからです。 現在、高速インターネット接続がある場合、パッケージマネージャーがパッケージをダウンロードする時間はごくわずかである可能性があります。 しかし、常にそうとは限らないので、サービスがダウンする時間を最小限に抑えたいと思います。 これが、私の最初のプレイブックがyumを使用してすべてのパッケージを事前にダウンロードする方法です。 そうすれば、アップグレードの時期が来たときに、ダウンロード手順はすでに処理されています。
- hosts: logstash gather_facts: no tasks: - name: Validate logstash Version fail: msg="Invalid ELK Version" when: elk_version is undefined or not elk_version is match("\d+\.\d+\.\d+") - name: Get logstash current version command: rpm -q logstash --qf %{VERSION} args: warn: no changed_when: False register: version_found - name: Pre-download logstash install package yum: name: logstash-{{ elk_version }} download_only: yes when: version_found.stdout is version_compare(elk_version, '<')
最初の行は、このプレイがlogstash
グループにのみ適用されることを示しています。 2行目は、Ansibleにホストに関する事実をわざわざ収集しないように指示しています。 これによりプレイがスピードアップしますが、プレイ内のどのタスクにもホストに関する事実が必要ないことを確認してください。
プレイの最初のタスクは、 elk_version
変数を検証します。 この変数は、アップグレードするElasticStackのバージョンを表します。 これは、ansible-playbookコマンドを呼び出すときに渡されます。 変数が渡されないか、有効な形式でない場合、プレイはすぐに終了します。 そのタスクは、実際にはすべての演劇の最初のタスクになります。 その理由は、必要に応じて、または必要に応じて、プレイを分離して実行できるようにするためです。
2番目のタスクは、 rpm
コマンドを使用して、Logstashの現在のバージョンを取得し、変数version_found
に登録します。 その情報は次のタスクで使用されます。 行args:
、 warn: no
およびchanged_when: False
は、ansible-lintを幸せにするためにありますが、厳密には必要ありません。
最後のタスクは、実際にパッケージを事前にダウンロードするコマンドを実行します。 ただし、インストールされているLogstashのバージョンがターゲットバージョンよりも古い場合に限ります。 使用しない場合は、ダウンロードおよび古いバージョンまたは同じバージョンをポイントしないでください。
Logstashの代わりにElasticsearchとKibanaを事前にダウンロードすることを除いて、他の2つのプレイは基本的に同じです。
- hosts: elasticsearch gather_facts: no tasks: - name: Validate elasticsearch Version fail: msg="Invalid ELK Version" when: elk_version is undefined or not elk_version is match("\d+\.\d+\.\d+") - name: Get elasticsearch current version command: rpm -q elasticsearch --qf %{VERSION} args: warn: no changed_when: False register: version_found - name: Pre-download elasticsearch install package yum: name: elasticsearch-{{ elk_version }} download_only: yes when: version_found.stdout is version_compare(elk_version, '<') - hosts: kibana gather_facts: no tasks: - name: Validate kibana Version fail: msg="Invalid ELK Version" when: elk_version is undefined or not elk_version is match("\d+\.\d+\.\d+") - name: Get kibana current version command: rpm -q kibana --qf %{VERSION} args: warn: no changed_when: False register: version_found - name: Pre-download kibana install package yum: name: kibana-{{ elk_version }} download_only: yes when: version_found.stdout is version_compare(elk_version, '<')
Logstashのアップグレード
Logstashは、アップグレードする最初のコンポーネントである必要があります。 これは、Logstashが古いバージョンのElasticsearchで動作することが保証されているためです。
プレイの最初のタスクは、ダウンロード前の対応するタスクと同じです。
- name: Upgrade logstash hosts: logstash gather_facts: no tasks: - name: Validate ELK Version fail: msg="Invalid ELK Version" when: elk_version is undefined or not elk_version is match("\d+\.\d+\.\d+") - name: Get logstash current version command: rpm -q logstash --qf %{VERSION} changed_when: False register: version_found
最後の2つのタスクは、ブロックに含まれています。
- block: - name: Update logstash yum: name: logstash-{{ elk_version }} state: present - name: Restart logstash systemd: name: logstash state: restarted enabled: yes daemon_reload: yes when: version_found.stdout is version_compare(elk_version, '<')
条件付きのwhen
は、ターゲットバージョンが現在のバージョンよりも新しい場合にのみブロック内のタスクが実行されることを保証します。 ブロック内の最初のタスクはLogstashアップグレードを実行し、2番目のタスクはサービスを再起動します。
Elasticsearchクラスターのローリングアップグレード
Elasticsearchクラスターのダウンタイムが発生しないようにするには、ローリングアップグレードを実行する必要があります。 これは、一度に1つのノードをアップグレードし、クラスターが緑色の状態(完全に正常)であることを確認した後でのみ、任意のノードのアップグレードを開始することを意味します。
プレイの開始から、あなたは何か違うことに気付くでしょう:
- name: Elasticsearch rolling upgrade hosts: elasticsearch gather_facts: no serial: 1
ここにラインserial: 1
。 Ansibleのデフォルトの動作は、構成で定義された同時ホストの数である複数のホストに対して並行してプレイを実行することです。 この行は、プレイが一度に1つのホストに対してのみ実行されることを確認します。

次に、プレイに沿って使用するいくつかの変数を定義します。
vars: es_disable_allocation: '{"transient":{"cluster.routing.allocation.enable":"none"}}' es_enable_allocation: '{"transient":{"cluster.routing.allocation.enable": "all","cluster.routing.allocation.node_concurrent_recoveries": 5,"indices.recovery.max_bytes_per_sec": "500mb"}}' es_http_port: 9200 es_transport_port: 9300
各変数の意味は、劇中に登場するときに明らかになります。
いつものように、最初のタスクはターゲットバージョンを検証することです。
tasks: - name: Validate ELK Version fail: msg="Invalid ELK Version" when: elk_version is undefined or not elk_version is match("\d+\.\d+\.\d+")
以下のタスクの多くは、Elasticsearchクラスターに対してREST呼び出しを実行することで構成されます。 呼び出しは、任意のノードに対して実行できます。 プレイ中の現在のホストに対して単純に実行できますが、Elasticsearchサービスが現在のホストに対してダウンしている間に、一部のコマンドが実行されます。 したがって、次のタスクでは、REST呼び出しを実行する別のホストを選択するようにしてください。 このために、set_factモジュールとAnsibleインベントリのgroups変数を使用します。
- name: Set the es_host for the first host set_fact: es_host: "{{ groups.elasticsearch[1] }}" when: "inventory_hostname == groups.elasticsearch[0]" - name: Set the es_host for the remaining hosts set_fact: es_host: "{{ groups.elasticsearch[0] }}" when: "inventory_hostname != groups.elasticsearch[0]"
次に、続行する前に、サービスが現在のノードで稼働していることを確認します。
- name: Ensure elasticsearch service is running systemd: name: elasticsearch enabled: yes state: started register: response - name: Wait for elasticsearch node to come back up if it was stopped wait_for: port: "{{ es_transport_port }}" delay: 45 when: response.changed == true
前作同様、現在のバージョンを確認します。 今回を除いて、rpmを実行する代わりにElasticsearchRESTAPIを使用します。 rpmコマンドを使用することもできますが、この代替手段を示したいと思います。
- name: Check current version uri: url: http://localhost:{{ es_http_port }} method: GET register: version_found retries: 10 delay: 10
残りのタスクは、現在のバージョンがターゲットバージョンよりも古い場合にのみ実行されるブロック内にあります。
- block: - name: Enable shard allocation for the cluster uri: url: http://localhost:{{ es_http_port }}/_cluster/settings method: PUT body_format: json body: "{{ es_enable_allocation }}"
さて、私のアドバイスに従ってドキュメントを読むと、この手順は逆になっていることに気付くでしょう。シャードの割り当てを無効にすることです。 何らかの理由でシャードが以前に無効にされた場合に備えて、このタスクを最初にここに置くのが好きです。 次のタスクはクラスターが緑色になるのを待つため、これは重要です。 シャードの割り当てが無効になっている場合、クラスターは黄色のままになり、タイムアウトするまでタスクがハングします。
したがって、シャード割り当てが有効になっていることを確認した後、クラスターが緑色の状態にあることを確認します。
- name: Wait for cluster health to return to green uri: url: http://localhost:{{ es_http_port }}/_cluster/health method: GET register: response until: "response.json.status == 'green'" retries: 500 delay: 15
ノードサービスの再起動後、クラスターが緑色に戻るまでに長い時間がかかる場合があります。 これが、回線のretries: 500
およびdelay: 15
の理由です。 これは、クラスターが緑色に戻るまで125分(500 x 15秒)待機することを意味します。 ノードが非常に大量のデータを保持している場合は、それを調整する必要があるかもしれません。 ほとんどの場合、それで十分です。
これで、シャードの割り当てを無効にできます。
- name: Disable shard allocation for the cluster uri: url: http://localhost:{{ es_http_port }}/_cluster/settings method: PUT body_format: json body: {{ es_disable_allocation }}
また、サービスをシャットダウンする前に、オプションでありながら推奨される同期フラッシュを実行します。 同期フラッシュを実行すると、一部のインデックスで409エラーが発生することは珍しくありません。 これは無視しても安全なので、成功ステータスコードのリストに409を追加しました。
- name: Perform a synced flush uri: url: http://localhost:{{ es_http_port }}/_flush/synced method: POST status_code: "200, 409"
これで、このノードをアップグレードする準備が整いました。
- name: Shutdown elasticsearch node systemd: name: elasticsearch state: stopped - name: Update elasticsearch yum: name: elasticsearch-{{ elk_version }} state: present
サービスが停止した状態で、すべてのシャードが割り当てられるのを待ってから、ノードを再開します。
- name: Wait for all shards to be reallocated uri: url=http://{{ es_host }}:{{ es_http_port }}/_cluster/health method=GET register: response until: "response.json.relocating_shards == 0" retries: 20 delay: 15
シャードが再割り当てされたら、Elasticsearchサービスを再起動し、完全に準備が整うのを待ちます。
- name: Start elasticsearch systemd: name: elasticsearch state: restarted enabled: yes daemon_reload: yes - name: Wait for elasticsearch node to come back up wait_for: port: "{{ es_transport_port }}" delay: 35 - name: Wait for elasticsearch http to come back up wait_for: port: "{{ es_http_port }}" delay: 5
ここで、シャードの割り当てを再度有効にする前に、クラスターが黄色または緑色であることを確認します。
- name: Wait for cluster health to return to yellow or green uri: url: http://localhost:{{ es_http_port }}/_cluster/health method: GET register: response until: "response.json.status == 'yellow' or response.json.status == 'green'" retries: 500 delay: 15 - name: Enable shard allocation for the cluster uri: url: http://localhost:{{ es_http_port }}/_cluster/settings method: PUT body_format: json body: "{{ es_enable_allocation }}" register: response until: "response.json.acknowledged == true" retries: 10 delay: 15
そして、ノードが完全に回復するのを待ってから、次のノードを処理します。
- name: Wait for the node to recover uri: url: http://localhost:{{ es_http_port }}/_cat/health method: GET return_content: yes register: response until: "'green' in response.content" retries: 500 delay: 15
もちろん、前に述べたように、このブロックは、実際にバージョンをアップグレードする場合にのみ実行する必要があります。
when: version_found.json.version.number is version_compare(elk_version, '<')
Kibanaのアップグレード
アップグレードされる最後のコンポーネントはKibanaです。
ご想像のとおり、最初のタスクはLogstashのアップグレードやダウンロード前の再生と同じです。 1つの変数の定義を除いて:
- name: Upgrade kibana hosts: kibana gather_facts: no vars: set_default_index: '{"changes":{"defaultIndex":"syslog"}}' tasks: - name: Validate ELK Version fail: msg="Invalid ELK Version" when: elk_version is undefined or not elk_version is match("\d+\.\d+\.\d+") - name: Get kibana current version command: rpm -q kibana --qf %{VERSION} args: warn: no changed_when: False register: version_found
set_default_index
変数を使用するタスクに到達したら、それについて説明します。
残りのタスクは、インストールされているバージョンのKibanaがターゲットバージョンよりも古い場合にのみ実行されるブロック内にあります。 最初の2つのタスクは、Kibanaを更新して再起動します。
- name: Update kibana yum: name: kibana-{{ elk_version }} state: present - name: Restart kibana systemd: name: kibana state: restarted enabled: yes daemon_reload: yes
そして、Kibanaにとってはそれで十分だったはずです。 残念ながら、何らかの理由で、アップグレード後、Kibanaはデフォルトのインデックスパターンへの参照を失います。 これにより、アップグレード後にアクセスする最初のユーザーにデフォルトのインデックスパターンを定義するように要求するため、混乱が生じる可能性があります。 これを回避するには、デフォルトのインデックスパターンをリセットするタスクを必ず含めてください。 以下の例ではsyslog
ですが、使用しているものに変更する必要があります。 ただし、インデックスを設定する前に、Kibanaが稼働していて、リクエストを処理する準備ができていることを確認する必要があります。
- name: Wait for kibana to start listening wait_for: port: 5601 delay: 5 - name: Wait for kibana to be ready uri: url: http://localhost:5601/api/kibana/settings method: GET register: response until: "'kbn_name' in response and response.status == 200" retries: 30 delay: 5 - name: Set Default Index uri: url: http://localhost:5601/api/kibana/settings method: POST body_format: json body: "{{ set_default_index }}" headers: "kbn-version": "{{ elk_version }}"
結論
Elastic Stackは貴重なツールであり、まだ使用していない場合はぜひご覧になることをお勧めします。 それはそれなりに素晴らしく、絶えず改善されているので、絶え間なくアップグレードされていることに追いつくのは難しいかもしれません。 これらのAnsiblePlaybookが、私と同じように役立つことを願っています。
https://github.com/orgito/elk-upgradeのGitHubで利用できるようにしました。 非実稼働環境でテストすることをお勧めします。
アプリにElasticsearchを組み込むことを検討しているRubyonRails開発者の方は、Elasticsearch for Ruby on Rails:CoreToptalソフトウェアエンジニアのArkadiyZabazhanovによるChewyGemのチュートリアルをご覧ください。