やりたいこと
- エックスサーバーのapacheログをもってきてEFK(Elasticsearch + Fluentd + Kibana)で可視化
- エックスサーバーでは毎日5時にそれまでのログをgzipファイルにまとめている
- 過去のログの保存日数はデフォルトで過去30日分
- 詳しくはこちら https://www.xserver.ne.jp/manual/man_server_log.php
- 別途バックアップしていた30日以上前のgzipで圧縮されたログも一緒に見たい
- dockerの勉強(これが一番の目的)
事前準備
エックスサーバーではSSHを使用するをONにしておいて、鍵の生成をしておく。
ファイル配置
.
├── docker-compose.yaml
├── fetch
│ ├── Dockerfile
│ ├── cron.sh
│ ├── fetch.sh
│ ├── init.sh
│ ├── private.key
│ └── oldlogs
│ ├── example.com.access_log_20170726.gz
│ ├── example.com.access_log_20170727.gz
│ ├── example.com.access_log_20170728.gz
│ ├── example.com.access_log_20170729.gz
│ ├── example.com.access_log_20170730.gz
│ └── example.com.access_log_20170731.gz
└── fluentd
├── Dockerfile
├── fluent.conf
└── plugins
fetchコンテナ
- エックスサーバーに鍵認証、ポート10022でSSH接続してログをrsync
- cronで5分ごとに
/home/username/example.com/logs/
以下を/var/log/apache2/
に持ってくる - 別途バックアップしていたログはホストの
oldlogs
ディレクトリに配置しておき、コンテナの/var/log/apache2_old/
に展開する - 当日のログはシンボリックリンクになっているので、rsyncは
--copy-links
オプション必須
SSHの秘密鍵をどうやってコンテナに渡せばよいのか・・・
このやり方だとcommitしたときにイメージに含まれてしまう。
fetch/Dockerfile
FROM ubuntu
ENV XSRV_USER=username \
XSRV_HOST=example.com \
XSRV_PORT=10022
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y openssh-client cron rsync && \
mkdir -p /root/.ssh && \
mkdir -p /var/log/apache2 && \
touch /var/log/apache2/access.log && \
touch /var/log/apache2/access.log.old
COPY "init.sh" "/root/init.sh"
COPY "cron.sh" "/root/cron.sh"
COPY "private.key" "/root/.ssh/private.key"
CMD /bin/bash /root/init.sh
最初の1回だけ実行するスクリプト
- SSH秘密鍵のパーミッションを設定
- 登録済みホストに登録
- rsyncで使うとIPアドレスになって渡されるので、どうにかしたい
- 昨日以前のgzになっているログを全部
access.log.old
に展開 - 環境変数を
env.sh
に書き出してcronに登録(c.f. http://qiita.com/rerofumi/items/fc0126c4e985b78f769b )
fetch/init.sh
#!/bin/bash
cd /var/log/apache2
if [ -d /var/log/apache2_old/ ]; then
rsync -av /var/log/apache2_old/*.gz ./
fi
if [ -f /root/.ssh/private.key ]; then
chmod 600 /root/.ssh/private.key
ssh-keyscan -p ${XSRV_PORT} -H ${XSRV_HOST} > /root/.ssh/known_hosts
rsync -av --copy-links -e "ssh -p ${XSRV_PORT} -i /root/.ssh/private.key" ${XSRV_USER}@${XSRV_HOST}:/home/${XSRV_USER}/${XSRV_HOST}/log/ ./
fi
zcat *.gz > access.log.old
printenv | awk '{print "export " $1}' > /root/env.sh
echo '*/5 * * * * root /bin/bash /root/cron.sh' >> /etc/crontab
cron -f
cronで起動するスクリプト
-
env.sh
から環境変数を読み込む - 当日分のログ(
${XSRV_HOST}.access_log
)をaccess.log
にコピー
fetch/cron.sh
#!/bin/bash
. /root/env.sh
cd /var/log/apache2
if [ -f /root/.ssh/private.key ]; then
rsync -av --copy-links -e "ssh -p ${XSRV_PORT} -i /root/.ssh/private.key" ${XSRV_USER}@${XSRV_HOST}:/home/${XSRV_USER}/${XSRV_HOST}/log/ ./
fi
cp -f ${XSRV_HOST}.access_log access.log
fluentdコンテナ
- エックスサーバーの吐くapacheログのフォーマットを読み込む
- ログをElasticsearchに渡す
fluentd/Dockerfile
FROM fluent/fluentd
RUN gem install fluent-plugin-elasticsearch
COPY fluent.conf /fluentd/etc/fluent.conf
fluentd/fluent.conf
<source>
type tail
path /var/log/apache2/access.log,/var/log/apache2/access.log.old
read_from_head true
tag apache.access_log
pos_file /home/fluent/access.log.pos
format /^(?<virtualhost>[^ ]*) (?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>(\\\"|[^\"])*)" "(?<agent>(\\\"|[^\"])*)")?$/
time_format %d/%b/%Y:%H:%M:%S %z
types code:integer,size:integer
</source>
<match apache.**>
type copy
<store>
type elasticsearch
include_tag_key true
tag_key _tag
host elasticsearch
port 9200
index_name access
logstash_format true
logstash_prefix logstash
buffer_type file
buffer_path /tmp/fluentd*.buffer
# buffer_chunk_limit 1g
# buffer_queue_limit 256
# flush_interval 60s
# retry_wait 5s
</store>
</match>
docker-compose.yaml
- ElasticsearchとKibanaはDockerHubのものではなく公式のものを使用
- 永続化データはNamed Data Volumeに保存
- ログをfluentdからElasticsearchに送るときにキューが溢れたりしないように、Elasticsearchのパラメータを調整
docker-compose.yaml
version: '2'
services:
fetch:
build: ./fetch
volumes:
- apachelog:/var/log/apache2
- ./fetch/oldlogs:/var/log/apache2_old:ro
restart: always
fluentd:
build: ./fluentd
volumes:
- apachelog:/var/log/apache2
- fluentdata:/var/log/fluent
environment:
FLUENTD_CONF: fluent.conf
restart: always
depends_on:
- elasticsearch
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:5.5.2
volumes:
- esdata:/usr/share/elasticsearch/data
- esconfig:/usr/share/elasticsearch/config
expose:
- "9200"
restart: always
environment:
- bootstrap.memory_lock=true
- xpack.security.enabled=false
- xpack.monitoring.enabled=false
- xpack.watcher.enabled=false
- xpack.graph.enabled=false
- xpack.ml.enabled=false
- http.max_content_length=1g
- thread_pool.index.queue_size=-1
- thread_pool.bulk.queue_size=-1
- "ES_JAVA_OPTS=-Xms2048m -Xmx2048m"
ulimits:
memlock:
soft: -1
hard: -1
mem_limit: 4g
kibana:
image: docker.elastic.co/kibana/kibana:5.5.2
ports:
- "5601:5601"
restart: always
environment:
- "ELASTICSEARCH_URL=http://elasticsearch:9200"
- xpack.graph.enabled=false
- xpack.security.enabled=false
- xpack.ml.enabled=false
depends_on:
- elasticsearch
volumes:
apachelog:
driver: local
fluentdata:
driver: local
esdata:
driver: local
esconfig:
driver: local
起動・終了
起動
docker-compose up -d --build
終了
docker-compose down
感想
- SSH秘密鍵ってどうすればいいの?
- 環境変数使うとcronからの実行時に見えないので、コンテナ内にファイルで書き出しておくのかな?
- でもファイルに書き出すとcommitでイメージに含まれてしまう
- docker swarmでないときにsecretって使えないの?
-
fluent.conf
でpath
にワイルドカードを指定しても認識してくれないんだけど・・・- しょうがないからgzipになっているログは全部1つのファイル(
access.log.old
)にまとめてしまえ。 - ログが入れ替わった直後(毎日5時)は、ログが抜けるけど仕方ないかな。
- 本当は前日までのログ(*.gz)と当日分のログを1つのファイルに結合させたいんだけど、inodeを変えないで差分だけ追記していく方法がうまくできなかった
- しょうがないからgzipになっているログは全部1つのファイル(
- 公式のElasticsearchとKibanaコンテナの設定難しい、難しくない?