6.1.1. 概要
システム運用の場面では、システムの稼働状況をモニタリングする手段としてログファイルを監視することがあります。
しかしながら、WebOTX MicroService のようにコンテナベースのアプリケーションは独立したコンテナ空間で動作し、
スケールイン・スケールアウトの契機でコンテナが増減するため、従来のログ監視ソフトウェアを用いてログファイルを
直接監視することは困難です。
そこで、 WebOTX MicroService においてELKスタックを構築して
個別のコンテナで動作する WebOTX のログを1箇所に集約する方法を説明します。
ELKスタックは以下のソフトウェアを組み合わせて、ノードに点在する情報(例えばログ)を1箇所に集約する構成です。
-
Elasticsearch: ログを保存するオブジェクトストア
-
Logstash: ログを収集してElasticsearchに送信する
-
Kibana: Elasticsearchに集約したログを可視化するWeb UI
Elasticsearch にログを集約することで、多数のコンテナで生成されたログの監視や可視化が容易になります。
また、ログファイルを収集する Logstash は定義によって Elasticsearch 以外のプロトコルやストレージへの保管が可能です。
各ソフトウェアの詳細については公式サイトを参照ください。
本節では、Logstash で WebOTX のログを収集し、事前に構築済みの Elasticsearch に送信する方法を説明します。
6.1.2. 構成
一般的にコンテナベースのアプリケーションのログ収集は、アプリケーションの実装に応じて次のいずれかの手段を用います。
(構成1) アプリケーションがログをコンテナに標準出力に出力し、ホストマシンのLogstashでそれを収集する
(構成2) アプリケーションがコンテナ内部のファイルにログを出力し、コンテナ内部のLogstashがそれを収集する
前者は構成がシンプルになるメリットがありますが、アプリケーションが複数のログファイルを出力する場合は対応できません。
WebOTX MicroService は複数のログファイルを出力するため、後者の構成を採用します。
WebOTX MicroService におけるログ収集の構成を次の図に示します。

この構成では WebOTX が出力する各種ログが更新される度に、 Logstash がログメッセージを読み取り Elasticsearch に送信します。
初期設定では個々のコンテナは別のコンテナのファイルシステムにアクセスできませんので、
Pod には WebOTX のログ出力先ディレクトリ /opt/WebOTX/domains/domain1/logsを共有ディレクトリとして扱う設定を行い、
Logstash コンテナが WebOTX コンテナのログファイルにアクセスできるようにします。
Elasticsearch に送られたログは監視や Kibana を通して WebUI でログメッセージを確認することができます。
6.1.3. 構築手順
Logstash イメージの作成
最初に、WebOTX のコンテナと同じPodで同居する Logstash のコンテナイメージを作成します。
ここでは、作業用のディレクトリを作成して、その中に次の3つのファイルを用意します。
-
Dockerfile : Logstash の Dockerfile
-
logstash.conf : Logstash のログ収集設定
-
webotx : WebOTX のログファイルのパースパターン
FROM docker.elastic.co/logstash/logstash-oss:6.6.0
COPY logstash.conf /usr/share/logstash/pipeline/logstash.conf
COPY webotx /usr/share/logstash/config/pattern
input {
file{
path=>["/opt/WebOTX/domains/domain1/logs/agent.log",
"/opt/WebOTX/domains/domain1/logs/agent.log.*",
"/opt/WebOTX/domains/domain1/logs/server.log",
"/opt/WebOTX/domains/domain1/logs/server.log.*"]
start_position=>"beginning"
sincedb_path => "/opt/WebOTX/domains/domain1/logs/sincedb"
codec => multiline {
patterns_dir => ["/usr/share/logstash/config/pattern"]
pattern => "^%{L4J2_DATE}"
negate => true
what => "previous"
auto_flush_interval => 10
}
}
file{
path=>["/opt/WebOTX/domains/domain1/logs/server_access.log",
"/opt/WebOTX/domains/domain1/logs/server_access.log.*",
"/opt/WebOTX/domains/domain1/logs/diagnostics/report.csv",
"/opt/WebOTX/domains/domain1/logs/diagnostics/report.csv.*",
"/opt/WebOTX/domains/domain1/logs/diagnostics/optional-stats.csv",
"/opt/WebOTX/domains/domain1/logs/diagnostics/optional-stats.csv.*" ]
start_position=>"beginning"
sincedb_path => "/opt/WebOTX/domains/domain1/logs/sincedb"
}
}
filter {
mutate {
add_field => {
"ip" => "${POD_IP:unkown}"
"node" => "${NODE_NAME:unkown}"
}
}
if [path] == "/opt/WebOTX/domains/domain1/logs/agent.log"
or [path] == "/opt/WebOTX/domains/domain1/logs/agent.log.1"
or [path] == "/opt/WebOTX/domains/domain1/logs/agent.log.2" {
grok {
patterns_dir => ["/usr/share/logstash/config/pattern"]
match => {
"message" => "%{AGENT_LOG}"
}
overwrite => [ "message" , "timestamp"]
}
}
if [path] == "/opt/WebOTX/domains/domain1/logs/server.log"
or [path] == "/opt/WebOTX/domains/domain1/logs/server.log.1"
or [path] == "/opt/WebOTX/domains/domain1/logs/server.log.2" {
grok {
patterns_dir => ["/usr/share/logstash/config/pattern"]
match => {
"message" => "%{SERVER_LOG}"
}
overwrite => [ "message" , "timestamp"]
}
}
if [path] == "/opt/WebOTX/domains/domain1/logs/server_access.log"
or [path] == "/opt/WebOTX/domains/domain1/logs/server_access.log.1"
or [path] == "/opt/WebOTX/domains/domain1/logs/server_access.log.2" {
grok {
patterns_dir => ["/usr/share/logstash/config/pattern"]
match => {
"message" => "%{ACCESS_LOG}"
}
overwrite => ["timestamp"]
}
}
if [path] == "/opt/WebOTX/domains/domain1/logs/diagnostics/report.csv"
or [path] == "/opt/WebOTX/domains/domain1/logs/report.csv.1"
or [path] == "/opt/WebOTX/domains/domain1/logs/report.csv.2" {
if [message] =~ /Date,.+/ {
drop { }
}
grok {
patterns_dir => ["/usr/share/logstash/config/pattern"]
match => {
"message" => "%{DIAGNOSTIC_REPORT_LOG}"
}
overwrite => ["timestamp"]
}
}
}
output {
stdout {
}
### To send the log to Elasticsearch, please uncomment and enter Elasticsearch's host name.
# elasticsearch {
# hosts => ["localhost:9200"]
# index => "webotx_agent"
# }
}
Memo
上記の設定では logstash.conf の output 定義により、 Logstash は集約したログを標準出力にします。
Elasticsearch 定義のコメントを除外して、構築済みの Elasticsearch の適切な接続情報を定義してください。
## WebOTX grok pattern file.
# Common pattern.
NULLABLE_NUMBER (%{NUMBER}|-)
IP_HOSTNAME (%{IP}|%{HOSTNAME})
MILLISECOND \d\d\d
L4J2_DATE %{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:%{MINUTE}:%{SECOND},%{MILLISECOND}
DIAGNOSTIC_DATE %{YEAR}/%{MONTHNUM}/%{MONTHDAY} %{HOUR}:%{MINUTE}:%{SECOND}.%{MILLISECOND}
AS_LOGLEVEL (ERROR|WARN|SLOGINFO|INFO|CONFIG|DEBUG|DETAIL|TRACE)
NULLABLE_USER (%{USER}|-)
DATE %{MONTHDAY}/%{MONTH}/%{YEAR}
DATETIME %{DATE:date}:%{TIME:time}
FULL_DATETIME %{DATETIME:datetime} %{INT}
HTTP_METHOD (OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT|PRI)
URI_STRING [:/?#@$&'()*+,;=a-zA-Z0-9.~_%\[\]\-]+
HTTP_VERSION HTTP/%{NUMBER:httpversion}
QUERY_STRING (\?%{URI_STRING:querystring})?
# Log file specific pattern.
AGENT_LOG %{L4J2_DATE:timestamp} %{AS_LOGLEVEL:level}\s*%{NOTSPACE:component}\s*- %{GREEDYDATA:text} \[%{GREEDYDATA:thread}\]%{GREEDYDATA:exception}
SERVER_LOG %{L4J2_DATE:timestamp} - %{GREEDYDATA:text} \[%{GREEDYDATA:thread}\]
ACCESS_LOG %{IP:clientip} - %{NULLABLE_USER:auth} \[%{FULL_DATETIME:timestamp}\] \"%{HTTP_METHOD:method} %{URI_STRING:request} %{QUERY_STRING} %{HTTP_VERSION}\" %{NULLABLE_NUMBER:status} %{NULLABLE_NUMBER:bytes} %{NUMBER:elapsed}
DIAGNOSTIC_REPORT_LOG %{DIAGNOSTIC_DATE:timestamp},%{NUMBER:average_response_time},%{NUMBER:cpu_usage},%{NUMBER:heap_used}, %{NUMBER:non_heap_used},%{NUMBER:web_threads},%{NUMBER:web_busy_threads},%{NUMBER:ap_threads},%{NUMBER:ap_busy_threads},%{NUMBER:jdbc_connections},%{NUMBER:disk_usage}
全てのファイルが準備できたら、oc コマンドを使って OpenShift 上に logstash イメージをビルドします。
> oc new-build . --to=webotx-micro-logstash
--> Found Docker image 8f45a77 (3 weeks old) from docker.elastic.co for "docker.elastic.co/logstash/logstash:6.6.0"
* An image stream will be created as "logstash:6.6.0" that will track the source image
* A Docker build using binary input will be created
* The resulting image will be pushed to image stream "webotx-micro-logstash:latest"
* A binary build was created, use 'start-build --from-dir' to trigger a new build
--> Creating resources with label build=webotx-micro-logstash ...
imagestream "logstash" created
imagestream "webotx-micro-logstash" created
buildconfig "webotx-micro-logstash" created
--> Success
> oc start-build webotx-micro-logstash -n <プロジェクト名> --from-dir=.
Uploading directory "." as binary input for the build ...
build "webotx-micro-logstash-2" started
ビルドの状況は oc get builds コマンドで確認ができます。
> oc get builds
NAME TYPE FROM STATUS STARTED DURATION
webotx-micro-logstash-1 Docker Binary Running 7 seconds ago
ビルドが完了すると OpenShift のイメージストリームにイメージが登録されます。
oc get is コマンドを実行して webotx-micro-logstash が登録されていることを確認します。
> oc get is
NAME DOCKER REPO TAGS UPDATED
rhel docker-registry.default.svc:5000/<プロジェクト名>/rhel latest 2 months ago
webotx-micro docker-registry.default.svc:5000/<プロジェクト名>/webotx-micro latest 3 hours ago
webotx-micro-logstash docker-registry.default.svc:5000/<プロジェクト名>/webotx-micro-logstash latest 1 minutes ago
WebOTX と Logstash の Pod 作成
OpenShift Webコンソールにログインし、 作業中のプロジェクトを選択して、 Overview 画面に移動します。
画面右上の "Add to Project" から "Import YAML/JSON" を選択して、以下の定義を入力します。
apiVersion: v1
kind: DeploymentConfig
metadata:
name: webotx-pod
spec:
template:
metadata:
labels:
name: webotx-pod
app: webotx-pod
spec:
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: webotx-micro
image: docker-registry.default.svc:5000/<プロジェクト名>/webotx-micro:latest
volumeMounts:
- name: shared-data
mountPath: /opt/WebOTX/domains/domain1/logs
- name: webotx-micro-logstash
image: docker-registry.default.svc:5000/<プロジェクト名>/webotx-micro-logstash:latest
volumeMounts:
- name: shared-data
mountPath: /opt/WebOTX/domains/domain1/logs
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
replicas: 1
selector:
name: webotx-pod
app: webotx-pod
各属性の概要は以下の通りです。
spec.template.spec.containers.image に定義するイメージ名を変更するなど、
環境や運用に合わせて適宜変更してください。
| 属性 |
概要 |
|
Podの名称 |
spec.template.metadata.labels
|
任意のメタ情報 |
spec.template.spec.volumes
|
Pod内のコンテナ同士でログファイルを共有するためのストレージ定義 |
spec.template.spec.containers.name
|
コンテナ名 |
spec.template.spec.containers.image
|
コンテナイメージ名 |
spec.template.spec.containers.volumeMounts
|
Pod内のコンテナ同士で共有するファイルパス |
spec.template.spec.containers.env
|
コンテナ内部で有効な環境変数の定義 |
|
起動するPodの数 |
以上で設定は終了です。
webotx-pod を起動すると WebOTX のログは出力の都度 logstash.conf に指定した Elasticsearch に転送されます。
ここでは、Microprofile Health Check との連携の例として、OpenShift 上で配備したマイクロサービスの
/health にリクエストをして、コンテナの監視を行う方法について説明します。
Initial Delay は Pod が起動してから /health にリクエストを行うまでの待ち時間です。
ここでは、例として 30 秒を指定しています。
この待ち時間を指定しなかった場合、WebOTX のドメインが起動していない状態で /health へ
リクエスト行うため、Pod 起動時に以下のような WARN がイベントログに記録されることが
あります。