(建议收藏)以nginx-ingress为基础的超高效持久化方案!

容器内的日志输出有多重方式:容器内文件、stdout、stderr等等,咱们在解决nginx-ingress-controller的日志持久化过程当中,以k8s的编排和组织能力,尝试非侵入地解决各类问题,包括目录权限与挂载的问题、nginx配置问题、日志轮转的问题。但愿这些解决方法,能够为读者容器化应用的日志落盘提供思路。nginx

从需求提及

轻舟微服务平台大量地依赖nginx-ingress-controller进行流量管理和负载均衡,所以该组件的日志维护很是重要。docker

轻舟k8s团队设计了一个较为无侵入的方案,经过这个方案能够将nginx-ingress-controller的日志输出,不管是accesslog仍是klog库输出的日志,都能进行重定向落盘和轮转、清理。 咱们发现这个日志持久化方案基本能够应对绝大多数的容器应用的日志持久化需求。bash

nginx-ingress-controller的日志

nginx-ingress-controller的日志包括三个部分:markdown

  • controller日志:输出到stdout,经过启动参数中的–log_dir可已配置输出到文件,重定向到文件后会自动轮转,但不会自动清理负载均衡

  • accesslog:输出到stdout,经过nginx-ingress-controller的配置文件——configmap:
    nginx-configuration中的字段能够配置输出到哪一个文件。输出到文件后不会自动轮转或清理运维

  • errorlog:输出到stderr,配置方式与accesslog相似。ide

给controller日志落盘

  1. 给nginx-ingress-controller挂一个hostpath:
    /data/log/nginx_ingress_controller/
    映射到容器里的/var/log/nginx_ingress_controller/ ,
  2. 给nginx-ingress-controller配置log-dir和logtostderr参数,将日志重定向到/var/log/nginx_ingress_controller/中。

controller的日志须要作定时清理。因为controller的日志是经过klog(k8s.io/klog)输出的,会进行日志滚动,因此咱们经过脚本定时清理必定时间以前的日志文件便可。微服务

给nginx日志落盘

  1. 修改configmap:
    nginx-configuration。配置accesslog和errorlog的输出路径,替换默认的stdout和stderr。输出路径咱们能够与controller一致,便于查找。spa

  2. 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

  3. 官方提供的模板中,nginx-ingress-controller默认都是以33这个用户登陆启动容器的,所以挂载hostpath路径时存在权限问题。咱们须要手动在机器上执行chown
    -R 33:33 /data/log/nginx_ingress_controller.

自动化ops

nginx日志落盘中,第二、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之类的配置)。

不过仍是看我的使用感觉吧~

做者:黄扬

看到这里的小伙伴,若是你喜欢这篇文章的话,别忘了转发、收藏、留言互动!

若是对文章有任何问题,欢迎在留言区和我交流~

最近我新整理了一些Java资料,包含面经分享、模拟试题、和视频干货,若是你须要的话,欢迎私信我!