このブログも含めていくつかVKE (Vultr Kubernetes Engine)で動いているものがあり、それらのログについてちゃんと保存しておかないとなとずっと思っていたのですが、ようやくS3に保存し始めたので簡単に紹介します。
今回はFluentd Daemonset for KubernetesをDaemonSetとして動かし、S3へログの送信を行います。
まずはじめに保存先のS3バケットとVKEから当該S3バケットへアクセスするためのアクセスキーを作成する必要があります。これはうまいこと作成してください。S3はライフサイクルルールを設定するなどしても良いですね。またIAMのポリシーについてはfluentdのS3プラグインのドキュメントと同様に以下を適用しています(バケット名は例となります)。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::k8s-logs-xxxxxxxx"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::k8s-logs-xxxxxxxx/*"
}
]
}
次にVKEで動かすFluentd Daemonset for Kubernetesでログを読み込んでS3に送信するためのConfigMapを作成します。調べたところVKEでのコンテナログは/var/log/pods/の中に、NamespaceやDeploymentごとにディレクトリ分けされて保存されているようですので、そちらを読み込むように設定します。また今回保存しようとしているログがこのブログのApacheログなのですが、行頭に文字列が追加されており既定の形式として読み込めなかったのでパターンを指定していたり、S3に保存したあとにAthenaで検索する考えでしたので、それで使用しやすいようにフィールドと追加したりパスを指定したりしています。他にもいろいろ変数的なものがありますが、NamespaceやDeploymentの名前などで指定しました。
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-s3-config
data:
fluent.conf: |
<source>
@type tail
path /var/log/pods/default_atgw-blog-*/atgw-blog/*.log
pos_file /var/log/td-agent/default_atgw-blog.log.pos
tag default_atgw-blog
<parse>
@type regexp
expression /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z \S+ \S+ (?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>(?:[^\"]|\\.)*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>(?:[^\"]|\\.)*)" "(?<agent>(?:[^\"]|\\.)*)")?$/
time_format %d/%b/%Y:%H:%M:%S %z
keep_time_key true
</parse>
</source>
<filter default_atgw-blog>
@type record_transformer
enable_ruby
<record>
unixtime ${Time.strptime(record["time"], "%d/%b/%Y:%H:%M:%S %z").to_i}
</record>
</filter>
<match default_atgw-blog>
@type s3
aws_key_id xxxxxxxx
aws_sec_key xxxxxxxx
s3_bucket k8s-logs-xxxxxxxx
s3_region ap-northeast-1
path "default_atgw-blog/"
time_slice_format year=%Y/month=%m/day=%d/%H_%M
<format>
@type json
</format>
</match>
ConfigMapができたらあとはDaemonSetを作成するだけです。先ほど作成したConfigMapに加え、ログが保存されいるパスをHostPathでマウントし、Podから使用できるようにします。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-s3
spec:
selector:
matchLabels:
app: fluentd-s3
template:
metadata:
labels:
app: fluentd-s3
spec:
containers:
- name: fluentd-s3
image: fluent/fluentd-kubernetes-daemonset:v1.17-debian-s3-amd64-1
volumeMounts:
- name: fluentd-s3-config
mountPath: /fluentd/etc
- name: fluentd-s3-tdagent
mountPath: /var/log/td-agent
- name: fluentd-s3-podlogs
mountPath: /var/log/pods
readOnly: true
volumes:
- name: fluentd-s3-config
configMap:
name: fluentd-s3-config
- name: fluentd-s3-tdagent
hostPath:
path: /var/log/td-agent
- name: fluentd-s3-podlogs
hostPath:
path: /var/log/pods
これですべてのNodeでログの読み込みとS3への送信を行うPodが動き、一番最初の画像のようにS3にログが保存されていきます。ちなみに該当するログが存在しない場合(対象のPodがそのNodeで動いていない場合)でも特にエラーなどは出ていないようです。一方で、エラーという観点ですと、今回はApacheのアクセスログのパターンしか指定してないため、Apacheのエラーログについてはパターンが一致しないということでエラーになっています。Apacheのエラーログも保存したい場合には適切な設定を追加する必要がありますね。
ということで無事にS3にログが保存されるようになって良かったです。なお現時点ではこれによる費用増加はほとんどなさそうな感じです。次はこの保存したログをAthenaで検索するところを紹介できればと思っていますが、そのために使っているGlueの方が個人レベルでは若干気になる費用という感じですかね。
皆さんも是非PodのログをS3に保存してみてください!