<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Zlatan Eevee</title>
    <description></description>
    <link>https://ieevee.com/</link>
    <atom:link href="https://ieevee.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 29 Dec 2025 23:28:04 +0800</pubDate>
    <lastBuildDate>Mon, 29 Dec 2025 23:28:04 +0800</lastBuildDate>
    <generator>Jekyll v3.8.7</generator>
    
      <item>
        <title>如何自建一个自己的 cursor codebase？</title>
        <description>
&lt;p&gt;使用过 cursor 做编程的同学，对于 cursor 的 code base功能一定不陌生。通过将代码的函数、类、逻辑块等转换为向量，然后通过向量计算，就可以快速找到代码中的相似代码。&lt;/p&gt;

&lt;p&gt;基于此，我们可以在 cursor 里进行智能问答，例如给定一个 log 日志，让 cursor 基于 codebase 进行分析，给出可能的原因。cursor 会查询 codebase 中的代码（基于相似度，而非类似 grep 的精确查询），理解代码，并进行解答。&lt;/p&gt;

&lt;p&gt;在做一些 Agent 的时候，我们通常会使用文档来作为知识库，进行专属领域内的知识解答，但是文档总是有穷尽的，不可能覆盖到方方面面。如果我们可以基于代码做知识库解答，把一些客户现场报错的 log 日志输入进去，让 AI 自动分析，给出可能的原因，这就可以大大提升我们的工作效率。&lt;/p&gt;

&lt;p&gt;但是 cursor 或者其他的一些 ai IDE，通常都不提供这样的能力，所以我们需要自己来实现。&lt;/p&gt;

&lt;p&gt;下面是基于 Agno 做的一个简单的 codebase 实现，注意，没有使用向量库，而是使用文本匹配。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;subprocess&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Optional&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;textwrap&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dedent&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;agno.agent&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;agno.models.google&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gemini&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;agno.models.nvidia&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nvidia&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;agno.models.openai&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAIChat&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;agno.models.openai.like&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAILike&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# ================= 配置部分 =================
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 你的代码仓库路径 (请修改为你本地代码的实际路径)
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;REPO_PATH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ================= 自定义工具函数 (Tools) =================
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search_codebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
    在代码仓库中执行全局搜索（类似 grep）。
    当你需要查找某个变量定义、错误提示字符串或函数名在何处出现时使用此工具。

    Args:
        query (str): 要搜索的字符串或正则表达式。

    Returns:
        str: 搜索结果，包含文件名、行号和匹配内容。
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;REPO_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: Repository path '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;REPO_PATH&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;' does not exist.&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;🔍 [Tool] Searching for: '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 使用 grep -rn 递归搜索并显示行号
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# -I 忽略二进制文件
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;grep&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-rnI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cwd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;REPO_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 在仓库根目录下执行
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;capture_output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;No matches found.&quot;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# 限制输出长度，防止 Token 溢出
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;... (and &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; more lines)&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error executing grep: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_file_segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
    读取指定文件在特定行号附近的代码片段。

    Args:
        filepath (str): 文件路径（相对于仓库根目录）。
        line_number (int): 核心行号。
        context (int): 读取上下多少行作为上下文，默认为 10 行。

    Returns:
        str: 带有行号的代码内容。
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;full_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;REPO_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;📖 [Tool] Reading file: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; around line &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line_number&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 尝试模糊匹配，因为 grep 有时返回的路径格式可能不同
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: File '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;' not found.&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'r'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'utf-8'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;start_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;end_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# 标记出报错的那一行，方便 AI 识别
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;&amp;gt;&amp;gt; &quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line_number&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;    &quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rstrip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result_lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error reading file: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ================= Agno Agent 定义 =================
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_code_analysis_agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;#model=Nvidia(id=&quot;qwen/qwen3-235b-a22b&quot;),
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gemini&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your gemini model id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;project_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;your google project id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;你是一个资深的云原生开发工程师，精通 Golang 和 Kubernetes 控制器模式。&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;instructions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dedent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;         你可以完成根据日志进行排障，也可以完成根据用户的问题，对代码进行分析，并输出有效的结论

         # 根据日志排障:
         你的目标是根据用户提供的日志，找出代码中的根本原因。
         **分析流程**：
         1. 提取日志中的`文件名`、`行号`和`错误关键信息`。
         2. 使用 `read_file_segment` 工具查看报错位置的代码。
         3. 仔细阅读代码。如果遇到不明来源的变量（特别是 Secret 名称、常量等），使用 `search_codebase` 全局搜索该变量的定义。
         4. 结合 Kubernetes 知识推理：比如 Secret 缺失意味着什么？它是如何被创建的？
         5. 最终输出清晰的中文报告：包含错误原因、代码证据链和可能的修复建议。

         # 代码分析:
         当用户询问关于“接口”、“API”、“功能模块”或“代码结构”的问题时，请遵循以下分析策略：

         1. **意图识别与策略选择**：
            - 如果用户问的是 **Go 语言层面的 Interface**（如“核心接口有哪些”）：
            - 动作：使用 `search_codebase` 搜索关键字 `type .* interface \{`。
            - 重点：关注 `pkg/` 目录下的核心定义，忽略 `vendor/` 或测试代码。
            - 如果用户问的是 **REST API / HTTP 接口**：
            - 动作：搜索路由定义关键字，如 `path`, `route`, `gin.Default`, `mux.NewRouter`, `RegisterWebHook` 或 `v1.`。
            - 重点：寻找 `cmd/` 入口或 `pkg/apiserver` 中的路由注册代码。

         2. **代码阅读与提取 (Deep Dive)**：
            - 找到相关文件后，使用 `read_file_segment` 读取接口定义及其**上方的注释文档**。
            - **必须**提取每个方法的注释（DocString），这是解释功能的关键依据。

         3. **内容生成要求**：
            - **不要**直接堆砌代码。
            - 请生成一份结构化的 Markdown 报告，格式如下：

            ### [接口/模块名称]
            **文件路径**: `pkg/xxx/xxx.go`
            **核心职责**: (一句话总结这个接口是做什么的)
            **包含方法**:
            - `MethodA()`: (功能描述)
            - `MethodB()`: (功能描述)

         4. **兜底策略**：
            - 如果搜索结果过多（超过 10 个），请先列出核心的 3-5 个接口，并询问用户是否需要展开特定模块。
            - 如果代码中没有注释，请通过方法名（Naming Convention）推断功能，并明确标注“（推断）”。
        &quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 将我们定义的 Python 函数直接传给 Agno
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search_codebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_file_segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;#debug_mode=True,
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;#debug_level=2,
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# 输出 Markdown 格式
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ================= 主程序运行 =================
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# 1. 实例化 Agent
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_code_analysis_agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cli_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;emoji&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;✍️ &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面的代码，我们定义了两个工具函数，分别是 search_codebase 和 read_file_segment，分别用于在代码仓库中搜索代码和读取代码片段。&lt;/p&gt;

&lt;p&gt;并且，Prompt 里面的 instructions 里面，我们定义了两个策略，一个是根据日志进行排障，另一个是根据用户的问题，对代码进行分析，并输出有效的结论。即面向了常见的两个场景，一是问题排查，二是代码走读。&lt;/p&gt;

&lt;p&gt;用我开源的小工具&lt;a href=&quot;https://github.com/silenceshell/topic&quot;&gt;topic&lt;/a&gt;来做个测试，运行效果如下：&lt;/p&gt;

&lt;p&gt;Agno Agent 会主动调用 search_codebase 和 read_file_segment 两个工具，来获取代码信息。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ai/codebase/git_codebase_1.png&quot; alt=&quot;codebase_1.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;获取代码后，会再提交个大模型进行分析，最终输出结果。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ai/codebase/git_codebase_2.png&quot; alt=&quot;codebase_2.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到，对于这种大模型没有信息的代码仓库，也可以通过 codebase 进行分析。&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Dec 2025 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/tech/2025/12/29/codebse.html</link>
        <guid isPermaLink="true">https://ieevee.com/tech/2025/12/29/codebse.html</guid>
        
        <category>ai</category>
        
        
        <category>tech</category>
        
      </item>
    
      <item>
        <title>k8s的域名解析总是需要coredns吗？</title>
        <description>
&lt;p&gt;我们知道k8s的域名解析是通过coredns来完成的，但是，总是需要coredns吗？或者换个问法，所有dns请求都经过coredns吗？&lt;/p&gt;

&lt;p&gt;答案：并不是，API Server就不是。&lt;/p&gt;

&lt;p&gt;API Server通常是host network方式部署的，其nameserver来源自节点的/etc/resolv.conf，并不是coredns的 .10 地址。那么API Server是怎么解析cluster ip类型的域名呢？&lt;/p&gt;

&lt;p&gt;在 Kubernetes 中，kube-apiserver 设计了一个 loopback resolver 是为了支持高可靠性和一致性的内部组件通信，特别是在需要通过自己的 API 与自己通信时。这种设计背后的核心思想包括以下几点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;支持可靠的自引用
kube-apiserver 本身需要处理一些会调用自身 API 的功能（例如 webhook、Admission Controller 或 API Aggregation）。通过 loopback resolver，kube-apiserver 可以在集群网络不可用或存在部分网络分区的情况下依然高效地与自己通信。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;问题：如果 kube-apiserver 通过普通的 Service IP 访问自己，可能依赖 DNS 解析、kube-proxy 或网络插件，这些组件可能未完全运行或存在网络延迟/异常。
解决：Loopback 直接将请求回路短路到本地，从而绕过外部网络依赖。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;提升可靠性和性能
Loopback resolver 不需要经过集群的 Service 解析、负载均衡及 kube-proxy 开销，而是直接在本地完成请求，这大幅降低了延迟并减少了网络层次的开销。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;支持集群初始化阶段
在 Kubernetes 的启动过程中，kube-apiserver 是服务启动的核心。其他组件（例如 kube-proxy、coredns）可能尚未完全可用，这种情况下，kube-apiserver 不能依赖外部网络资源。通过 loopback resolver，可以确保即使在集群组件未完全就绪时，自身功能仍然可用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;避免复杂的网络配置问题
Loopback resolver 可以避免额外引入不必要的配置复杂性，例如确保 kube-proxy 和网络插件的双向可靠通信。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;相关的代码实现可以参见 &lt;code class=&quot;highlighter-rouge&quot;&gt;staging/src/k8s.io/kube-aggregator/pkg/apiserver/resolvers.go&lt;/code&gt;：&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loopbackResolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResolveEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;kubernetes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResolveEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 18 Jun 2025 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/tech/2025/06/18/loopbackresolver.html</link>
        <guid isPermaLink="true">https://ieevee.com/tech/2025/06/18/loopbackresolver.html</guid>
        
        <category>kubernetes</category>
        
        
        <category>tech</category>
        
      </item>
    
      <item>
        <title>降本增效，下掉一台小主机</title>
        <description>
&lt;p&gt;前情回顾：
&lt;a href=&quot;https://ieevee.com/life/2019/05/05/homelab.html&quot;&gt;说说我的homelab&lt;/a&gt;
&lt;a href=&quot;https://ieevee.com/life/2024/02/03/homelab2.html&quot;&gt;homelab v2.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;家里之前是有2台Dell的optiplex小型机，最近盘算了下，其实完全没有用到（optiplex 5080 sff 4c24G + optiplex 7080 sff 12c64G），完全可以下掉一台。&lt;/p&gt;

&lt;p&gt;比较理想的是留下来12c64G的这台，但是openwrt虚拟机在4c24G，PCI网卡也在这台机器上。如果要重新搞openwrt、重新配置clash，想想就觉得头大。&lt;/p&gt;

&lt;p&gt;前两天有一个空闲的半天，突然想到：其实我不用重新安装openwrt，把4c24G机器上的ssd和4T盘，换到12c64G机器上，不就行了？&lt;/p&gt;

&lt;p&gt;事实证明，确实可以，不过会有一些小问题，好在都能解决。&lt;/p&gt;

&lt;p&gt;其一是由于4c24G机器安装的比较早，迁移后操作系统不认识新机器的网卡（连网卡都识别不到），因此一直网络不通。解决办法是升级系统，apt update后驱动就安装上去了，重新配置下网桥就OK了。&lt;/p&gt;

&lt;p&gt;其二是由于之前闲着没事把2台proxmox组了集群，但是现在只剩下一台了，达不到qurom，集群不可用，连管控页面都进不去。解决办法是强行恢复成单节点模式。&lt;/p&gt;

&lt;p&gt;其他就都比较顺利了，新机器资源比较充裕，因此可以把之前的虚拟机都调大规格。&lt;/p&gt;

&lt;p&gt;现在是彻底的All In Boom了。只是空出来一台机器，用来做什么好呢？&lt;/p&gt;
</description>
        <pubDate>Fri, 08 Nov 2024 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/life/2024/11/08/homelab3.html</link>
        <guid isPermaLink="true">https://ieevee.com/life/2024/11/08/homelab3.html</guid>
        
        <category>tech</category>
        
        
        <category>life</category>
        
      </item>
    
      <item>
        <title>域名迁移到Cloudflare上了</title>
        <description>
&lt;p&gt;ieevee.com 这个域名是2017在Godaddy上购买的，当时价格还不贵，2年109.14人民币，但后来价格就越来越高，去年续费的时候已经是170.4HKD一年了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/dns/godaddy.png&quot; alt=&quot;Godaddy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最近看到有网友将域名迁移到了互联网活菩萨 Cloudflare 上，据说域名是成本价售卖。看了下，狗爹续费差不多要180块钱一年，而Cloudflare只要9.77刀一年，确实便宜了很多。&lt;/p&gt;

&lt;p&gt;而且Cloudflare还有很多其他的服务，比如对象存储、Zero Trust、AI workers等等，而狗爹其实只是一家新加坡的小公司，并且狗爹的管理面板一直很迷（但狗爹的广告做的确实很好，经常在英超赛场上看到），迁移势在必行。&lt;/p&gt;

&lt;p&gt;迁移过程凭记忆大概是这样的一个步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在GoDaddy发起域名转移到其他域名商，这个时候Godaddy会提供一个验证码&lt;/li&gt;
  &lt;li&gt;到Cloudflare注册网站，并将域名转入，填写GoDaddy提供的验证码&lt;/li&gt;
  &lt;li&gt;到GoDaddy确认转出。默认是要等好几天的，可以到GoDaddy上手工转出下，立即就可以生效了。&lt;/li&gt;
  &lt;li&gt;确认DNS配置是否正常，实测Cloudflare迁移过去的DNS域名有错误，需要手工修正。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意，Cloudflare默认是开启域名代理的，即解析域名时，是先解析到Cloudflare的代理，然后再由Cloudflare转发给实际的Real Server IP，理论上这样会提高安全性，也能提供global wide的就近访问，但是因为我的域名还有其他的用途，没办法使用代理，所以需要在域名管理里关闭代理。&lt;/p&gt;

&lt;p&gt;我的域名还绑定了腾讯企业邮，迁移过程中几乎无任何感知，没有受到影响。&lt;/p&gt;

&lt;p&gt;Ref：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://tw.godaddy.com/help/transfer-my-domain-away-from-godaddy-3560&quot;&gt;將我的網域轉出 GoDaddy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 11 Apr 2024 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/life/2024/04/11/dns.html</link>
        <guid isPermaLink="true">https://ieevee.com/life/2024/04/11/dns.html</guid>
        
        <category>tech</category>
        
        
        <category>life</category>
        
      </item>
    
      <item>
        <title>homelab v2.0</title>
        <description>
&lt;p&gt;前情回顾：&lt;a href=&quot;https://ieevee.com/life/2019/05/05/homelab.html&quot;&gt;说说我的homelab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;前几年搬家，有机会重新布线，于是把家里的网络做了更新，有了homelab v2.0。&lt;/p&gt;

&lt;p&gt;跟v1版本相比，最大的不同是将网件的WNDR4300改为了Optiplex 5050的一台虚拟机，加了一块网卡直通给虚拟机（软路由），增加一台H3C的wifi6路由器作为AP，同时所有需要有线接入的设备都接到一个H3C的二层交换机上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/homelab/homelab_v2.png&quot; alt=&quot;homelab v2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;配置软路由后，就可以配置Clash服务实现内外网分流了。外网服务器是一台香港的腾讯云轻量，带宽、时延都还不错。&lt;/p&gt;

&lt;p&gt;新增加了一台Optiplex 5080，也安装了PVE，总的资源升级到了16C86G。&lt;/p&gt;

&lt;p&gt;k8s从1.11版本逐渐升级到了1.23。由于1.24以后只能使用containerd，所以暂时就停留在1.23版本不再升级。&lt;/p&gt;

&lt;p&gt;新增了一台PS5，但是发现PS上的游戏不是很对我的口味，这个游戏机基本沦为了netflix播放器，最近网飞在打击各种合租账号，现在也用不了了。这台PS5买的实在是不划算，使用率远不如switch。&lt;/p&gt;

&lt;p&gt;新增了2台homepod mini，可以组成一对音箱，方便播放儿歌。&lt;/p&gt;

&lt;p&gt;空调也接入了家庭网络，之前配置home-assistant，可以通过siri控制空调的开关，后来可能美的升级了，已经不能用了。&lt;/p&gt;

&lt;p&gt;相比v1的homelab，v2增加了异地管理（- -!）。福州放了一个绿联云的NAS，为了方便观看电视，还用N1电视盒子装了armbian操作系统，安装PLEX挂载NAS的samba，这样就可以在电视上用PLEX观看、管理影视了。有的时候NAS可能关闭或者samba挂载掉线之类，需要穿梭过去到N1盒子查下问题，所以将N1挂到了tailscale上，这样我将本地的虚拟机也挂到tailscale上，就可以通过小局域网连接回去了。甚至可以PC加进来以后，从PC上远程查看Plex的内容，不过确实带宽有限，体验一般。&lt;/p&gt;

&lt;p&gt;绿联云还出了电视端的App，安装到异地的另外一个电视上，直接通过绿联云观看。但是我买的绿联云型号比较早，App只能直接列表式查看文件，不能像Plex一样管理影视，看起来比较费劲（比如无法记录昨天看到哪里了）。新的型号是有这个功能的。另外还推荐极空间，他们的NAS有比较优秀的极影视。&lt;/p&gt;

&lt;p&gt;2台小服务器接了UPS，防止断电，然而搬过来好几年了，从来没用上过，白白占了一大片空间。&lt;/p&gt;

&lt;p&gt;服务部署太多，入口难以管理，所以用了一个开源的组件做了个Portal，管理起来方便一些。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/homelab/protal.jpg&quot; alt=&quot;portal&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 03 Feb 2024 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/life/2024/02/03/homelab2.html</link>
        <guid isPermaLink="true">https://ieevee.com/life/2024/02/03/homelab2.html</guid>
        
        <category>kubernetes</category>
        
        
        <category>life</category>
        
      </item>
    
      <item>
        <title>一个Projeted Volume挂载service account引发的故障</title>
        <description>
&lt;p&gt;最近接到一个业务报告的故障，提到argo创建的流水线运行失败，其中Pod去访问kube API Server时，偶发出现401认证不通过的问题。&lt;/p&gt;

&lt;p&gt;API Server报错如下：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;E1212 18:37:53.063390       1 claims.go:126] unexpected validation error: *errors.errorString
E1212 18:37:53.063583       1 authentication.go:63] &quot;Unable to authenticate the request&quot; err=&quot;[invalid bearer token, Token could not be validated.]&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从报错上来看，的确没有通过API Server的认证。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateObj&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apiserverserviceaccount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceAccountInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateObj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;privateClaims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;jwt validator expected private claim of type *privateClaims but got: %T&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token could not be validated.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;nowTime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nowTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ErrExpired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token has expired.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;unexpected validation error: %T&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token could not be validated.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先不看API Server的报错，看看业务使用的认证方式是什么。&lt;/p&gt;

&lt;p&gt;通常业务使用的是service account来访问API Server，这种情况业务代码使用incluster kubeconfig就可以通过API Server的认证了；如果需要相应的权限，则给这个service account授予对应的RBAC权限即可。&lt;/p&gt;

&lt;p&gt;这种用法的一个弊端是一旦授予出去了，service account的有效期就是永久的，回收很困难。&lt;/p&gt;

&lt;p&gt;projected volumes提供了一种新的service account注入的方法： https://kubernetes.io/docs/concepts/storage/projected-volumes/#serviceaccounttoken&lt;/p&gt;

&lt;p&gt;如下是一个示例：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pod&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sa-token-test&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;container-test&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;busybox:1.28&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;sleep&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3600&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token-vol&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/service-account&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceAccountName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token-vol&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;projected&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;serviceAccountToken&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;api&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;expirationSeconds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3600&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;kubelet会为service account临时生成一个token，并将token注入到/service-account，token有效期为3600秒。你可以将token取出来，写到kubectl使用的config中，同样可以访问API Server。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;certificate-authority-data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;「CA」&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://kubernetes-apiserver.kube-system.svc.global.tcs.internal:6443&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;global&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;contexts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;global&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xxx&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xxx@global&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;current-context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xxx@global&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Config&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;xxx&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;「token」&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;回到这个问题，既然怀疑这个token有问题，于是我们将Pod的启动命令改成 sleep infinity，等Pod启动后，将token拿出来放到config中，发现token没有任何问题，而且过期时间也足够。&lt;/p&gt;

&lt;p&gt;陷入了沉思。&lt;/p&gt;

&lt;p&gt;不过没关系，我们回过来看上面API Server的报错。我们使用的是k8s 1.22版本，只有ErrExpired会打印具体的报错，其他的错误只会傻傻的打印  &lt;code class=&quot;highlighter-rouge&quot;&gt;*errors.errorString&lt;/code&gt; 。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValidateWithLeeway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;leeway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Issuer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Issuer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Issuer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrInvalidIssuer&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Subject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrInvalidSubject&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrInvalidID&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Audience&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Audience&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Audience&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrInvalidAudience&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsZero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leeway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotBefore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrNotValidYet&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsZero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leeway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Expiry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrExpired&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从上面的代码来看，返回的错误情况有很多，但是最大的嫌疑，就是 ErrNotValidYet 。于是检查了各个节点的时间，发现果然时钟不同步，有一台服务器的时钟慢了2分钟。&lt;/p&gt;

&lt;p&gt;那么是为什么呢？&lt;/p&gt;

&lt;p&gt;原因很简单。证书签发后，如果是发给时钟慢的节点，会被API Server认为证书签发在未来的一个时间，所以还没生效，认证失败；为什么取出来token就好了呢？因为这个动作是人来做的，等把token取出来编辑好 kubeconfig，已经过去了2分钟，证书也就生效了。&lt;/p&gt;

&lt;p&gt;btw，token是一个jwt，可以到 jwt.io 上检查，可视化做的非常好。&lt;/p&gt;

&lt;p&gt;Ref：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/storage/projected-volumes/&quot;&gt;Projected Volumes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 13 Dec 2023 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/tech/2023/12/13/projected-volume.html</link>
        <guid isPermaLink="true">https://ieevee.com/tech/2023/12/13/projected-volume.html</guid>
        
        <category>kubernetes</category>
        
        
        <category>tech</category>
        
      </item>
    
      <item>
        <title>老古董：Kindle 4 刷多看系统</title>
        <description>
&lt;p&gt;好多年前买的Kindle 4，以为丢了，找了好久都没找到，这次搬家在一个抽屉最里面发现了，插上电居然还能用。&lt;/p&gt;

&lt;p&gt;和现在用的文石leaf相比，kindle4的实体翻页键还是非常带感，厚厚的实体也很有分量，更像一本书。不过Kindle 4的中文支持的很奇怪，有些书的中文显示会很怪，有些又很正常，于是萌生了刷机的想法。&lt;/p&gt;

&lt;p&gt;古早时期，Kindle是可以刷多看系统的，当时嗤之以鼻，嫌弃不够清真，现在多看还在，但已经不再提供Kindle系统了，只有多看阅读App了。&lt;/p&gt;

&lt;p&gt;网络上去查找Kindle的刷多看说明，基本都会贴一个多看网站的链接，然而这个链接已经失效了。物是人非呀，一晃好多年了。&lt;/p&gt;

&lt;p&gt;不过幸好还有互联网博物馆  web.archive.org ，还可以下载到刷机用的文件。你可以到 &lt;a href=&quot;https://web.archive.org/web/20190907135422/http://www.miui.com/thread-2558456-1-1.html&quot;&gt;这里&lt;/a&gt; 看看之前的历史记录，从这里下载刷机包。&lt;/p&gt;

&lt;p&gt;我也把多看刷机包上传到天翼云盘上了，可以你也有这样的老古董Kindle，不妨也刷一下。如今多看商城已经不可用了，但是系统还不错，对中文的支持也蛮好的，刷机后还是双系统，默认进多看，可以重启进Kindle原生系统。&lt;/p&gt;

&lt;p&gt;https://cloud.189.cn/t/F3iaqyueya2m（访问码：9wwb）&lt;/p&gt;

&lt;p&gt;刷机很简单，包里有说明。&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Dec 2023 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/life/2023/12/10/kindle-duokan.html</link>
        <guid isPermaLink="true">https://ieevee.com/life/2023/12/10/kindle-duokan.html</guid>
        
        <category>kindle</category>
        
        
        <category>life</category>
        
      </item>
    
      <item>
        <title>如何使用Prometheus+Grafana监控Proxmox</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#prometheus-pve-exporter&quot; id=&quot;markdown-toc-prometheus-pve-exporter&quot;&gt;prometheus-pve-exporter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#prometheus&quot; id=&quot;markdown-toc-prometheus&quot;&gt;Prometheus&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#grafana&quot; id=&quot;markdown-toc-grafana&quot;&gt;Grafana&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;家里的软路由是跑在一台Dell的小机器（Optiplex 5050）上，使用Proxmox做的虚拟化，跑的Openwrt。前不久发现家里有时候网络会很卡顿，后来在Proxmox上看监控发现openwrt虚拟机在这个时间段内，CPU跑到了100%，难怪会非常卡。&lt;/p&gt;

&lt;p&gt;我还有一台Dell的小机器（Optiplex 5080），也是Proxmox的虚拟化，两台Dell加起来一共有5个虚拟机，是时候上个监控了。&lt;/p&gt;

&lt;p&gt;社区对于Proxmox的监控一般有 +Prometheus 和 +influxdb 两种方式，我这里采用的是 +Prometheus。&lt;/p&gt;

&lt;h1 id=&quot;prometheus-pve-exporter&quot;&gt;prometheus-pve-exporter&lt;/h1&gt;

&lt;p&gt;和Node exporter一样，也需要一个 Proxmox exporter，可以用&lt;a href=&quot;https://github.com/prometheus-pve/prometheus-pve-exporter&quot;&gt;prometheus-pve-exporter项目&lt;/a&gt;。prometheus-pve-exporter调用Proxmox的API采集指标，然后Prometheus再从 prometheus-pve-exporter 抓取metrics数据，最后通过Grafana展示。&lt;/p&gt;

&lt;p&gt;prometheus-pve-exporter的配置可以使用配置文件，也可以使用环境变量。我这里传入了 PVE_USER/PVE_PASSWORD/PVE_VERIFY_SSL，用来调用Proxmox API时进行认证。由于是监控请求，因此建议在Proxmox上新建一个只读的用户，不要使用root。Proxmox新建用户很简单，在页面上操作即可，但给用户授权只能通过命令行进行。&lt;/p&gt;

&lt;p&gt;如下，给用户 &lt;code class=&quot;highlighter-rouge&quot;&gt;monitor@pve&lt;/code&gt;授予&lt;code class=&quot;highlighter-rouge&quot;&gt;PVEAuditor&lt;/code&gt;权限。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pveum aclmod / &lt;span class=&quot;nt&quot;&gt;-user&lt;/span&gt; monitor@pve &lt;span class=&quot;nt&quot;&gt;-role&lt;/span&gt; PVEAuditor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意，&lt;code class=&quot;highlighter-rouge&quot;&gt;PVE_USER&lt;/code&gt;的格式是&lt;code class=&quot;highlighter-rouge&quot;&gt;user@pve&lt;/code&gt;或者&lt;code class=&quot;highlighter-rouge&quot;&gt;user@pam&lt;/code&gt;，两者的区别是，pve是proxmox的用户，比如我这里是2台Proxmox组成的集群，建立一个pve用户就可以访问2台Proxmox机器；pam是linux的用户，不同的Proxmox机器的用户名密码可能不同（Proxmox实际也是一台Linux机器）。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PVE_VERIFY_SSL&lt;/code&gt;需要设置为false，因为是自签的证书。&lt;/p&gt;

&lt;p&gt;prometheus-pve-exporter可以直接启动，也可以容器化启动。因为后面要使用Prometheus来监控，所以我将prometheus-pve-exporter部署到了kubernetes上去，并创建了一个svc。Deployment的写法类似于：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; prometheus-pve-exporter &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 127.0.0.1:9221:9221 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /path/to/pve.yml:/etc/pve.yml prompve/prometheus-pve-exporter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;创建的svc：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;k8s-app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter-0&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9221&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9221&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pvc-exporter-5050&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LoadBalancer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;prometheus-pve-exporter启动后，可以通过http请求metrics：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://{service loadbalancer ip}:9221/pve?target=1.2.3.4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一个prometheus-pve-exporter可以请求多个Proxmox(target)。&lt;/p&gt;

&lt;h1 id=&quot;prometheus&quot;&gt;Prometheus&lt;/h1&gt;

&lt;p&gt;我这里使用的是Prometheus Operator，因此只要配置&lt;code class=&quot;highlighter-rouge&quot;&gt;ServiceMonitor&lt;/code&gt;即可。endpoints配置2台Proxmox。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;monitoring.coreos.com/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ServiceMonitor&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;k8s-app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;30s&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1.1.1.1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/pve&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter-0&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;30s&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1.1.1.2&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/pve&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter-0&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;jobLabel&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;k8s-app&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespaceSelector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchNames&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;k8s-app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pve-exporter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意&lt;code class=&quot;highlighter-rouge&quot;&gt;namespaceSelector&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;selector.matchLabels&lt;/code&gt;和上面创建的&lt;code class=&quot;highlighter-rouge&quot;&gt;pve-exporter&lt;/code&gt;要匹配起来。&lt;/p&gt;

&lt;p&gt;ServiceMonitor创建后，Prometheus Operator会自动刷新Prometheus配置，之后在Prometheus上可以查看pve的监控信息（例如pve_up）。&lt;/p&gt;

&lt;h1 id=&quot;grafana&quot;&gt;Grafana&lt;/h1&gt;

&lt;p&gt;Proxmox数据收集到Prometheus后，就可以通过Grafana展示了。这里我使用的是&lt;a href=&quot;https://grafana.com/grafana/dashboards/10347-proxmox-via-prometheus/&quot;&gt;Proxmox via Prometheus by Pietro Saccardi&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;至此，就可以比较方便的统一展示所有Proxmox虚拟机的状态了。&lt;/p&gt;

</description>
        <pubDate>Sat, 10 Sep 2022 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/tech/2022/09/10/proxmox.html</link>
        <guid isPermaLink="true">https://ieevee.com/tech/2022/09/10/proxmox.html</guid>
        
        <category>prometheus</category>
        
        
        <category>tech</category>
        
      </item>
    
      <item>
        <title>智能家居: 如何使用siri控制美的空调</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#home-assistant&quot; id=&quot;markdown-toc-home-assistant&quot;&gt;home assistant&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#hacs&quot; id=&quot;markdown-toc-hacs&quot;&gt;HACS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#midea-ac-lan&quot; id=&quot;markdown-toc-midea-ac-lan&quot;&gt;Midea AC LAN&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#homekit-bridge&quot; id=&quot;markdown-toc-homekit-bridge&quot;&gt;HomeKit Bridge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;杭州今年热出了天际，温度稳定达到了40摄氏度以上，家里的3台美的空调也是经常开开关关（开了冷，不开热）。美的空调是可以通过App“美的美居”控制的，不需要遥控器。貌似现在新发货的美的空调已经不带遥控器了。&lt;/p&gt;

&lt;p&gt;不过，每次都要经历“找手机-解锁-找到美的美居-打开美的美居-等待启动-选中空调-打开”的一系列操作，很不友好。&lt;/p&gt;

&lt;p&gt;能不能用siri来控制呢？“hey siri，打开客厅空调”，这样体验会更好。&lt;/p&gt;

&lt;p&gt;这里我使用的是home assistant + Media AC LAN + HASS Bridge + Apple Home + HomePod mini 的方案。&lt;/p&gt;

&lt;h1 id=&quot;home-assistant&quot;&gt;home assistant&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://www.home-assistant.io/&quot;&gt;home assistant&lt;/a&gt;简称hass，一个开源的智能家居设备，能够控制非常多的设备（homepod,plex,kodi,etc），官方宣称支持1900多种设备。我这里用hass的目的是为了控制美的空调。&lt;/p&gt;

&lt;p&gt;hass有2种安装方式，一种是使用树莓派，斐讯N1，玩客云等嵌入式设备，安装hass os，另一种是直接安装应用。&lt;/p&gt;

&lt;p&gt;我这里是使用docker 安装的，具体可以参考&lt;a href=&quot;https://www.home-assistant.io/installation/linux#install-home-assistant-container&quot;&gt;官方这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;官方给出了docker的安装命令，我这里部署到了家里的k8s上，config使用了一个nfs pvc，并且提供了一个svc供集群外访问。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -d \
  --name homeassistant \
  --privileged \
  --restart=unless-stopped \
  -e TZ=MY_TIME_ZONE \
  -v /PATH_TO_YOUR_CONFIG:/config \
  --network=host \
  ghcr.io/home-assistant/home-assistant:stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意2点：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;务必使用hostnetwork，实测如果使用overlay网络方案，评估手机的home app无法连接到hass。只是hass网页打开没这个限制。&lt;/li&gt;
  &lt;li&gt;privileged不是必须的。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pod起来以后，使用Pod的IP或者使用svc IP打开 http://{ip}:8123，就可以进入hass的页面了。&lt;/p&gt;

&lt;h1 id=&quot;hacs&quot;&gt;HACS&lt;/h1&gt;

&lt;p&gt;接下来我们要安装 &lt;a href=&quot;https://hacs.xyz/&quot;&gt;HACS&lt;/a&gt;。HACS是hass的社区市场，提供一些设备、主题的第三方资源。&lt;/p&gt;

&lt;p&gt;如果hass是容器化安装的，HACS的安装可以参考&lt;a href=&quot;https://zhuanlan.zhihu.com/p/400985801&quot;&gt;Home Assistant 安装 HACS&lt;/a&gt;，安装后左边栏会多一个“HACS”的菜单。简单来说就是：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget -q -O - https://install.hacs.xyz | bash -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;重启hass，然后在hass配置界面添加集成HACS即可。&lt;/p&gt;

&lt;p&gt;注意，HACS只是一个“市场”，在HACS上添加的集成，其实是只做了下载，真正的管理还是要到hass的 配置-设备与服务 界面。&lt;/p&gt;

&lt;h1 id=&quot;midea-ac-lan&quot;&gt;Midea AC LAN&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/georgezhao2010/midea_ac_lan/blob/master/README_hans.md&quot;&gt;Midea AC LAN&lt;/a&gt;可以通过本地局域网控制你的美的空调。&lt;/p&gt;

&lt;p&gt;这个插件可以自动配置，其他插件使用起来会繁琐一些。安装很简单，在HACS中搜索’Midea AC LAN’并安装就可以。&lt;/p&gt;

&lt;p&gt;安装后，到hass的 配置-设备与服务-添加集成，搜索Midea AC LAN，添加新设备，选择auto方式，然后插件会自动发现家里的空调，依次添加即可。&lt;/p&gt;

&lt;p&gt;如果家里有多台美的空调，由于自动发现的是一个序列号，需要自己挨个开关下，看看各自对应的是什么，并且重命名为对应的房间，例如我这里的“客厅”。注意一定要重命名，因为后面的siri控制需要使用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hass/hass-1.png&quot; alt=&quot;客厅&quot; /&gt;&lt;/p&gt;

&lt;p&gt;到这里，我们就可以通过hass控制家里的美的空调了。&lt;/p&gt;

&lt;h1 id=&quot;homekit-bridge&quot;&gt;HomeKit Bridge&lt;/h1&gt;

&lt;p&gt;在hass配置界面，添加新集成“HomeKit”，注意不要选择”HomeKit控制器”。前者的作用是将hass作为一个设备加入到HomeKit，后者是让hass去控制HomeKit设备。我们这里是为了让siri去控制美的空调，所以需求是前者。少数派的&lt;a href=&quot;https://sspai.com/post/70089&quot;&gt;这篇文章&lt;/a&gt;说的比较清楚。&lt;/p&gt;

&lt;p&gt;安装后，需要进行配置。选择bridge+exclude模式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hass/hass-2.png&quot; alt=&quot;HomeKit Bridge&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来选择要排除的实体，我们只是想用siri去控制空调的开关和简单设定，一些舒适模式、ECO模式平时也很少用到，所以在这里要把他们排除，免得HomeKit这里太乱。我这里只保留了各个房间的控制。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hass/hass-3.png&quot; alt=&quot;HomeKit Bridge&quot; /&gt;&lt;/p&gt;

&lt;p&gt;完成后，hass会有一个提示，点开是一个二维码，用手机的Apple Home扫码，可以添加这个设备。这里要注意，如果前面Pod使用overlay网络模式，添加会失败。&lt;/p&gt;

&lt;p&gt;添加后，在Home就可以看到3个空调了。如果在Home上看到的命名比较乱，可以在设备图标上右键设置，重命名下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hass/hass-4.png&quot; alt=&quot;HomeKit Bridge&quot; /&gt;&lt;/p&gt;

&lt;p&gt;配置好之后，就可以使用siri来控制了。实测可以打开、关闭某个空调，设置空调的模式、温度、风量等等，还是比较方便的。&lt;/p&gt;

</description>
        <pubDate>Sat, 16 Jul 2022 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/tech/2022/07/16/hass.html</link>
        <guid isPermaLink="true">https://ieevee.com/tech/2022/07/16/hass.html</guid>
        
        <category>hass</category>
        
        
        <category>tech</category>
        
      </item>
    
      <item>
        <title>istio: 如何对istio数据平面进行benchmark</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#背景介绍&quot; id=&quot;markdown-toc-背景介绍&quot;&gt;背景介绍&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#准备&quot; id=&quot;markdown-toc-准备&quot;&gt;准备&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#tools-install&quot; id=&quot;markdown-toc-tools-install&quot;&gt;tools install&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#fortioclient&quot; id=&quot;markdown-toc-fortioclient&quot;&gt;fortioclient&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#fortioserver&quot; id=&quot;markdown-toc-fortioserver&quot;&gt;fortioserver&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#测试&quot; id=&quot;markdown-toc-测试&quot;&gt;测试&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#运行测试用例&quot; id=&quot;markdown-toc-运行测试用例&quot;&gt;运行测试用例&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#收集指标信息&quot; id=&quot;markdown-toc-收集指标信息&quot;&gt;收集指标信息&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#可视化&quot; id=&quot;markdown-toc-可视化&quot;&gt;可视化&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#总结&quot; id=&quot;markdown-toc-总结&quot;&gt;总结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ref&quot; id=&quot;markdown-toc-ref&quot;&gt;Ref&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;背景介绍&quot;&gt;背景介绍&lt;/h1&gt;

&lt;p&gt;istio在应用时，会遇到的2个典型质疑是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;istio增加了单独的数据平面，从传输的角度来说增加了2跳，势必会带来latency的增加。那么latency的增加到底是多少呢？&lt;/li&gt;
  &lt;li&gt;proxy容器需要实现数据面的报文劫持、转发，以及一些策略的实施，其需要的cpu、内存，是多少呢？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;要回答这些问题，就要对istio的数据面进行量化。
istio社区提供了一个工具来进行具体的测试：&lt;a href=&quot;https://github.com/istio/tools/tree/master/perf/benchmark&quot;&gt;istio tools perf benchmark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;为什么不需要对控制面进行量化呢？主要原因是数据面是O(n)的空间复杂度，而控制面几乎是O(1)的空间复杂度。另外一个原因是目前istio的遥测、限流等策略，均为envoy实现，不再交由mixer组件处理，因此集群规模提升后对控制面来说变化不大。&lt;/p&gt;

&lt;h1 id=&quot;准备&quot;&gt;准备&lt;/h1&gt;

&lt;p&gt;istio tools perf benchmark需要读取Pod的监控信息，因此部署istio时，需要增加Prometheus组件。具体参考&lt;a href=&quot;https://istio.io/latest/docs/ops/integrations/prometheus/&quot;&gt;Prometheus安装&lt;/a&gt;。&lt;/p&gt;

&lt;h1 id=&quot;tools-install&quot;&gt;tools install&lt;/h1&gt;
&lt;p&gt;安装参考 &lt;a href=&quot;https://github.com/istio/tools/tree/master/perf/benchmark&quot;&gt;readme&lt;/a&gt;即可。&lt;/p&gt;

&lt;p&gt;注意：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;资源需求比较多，要求至少32 cpu、4个node。建议使用完善的生产集群，确保硬件资源充裕。&lt;/li&gt;
  &lt;li&gt;测试套依赖python3/pip3/pipenv，所以最好是在有网环境进行安装，我这里选择的是本地PC；若离线安装，可能会遇到一些镜像源获取不到的问题。&lt;/li&gt;
  &lt;li&gt;不要在docker环境中安装，测试套运行过程中会使用docker执行一些命令，如果在docker中安装会执行失败。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;安装后，将在namespace twopods-istio中部署2个Deployment：fortioclient/fortioserver。下面简要说明下。&lt;/p&gt;

&lt;h2 id=&quot;fortioclient&quot;&gt;fortioclient&lt;/h2&gt;

&lt;p&gt;Deployment的编排文件请见&lt;a href=&quot;/assets/isito/misc/perf/benchmark/fortioclient.yaml&quot;&gt;deployment fortioclient&lt;/a&gt;。Pod的编排文件请见&lt;a href=&quot;/assets/isito/misc/perf/benchmark/fortioclient-54894c5758-htdgl.yaml&quot;&gt;pod fortioclient&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;来看 Pod fortioclient。其包括4个container和1个init container：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;captured：运行nighthawk客户端，客户端发出的报文会被proxy劫持&lt;/li&gt;
  &lt;li&gt;uncaptured：运行fortio客户端，客户端发出的报文不会被proxy劫持；监听9076端口，用于托管测试生成的json文件&lt;/li&gt;
  &lt;li&gt;shell：用于执行curl、jq等命令行&lt;/li&gt;
  &lt;li&gt;istio-proxy：proxy，istio注入的sidecar&lt;/li&gt;
  &lt;li&gt;init container：istio注入的init container，用于初始化iptables规则进行流量劫持。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;注意Deployment 中设置的 excludeInboundPorts/excludeOutboundPorts的端口范围，8076/8077/8078/8081/端口是不走proxy的(具体查看&lt;a href=&quot;/assets/isito/misc/perf/benchmark/fortioclient.iptables.log&quot;&gt;fortioclient iptables规则&lt;/a&gt;)，这里代表测试的是原生（非mesh）的性能，用于作为baseline对比；而发往8079/8080端口的报文则会被iptables劫持给proxy。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;fortioclient&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;twopods-istio&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;..&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;linkerd.io/inject&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;disabled&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;sidecar.istio.io/inject&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;sidecar.istio.io/interceptionMode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;REDIRECT&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;traffic.sidecar.istio.io/excludeInboundPorts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8076,8077,8078,8081,9999&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;traffic.sidecar.istio.io/excludeOutboundPorts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;80,8076,8077,8078,8081&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;traffic.sidecar.istio.io/includeOutboundIPRanges&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.222.0.0/16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;fortioserver&quot;&gt;fortioserver&lt;/h2&gt;

&lt;p&gt;Deployment的编排文件请见&lt;a href=&quot;/assets/isito/misc/perf/benchmark/fortioserver.yaml&quot;&gt;deployment fortioserver&lt;/a&gt;。Pod的编排文件请见&lt;a href=&quot;/assets/isito/misc/perf/benchmark/fortioserver-6bc8c484d4-n2tq6.yaml&quot;&gt;pod fortioserver&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;fortioserver包括4个container和1个init container。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;captured：nighthawk-test-server服务端，发给该服务的报文会被proxy劫持。具体来说，监听8079/8080/9080端口，均被劫持。&lt;/li&gt;
  &lt;li&gt;uncaptured：运行nighthawk-test-server服务端，发给该服务的报文&lt;strong&gt;不会&lt;/strong&gt;被劫持。具体来说，监听8076/8077/8078端口，均不会被劫持。&lt;/li&gt;
  &lt;li&gt;shell：用于执行curl、jq等命令行&lt;/li&gt;
  &lt;li&gt;istio-proxy：proxy，istio注入的sidecar&lt;/li&gt;
  &lt;li&gt;init container：istio注入的init container，用于初始化iptables规则进行流量劫持。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;fortioserver的excludeInboundPorts/excludeOutboundPorts的配置与fortioclient是一致的。&lt;/p&gt;

&lt;h1 id=&quot;测试&quot;&gt;测试&lt;/h1&gt;

&lt;h2 id=&quot;运行测试用例&quot;&gt;运行测试用例&lt;/h2&gt;

&lt;p&gt;runner/runner.py 用来执行测试用例。具体参数参考&lt;a href=&quot;https://github.com/istio/tools/tree/master/perf/benchmark#run-performance-tests&quot;&gt;run-performance-tests&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;如下，将采用fortio作为压力源，并发连接设置为16，基准测试的qps依次设置为100,500,1000,2000,4000，每个基准测试用例持续时间为120s，遥测模式为telemetry-v2，针对如下场景进行测试：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;both: 默认值，收发两端均通过sidecar&lt;/li&gt;
  &lt;li&gt;baseline: 基线，收发两端直连，均不通过sidecar&lt;/li&gt;
  &lt;li&gt;serversidecar: 只有server端启用sidecar&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python runner/runner.py &lt;span class=&quot;nt&quot;&gt;--conn&lt;/span&gt; 16 &lt;span class=&quot;nt&quot;&gt;--qps&lt;/span&gt; 100,500,1000,2000,4000 &lt;span class=&quot;nt&quot;&gt;--duration&lt;/span&gt; 120 &lt;span class=&quot;nt&quot;&gt;--serversidecar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--baseline&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--load_gen_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;fortio &lt;span class=&quot;nt&quot;&gt;--telemetry_mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;v2-nullvm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样，就可以得到5 * 3 个测试数据，从而比对不同qps下，直连、全mesh、服务端mesh场景下，时延的变化情况。&lt;/p&gt;

&lt;p&gt;测试完毕后，在fortioclient pod的shell container中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;/var/lib/fortio/&lt;/code&gt;下，会生成15个json文件，数据参考&lt;a href=&quot;/assets/isito/misc/perf/benchmark/fortio_json_data/2021-08-04-121644_a551ba6e_qps_100_c_16_1024_v2_nullvm_baseline.json&quot;&gt;fortio_json_data&lt;/a&gt;，该文件描述了本次基准测试的数据，如基准测试起始时间，P50/P90/P99，基准测试报文特征等。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意，由于每次测试生成的文件都放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;/var/lib/fortio/&lt;/code&gt;，多次测试的数据会有干扰，建议测试前先清理下该目录。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;收集指标信息&quot;&gt;收集指标信息&lt;/h2&gt;

&lt;p&gt;runner/fortio.py 用来收集基准测试数据、监控指标数据。具体参数参考&lt;a href=&quot;https://github.com/istio/tools/tree/master/perf/benchmark#gather-result-metrics&quot;&gt;gather-result-metrics&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;fortio.py依赖 fortioclient uncaptured(9076端口)、Prometheus，需要确保本地能够访问这两个地址。&lt;/p&gt;

&lt;p&gt;我这里使用了kubectl port-forward的方式。也可以直接访问url，注意FORTIO_CLIENT_URL、PROMETHEUS_URL设置为正确的值即可。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; twopods-istio port-forward svc/fortioclient 9076:9076 &amp;amp;
kubectl &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; istio-system port-forward svc/prometheus 9090:9090 &amp;amp;

&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROMETHEUS_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;http://localhost:9090
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FORTIO_CLIENT_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;http://localhost:9076
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;配置好后，执行下面的命令。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python ./runner/fortio.py &lt;span class=&quot;nv&quot;&gt;$FORTIO_CLIENT_URL&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prometheus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PROMETHEUS_URL&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--csv&lt;/span&gt; StartTime,ActualDuration,Labels,NumThreads,ActualQPS,p50,p90,p99,p999,cpu_mili_avg_istio_proxy_fortioclient,cpu_mili_avg_istio_proxy_fortioserver,cpu_mili_avg_istio_proxy_istio-ingressgateway,mem_Mi_avg_istio_proxy_fortioclient,mem_Mi_avg_istio_proxy_fortioserver,mem_Mi_avg_istio_proxy_istio-ingressgateway
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;fortio.py 会调用kubectl将json文件拷贝到本地的tmp目录，并依次处理目录下的json文件。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意，由于每次收集指标时，拷贝的文件都放在相同的tmp 目录(macos)，多次测试的数据会有干扰，建议测试前先清理下该目录。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;执行后，会生成一个json文件和一个csv文件。csv文件参考&lt;a href=&quot;/assets/isito/misc/perf/benchmark/tmpld48kepv.csv&quot;&gt;tmpld48kepv.csv&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;通过简单的数据分析，可以发现，both/serveronly模式下，相对baseline，其P50时延增加了基本不超过5ms，P99不超过8ms。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/isito/misc/perf/benchmark/tmpld48kepv.png&quot; alt=&quot;tmpld48kepv.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在一些对时延要求相对宽松的场景下，增加8ms可能不是一个很致命的问题，但在一些严苛的场景（例如结算页），留给业务处理的时间可能就几十ms（包括业务逻辑、访问数据库、访问缓存），proxy增加的延迟还是比较严重的。&lt;/p&gt;

&lt;h2 id=&quot;可视化&quot;&gt;可视化&lt;/h2&gt;

&lt;p&gt;graph_plotter/graph_plotter.py 可以用来对数据进行可视化。具体参数参考&lt;a href=&quot;https://github.com/istio/tools/blob/master/perf/benchmark/graph_plotter/README.md#usage-and-example-plot&quot;&gt;usage-and-example-plot&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;接上面的示例，我们设置横轴(x_axis)为qps，横轴的点为qps列表(query_list) [100,500,1000,2000,4000] ，纵轴(graph_type)为P90的延迟数据，进行绘图。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python3 ./graph_plotter/graph_plotter.py &lt;span class=&quot;nt&quot;&gt;--graph_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;latency-p90 &lt;span class=&quot;nt&quot;&gt;--x_axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;qps &lt;span class=&quot;nt&quot;&gt;--telemetry_modes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;v2-nullvm_serveronly,v2-nullvm_baseline,v2-nullvm_both &lt;span class=&quot;nt&quot;&gt;--query_list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;100,500,1000,2000,4000 &lt;span class=&quot;nt&quot;&gt;--query_str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NumThreads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;16 &lt;span class=&quot;nt&quot;&gt;--csv_filepath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/folders/sf/ndqwtn_105n5c4pfcg9kh1rc19706f/T/tmpld48kepv.csv &lt;span class=&quot;nt&quot;&gt;--graph_title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;./graph_plotter/example_plot/plotter_output4.png
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;绘制图形如下，可以直观的看到both和serveronly模式相对baseline的时延增加情况。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/isito/misc/perf/benchmark/plotter_output-p90.png&quot; alt=&quot;plotter_output-p90.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;将纵轴改为 服务端的cpu使用率 &lt;code class=&quot;highlighter-rouge&quot;&gt;--graph_type=cpu-server&lt;/code&gt;，可以查看到随着qps的提高，pod的cpu使用率有着明显的提升。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/isito/misc/perf/benchmark/plotter_output-cpu-server.png&quot; alt=&quot;plotter_output-cpu-server.png&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;总结&quot;&gt;总结&lt;/h1&gt;

&lt;p&gt;istio tools perf benchmark 通过比较巧妙Deployment编排，设计了baseline/serveronly/both等测试场景，能够形象的对istio 数据面进行基准测试，并对延迟、cpu使用率、内存使用率进行度量。&lt;/p&gt;

&lt;h1 id=&quot;ref&quot;&gt;Ref&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/istio/tools/tree/master/perf/benchmark&quot;&gt;istio tools perf benchmark&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://istio.io/latest/blog/2019/performance-best-practices/&quot;&gt;Best Practices: Benchmarking Service Mesh Performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 27 Jun 2022 08:11:12 +0800</pubDate>
        <link>https://ieevee.com/tech/2022/06/27/11-benchmark.html</link>
        <guid isPermaLink="true">https://ieevee.com/tech/2022/06/27/11-benchmark.html</guid>
        
        <category>istio</category>
        
        
        <category>tech</category>
        
      </item>
    
  </channel>
</rss>
