0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Power Systems Virtual ServerへのOpenShift 4.7導入(6): Elasticsearch保存データのエクスポート

Posted at

はじめに

こちらの記事でPower Systems Virtual Server(以下PowerVS)へのOpenShift 4.7の導入後作業としてinfraノードの構成を実施しました。infraノードのElasticsearchは短期的なログ保存を目的としています。長期的にログを保存するために外部ElasticsearchやFluentdをサポートする外部ログ集計ソリューションへログを転送することができますが、この記事ではテキストファイルへエクスポートします。

OpenShift Logging Elasticsearch インスタンスは、短期 (約 7 日間) の保存について最適化され、テストされています。長期間ログを保持する必要がある場合は、データをサードパーティーのストレージシステムに移動することが推奨されます。

ログを他のログアグリゲーターに送信するには、OpenShift Container Platform ログ転送 API を使用します。この API を使用すると、コンテナー、インフラストラクチャーおよび監査ログをクラスター内外の特定のエンドポイントに送信できます。

1. Elasticsearch保存データのエクスポート

1.1. Elasticsearch接続用ルート作成

bastionノードからElasticsearchのログストアに対して、curlで接続できるようにルートを作成します。

bastionノード
oc project openshift-logging
vi route.yaml
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: elasticsearch
  namespace: openshift-logging
spec:
  host:
  to:
    kind: Service
    name: elasticsearch
  tls:
    termination: reencrypt
    destinationCACertificate: |
### vi終了

oc extract secret/elasticsearch --to=. --keys=admin-ca
cat ./admin-ca | sed -e "s/^/      /" >> route.yaml
oc apply -f route.yaml
oc get route
### 標準出力↓
NAME            HOST/PORT                                          PATH   SERVICES        PORT    TERMINATION          WILDCARD
elasticsearch   elasticsearch-openshift-logging.apps.ocp.powervs          elasticsearch   <all>   reencrypt            None
kibana          kibana-openshift-logging.apps.ocp.powervs                 kibana          <all>   reencrypt/Redirect   None

1.2. インデックス一覧の取得

OpenShiftユーザーのトークンでElasticsearchに接続してインデックス一覧を取得します。
ここではユーザーとしてkubeadminを使用しています。

bastionノード
# 準備
oc login -u kubeadmin -p <kubeadminパスワード>
token=$(oc whoami -t)
domain=elasticsearch-openshift-logging.apps.ocp.powervs
header1="Authorization: Bearer ${token}"

# インデックス一覧取得
curl -sk -H "${header1}" https://${domain}/_cat/indices
### 標準出力↓
green open audit-000003                   ltFqYg6jT9qZ_wV6QRGIuA 3 1       0 0   1.5kb    783b
green open app-000077                     WGatHRvEQrSrEuUeNyEwyg 3 1       0 0   1.5kb    783b
green open app-000076                     yZ4NX2HVRHymJBSCWs4J7Q 3 1       0 0   1.5kb    783b
green open app-000084                     H-vMl6moTV6b70kF-79-Og 3 1       0 0   1.5kb    783b
green open infra-000007                   ZOD5e6BwQdSVJrGYxCSRFQ 3 1 3391227 0   3.8gb   1.9gb
green open infra-000013                   WUTnjBHMTSupphIesJf0JA 3 1 2620386 0   3.1gb   1.5gb
green open infra-000003                   NqJ0DCXMSMWD7IUrApM_Zg 3 1 3304724 0   3.9gb   1.9gb
green open app-000093                     olJZao2VS5OqmM3KpRINKQ 3 1       0 0   1.5kb    783b
green open infra-000014                   i9KXTBGOQRO2vYfulIXmcw 3 1 1227321 0   1.5gb 776.5mb
green open app-000085                     1m-sM80PQaa5ipZtvWY-PA 3 1       0 0   1.5kb    783b
green open infra-000005                   hjyO2OJBTcKwozQzF0279A 3 1 3396051 0   3.8gb   1.9gb
green open audit-000005                   _EmhHSEpQKSEL6tyVCKw1w 3 1       0 0   1.5kb    783b
green open .security                      pBEUFDweSnqDNL7icj7g2A 1 1       5 0  58.3kb  29.1kb
green open app-000095                     b6l0rD6hTnKGWgF7L1GB4A 3 1      32 0 576.8kb 266.9kb
・・・

1.3. JSON形式でのエクスポート

Elasticsearchでは一度に取得できる件数が「10,000」に制限されています。

■「10,000」件以下の場合のJSON形式でのエクスポート例

bastionノード
# 準備
oc login -u kubeadmin -p <kubeadminパスワード>
token=$(oc whoami -t)
domain=elasticsearch-openshift-logging.apps.ocp.powervs
header1="Authorization: Bearer ${token}"

# 件数確認(32件)
curl -sk -H "${header1}" https://${domain}/_cat/count/app-000095
### 標準出力↓
1621402132 14:28:52 32

# JSON形式でのエクスポート
curl -sk -H "${header1}" https://${domain}/app-000095/_search?size=32 > app-000095.json

■「10,000」件より多い場合のJSON形式でのエクスポート例

bastionノード
# 準備
oc login -u kubeadmin -p <kubeadminパスワード>
token=$(oc whoami -t)
domain=elasticsearch-openshift-logging.apps.ocp.powervs
header1="Authorization: Bearer ${token}"
header2="Content-Type: application/json"

# 最初の10,000件とscroll_idを取得
curl -sk -H "${header1}" -H "${header2}" -d @test.json \
  https://${domain}/infra-000007/_search?scroll=1m > infra-000007.json
scroll_id=`jq -r "._scroll_id" infra-000007.json | tr -d '\n'`

# 下記(1)~(3)を取得件数が『0』なるまで繰り返す
# (1) 次の10,000件を取得
curl -sk -H "${header1}" -H "${header2}" -d "{\"scroll\":\"1m\",\"scroll_id\":\"${scroll_id}\"}" \
  https://${domain}/_search/scroll > temp.json

# (2) 件数確認
jq -r ".hits.hits | length" temp.json

# (3) 0件でない場合に実行
cat temp.json >> infra-000007.json
test.json
{
  "size": "10000",
  "_source": [
    "kubernetes.container_name",
    "kubernetes.namespace_name",
    "kubernetes.pod_name",
    "hostname",
    "message",
    "@timestamp"
  ]
}

■JSON出力例(1件)

{
  "took" : 84,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 32,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "app-000095",
        "_type" : "_doc",
        "_id" : "YzBhMGZhZDctZWRiNS00YmM3LThlNDUtOTFkNzJjZDhiMTM4",
        "_score" : 1.0,
        "_source" : {
          "docker" : {
            "container_id" : "30d33fb8920008f1e48c09b7b0fd2c4c51bfcb30240dc8e93f429faba7882e53"
          },
          "kubernetes" : {
            "container_name" : "open-liberty",
            "namespace_name" : "open-liberty",
            "pod_name" : "open-liberty-0",
            "container_image" : "docker.io/library/open-liberty:21.0.0.4-full-java11-openj9",
            "container_image_id" : "docker.io/library/open-liberty@sha256:3b4da7bbf3a9298ffff3fbc9c311c65221192d10529c3a83312e9243c710e3c4",
            "pod_id" : "bfad4941-c019-44a0-98e3-a8343dac2c08",
            "host" : "worker-0",
            "master_url" : "https://kubernetes.default.svc",
            "namespace_id" : "fec969a8-4f62-4ef4-b82f-6c07919a3cf5",
            "flat_labels" : [
              "app=open-liberty",
              "controller-revision-hash=open-liberty-56dd67f46b",
              "statefulset_kubernetes_io/pod-name=open-liberty-0"
            ]
          },
          "message" : "[AUDIT   ] CWWKE0085I: The server defaultServer is stopping because the JVM is exiting.",
          "level" : "unknown",
          "hostname" : "worker-0",
          "pipeline_metadata" : {
            "collector" : {
              "ipaddr4" : "192.168.25.113",
              "inputname" : "fluent-plugin-systemd",
              "name" : "fluentd",
              "received_at" : "2021-05-20T14:56:40.664956+00:00",
              "version" : "1.7.4 1.6.0"
            }
          },
          "@timestamp" : "2021-05-20T14:56:39.647267+00:00",
          "viaq_msg_id" : "YzBhMGZhZDctZWRiNS00YmM3LThlNDUtOTFkNzJjZDhiMTM4"
        }
      }
    ]
  }
}

1.4. JSON形式からCSV形式への変換

前項でエクスポートした「app-000095.json」をCSV形式に変換します。ダブルクオーテーションを除去し、message内のコンマはシャープに置換しています。

bastionノード
cat app-000095.json | jq -r '.hits.hits[] |
[ ._source."@timestamp",
  ._source.hostname,
  ._source.kubernetes.namespace_name,
  ._source.kubernetes.container_name,
  ._source.kubernetes.pod_name,
  ._source.message] | @csv' | sed -e 's/\"//g' -e 's/,/#/6g' > app-000095.csv
app-000095.csv抜粋
2021-05-20T14:56:39.647267+00:00,worker-0,open-liberty,open-liberty,open-liberty-0,[AUDIT   ] CWWKE0085I: The server defaultServer is stopping because the JVM is exiting.

1.5. CSV形式ファイルの分割

「app-000095.csv」は複数のノードやネームスペースのログを集約したものなので分割します。

bastionノード
awk -F, '{out=$2"_"$3"_"$4".csv"; print $1","$5","$6 >> out}' app-000095.csv
ls -l
### 標準出力↓
-rw-r--r--. 1 root root  1400  5月 21 00:07 worker-0_open-liberty_open-liberty.csv
-rw-r--r--. 1 root root  1134  5月 21 00:07 worker-1_open-liberty_open-liberty.csv
worker-0_open-liberty_open-liberty.csv抜粋
2021-05-20T14:56:39.647267+00:00,open-liberty-0,[AUDIT   ] CWWKE0085I: The server defaultServer is stopping because the JVM is exiting.

1.6. シェルスクリプトによる一括処理

一連の処理をシェルスクリプトとして実行します。前項ではインデックスの全件をエクスポートしましたが、ここでは日付「2021-05-20」を範囲指定して件数を取得し、0件以上だった場合にCSVファイルに出力しています。

bastionノード
mkdir /tmp/output
./export.sh
### 標準出力↓
[2021/05/21 20:25:33] JSON取得: infra-000013, 2620386
[2021/05/21 20:36:57] JSON取得: infra-000014, 2605663
[2021/05/21 20:46:52] JSON取得: app-000095, 32
[2021/05/21 20:46:52] JSON取得: infra-000015, 1579811
[2021/05/21 20:52:02] JSON取得: infra-000012, 1035194
[2021/05/21 20:55:13] CSV変換: /tmp/app-000095.json
[2021/05/21 20:55:13] CSV変換: /tmp/infra-000012.json
[2021/05/21 20:56:12] CSV変換: /tmp/infra-000013.json
[2021/05/21 20:58:43] CSV変換: /tmp/infra-000014.json
[2021/05/21 21:01:12] CSV変換: /tmp/infra-000015.json
[2021/05/21 21:02:42] CSVファイル分割: /tmp/app-000095.csv
[2021/05/21 21:02:42] CSVファイル分割: /tmp/infra-000012.csv
[2021/05/21 21:02:55] CSVファイル分割: /tmp/infra-000013.csv
[2021/05/21 21:03:27] CSVファイル分割: /tmp/infra-000014.csv
[2021/05/21 21:03:59] CSVファイル分割: /tmp/infra-000015.csv
[2021/05/21 21:04:19] CSVファイルソート: /tmp/infra-0__.tmp
[2021/05/21 21:04:19] CSVファイルソート: /tmp/infra-0_openshift-image-registry_node-ca.tmp
・・・

CSVファイルの行数が各インデックスの検索件数と一致しています。

bastionノード
wc -l /tmp/*.csv
### 標準出力↓
        32 /tmp/app-000095.csv
   1035194 /tmp/infra-000012.csv
   2620386 /tmp/infra-000013.csv
   2605663 /tmp/infra-000014.csv
   1579811 /tmp/infra-000015.csv
   7841086 合計
export.sh
token=$(oc whoami -t)
header1="Authorization: Bearer ${token}"
header2="Content-Type: application/json"
domain=elasticsearch-openshift-logging.apps.ocp.powervs

# インデックス一覧取得
curl -sk -H "${header1}" https://${domain}/_cat/indices | egrep "app-|infra-" | awk '{print $3}' > /tmp/indices.lst

cat /tmp/indices.lst | while read index
do
  # インデックスの件数取得
  #count=`curl -sk -H "${header1}" https://${domain}/_cat/count/${index} | awk '{print $3}'`
  count=`curl -sk -H "${header1}" -H "${header2}" https://${domain}/${index}/_count -d @count.json | jq .count`
  if [ ${count}"X" = "X" ]; then
    count = -1
  fi

  # JSON取得
  if [ ${count} -gt 0 -a ${count} -le 10000 ]; then
    # レコード数10000以下
    echo [`date "+%Y/%m/%d %H:%M:%S"`] "JSON取得: ${index}, ${count}"
    curl -sk -H "${header1}" https://${domain}/${index}/_search?size=${count} > /tmp/${index}.json
  elif [ ${count} -gt 10000 ]; then
    # レコード数10000より大きい
    echo [`date "+%Y/%m/%d %H:%M:%S"`] "JSON取得: ${index}, ${count}"
    curl -sk -H "${header1}" -H "${header2}" -d @export.json \
      https://${domain}/${index}/_search?scroll=1m > /tmp/${index}.json
    scroll_id=`jq -r "._scroll_id" /tmp/${index}.json  | tr -d '\n'`

    length=-1
    while [ ${length} -ne 0 ]
    do
      curl -sk -H "${header1}" -H "${header2}" -d "{\"scroll\":\"1m\",\"scroll_id\":\"${scroll_id}\"}" \
        https://${domain}/_search/scroll > /tmp/temp.json
      length=`jq -r ".hits.hits | length" /tmp/temp.json`
      cat /tmp/temp.json >> /tmp/${index}.json
      rm -f /tmp/temp.json
    done

    # スクロールID削除
    curl -sk -H "${header1}" -H "${header2}" -XDELETE -d "{ \"scroll_id\": \"${scroll_id}\"}" \
      https://${domain}/_search/scroll > /dev/null
  fi
done

# JSONからCSVへの変換
ls -l /tmp/*.json | grep -v "temp.json" | awk '{print $9}' | while read fname
do
  echo [`date "+%Y/%m/%d %H:%M:%S"`] "CSV変換: ${fname}"
  bname=`basename ${fname} | awk -F. '{print $1}'`
  cat ${fname} | jq -r '.hits.hits[] |
  [ ._source."@timestamp",
    ._source.hostname,
    ._source.kubernetes.namespace_name,
    ._source.kubernetes.container_name,
    ._source.kubernetes.pod_name,
    ._source.message] | @csv' | sed -e 's/\"//g' -e 's/,/#/6g' > /tmp/${bname}.csv
done

# CSVファイルをコンテナ単位に分割
ls -l /tmp/*.csv | awk '{print $9}' | while read fname
do
  echo [`date "+%Y/%m/%d %H:%M:%S"`] "CSVファイル分割: ${fname}"
  awk -F, '{out="/tmp/"$2"_"$3"_"$4".tmp"; print $1","$5","$6 >> out}' ${fname}
done

# コンテナ単位に分割したCSVファイルを時系列でソート
ls -l /tmp/*.tmp | awk '{print $9}' | while read fname
do
  echo [`date "+%Y/%m/%d %H:%M:%S"`] "CSVファイルソート: ${fname}"
  bname=`basename ${fname} | sed -e 's/\.tmp$//g'`
  sort ${fname} > /tmp/output/${bname}.csv
  rm -f ${fname}
done
count.json
{
  "query": {
    "range": {
      "@timestamp": {
        "lt": "2021-05-21",
        "gte": "2021-05-20"
      }
    }
  }
}
export.json
{
  "size": "10000",
  "_source": [
    "kubernetes.container_name",
    "kubernetes.namespace_name",
    "kubernetes.pod_name",
    "hostname",
    "message",
    "@timestamp"
  ],
  "query": {
    "range": {
      "@timestamp": {
        "lt": "2021-05-21",
        "gte": "2021-05-20"
      }
    }
  }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?