1. 问题现象与初步排查
最近在维护Kubernetes集群时遇到了一个棘手的问题:日志文件不再自动轮转压缩,导致节点磁盘空间告急。检查logrotate状态时发现大量"Permission denied"错误,而这一切的罪魁祸首竟是SELinux。这个看似简单的权限问题背后,其实涉及容器运行时、主机安全策略和日志管理系统的复杂交互。
典型错误日志如下:
code复制error: error accessing /var/log/pods/*/*.log: Permission denied
error: found error in /var/log/pods/namespace_pod-uid_container/*.log: Permission denied
failed to rotate /var/log/pods/.../container.log: Permission denied
通过audit2why工具查看SELinux拒绝记录:
code复制type=AVC msg=audit(1625097600.123:456): avc: denied { read } for pid=1234 comm="logrotate"
name="container.log" dev="sda1" ino=567890 scontext=system_u:system_r:logrotate_t:s0
tcontext=system_u:object_r:container_var_lib_t:s0 tclass=file
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. SELinux与K8s日志目录的安全上下文
2.1 安全上下文冲突根源
在默认安装的Kubernetes环境中,/var/log/pods目录的安全上下文类型通常是container_var_lib_t,而logrotate进程运行在logrotate_t域中。SELinux策略默认不允许logrotate_t域访问container_var_lib_t类型的文件,这就导致了权限拒绝。
查看目录安全上下文的命令:
bash复制ls -Z /var/log/pods/
# 输出示例:
system_u:object_r:container_var_lib_t:s0 /var/log/pods
2.2 容器日志的存储机制
Kubernetes通过kubelet管理容器日志,其存储路径有固定格式:
code复制/var/log/pods/<namespace>_<pod-name>_<pod-uid>/<container-name>/<n>.log
例如一个NGINX Pod的日志可能位于:
code复制/var/log/pods/default_nginx-7d5f8c9f6b-abcde_12345678-90ab-cdef-ghij-klmnopqrstuv/nginx/0.log
3. 解决方案对比与实施
3.1 临时解决方案:修改SELinux布尔值
最快的解决方法是设置logrotate可以访问容器日志的布尔值:
bash复制setsebool -P logrotate_read_container_logs 1
这个方法的优缺点:
- ✅ 立即生效,无需重启服务
- ❌ 只适用于RHEL/CentOS 8+和Fedora
- ❌ 放宽了安全策略,可能不符合严格的安全要求
3.2 持久化解决方案:自定义SELinux策略
更安全的做法是创建自定义策略模块:
- 收集审计日志生成.te文件:
bash复制grep logrotate /var/log/audit/audit.log | audit2allow -m mylogrotate > mylogrotate.te
- 编译并安装模块:
bash复制checkmodule -M -m -o mylogrotate.mod mylogrotate.te
semodule_package -o mylogrotate.pp -m mylogrotate.mod
semodule -i mylogrotate.pp
示例策略文件内容:
code复制module mylogrotate 1.0;
require {
type logrotate_t;
type container_var_lib_t;
class file { read open getattr };
}
#============= logrotate_t ==============
allow logrotate_t container_var_lib_t:file { read open getattr };
3.3 替代方案:修改日志目录标签
另一种方法是修改日志目录的安全上下文:
bash复制semanage fcontext -a -t var_log_t "/var/log/pods(/.*)?"
restorecon -Rv /var/log/pods
注意事项:
- 此更改会在kubelet重启后失效
- 需要配合crontab定期执行restorecon
- 可能影响其他依赖container_var_lib_t标签的服务
4. 生产环境最佳实践
4.1 多维度访问控制配置
建议的组合配置方案:
- 创建自定义SELinux策略模块(如3.2节)
- 设置适当的文件权限:
bash复制chmod 0750 /var/log/pods
find /var/log/pods -type f -exec chmod 0640 {} \;
- 配置logrotate参数(/etc/logrotate.d/k8s-logs):
code复制/var/log/pods/*/*/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
/usr/bin/systemctl kill -s HUP kubelet.service
endscript
}
4.2 性能优化参数
对于高负载集群,建议调整:
bash复制# 增加inotify监视数量
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sysctl -p
# 优化logrotate执行时间
mv /etc/cron.daily/logrotate /etc/cron.hourly/
4.3 监控与告警配置
建议部署以下监控措施:
- Prometheus监控指标:
yaml复制- job_name: 'logrotate'
static_configs:
- targets: ['localhost:9253']
- 节点 exporter 自定义收集器(/usr/local/bin/logrotate_collector):
bash复制#!/bin/bash
echo "# HELP logrotate_last_run Last run timestamp"
echo "# TYPE logrotate_last_run gauge"
echo "logrotate_last_run $(date +%s -r /var/lib/logrotate/logrotate.status)"
5. 深度排查技巧
5.1 高级SELinux调试方法
- 获取详细拒绝信息:
bash复制sealert -a /var/log/audit/audit.log
- 生成可读报告:
bash复制ausearch -m avc -ts recent | audit2allow -w
- 临时切换为permissive模式调试:
bash复制semanage permissive -a logrotate_t
# 复现问题后记得禁用
semanage permissive -d logrotate_t
5.2 Kubelet日志收集机制分析
kubelet的日志处理流程:
- 通过CRI接口从容器运行时获取日志流
- 写入/var/log/pods目录
- 维护符号链接到/var/log/containers
关键配置参数(/var/lib/kubelet/config.yaml):
yaml复制containerLogMaxSize: 10Mi
containerLogMaxFiles: 5
5.3 文件描述符泄漏排查
当发现logrotate后旧日志文件仍被占用时:
bash复制# 查找仍打开文件的进程
lsof +L1 /var/log/pods
# 容器环境专用命令
ls -l /proc/*/fd/* 2>/dev/null | grep deleted
6. 延伸问题与解决方案
6.1 Journald与Docker日志驱动冲突
当同时使用journald和json-file驱动时:
bash复制# 检查当前配置
docker info --format '{{.LoggingDriver}}'
# 解决方案
mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
6.2 CRI-O运行时特殊配置
对于CRI-O运行时需要额外设置:
bash复制# 修改/etc/crio/crio.conf
log_size_max = 10485760 # 10MB
log_to_journald = false
6.3 非root用户容器日志收集
当容器以非root用户运行时:
bash复制# 在Pod规范中添加
securityContext:
fsGroup: 1001
supplementalGroups: [1001]
7. 版本兼容性注意事项
不同Kubernetes版本的日志处理差异:
| 版本范围 | 日志目录结构 | SELinux上下文 | 推荐解决方案 |
|---|---|---|---|
| 1.18-1.20 | /var/log/pods | container_var_lib_t | 自定义策略模块 |
| 1.21-1.23 | /var/log/pods | var_log_t | 保持默认配置 |
| 1.24+ | 可配置路径 | 动态标签 | 使用--log-dir参数 |
关键版本变更点:
- v1.21: 默认日志目录标签改为var_log_t
- v1.23: 引入日志旋转内置支持
- v1.25: 可配置日志存储驱动
8. 性能影响评估与优化
8.1 SELinux策略查找开销
测量命令:
bash复制time seinfo -t | wc -l
time sesearch -A -s logrotate_t -t container_var_lib_t -c file
优化建议:
- 精简策略模块数量
- 使用策略缓存:
bash复制semodule -DB
8.2 Logrotate执行时间分析
使用systemd-analyze测量:
bash复制systemd-analyze blame | grep logrotate
优化方案:
- 并行处理:
bash复制cat > /etc/logrotate.parallel <<EOF
#!/bin/bash
for f in /etc/logrotate.d/*; do
logrotate $f &
done
wait
EOF
9. 安全加固建议
9.1 最小权限原则实施
精确控制访问权限的策略示例:
code复制allow logrotate_t container_var_lib_t:file
{ open read getattr lock ioctl };
禁止的操作:
code复制neverallow logrotate_t container_var_lib_t:file
{ write execute append unlink };
9.2 审计规则配置
监控关键访问:
bash复制auditctl -a always,exit -F arch=b64 -S openat -F dir=/var/log/pods -F success=0
查看审计结果:
bash复制ausearch -k logrotate_denial -i
10. 自动化运维方案
10.1 Ansible Playbook示例
yaml复制- name: Configure K8s logrotate with SELinux
hosts: k8s_nodes
tasks:
- name: Install required packages
package:
name: ['policycoreutils-python-utils', 'setools-console']
state: present
- name: Generate custom policy
command: >
grep logrotate /var/log/audit/audit.log |
audit2allow -M k8s_logrotate
args:
creates: /tmp/k8s_logrotate.pp
- name: Load SELinux policy
command: semodule -i /tmp/k8s_logrotate.pp
- name: Configure logrotate
copy:
dest: /etc/logrotate.d/k8s
content: |
/var/log/pods/*/*/*.log {
daily
rotate 7
compress
missingok
notifempty
}
- name: Set proper permissions
file:
path: /var/log/pods
mode: '0750'
recurse: yes
10.2 Prometheus告警规则
yaml复制groups:
- name: logrotate_alerts
rules:
- alert: LogrotateFailed
expr: time() - logrotate_last_run > 86400
for: 1h
labels:
severity: warning
annotations:
summary: "Logrotate has not run in 24 hours"
description: "Logrotate last run at {{ $value }}"
11. 疑难案例解析
11.1 案例:重启后策略失效
现象:服务器重启后自定义策略丢失
根因:/var/log/pods被临时文件系统覆盖
解决方案:
bash复制cat > /etc/systemd/system/restore-selinux.service <<EOF
[Unit]
Description=Restore SELinux context for K8s logs
After=kubelet.service
[Service]
Type=oneshot
ExecStart=/sbin/restorecon -R /var/log/pods
[Install]
WantedBy=multi-user.target
EOF
systemctl enable restore-selinux
11.2 案例:日志文件未压缩
现象:rotate后产生.log.1但未压缩
排查步骤:
- 检查磁盘空间:df -h /var
- 检查压缩工具:which gzip
- 检查SELinux布尔值:
bash复制getsebool logrotate_use_fusermount
解决方案:
bash复制setsebool -P logrotate_use_fusermount on
install -m 0755 /usr/bin/gzip /var/lib/logrotate/
12. 替代方案评估
12.1 使用Sidecar容器收集日志
示例配置:
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-with-logrotate
spec:
template:
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: logs
mountPath: /var/log/nginx
- name: logrotate
image: busybox
command: ["/bin/sh", "-c"]
args:
- >
while true; do
logrotate /etc/logrotate.conf;
sleep 86400;
done
volumeMounts:
- name: logs
mountPath: /var/log/nginx
- name: logrotate-config
mountPath: /etc/logrotate.conf
subPath: logrotate.conf
volumes:
- name: logs
emptyDir: {}
- name: logrotate-config
configMap:
name: logrotate-config
12.2 使用Fluent Bit替代logrotate
优势对比:
| 特性 | logrotate | Fluent Bit |
|---|---|---|
| 实时性 | 定时任务 | 实时流式 |
| 压缩效率 | 高 | 中等 |
| 资源占用 | 低 | 中等 |
| SELinux兼容 | 需要配置 | 自带策略 |
配置示例:
ini复制[INPUT]
Name tail
Path /var/log/pods/*/*/*.log
Tag k8s.*
Mem_Buf_Limit 5MB
Skip_Long_Lines On
[OUTPUT]
Name file
Match *
Path /var/log/fluent/
Format template
Template {time} {tag} {message}
Compress gzip
Rotate_Wait 24
Rotate_Interval 24
