ログを出力するアプリケーションと、
package main
import (
"os"
"time"
"github.com/sirupsen/logrus"
)
func main() {
logFile, err := os.OpenFile("/var/log/app/test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
logrus.Fatal("failed to open file", err)
}
logrus.SetOutput(logFile)
i := 0
ticker := time.NewTicker(1 * time.Millisecond)
for {
<-ticker.C
logrus.Print(i)
i++
}
}
logrotate をインストールしたイメージを作り、
$ cat Dockerfile_logrotate
FROM alpine:latest
RUN apk add --no-cache logrotate
# RUN echo '/usr/sbin/logrotate /etc/logrotate.d/logrotate.conf' > /etc/periodic/daily/logrotate
# RUN chmod +x /etc/periodic/daily/logrotate
# CMD ["crond", "-f"]
volume でログを共有して実行する。
logrotateでログをローテーションする - sambaiz-net
apiVersion: v1
kind: ConfigMap
metadata:
name: logrotate-config
data:
logrotate.conf: |
/var/log/app/*.log {
size 1k
copytruncate
rotate 5
compress
delaycompress
missingok
notifempty
}
---
apiVersion: v1
kind: Pod
metadata:
name: testapp
spec:
containers:
- name: testapp
image: testapp
imagePullPolicy: Never
volumeMounts:
- name: log-volume
mountPath: /var/log/app
- name: logrotate
image: my-logrotate
imagePullPolicy: Never
volumeMounts:
- name: log-volume
mountPath: /var/log/app
- name: logrotate-config-volume
mountPath: /etc/logrotate.d
command:
- /bin/sh
- -c
- |
chmod 644 /var/log/app &&
while true; do
/usr/sbin/logrotate /etc/logrotate.d/logrotate.conf;
sleep 10;
done
volumes:
- name: log-volume
emptyDir: {}
- name: logrotate-config-volume
configMap:
name: logrotate-config
結果次のように rotate はされているが、
$ ls -li /var/log/app
total 456
5505178 -rw-r--r-- 1 root root 139050 Mar 6 15:30 test.log
5505181 -rw-r--r-- 1 root root 261200 Mar 6 15:30 test.log.1
5505202 -rw-r--r-- 1 root root 14024 Mar 6 15:29 test.log.2.gz
5505201 -rw-r--r-- 1 root root 13760 Mar 6 15:29 test.log.3.gz
5505218 -rw-r--r-- 1 root root 13918 Mar 6 15:29 test.log.4.gz
5505205 -rw-r--r-- 1 root root 14132 Mar 6 15:29 test.log.5.gz
copytruncate する際に一部のログが失われてしまう。
$ tail test.log.1
time="2024-03-06T15:27:11Z" level=info msg=26132
time="2024-03-06T15:27:11Z" level=info msg=26133
time="2024-03-06T15:27:11Z" level=info msg=26134
time="2024-03-06T15:27:11Z" level=info msg=26135
time="2024-03-06T15:27:11Z" level=info msg=26136
time="2024-03-06T15:27:11Z" level=info msg=26137
time="2024-03-06T15:27:11Z" level=info msg=26138
time="2024-03-06T15:27:11Z" level=info msg=26139
time="2024-03-06T15:27:11Z" level=info msg=26140
time="2024-03-06T15:27:11Z" level=info msg=26141
$ head test.log
time="2024-03-06T15:27:11Z" level=info msg=26143
time="2024-03-06T15:27:11Z" level=info msg=26144
time="2024-03-06T15:27:11Z" level=info msg=26145
time="2024-03-06T15:27:11Z" level=info msg=26146
time="2024-03-06T15:27:11Z" level=info msg=26147
time="2024-03-06T15:27:11Z" level=info msg=26148
time="2024-03-06T15:27:11Z" level=info msg=26149
time="2024-03-06T15:27:11Z" level=info msg=26150
time="2024-03-06T15:27:11Z" level=info msg=26151
time="2024-03-06T15:27:11Z" level=info msg=26152
copytruncate をやめるには postrotate でアプリケーションコンテナのプロセスに SIGHUP を送るなどして書き込むファイルの inode を更新させる必要がある。 shareProcessNamespace を true にすると他のコンテナのプロセスを見られるようになり、SYS_PTRACE capabilities を付与することでシグナルを送ることができる。
apiVersion: v1
kind: ConfigMap
metadata:
name: logrotate-config
data:
logrotate.conf: |
/var/log/app/*.log {
...
sharedscripts
postrotate
kill -HUP `cat /var/run/app/app.pid`
endscript
}
---
apiVersion: v1
kind: Pod
metadata:
name: testapp
spec:
shareProcessNamespace: true
containers:
- name: testapp
...
volumeMounts:
- name: run-volume
mountPath: /var/run/app
...
- name: logrotate
volumeMounts:
- name: run-volume
mountPath: /var/run/app
...
securityContext:
capabilities:
add:
- SYS_PTRACE
volumes:
- name: run-volume
emptyDir: {}
...
PID は 1 でなくなるのでどこかに PID を保存して共有する。
#!/bin/sh
/main &
PID=$!
echo $PID > /var/run/app/app.pid
terminate() {
kill -TERM "$PID"
exit
}
trap terminate TERM
wait $PID
あとはアプリケーション側でシグナルを受け取ってファイルを開きなおせばよい。
sighupChan := make(chan os.Signal, 1)
signal.Notify(sighupChan, syscall.SIGHUP)
go func() {
for {
<-sighupChan
newLogFile, err := os.OpenFile("/var/log/app/test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("failed to open file: %v", err))
}
logrus.SetOutput(newLogFile)
logFile.Close()
logFile = newLogFile
fmt.Println("Reopened log file")
}
}()