Kubernetes 中日志的正确输出姿势

本文介绍了如何在Kubernetes环境中构建日志监控体系,包括日志等级的选择,如FATAL、ERROR、WARN、INFO等的使用场景,日志内容的规范,如Time、Level、Location等字段的设定,以及日志输出的控制,如合理控制日志量、选择输出目标、保证日志性能和形态选择。强调了日志库的选择、日志是否落盘以及存储周期的保证,旨在帮助开发者建立高效、可靠的日志系统。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介: 本文我们将从实践角度出发来一步步构建K8s中的日志监控体系。构建日志系统的第一步是如何去产生这些日志,而这也往往是最繁杂最困难的一步。

1579579315780-7225ce4d-97e1-4408-b27e-08fdec13cd3b.png


镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

作者:元乙

一、Kubernetes 日志等级选择 

日志等级是用来区分日志对应事件严重程度的说明,这是所有日志中必须具备的一个选项。通常日志会分为6个不同的等级:

  • FATAL(致命):用来输出非常严重或预期中不会发生的错误,遇到此种错误应当立即报警并人工介入处理。
  • ERROR (错误):非预期中的错误,此种错误可能导致部分系统异常但不会影响核心业务和系统正常运行。
  • WARN(警告):潜在的危险或值得关注的信息(比较核心的路径)。
  • INFO(信息):应用执行过程中的详细信息,一般通过该信息可以看到每个请求的主要执行过程。
  • DEBUG(调试):用于线下调试的日志信息,用于分析应用执行逻辑,线上应用切勿开启。
  • TRACE(跟踪):输出最细致的运行轨迹,可能包含涉及的数据内容。

作为程序员,一定要合理设置日志等级,个人在开发过程中总结以下几点经验:

  1. FATAL类型日志一定是非常严重的错误、需要人工处理的场景打印的。
  2. ERROR和WARNING的区别很多程序员难以选择,可以从告警角度考虑:ERROR一般需要告警,WARNING不需要。
  3. 日志等级一方面是为了能够表示日志的严重程度,另一方面也是为了控制应用程序的日志输出量,通常线上只能打开INFO或WARN的日志。
  4. DEBUG日志可以多打,方便分析问题。
  5. 所有用户请求日志,必须记录。
  6. 对于不确定的外部系统调用,日志需尽可能覆盖周全。
  7. 程序中的日志库需要具备运行期间变更日志等级的能力,方便在遇到问题需要分析时临时更改日志等级。
  8. 通常在新功能上线,涉及的日志可适当提升一个等级,方便实时观察和监控,待稳定后再调整到正常(记得加上注释,方便改回来)。

二、日志内容规范

通常在没有约束的情况下,程序员的发挥天马行空,各种日志内容都会出现,这些只有开发自己才能看懂的日志很难进行分析和告警。因此我们需要一个日志顶向下的规范来约束项目中的开发人员,让所有的日志看起来是一个人打印的而且是易于分析的。

2.1 日志的字段

日志中通常必备的字段有:Time、Level、Location。对于特定模块/流程/业务,还需要有一些Common的字段,例如:

  1. 如果使用Trace系统,可以把TraceID附加到日志中。
  2. 固定的流程需要附加对应的字段,例如订单的生命周期中,一定要有订单号、用户ID等信息,这些信息可以通过Context附加到对应流程的日志实例上。
  3. HTTP请求需要记录:URL、Method、Status、Latency、Inflow、OutFlow、ClientIP、UserAgent等,详情可以参考Nginx日志格式
  4. 如果多个模块的日志都打印到同一个流/文件中,必须有字段标识模块名。

日志的字段规约最好由运维平台/中间件平台自顶向下推动,约束每个模块/流程的程序员按照规定打印日志。

2.2 日志表现形式

通常我们建议使用KeyValue对形式的日志格式,比如我们阿里的飞天日志库采用的就是这种形式:

[2019-12-30 21:45:30.611992]    [WARNING]       [958] [block_writer.cpp:671]  path:pangu://localcluster/index/3/prom/7/1577711464522767696_0_1577711517     min_time:1577712000000000       max_time:1577715600000000       normal_count:27595      config:prom     start_line:57315569     end_line:57343195       latency(ms):42  type:AddBlock

KeyValue对的日志可以完全自解析且易于理解,同时便于日志采集时自动解析。
另外推荐的是JSON日志格式,支持以JSON格式输出的日志库很多,而且大部分的日志采集Agent都支持JSON格式的日志收集。

{"addr":"tcp://0.0.0.0:10010","caller":"main.go:98","err":"listen tcp: address tcp://0.0.0.0:10010: too many colons in address","level":"error","msg":"Failed to listen","ts":"2019-03-08T10:02:47.469421Z"}
  • 注意:绝大部分场景不建议使用非可读的日志格式(例如ProtoBuf、Binlog等)。

2.3 单条日志换行问题

非必要情况下,尽量不要一条日志输出成多行,这种对于采集、解析和索引的代价都比较高。

三、日志输出

3.1 合理控制日志输出量

日志的输出量直接影响到磁盘使用以及对于应用的性能消耗,日志太多也不利于查看、采集、分析;日志太少不利于监控,同时在出现问题的时候没办法调查。一般线上应用需合理控制日志的数据量:

  1. 服务入口的请求和响应日志没有特殊原因都要输出并采集,采集的字段可以根据需求调整。
  2. 错误日志一般都要打印,如果太多,可以使用采样方式打印。
  3. 减少无效日志输出,尤其是循环中打印日志的情况需尽量减少。
  4. 请求型的日志(比如Ingress、Nginx访问日志)一般不超过5MB/s(500字节每条,不超过1W/s),应用程序日志不超过200KB/s(2KB每条,不超过100条/s)。

3.2 选择多种日志输出目标

建议一个应用不同类型的日志输出到不同的目标(文件),这样便于分类采集、查看和监控。例如:

  1. 访问日志单独放到一个文件,如果域名不多,可以按照一个域名一个文件的形式。
  2. 错误类的日志单独放一个文件,单独配置监控告警。
  3. 调用外部系统的日志单独放一个文件,便于后续对账、审计。
  4. 中间件通常都由统一的平台提供,日志一般单独打印一个文件。

3.3 控制日志性能消耗

日志作为业务系统的辅助模块,一定不能影响到业务正常的工作,因此日志模块的性能消耗需要单独额外注意,一般在选择/开发日志库时,需要对日志库进行性能测试,确保正常情况下日志的性能消耗不超过整体CPU占用的5%。

  • 注意:一定要确保日志打印是异步的,不能阻塞业务系统运行。

3.4 如何选择日志库

开源的日志库非常多,基本每个语言都有数十种,选择一个符合公司/业务需求的日志库需要精挑细选,有一个简单的指导原则是尽可能使用比较流行的日志库的稳定版本,入坑的几率要小一点。例如:

  1. Java 使用 Log4J、LogBack。
  2. Golang 使用 go-kit
  3. Python默认集成的日志库大部分场景都够用,建议阅读一下CookBook
  4. C++ 推荐使用 spdlog,高性能、跨平台。

3.5 日志形态选择

在虚拟机/物理机的场景中,绝大部分应用都以文件的形式输出日志(只有一些系统应用输出到syslog/journal);而在容器场景中,多了一个标准输出的方式,应用把日志打到stdout或stderr上,日志会自动进入到docker的日志模块,可以通过 docker logs 或 kubectl logs 直接查看。
容器的标准输出只适应于比较单一的应用,例如K8s中的一些系统组件,线上的服务类应用通常都会涉及到多个层级(中间件)、和各种服务交互,一般日志都会分为好几类,如果全部打印到容器的标准输出,很难区分处理。
同时容器标准输出对于DockerEngine的性能消耗特别大,实测10W/s的日志量会额外占用DockerEngine 1个核心的CPU(单核100%)。

3.6 日志是否落盘以及落盘介质

在Kubernetes中,还可以将日志库直接对接日志系统,日志打印的时候不落盘而直接传输到日志系统后端。这种使用方式免去了日志落盘、Agent采集的过程,整体性能会高很多。这种方式我们一般只建议日志量极大的场景使用,普通情况下还是直接落盘,相比直接发送到后端的方式,落盘增加了一层文件缓存,在网络失败的情况下还能缓存一定的数据,在日志系统不可用的情况下我们的研发运维同学可以直接查看文件的日志,提高整体的可靠性。
Kubernetes提供了多种存储方式,一般在云上,都会提供本地存储、远程文件存储、对象存储等方式。由于日志写入的QPS很高,和应用也直接相关,如果使用远程类型的存储,会额外多2-3次网络通信开销。我们一般建议使用本地存储的方式,可以使用HostVolume或者EmptyDir的方式,这样对于写入和采集的性能影响会尽可能的小。

3.7 如何保证日志存储周期

相比传统虚拟机/物理机的场景,Kubernetes对于节点、应用层提供了强大的调度、容错、缩/扩容能力,我们通过Kubernetes很容易就能让应用获得高可靠运行、极致弹性。这些优势带来的一个现象是:节点动态创建/删除、容器动态创建/删除,这样日志也会随时销毁,没办法保证日志的存储周期能够满足DevOps、审计等相关的需求。
在动态的环境下实现日志的长期存储只能通过中心化的日志存储来实现,通过实时的日志采集方式,将各个节点、各个容器的日志在秒级内采集到日志中心系统上,即使节点/容器挂掉也能够通过日志还原当时的现场。

四、总结

日志输出是日志系统建设中非常重要的环节,公司/产品线一定要遵循一个统一的日志规范,这样才能保证后续日志采集、分析、监控、可视化能够顺利进行。

 本文转自:Kubernetes 中日志的正确输出姿势-阿里云开发者社区

<think>好的,我现在需要帮助用户解决他们关于在Jupyter Notebook中使用Nginx和uWSGI进行部署的问题。首先,用户提到他们已经在虚拟环境中安装了所有必要的模块,并且能够通过`jupyter notebook --allow-root`启动Jupyter,但遇到了启动慢和开多后崩溃的问题。他们想知道是否可以使用Nginx和uWSGI来部署,以改善这些问题。 首先,我需要确认用户的需求。他们可能希望将Jupyter Notebook作为生产环境下的服务运行,提高其稳定性和性能。但Jupyter本身设计用于交互式开发,并非为生产环境设计。用户可能误解了Jupyter的用途,或者希望优化其运行方式。 接下来,我需要分析Jupyter Notebook的架构。Jupyter是基于Tornado框架的Web应用,内置了Web服务器。直接使用`--allow-root`启动可能会有安全隐患,且单进程运行容易崩溃,尤其是在处理多个并发请求时。用户提到的启动慢和崩溃问题,可能源于资源不足或配置不当。 然后,考虑Nginx和uWSGI的作用。通常,uWSGI用于部署WSGI应用,如Flask或Django。但Jupyter并不是一个标准的WSGI应用,它使用WebSocket进行实时通信,这可能与uWSGI不兼容。因此,直接使用uWSGI可能不可行。Nginx可以作为反向代理,处理SSL、静态文件和负载均衡,但需要正确配置以支持WebSocket。 用户可能没有意识到Jupyter和传统Web应用的区别。需要解释为什么直接使用Nginx和uWSGI可能不适用,并提供替代方案。比如,使用Nginx作为反向代理,配合Jupyter的内置服务器,或者考虑JupyterHub来管理多用户环境。此外,优化Jupyter配置,如限制并发数、增加资源,或者使用Docker容器化部署,可能是更有效的解决方案。 还需要检查用户可能存在的配置问题。例如,是否设置了足够的内存和CPU资源,是否正确配置了Jupyter的安全选项,如密码和SSL。同时,建议用户查看日志文件,确定崩溃的具体原因,是内存泄漏、资源不足还是其他问题。 最后,总结建议:虽然不能直接使用uWSGI,但可以通过Nginx反向代理、优化Jupyter配置、使用JupyterHub或切换至JupyterLab等方式来改善性能和稳定性。同时,强调生产环境中使用Jupyter的风险,建议仅在开发或受控环境中运行。</think>### 核心问题定位: **Jupyter Notebook 和传统 Web 应用(如 Flask/Django)有本质区别**,直接使用 Nginx + uWSGI 部署不仅无法解决问题,反而会导致更多异常。需要先理解两者的技术差异: --- ### 一、Jupyter Notebook 的特殊性(不可用常规 Web 部署方案) #### 1. 架构差异对比 | | 传统 Web 应用 (Flask/Django) | Jupyter Notebook | |------------------|------------------------------------|----------------------------------| | **协议** | 纯 HTTP + WSGI | HTTP + WebSocket (实时交互) | | **核心功能** | 处理请求-响应 | 交互式代码执行 + 实时输出 | | **会话管理** | 无状态 | 有状态(保持内核运行) | | **适用场景** | 生产环境 | 开发/调试环境 | #### 2. 你遇到的问题根源 - **启动慢**:Jupyter 需要初始化 IPython 内核 + 加载所有扩展 - **开多崩溃**:每个 Notebook 都会占用独立内核进程,内存爆炸式增长 - **无法水平扩展**:内核状态无法在多个进程间共享 --- ### 二、Nginx + uWSGI 为什么不适用? #### 技术冲突点: 1. **协议不兼容**: - uWSGI 仅支持 WSGI 协议 - Jupyter 依赖 WebSocket 实现实时交互(代码执行/输出) 2. **状态保持难题**: - uWSGI 的多进程模型会导致内核状态不一致 - 不同请求可能被分配到不同进程,破坏 Notebook 连贯性 3. **资源管理失效**: - uWSGI 的进程管理无法控制 Jupyter 内核生命周期 - 内存泄漏风险加剧(如忘记关闭长期运行的 Notebook) --- ### 三、生产级 Jupyter 部署的正确姿势 #### 方案 ①:Nginx 反向代理(基础优化) ```nginx # nginx.conf 片段(支持 WebSocket 的关键配置) map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; server_name jupyter.yourdomain.com; location / { proxy_pass http://localhost:8888; # 指向 Jupyter 实际端口 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; } } ``` **优化效果**: - 通过 Nginx 实现 HTTPS 加密(配合 SSL 证书) - 负载均衡(如果有多个 Jupyter 实例) - 隐藏真实端口,提升安全性 #### 方案 ②:JupyterLab + 资源限制(推荐) ```bash # 启动命令优化(限制资源占用) jupyter lab --allow-root \ --ip=0.0.0.0 \ --port=8888 \ --no-browser \ --NotebookApp.token='your_password' \ --ResourceUseDisplay.mem_limit=4096m \ # 限制单个 Notebook 内存 --ResourceUseDisplay.track_cpu_percent=True # 启用 CPU 监控 ``` **关键参数**: - `--NotebookApp.max_buffer_size=536870912` (512MB 输出限制) - `--KernelManager.cull_idle_timeout=600` (10分钟无活动关闭内核) - `--KernelManager.cull_interval=300` (每5分钟检查一次) #### 方案 ③:JupyterHub 集群部署(企业级方案) ``` [架构图] Nginx ⇨ JupyterHub ⇨ Docker/Kubernetes 动态生成 Notebook 实例 ``` - **优势**: - 每个用户独立容器,资源隔离 - 按需创建/销毁实例,避免内存堆积 - 支持 OAuth 等企业认证方式 --- ### 四、性能优化实操指南 #### 1. 启动加速方案 ```python # 在 ~/.ipython/profile_default/ipython_config.py 中添加 c.InteractiveShellApp.exec_lines = [ 'import numpy as np', 'import pandas as pd', # 预加载常用库减少首次导入时间 ] c.TerminalIPythonApp.display_banner = False # 关闭启动横幅 # 禁用非必要扩展 jupyter labextension disable @jupyterlab/git jupyter labextension disable @jupyterlab/shortcutui ``` #### 2. 内存泄漏防护 ```bash # 使用 jupyter_contrib_nbextensions 的 Memory Usage 插件 conda install -c conda-forge jupyter_contrib_nbextensions jupyter contrib nbextension install --user ``` - 在浏览器中启用内存监控条(实时显示使用量) #### 3. 内核管理自动化 ```python # 在 ~/.jupyter/jupyter_notebook_config.py 中配置 import os from subprocess import check_call def post_save(model, os_path, contents_manager): if model['type'] != 'notebook': return check_call(['jupyter', 'nbconvert', '--to', 'script', os_path]) c.FileContentsManager.post_save_hook = post_save # 自动保存 .py 备份 ``` --- ### 五、终极建议(架构层面) **如果追求生产环境的稳定性**: 1. 将 Jupyter 仅用于探索性数据分析(EDA)阶段 2. 将最终成果转化为传统 Web 应用(Flask/Django + Celery) 3. 使用 Apache Airflow 或 Prefect 编排批处理任务 **Jupyter 的本质是交互式工具**,强行将其改造为生产服务反而会陷入持续的技术债务。理解工具的设计边界,才能选择最合适的架构方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值