Container Compute Service (ACS) clusters use a serverless architecture to manage cluster resources, which prevents you from deploying a DaemonSet to manage log rotation. If not rotated, log files can grow to exhaust all available disk space. ACS provides a sidecar-based solution: a cron job triggers logrotate inside a sidecar container to rotate application log files automatically.
How it works
This log rotation solution is not recommended in the following scenarios:
-
Applications that write to a single output file. Use stdout instead of file-based logging. Containers natively rotate stdout, and this approach also simplifies log monitoring for container anomalies.
-
Applications built with languages like Java or Python that have built-in log rotation. Use the logging library's native rotation features instead.
-
If you have a small number of workloads, you can configure a logrotate sidecar for each application to implement log rotation.
-
If you have a large number of workloads, you can use a SidecarSet to inject a logrotate sidecar into workloads within a specified scope, such as an entire cluster or a specific namespace.
Configure a logrotate sidecar for an application
The following example deploys the logrotate container as a native sidecar: an init container configured with restartPolicy: Always. For ACS clusters running Kubernetes 1.28 or earlier, you can use the ACS-enhanced environment variable __IS_SIDECAR=true to mark a regular container as a sidecar, allowing you to manage its lifecycle the same way. For more information, see Configure Sidecar container startup and shutdown sequence.
The logrotate container must share a volume with the application container. Mount the /var/lib directory to the logrotate container to persist rotation state data. This prevents state loss on container restart and avoids incorrect rotation actions. Ensure that the application's log files are stored in the shared volume.
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-logrotate
labels:
app: test
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
initContainers:
- name: logrotate
env:
- name: CRON_EXPR
value: "5 * * * *"
- name: LOGROTATE_LOGFILES
value: "/var/log/*/*.log"
- name: LOGROTATE_FILENUM
value: "5"
- name: LOGROTATE_FILESIZE
value: "10M"
- name: __IGNORE_RESOURCE__
value: "true"
- name: __IGNORE_READY__
value: "true"
command: [ "sh", "/start.sh" ]
image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/logrotate:v1.1
volumeMounts:
- mountPath: /var/log
name: shared-log
- mountPath: /var/lib
name: logrotate-state
restartPolicy: Always
resources:
limits:
cpu: 0.25
memory: 0.5Gi
containers:
- name: busybox
image: mirrors-ssl.aliyuncs.com/busybox:latest
command: [ "sh", "-c" ]
args:
- |
mkdir /var/log/busybox
while true; do
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
LOG_LEVEL=$(shuf -n1 -e INFO WARNING ERROR)
APP_NAME="busybox"
HOSTNAME=$(hostname)
MESSAGE=$(shuf -n1 -e "Success" "Timeout" "Database error")
echo "$TIMESTAMP $HOSTNAME [$LOG_LEVEL] $APP_NAME: $MESSAGE" >> /var/log/busybox/busybox.log
sleep 1
done
volumeMounts:
- mountPath: /var/log
name: shared-log
volumes:
## Collect stdout logs.
- emptyDir:
name: shared-log
- emptyDir:
name: logrotate-state
The following table describes the key environment variables.
|
Environment variable name |
Description |
Example |
|
CRON_EXPR |
Cron expression for the logrotate schedule. |
|
|
LOGROTATE_LOGFILES |
Path to the log files to rotate. |
|
|
LOGROTATE_FILENUM |
Number of rotated log files to retain per log file. |
|
|
LOGROTATE_FILESIZE |
Maximum size of a log file. Files exceeding this size are rotated. |
Note
|
|
__IGNORE_RESOURCE__ |
Excludes the logrotate container's resource declaration from scheduling to avoid extra resource costs. The logrotate container shares CPU and memory with the application container and remains constrained by the configured resource.limits. For more information, see Configure scheduling policies to ignore the resource requests of specific sidecar containers. |
|
|
__IGNORE_READY__ |
Excludes the logrotate container from Pod readiness checks. This prevents a NotReady sidecar from affecting the overall Pod status. For more information, see Ignore the NotReady state of the sidecar container. |
|
Verify the shared volume and log rotation
-
Verify the shared volume:
Check the
/var/log/busyboxdirectory in both thebusyboxandlogrotatecontainers.❯ kubectl exec -it deploy/test-logrotate -c busybox -- sh / # ls -al var/log/busybox/ total 36 drwxr-xr-x 2 root root 4096 May 30 03:10 . drwxrwxrwx 3 root root 4096 May 30 03:10 .. -rw-r--r-- 1 root root 21426 May 30 03:15 busybox.log / # exit ❯ kubectl exec -it deploy/test-logrotate -c logrotate -- sh / # ls -al var/l lib/ local/ lock/ log/ / # ls -al var/log/busybox/ total 36 drwxr-xr-x 2 root root 4096 May 30 03:10 . drwxrwxrwx 3 root root 4096 May 30 03:10 .. -rw-r--r-- 1 root root 23780 May 30 03:15 busybox.log / #The
busybox.logfile appears in the same directory within both containers, confirming that the shared volume is mounted correctly. -
Check the log rotation result:
NoteTo quickly demonstrate the rotation effect, we adjusted the
CRON_EXPRandLOGROTATE_FILESIZEvalues to accelerate the rotation cycle, producing the following result.> kubectl exec -it deploy/test-logrotate -c logrotate -- sh / # ls var/log/busybox/ busybox.log busybox.log.1 busybox.log.2 busybox.log.3 busybox.log.4 busybox.log.5 / #
Inject logrotate sidecars with a SidecarSet
To create and use a SidecarSet in an ACS cluster, you must first install the ack-kruise component. For more information, see Manage components.
The OpenKruise SidecarSet manages sidecar containers with automatic injection and independent upgrades. The following SidecarSet injects the logrotate sidecar into all Pods labeled with kruise.io/inject-logrotate: "true". It also enables the shared volume policy shareVolumePolicy.type=enabled, which automatically mounts all Pod volumeMounts into the sidecar. If the injected Pods share a common volume name, you can also declare that name in the SidecarSet.
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: logrotate-sidecarset
spec:
selector:
matchLabels:
kruise.io/inject-logrotate: "true"
updateStrategy:
type: NotUpdate
initContainers:
- name: logrotate
env:
- name: CRON_EXPR
value: "5 * * * *"
- name: LOGROTATE_LOGFILES
value: "/var/log/*/*.log"
- name: LOGROTATE_FILENUM
value: "5"
- name: LOGROTATE_FILESIZE
value: "10M"
- name: __IGNORE_RESOURCE__
value: "true"
command: [ "sh", "/start.sh" ]
image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/logrotate:v1.1
shareVolumePolicy:
type: enabled
volumeMounts:
- mountPath: /var/lib/
name: logrotate-state
restartPolicy: Always
resources:
limits:
cpu: 0.25
memory: 0.5Gi
volumes:
- name: logrotate-state
emptyDir: {}
Related information
The following Dockerfile and script build the logrotate image. Modify them to meet your requirements.
-
Dockerfile:
FROM registry-cn-hangzhou.ack.aliyuncs.com/dev/alpine:3.20-update RUN apk --update add bash logrotate ADD start.sh /start.sh CMD ["/start.sh"] -
start.sh:
#!/bin/sh LOGROTATE_LOGFILES="${LOGROTATE_LOGFILES:?Files for rotating must be given}" LOGROTATE_FILESIZE="${LOGROTATE_FILESIZE:-10M}" LOGROTATE_FILENUM="${LOGROTATE_FILENUM:-5}" cat > /etc/logrotate.conf << EOF ${LOGROTATE_LOGFILES} { size ${LOGROTATE_FILESIZE} missingok notifempty copytruncate rotate ${LOGROTATE_FILENUM} } EOF if [ -z "$CRON_EXPR" ]; then CRON_EXPR="0 6 * * *" echo "CRON_EXPR environment variable is not set. Set to default: $CRON_EXPR" else echo "CRON_EXPR environment variable set to $CRON_EXPR" fi echo "$CRON_EXPR /usr/sbin/logrotate -v /etc/logrotate.conf" >> /etc/crontabs/root (crond -f) & CRONPID=$! trap "kill $CRONPID; wait $CRONPID" SIGINT SIGTERM wait $CRONPID