由于容器计算服务 ACS(Container Compute Service)集群采用Serverless形式管理集群资源,无法通过部署DaemonSet来管理日志的轮转。日志文件若不轮转,可能持续增长至占用全部磁盘空间。因此,ACS提供了一种通过Sidecar来实现通用的容器日志轮转的方案。本文介绍如何在Sidecar中通过配置Cron定时触发logrotate来实现容器日志文件的轮转。
原理说明
本文提供的是通用的日志轮转方案, 不推荐用于以下两个场景:
只有一个文件输出的场景。此场景推荐使用标准输出而不是文件日志,因为容器对标准输出有原生的轮转机制,并且也更方便在容器出现异常时观察日志。
Java、Python等自带日志轮转能力的场景,推荐直接使用日志库的日志轮转功能。
本文提供的方案设计如下:
工作负载数量较少时(上图①部分),可以采用单独为应用配置logrotate Sidecar的方式来实现日志轮转。
工作负载数量较多时(上图②部分),采用SidecarSet的方式,批量为指定范围内(集群维度或命名空间维度)的工作负载注入Logrotate Sidecar。
为应用配置logrotate Sidecar
以下示例采用原生的Sidecar机制(即配置restartPolicy: Always
的init容器)来部署logrotate容器。 对于K8s 1.28及以下的ACS集群,可以用ACS增强的环境变量__IS_SIDECAR=true
来标记普通容器,实现和原生Sidecar一样的容器生命周期管理能力。更多信息,请参见配置Sidecar容器启停顺序。
Logrotate容器需与业务容器共享数据卷,并额外挂载/var/lib
目录保存轮转状态数据,防止logrotate容器重启后丢失状态,从而做出错误的日志轮转动作。同时,需要确保应用输出的日志文件存放在共享数据卷中。
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:
## 采集标准输出日志
- emptyDir:
name: shared-log
- emptyDir:
name: logrotate-state
主要环境变量说明:
环境变量名 | 说明 | 示例 |
CRON_EXPR | 执行logrotate的Cron表达式。 |
|
LOGROTATE_LOGFILES | 需要轮转的日志路径。 |
|
LOGROTATE_FILENUM | 每一个日志文件需要保留的历史文件数。 |
|
LOGROTATE_FILESIZE | 每个日志文件的最大空间占用量。超过LOGROTATE_FILESIZE后调用logrotate来轮转日志。 |
说明 LOGROTATE_FILESIZE * LOGROTATE_FILENUM 决定了所有日志文件要占用的最大磁盘空间,您可以根据实例申请的存储空间大小进行合理的设置。 |
__IGNORE_RESOURCE__ | 忽略logrotate容器的资源声明,避免额外带来资源花费。同时可以确保logrotate容器与业务容器共享CPU和内存资源,且仍然受配置的resource.limits的限制。更多信息,请参见配置调度忽略特定容器资源。 |
|
__IGNORE_READY__ | 忽略logrotate容器的Ready状态,避免由于logrotate容器处于NotReady状态而影响整个Pod运行。更多信息,请参见忽略Sidecar容器的NotReady状态。 |
|
共享数据卷及轮转效果
共享数据卷验证:
分别查看busybox和logrotate容器的
/var/log/busybox
目录。可以看到,两个容器的相同目录下都存在
busybox.log
的日志,说明共享数据卷挂载成功。日志轮转效果:
说明为了快速演示轮转效果,以下演示结果通过调整Cron和LOGROTATE_FILESIZE值加速轮转周期。
批量为应用配置logrotate Sidecar
在ACS集群中创建和使用SidecarSet需要先安装ack-kruise组件。具体操作,请参见管理组件。
OpenKruise的SidecarSet提供了一种对Sidecar容器的管理能力,能够自动注入和独立升级Sidecar容器。本示例的SidecarSet会对所有带有kruise.io/inject-logrotate: "true"
标签的Pod注入logrotate Sidecar。同时,此SidecarSet开启了shareVolume策略shareVolumePolicy.type=enabled
,开启shareVolumePolicy
后,Pod的所有volumeMounts会自动挂载到Sidecar中。 若所有待注入的Pod有约定好的数据卷名称,也可以在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: {}
相关信息
以下提供用于构建本文中logrotate镜像的Dockerfile和相关脚本文件。您可以根据实际需求对Dockerfile或脚本文件进行修改。
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