容器内的日志输出有多重方式:容器内文件、stdout、stderr等等,我们在解决nginx-ingress-controller的日志持久化过程中,以k8s的编排和组织能力,尝试非侵入地解决各种问题,包括目录权限与挂载的问题、nginx配置问题、日志轮转的问题。希望这些解决方法,可以为读者容器化应用的日志落盘提供思路。
从需求说起
轻舟微服务平台大量地依赖nginx-ingress-controller进行流量管理和负载均衡,因此该组件的日志维护非常重要。
轻舟k8s团队设计了一个较为无侵入的方案,通过这个方案可以将nginx-ingress-controller的日志输出,不论是accesslog还是klog库输出的日志,都能进行重定向落盘和轮转、清理。 我们发现这个日志持久化方案基本可以应对绝大多数的容器应用的日志持久化需求。
nginx-ingress-controller的日志
nginx-ingress-controller的日志包括三个部分:
-
controller日志:输出到stdout,通过启动参数中的–log_dir可已配置输出到文件,重定向到文件后会自动轮转,但不会自动清理
-
accesslog:输出到stdout,通过nginx-ingress-controller的配置文件——configmap:
nginx-configuration中的字段可以配置输出到哪个文件。输出到文件后不会自动轮转或清理 -
errorlog:输出到stderr,配置方式与accesslog类似。
给controller日志落盘
- 给nginx-ingress-controller挂一个hostpath:
/data/log/nginx_ingress_controller/
映射到容器里的/var/log/nginx_ingress_controller/ , - 给nginx-ingress-controller配置log-dir和logtostderr参数,将日志重定向到/var/log/nginx_ingress_controller/中。
controller的日志需要做定时清理。由于controller的日志是通过klog(k8s.io/klog)输出的,会进行日志滚动,所以我们通过脚本定时清理一定时间之前的日志文件即可。
给nginx日志落盘
-
修改configmap:
nginx-configuration。配置accesslog和errorlog的输出路径,替换默认的stdout和stderr。输出路径我们可以与controller一致,便于查找。 -
accesslog和errorlog都只有一个日志文件,我们可以使用logrotate进行日志轮转,将输出到宿主机上的日志进行轮转和清理。配置如:
$ cat /etc/logrotate.d/nginx.log /data/log/nginx_ingress_controller/access.log { su root list rotate 7 daily maxsize 50M copytruncate missingok create 0644 www-data root } 12345678910
-
官方提供的模板中,nginx-ingress-controller默认都是以33这个用户登录启动容器的,因此挂载hostpath路径时存在权限问题。我们需要手动在机器上执行chown
-R 33:33 /data/log/nginx_ingress_controller.
自动化ops
nginx日志落盘中,第2、3两点均需要人工运维,有什么解决办法吗?
initContainer
问题的关键是:有什么办法可以在nginx-ingress-controller容器启动之前加一个hook,将宿主机的指定目录执行chown呢?
可以用initContainer。initcontainer必须在containers中的容器运行前运行完毕并成功退出。利用这一k8s特性,我们开发一个docker image:hub.c.163.com/combk8s/adddirperm:1.0.0 ,里面只执行如下脚本:
#!/bin/bash
logdir=$LOG_DIR
userID=$USER_ID
echo "try to set dir: $logdir 's group as $userID"
chown -R $userID:$userID $logdir
12345
脚本读取一些环境变量, 确认需要修改哪个目录,改成怎样的user group。
将脚本打包成dockerimage, 放在nginx-ingress-controller的deploy yaml中,作为initcontainers。 注意要对该initcontainer配置环境变量和volumeMount.
sidecar
再说第二点,我们注意到nginx-ingress-controller的基础镜像中并不包含logrotate,所以我们初步的思路是在宿主机上运行并配置logrotate。 但机器上安装和运行logrotate有需要批量运维的脚本,并且有些环境的机器可能不允许我们安装这些东西。 所以还是应该思考一个容器化的方案。
我们于是设计了一个sidecar,使用我们自己构建好的镜像:hub.c.163.com/combk8s/logrotate:v1.1 。这个镜像启动的容器中,会每个6小时执行一次logrotate,定时时间可以通过环境变量CRON_EXPR注入,例如CRON_EXPR= */3 * * * * 表示每隔三分钟执行一次。
我们将针对nginx accesslog和errorlog的日志的轮转策略保存到configmap中,并以volume的方式mount到这个sidecar容器中, 同时,这个sidecar容器也要mount nginx-ingress-controller的日志目录。
归纳
我们总结一下这些问题的解决办法:
- 镜像user 权限导致的目录挂载问题:通过initcontainer在业务容器启动前先进行chown,调整目录用户组
- 对于业务的stdout、stderr日志,需要直接调整业务的配置,进行日志落地,否则这类日志只能通过docker保存的容器输出文件来查看,可读性较差
- 对于需要进行轮转的日志,我们可以通过一个sidecar容器进行定时轮转。
其他方案的思考
镜像覆盖
有的人建议将initcontainer去掉,改为基于原有的nginx-ingress-controller镜像加一层layer,将配置路径权限的脚本放在该层执行。 个人认为这种方法既不美观,也不方便。唯一的好处仅在于deploy yaml仍然简洁(但少不了volumeMount之类的配置)。
不过还是看个人使用感受吧~
作者:黄扬