Container Compute Service (ACS) clusters use a serverless architecture to manage cluster resources. In this architecture, you cannot deploy a DaemonSet to manage log rotation. If log files are not rotated, they can grow continuously and consume all available disk space. Therefore, ACS provides a general solution to rotate container logs using a sidecar. This topic describes how to configure a cron job in a sidecar to trigger logrotate and rotate container log files.
How it works
This topic provides a general log rotation solution. This solution is not recommended in the following two scenarios:
Scenarios that output to a single file. In these scenarios, use standard output instead of file logs. Containers have a native rotation mechanism for standard output. This also makes it easier to observe logs if a container becomes abnormal.
Scenarios that use languages with built-in log rotation capabilities, such as Java and Python. In these scenarios, use the log rotation feature of the logging library directly.
The solution in this topic is designed as follows:
For a small number of workloads (Part ① in the figure), you can configure a logrotate sidecar for each application to rotate logs.
For many workloads (Part ② in the figure), you can use a SidecarSet to inject a logrotate sidecar into workloads in batches. You can specify a scope at the cluster or namespace level.
Configure a logrotate sidecar for an application
The following example shows how to use the native sidecar mechanism to deploy a logrotate container. This mechanism uses an init container that is configured with restartPolicy: Always. For ACS clusters that run Kubernetes 1.28 or earlier, you can use the ACS-enhanced environment variable __IS_SIDECAR=true to mark a regular container as a sidecar. This lets you manage its lifecycle in the same way as a native sidecar container. For more information, see Configure the startup and shutdown order of sidecar containers.
The logrotate container must share a volume with the application container. You must also mount the /var/lib directory to save rotation state data. This prevents incorrect log rotation if the container restarts and its state is lost. Ensure that the application's output 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-stateThe following table describes the main environment variables.
Environment variable | Description | Example |
CRON_EXPR | The cron expression used to run logrotate. |
|
LOGROTATE_LOGFILES | The path to the log files that you want to rotate. |
|
LOGROTATE_FILENUM | The number of historical log files to retain for each log file. |
|
LOGROTATE_FILESIZE | The maximum size of each log file. When a file exceeds this size, logrotate is triggered to rotate the log file. |
Note The value of LOGROTATE_FILESIZE × LOGROTATE_FILENUM determines the maximum disk space that the log files will occupy. Set these values based on the storage space that is requested for the instance. |
__IGNORE_RESOURCE__ | Ignores the resource declaration of the logrotate container to avoid extra resource costs. This ensures the logrotate container shares CPU and memory resources with the application container and remains constrained by the configured resource.limits. For more information, see Configure scheduling to ignore resources of specific containers. |
|
__IGNORE_READY__ | Ignores the Ready status of the logrotate container. This prevents the entire pod from being affected if the logrotate container enters a NotReady state. For more information, see Ignore the NotReady status of a sidecar container. |
|
Shared volume and rotation result
Verify the shared volume:
To verify the shared volume, check the
/var/log/busyboxdirectory in both the busybox and logrotate containers.
The
busybox.logfile exists in the same directory in both containers. This indicates that the shared volume is mounted successfully.Log rotation result:
NoteTo demonstrate the rotation results quickly, the following results are from a test where the CRON_EXPR and LOGROTATE_FILESIZE values were adjusted to accelerate the rotation cycle.

Configure logrotate sidecars for applications in batches
To create and use SidecarSets in an ACS cluster, you must install the ack-kruise component. For more information, see Manage components.
OpenKruise's SidecarSet lets you manage sidecar containers by automatically injecting and upgrading them independently. In this example, a SidecarSet injects a logrotate sidecar into all pods that have the kruise.io/inject-logrotate: "true" label. This SidecarSet also enables the shareVolumePolicy (shareVolumePolicy.type=enabled). When the shareVolumePolicy is enabled, all the pod's volumeMounts are automatically mounted into the sidecar. If all 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"
shareVolumePolicy:
type: enabled
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
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 are used to build the logrotate image that is described in this topic. You can modify the Dockerfile or script as needed.
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 file:
#!/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