Develop an application outputting logs,
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++
}
}
create image logrotate installed,
$ 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"]
share logs with volume, and run a pod.
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
As a result, rotate has been successful,
$ 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
but some logs have lost on 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
To avoid copytruncate, you need to update the inode of the file to be written by sending SIGHUP to the application container process etc. Setting shareProcessNamespace to true allows containers to see processes in other containers, and SYS_PTRACE capabilities allows them to send signals.
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: {}
...
Since the PID is no longer 1, save the PID somewhere and share it.
#!/bin/sh
/main &
PID=$!
echo $PID > /var/run/app/app.pid
terminate() {
kill -TERM "$PID"
exit
}
trap terminate TERM
wait $PID
All that’s left to do is receive the signal on the application side and reopen the file.
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")
}
}()