YasuBlog

中年インフラエンジニアの備忘録です。

Kubernetes の標準出力と標準エラー出力

Kubernetes では、コンテナの標準出力/標準エラー出力ログを kubectl logs コマンドで取得できます。実際にどのファイルに標準出力/標準エラー出力ログが出力されて、ローテートはどういう設定になっているのか整理しました。

1. ログの保存場所

まずは標準出力/標準エラー出力ログの保存場所です。

コンテナが標準出力/標準エラー出力に出力したログは Node に保存されます。実体のファイルとシンボリックリンクの構成は以下です。

f:id:dunkshoot:20220215230154p:plain

整理すると以下になります。

# ログ 説明
/var/lib/docker/containers/< Container ID >/< Container ID >-json.log ・コンテナの stdout/stderr に出力されたログが保存される実体のファイル
・起動中の Pod のログのみ保存
・終了した Pod のログは消える
・コンテナが再起動した場合、再起動前のコンテナのログは消えない
・保存されるのは 2 世代分のコンテナ(起動中のコンテナと再起動前のコンテナのログのみ保存)
/var/log/pods/< Namespace >_< Pod Name >_< Pod UID >/< Container Name >/< Index >.log ・①のファイルへのシンボリックリンク
・0.log が現在起動中のコンテナのログのリンク
・1.log が一つ前に起動していたコンテナのログのリンク
kubectl logs コマンドは API Server 経由で /var/log/pods にアクセスしている
/var/log/containers/< Pod Name >_< Namespace >_< Container Name >-< Container ID >.log ・②のファイルへのシンボリックリンク

実際のログです。

@Node $ ls -lh /var/log/pods/kube-system_coredns-76f4967988-pkkx9_1ab7cf50-3781-4576-8a4d-d918e321ee64/coredns/*
lrwxrwxrwx 1 root root 165  215 14:07 /var/log/pods/kube-system_coredns-76f4967988-pkkx9_1ab7cf50-3781-4576-8a4d-d918e321ee64/coredns/0.log -> /var/lib/docker/containers/52d7283862b0b64d6bdfc7dc4dce964bb3ca284ec3048325dbb7e5d61a1a00e3/52d7283862b0b64d6bdfc7dc4dce964bb3ca284ec3048325dbb7e5d61a1a00e3-json.log
lrwxrwxrwx 1 root root 165  215 15:17 /var/log/pods/kube-system_coredns-76f4967988-pkkx9_1ab7cf50-3781-4576-8a4d-d918e321ee64/coredns/1.log -> /var/lib/docker/containers/a402cbab8d63b90c3d76f87579fe73bca77a2bffae2d5e0b3bd1617a1dbc037b/a402cbab8d63b90c3d76f87579fe73bca77a2bffae2d5e0b3bd1617a1dbc037b-json.log
@Node $ ls -lh /var/log/containers/coredns-76f4967988-pkkx9_kube-system_coredns-*
lrwxrwxrwx 1 root root 101  215 14:07 /var/log/containers/coredns-76f4967988-pkkx9_kube-system_coredns-52d7283862b0b64d6bdfc7dc4dce964bb3ca284ec3048325dbb7e5d61a1a00e3.log -> /var/log/pods/kube-system_coredns-76f4967988-pkkx9_1ab7cf50-3781-4576-8a4d-d918e321ee64/coredns/0.log
lrwxrwxrwx 1 root root 101  215 15:17 /var/log/containers/coredns-76f4967988-pkkx9_kube-system_coredns-a402cbab8d63b90c3d76f87579fe73bca77a2bffae2d5e0b3bd1617a1dbc037b.log -> /var/log/pods/kube-system_coredns-76f4967988-pkkx9_1ab7cf50-3781-4576-8a4d-d918e321ee64/coredns/1.log

※ Pod の UID は kubectl get pods -n <Namespace> <Pod Name> -o jsonpath='{.metadata.uid}' で確認可能

2. コンテナログのローテート、世代

ログが増え続けると Node のディスクが逼迫するので、ローテートと削除の設定が必要です。Kubernetes では /etc/docker/daemon.json でそれらを設定しています。

オプション 説明
log-opts.max-size ・ログをローテートするファイルサイズ
・単位はキロバイト(k)/メガバイト(m)/ギガバイト(g)
log-opts.max-file ・ログの世代数
・max-size が設定されていない場合は無効

例えば、max-size:100m,max-file:10 の場合、一つのコンテナが最大 1 GB のログを保存する状態になります。最初に記載した通り、Pod としては 2 世代のコンテナのログを保存するため最大 2 GB になります。Node のディスクをサイジングする際はこれらを考慮する必要があります。

※ kubelet フラグにも同様の意味の containerLogMaxSizecontainerLogMaxFiles がありますが、検証したところ /etc/docker/daemon.jsonlog-opts に設定されている値でローテート、削除されていました

3. 検証

3.1. 検証環境構築

eksctl コマンドで EKS Cluster を作成する - YasuBlog の記事で作成した EKS Cluster を使用します。

3.2. EKS の設定値

max-size/max-file は Node 上の /etc/docker/daemon.json で確認できます。

@Node $ cat /etc/docker/daemon.json
{
  "bridge": "none",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "10"
  },
  "live-restore": true,
  "max-concurrent-downloads": 10,
  "default-ulimits": {
    "memlock": {
      "Hard": -1,
      "Name": "memlock",
      "Soft": -1
    }
  }
}

max-size10m, max-file10 に設定されています。

インスタンスタイプ毎の設定値を確認してみました。Node のリソース量に関わらず固定値が設定されているようです。

インスタンスタイプ max-size max-file
t3.small 10m 10
t3.medium 10m 10
m5.large 10m 10
m5.xlarge 10m 10

3.3. 挙動確認

つづいて実際の挙動を確認してみます。

3.3.1. ローテートと世代

まずはシンプルな Pod を起動します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: amazonlinux
        image: public.ecr.aws/amazonlinux/amazonlinux:latest
        command:
          - "bin/bash"
          - "-c"
          - "sleep 3600"

コンテナ ID を確認します。

$ kubectl get pods -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE                                              NOMINATED NODE   READINESS GATES
deployment-6bb985c8c9-p7b5w   1/1     Running   0          114s   10.0.102.47   ip-10-0-102-148.ap-northeast-1.compute.internal   <none>           <none>
$ kubectl describe pod deployment-6bb985c8c9-7xmm4 | grep 'Container ID'
    Container ID:  docker://057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7

Node 上のログを確認します。

@Node $ ls -lh /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/
合計 12K
-rw-r----- 1 root root    0  216 14:55 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log
drwx------ 2 root root    6  216 14:55 checkpoints
-rw------- 1 root root 4.8K  216 14:55 config.v2.json
-rw-r--r-- 1 root root 2.1K  216 14:55 hostconfig.json
drwx--x--- 2 root root    6  216 14:55 mounts
@Node $ ls -lh /var/log/pods/default_deployment-6bb985c8c9-p7b5w_ef8acb63-8389-47d6-aad8-c2976eac3665/amazonlinux/
合計 0
lrwxrwxrwx 1 root root 165  216 14:55 0.log -> /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log

標準出力/標準エラー出力に何も出力していないので、上記の通りサイズが 0 のファイルが一つだけあります。この状態で Pod にログインし、標準出力に大量にログを出力してみます。

$ kubectl exec -it deployment-6bb985c8c9-p7b5w -- /bin/bash
@Pod $ while true;do cat /etc/services > /proc/1/fd/1;done

Node 上のログを確認します。

@Node $ ls -lh /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/
合計 90M
-rw-r----- 1 root root 2.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.1
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.2
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.3
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.4
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.5
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.6
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.7
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.8
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.9
drwx------ 2 root root    6  216 14:55 checkpoints
-rw------- 1 root root 4.8K  216 14:55 config.v2.json
-rw-r--r-- 1 root root 2.1K  216 14:55 hostconfig.json
drwx--x--- 2 root root    6  216 14:55 mounts
@Node $ ls -lh /var/log/pods/default_deployment-6bb985c8c9-p7b5w_ef8acb63-8389-47d6-aad8-c2976eac3665/amazonlinux/
合計 0
lrwxrwxrwx 1 root root 165  216 14:55 0.log -> /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log

max-size/max-file の設定通り、ログサイズが 10M でローテートされ、10 世代保存されている事が確認できました。/var/log/pods0.log は常に最新のファイルのシンボリックリンクになっています。

3.3.2. コンテナ再起動

上記の状態で、Node 上で docker stop を実行してコンテナを再起動してみます。

@Node $ docker ps | grep amazonlinux
057fc611f5c4   public.ecr.aws/amazonlinux/amazonlinux                                       "bin/bash -c 'sleep …"   5 minutes ago    Up 5 minutes              k8s_amazonlinux_deployment-6bb985c8c9-p7b5w_default_ef8acb63-8389-47d6-aad8-c2976eac3665_0
@Node $ docker stop 057fc611f5c4
057fc611f5c4
$ kubectl get pods -o wide
NAME                          READY   STATUS    RESTARTS   AGE     IP            NODE                                              NOMINATED NODE   READINESS GATES
deployment-6bb985c8c9-p7b5w   1/1     Running   1          6m15s   10.0.102.47   ip-10-0-102-148.ap-northeast-1.compute.internal   <none>           <none>
$ kubectl describe pod deployment-6bb985c8c9-p7b5w | grep 'Container ID'
    Container ID:  docker://89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476

コンテナを再起動したので Pod の RESTARTS が 0 から 1 に変わり、コンテナ ID も変わりました。

Node 上のログを確認します。

# 旧コンテナ
@Node $ ls -lh /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/
合計 89M
-rw-r----- 1 root root 2.6M  216 15:01 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.1
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.2
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.3
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.4
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.5
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.6
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.7
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.8
-rw-r----- 1 root root 9.6M  216 14:59 057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log.9
drwx------ 2 root root    6  216 14:55 checkpoints
-rw------- 1 root root 4.8K  216 15:01 config.v2.json
-rw-r--r-- 1 root root 2.1K  216 15:01 hostconfig.json
drwx--x--- 2 root root    6  216 14:55 mounts
# 新コンテナ
@Node $ ls -lh /var/lib/docker/containers/89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476/
合計 12K
-rw-r----- 1 root root    0  216 15:01 89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476-json.log
drwx------ 2 root root    6  216 15:01 checkpoints
-rw------- 1 root root 4.8K  216 15:01 config.v2.json
-rw-r--r-- 1 root root 2.1K  216 15:01 hostconfig.json
drwx--x--- 2 root root    6  216 15:01 mounts
@Node $ ls -lh /var/log/pods/default_deployment-6bb985c8c9-p7b5w_ef8acb63-8389-47d6-aad8-c2976eac3665/amazonlinux/
合計 0
lrwxrwxrwx 1 root root 165  216 14:55 0.log -> /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7-json.log
lrwxrwxrwx 1 root root 165  216 15:01 1.log -> /var/lib/docker/containers/89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476/89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476-json.log

旧コンテナ、新コンテナともにログが存在している事が確認できました。/var/log/pods の方は 0.log が旧コンテナにリンクし、1.log が新コンテナにリンクしてます。

3.3.3. Pod 削除

上記の状態で Pod を削除してみます。

$ kubectl delete pod deployment-6bb985c8c9-p7b5w
pod "deployment-6bb985c8c9-p7b5w" deleted
@Node $  ls -lh /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/
ls: /var/lib/docker/containers/057fc611f5c416c77a00cfb02e24714b52e75af0c14c42ed11c58d42ca115ea7/ にアクセスできません: No such file or directory
@Node $  ls -lh /var/lib/docker/containers/89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476/
ls: /var/lib/docker/containers/89f425cce426a87fad882cfc5f091b3f87dca4942bd5c9f9fbde69fc217ea476/ にアクセスできません: No such file or directory
@Node $  ls -lh /var/log/pods/default_deployment-6bb985c8c9-p7b5w_ef8acb63-8389-47d6-aad8-c2976eac3665/amazonlinux/
合計 0

Pod を削除するとログも全て消える事が確認できました。

3.4. 設定変更

最後に max-size/max-file の変更方法です。

Node 上の /etc/docker/daemon.json を修正して docker デーモンを再起動する事で設定変更が可能です。 Node 再作成時にも同様に設定されるように、カスタム起動テンプレートかカスタム AMI を使用する必要があります。

例えば、max-size100mmax-file5 にしたい場合は、カスタム起動テンプレートのユーザデータに以下のようなコードを追加する事で設定変更が可能です。

sed -i -e 's/"max-size".*/"max-size": "100m",/g' /etc/docker/daemon.json
sed -i -e 's/"max-file".*/"max-file": "5"/g' /etc/docker/daemon.json
systemctl restart docker

個人的には AWS マネージドという事は AWS が良かれと思って設計した値なのでユーザ側がカスタマイズする必要は無いと思っています。

4. まとめ

コンテナの標準出力/標準エラー出力ログまわりについて整理してみました。EKS 等のマネージドサービスの場合は特別な要件がない限り設定値は変更しなくて良いと思います。

5. 参考

Logging Architecture | Kubernetes

ロギング・ドライバの設定 — Docker-docs-ja 1.9.0b ドキュメント

Amazon EKS ワーカーノードを設定して特定のディスク使用率でイメージキャッシュをクリーンアップする

起動テンプレートのサポート - Amazon EKS