<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
    <generator uri="https://gohugo.io/" version="0.121.2">Hugo</generator><title type="html"><![CDATA[😄 yunpiao 的 Blog]]></title>
    
    
    
            <link href="https://blog.yunpiao.site/" rel="alternate" type="text/html" title="html" />
            <link href="https://blog.yunpiao.site/atom.xml" rel="self" type="application/rss+xml" title="atom" />
            <link href="https://blog.yunpiao.site/index.json" rel="alternate" type="application/json" title="json" />
            <link href="https://blog.yunpiao.site/sitemap.xml" rel="alternate" type="application/xml" title="sitemap" />
    <updated>2026-04-06T19:13:35+08:00</updated>
    
    
    
    
        <id>https://blog.yunpiao.site/</id>
    
        
        <entry>
            <title type="html"><![CDATA[文件系统-从 vmdk 镜像解析与导出文件]]></title>
            <link href="https://blog.yunpiao.site/post/20260203135314/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20260203135314/</id>
            
            
            <published>2026-02-03T13:53:14+08:00</published>
            <updated>2026-02-03T13:53:14+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>平淡无奇的周一, 老板突然说 7z 怎么无法在 esxi host 解析 vmdk 呢? 于是有这个工具的探索. 实现起来倒是不复杂, 只因为 AI 可以闭环验证, 很快就完成了</p>
</blockquote>
<h2 id="一问题背景">一、问题背景</h2>
<p>在日常运维中，我们经常会遇到以下场景：</p>
<ul>
<li><strong>虚拟机无法启动</strong>：操作系统损坏，但数据还在磁盘上</li>
<li><strong>ESXi 环境受限</strong>：不能安装第三方工具</li>
<li><strong>快照数据恢复</strong>：需要理解 VMware 快照中的增量数据</li>
</ul>
<p>传统方案需要挂载虚拟磁盘，这要求 root 权限和特定的文件系统驱动。但实际上，文件系统的本质就是<strong>按照特定规则组织的二进制数据</strong>。理解这些规则，是进行数据恢复、取证分析、存储系统开发的基础。</p>
<p>本文将详细讲解：</p>
<ol>
<li><strong>分区表结构</strong>：MBR 和 GPT 的二进制布局</li>
<li><strong>文件系统原理</strong>：ext4、NTFS、XFS 的核心数据结构</li>
<li><strong>LVM 机制</strong>：逻辑卷管理器的元数据组织</li>
<li><strong>VMware SESparse</strong>：快照格式的增量存储原理</li>
</ol>
<h2 id="二磁盘数据的层次结构">二、磁盘数据的层次结构</h2>
<p>要理解磁盘数据恢复，首先需要理解数据的层次组织：</p>
<pre tabindex="0"><code>┌───────────────────────────────────────────────────────────┐
│               磁盘镜像 (Raw Disk Image)                   │
└────────────────────────────┬──────────────────────────────┘
                             │
                             ▼
┌───────────────────────────────────────────────────────────┐
│                  分区表层 (MBR / GPT)                     │
│              → 定义分区的起始偏移和大小                   │
└────────────────────────────┬──────────────────────────────┘
                             │
                             ▼
┌───────────────────────────────────────────────────────────┐
│                     LVM 层 (可选)                         │
│               → 物理卷 → 卷组 → 逻辑卷                    │
└────────────────────────────┬──────────────────────────────┘
                             │
                             ▼
┌───────────────────────────────────────────────────────────┐
│             文件系统层 (NTFS / ext4 / XFS)                │
│           → 元数据结构 → 目录树 → 文件数据                │
└───────────────────────────────────────────────────────────┘
</code></pre><pre tabindex="0"><code>AAAA中文AAAA
||||中文||||
----中文----
</code></pre><h3 id="关键魔数magic-numbers">关键魔数（Magic Numbers）</h3>
<p>文件系统检测依赖特定位置的魔数签名：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>魔数</th>
<th>位置</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>MBR</td>
<td><code>0x55AA</code></td>
<td>偏移 510</td>
<td>引导扇区签名</td>
</tr>
<tr>
<td>GPT</td>
<td><code>EFI PART</code></td>
<td>LBA 1</td>
<td>GPT 头部签名</td>
</tr>
<tr>
<td>LVM</td>
<td><code>LABELONE</code></td>
<td>分区偏移 +512</td>
<td>LVM2 物理卷标签</td>
</tr>
<tr>
<td>ext4</td>
<td><code>0xEF53</code></td>
<td>分区偏移 +1080</td>
<td>Superblock 魔数</td>
</tr>
<tr>
<td>XFS</td>
<td><code>0x58465342</code></td>
<td>分区偏移 +0</td>
<td>&lsquo;XFSB&rsquo; 大端序</td>
</tr>
<tr>
<td>NTFS</td>
<td><code>NTFS    </code></td>
<td>分区偏移 +3</td>
<td>OEM ID</td>
</tr>
</tbody>
</table>
<h2 id="三分区表结构">三、分区表结构</h2>
<h3 id="31-mbr-master-boot-record">3.1 MBR (Master Boot Record)</h3>
<p>MBR 位于磁盘第一个扇区（512 字节），包含 4 个分区条目。</p>
<p><strong>核心信息</strong>：每个分区条目记录<strong>分区类型</strong>和<strong>起始 LBA</strong></p>
<pre tabindex="0"><code>分区偏移 = 起始 LBA × 512
</code></pre><p>常见分区类型：<code>0x07</code> NTFS、<code>0x83</code> Linux、<code>0x8E</code> LVM、<code>0xEE</code> GPT 保护分区</p>
<h3 id="32-gpt-guid-partition-table">3.2 GPT (GUID Partition Table)</h3>
<p>GPT 是现代标准，支持超过 2TB 磁盘和 128+ 个分区。</p>
<p><strong>核心信息</strong>：每个分区条目记录<strong>类型 GUID</strong>、<strong>起始/结束 LBA</strong>、<strong>分区名称</strong></p>
<pre tabindex="0"><code>分区偏移 = 起始 LBA × 512（64 位 LBA，支持超大磁盘）
</code></pre><h2 id="四ext4-文件系统结构">四、ext4 文件系统结构</h2>
<p>ext4 是 Linux 最常用的文件系统，采用<strong>块组 + inode + extents</strong>三层结构。</p>
<h3 id="41-整体架构">4.1 整体架构</h3>
<pre tabindex="0"><code>Superblock (偏移 1024) → 块组描述符表 → 块组 0/1/2...
                                            │
                              ┌─────────────┴─────────────┐
                              ▼                           ▼
                         Inode Table                 Data Blocks
                              │
                              ▼
                    Extents (连续块映射)
</code></pre><h3 id="42-核心概念">4.2 核心概念</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>作用</th>
<th>关键点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Superblock</strong></td>
<td>文件系统全局信息</td>
<td>魔数 <code>0xEF53</code>，块大小 = <code>1024 &lt;&lt; log值</code></td>
</tr>
<tr>
<td><strong>块组</strong></td>
<td>将磁盘分区管理</td>
<td>每组有独立的 inode 表和数据块</td>
</tr>
<tr>
<td><strong>Inode</strong></td>
<td>文件元数据</td>
<td>根目录固定为 inode 2</td>
</tr>
<tr>
<td><strong>Extents</strong></td>
<td>数据块映射</td>
<td>魔数 <code>0xF30A</code>，B+ 树结构</td>
</tr>
</tbody>
</table>
<h3 id="43-inode-定位">4.3 Inode 定位</h3>
<pre tabindex="0"><code>块组号 = (inode_num - 1) / inodes_per_group
组内索引 = (inode_num - 1) % inodes_per_group
</code></pre><h3 id="44-extents-树">4.4 Extents 树</h3>
<p>ext4 用 extents 描述文件数据的物理位置，每个 extent 表示一段<strong>连续的物理块</strong>：</p>
<pre tabindex="0"><code>Extent = (逻辑块号, 连续块数, 物理起始块)

物理偏移 = 物理起始块 × 块大小
</code></pre><p>大文件使用 B+ 树组织多个 extents（depth &gt; 0 时有索引节点）。</p>
<h2 id="五ntfs-文件系统结构">五、NTFS 文件系统结构</h2>
<p>NTFS 是 Windows 文件系统，核心设计是<strong>一切皆属性</strong>——文件名、时间戳、数据都是属性。</p>
<h3 id="51-整体架构">5.1 整体架构</h3>
<pre tabindex="0"><code>Boot Sector → MFT (Master File Table) → 属性列表 → 数据
                     │
         ┌───────────┴───────────┐
         ▼                       ▼
    MFT 记录 0: $MFT        MFT 记录 5: 根目录
    (MFT 自身)              (目录索引)
</code></pre><h3 id="52-核心概念">5.2 核心概念</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>作用</th>
<th>关键点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Boot Sector</strong></td>
<td>分区元信息</td>
<td>OEM ID <code>NTFS</code>，记录 MFT 位置</td>
</tr>
<tr>
<td><strong>MFT</strong></td>
<td>文件索引表</td>
<td>每条记录 1024 字节，签名 <code>FILE</code></td>
</tr>
<tr>
<td><strong>属性</strong></td>
<td>存储一切信息</td>
<td><code>$DATA</code> 存数据，<code>$FILE_NAME</code> 存文件名</td>
</tr>
<tr>
<td><strong>簇</strong></td>
<td>分配单位</td>
<td>MFT 偏移 = MFT_LCN × 簇大小</td>
</tr>
</tbody>
</table>
<h3 id="53-mft-记录">5.3 MFT 记录</h3>
<p>每个文件/目录对应一条 MFT 记录，包含多个属性：</p>
<table>
<thead>
<tr>
<th>属性类型</th>
<th>名称</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>0x10</td>
<td>$STANDARD_INFORMATION</td>
<td>时间戳、权限</td>
</tr>
<tr>
<td>0x30</td>
<td>$FILE_NAME</td>
<td>文件名</td>
</tr>
<tr>
<td>0x80</td>
<td>$DATA</td>
<td>文件数据</td>
</tr>
<tr>
<td>0x90</td>
<td>$INDEX_ROOT</td>
<td>目录索引</td>
</tr>
</tbody>
</table>
<h3 id="54-驻留-vs-非驻留">5.4 驻留 vs 非驻留</h3>
<p>NTFS 的独特优化：<strong>小文件数据直接存在 MFT 记录内</strong>（约 700-900 字节以内）。</p>
<pre tabindex="0"><code>$DATA 属性
    │
    ├── 驻留 (flag=0): 数据在 MFT 记录内，无需额外 I/O
    │
    └── 非驻留 (flag=1): 数据在外部簇，通过 Data Runs 定位
</code></pre><h3 id="55-data-runs数据运行">5.5 Data Runs（数据运行）</h3>
<p>Data Runs 用变长编码描述非驻留数据的物理位置：</p>
<pre tabindex="0"><code>编码格式: [Header][Length][Offset] ...

Header = (offset_size &lt;&lt; 4) | length_size
Offset 是累积偏移: LCN_n = LCN_{n-1} + offset_n
Offset = 0 表示稀疏区域（虚拟空间）
</code></pre><p><strong>示例</strong>：<code>31 01 02 03</code> → 1 个簇，起始于 LCN 0x030201</p>
<h2 id="六xfs-文件系统结构">六、XFS 文件系统结构</h2>
<p>XFS 是 CentOS/RHEL 默认文件系统，最大特点是<strong>大端序</strong>和<strong>AG 分区管理</strong>。</p>
<h3 id="61-整体架构">6.1 整体架构</h3>
<pre tabindex="0"><code>Superblock (偏移 0) → AG 0 / AG 1 / AG 2 ...
                           │
              ┌────────────┴────────────┐
              ▼                         ▼
         Inode Table              Data Blocks
              │
              ▼
      128-bit Extents (大端序)
</code></pre><h3 id="62-核心概念">6.2 核心概念</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>作用</th>
<th>关键点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Superblock</strong></td>
<td>文件系统元信息</td>
<td>魔数 <code>XFSB</code>，<strong>偏移 0</strong>（不是 1024）</td>
</tr>
<tr>
<td><strong>AG</strong></td>
<td>分配组</td>
<td>每个 AG 独立管理，支持并行 I/O</td>
</tr>
<tr>
<td><strong>Inode 编码</strong></td>
<td>inode 号含 AG 信息</td>
<td><code>inode = (ag_num &lt;&lt; ag_ino_log) | offset</code></td>
</tr>
<tr>
<td><strong>Extents</strong></td>
<td>128 位结构</td>
<td>全部<strong>大端序</strong></td>
</tr>
</tbody>
</table>
<h3 id="63-fsblock-的-ag-编码最大陷阱">6.3 fsblock 的 AG 编码（最大陷阱）</h3>
<p>XFS 的 fsblock <strong>高位编码了 AG 号</strong>，不能直接乘块大小：</p>
<pre tabindex="0"><code>错误：disk_offset = fsblock × block_size

正确：
  ag_num = fsblock &gt;&gt; ag_blk_log
  ag_blk = fsblock &amp; ((1 &lt;&lt; ag_blk_log) - 1)
  actual_block = ag_num × ag_blocks + ag_blk
  disk_offset = actual_block × block_size
</code></pre><p><strong>为什么？</strong> 因为 <code>ag_blocks</code> 不一定等于 <code>2^ag_blk_log</code>（最后一个 AG 可能不完整）</p>
<h2 id="七lvm-逻辑卷管理">七、LVM 逻辑卷管理</h2>
<p>Linux 常在分区之上使用 LVM，需要先解析 LVM 元数据找到逻辑卷位置。</p>
<h3 id="71-lvm-层次结构">7.1 LVM 层次结构</h3>
<pre tabindex="0"><code>物理分区 → 物理卷 (PV) → 卷组 (VG) → 逻辑卷 (LV) → 文件系统
</code></pre><h3 id="72-核心概念">7.2 核心概念</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>魔数/签名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>PV 标签</strong></td>
<td><code>LABELONE</code></td>
<td>位于分区偏移 512 字节</td>
</tr>
<tr>
<td><strong>元数据头</strong></td>
<td><code>LVM2</code></td>
<td>包含文本格式的配置</td>
</tr>
<tr>
<td><strong>extent_size</strong></td>
<td>-</td>
<td>每个 extent 的扇区数</td>
</tr>
<tr>
<td><strong>pe_start</strong></td>
<td>-</td>
<td>物理 extent 起始扇区</td>
</tr>
</tbody>
</table>
<h3 id="73-逻辑卷偏移计算">7.3 逻辑卷偏移计算</h3>
<p>LVM 元数据是<strong>人类可读的文本格式</strong>，关键信息：</p>
<pre tabindex="0"><code>lv_offset = (pe_start + stripe_offset × extent_size) × 512
</code></pre><p>找到 LV 偏移后，就可以在该位置解析 ext4/XFS 文件系统。</p>
<h2 id="八vmware-sesparse-快照格式">八、VMware SESparse 快照格式</h2>
<p>VMware 在 VMFS6 上使用 SESparse 格式存储快照，这是一种<strong>增量存储格式</strong>。</p>
<h3 id="81-基本原理">8.1 基本原理</h3>
<pre tabindex="0"><code>Base Disk:     [sector 0][sector 1][sector 2]...[sector N]
                   ↑          ↑
SESparse:      [modified]  [unallocated → 读 base]
</code></pre><p>只有被修改的扇区存储在 SESparse 文件中，未修改的数据从 base disk 读取。</p>
<h3 id="82-核心概念">8.2 核心概念</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>魔数</strong></td>
<td><code>0xCAFEBABE</code></td>
</tr>
<tr>
<td><strong>粒度</strong></td>
<td>grain_size，数据分配的最小单位</td>
</tr>
<tr>
<td><strong>L1/L2 表</strong></td>
<td>两级索引定位数据</td>
</tr>
<tr>
<td><strong>状态位</strong></td>
<td>0x0=未分配，0x2=零，0x3=已分配</td>
</tr>
</tbody>
</table>
<h3 id="83-透明合并">8.3 透明合并</h3>
<p>通过封装类提供统一接口，上层文件系统工具无需关心快照逻辑：</p>
<pre tabindex="0"><code>读取请求 → 查 L1/L2 表
         ├── 已分配 → 从 SESparse 读取
         ├── 零粒度 → 返回全零
         └── 未分配 → 从 Base Disk 读取
</code></pre><h2 id="九文件数据索引机制">九、文件数据索引机制</h2>
<p>三大文件系统采用不同策略定位文件数据的物理位置。</p>
<h3 id="91-三者对比">9.1 三者对比</h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>ext4</th>
<th>NTFS</th>
<th>XFS</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>索引结构</strong></td>
<td>Extents B+ 树</td>
<td>Data Runs 链表</td>
<td>128-bit Extents</td>
</tr>
<tr>
<td><strong>小文件优化</strong></td>
<td>无</td>
<td>驻留存储（≤900B）</td>
<td>inline 存储</td>
</tr>
<tr>
<td><strong>稀疏文件</strong></td>
<td>未初始化 extent</td>
<td>offset=0</td>
<td>未分配 extent</td>
</tr>
<tr>
<td><strong>字节序</strong></td>
<td>小端</td>
<td>小端</td>
<td><strong>大端</strong></td>
</tr>
</tbody>
</table>
<h3 id="92-ext4---extents-b-树">9.2 ext4 - Extents B+ 树</h3>
<pre tabindex="0"><code>inode → Extent Header (magic=0xF30A)
              │
    ┌─────────┴─────────┐
    ▼                   ▼
depth=0 (叶子)     depth&gt;0 (索引)
    │                   │
    ▼                   ▼
Extent Entry       Index Entry → 递归
(逻辑块, 块数,
 物理起始块)
</code></pre><p><strong>查找逻辑块 N</strong>：遍历 extents，找到包含 N 的 extent，计算物理块</p>
<h3 id="93-ntfs---data-runs">9.3 NTFS - Data Runs</h3>
<pre tabindex="0"><code>MFT Record → $DATA 属性
                │
     ┌──────────┴──────────┐
     ▼                     ▼
驻留 (flag=0)         非驻留 (flag=1)
数据在 MFT 内         Data Runs 链表
(≤900 bytes)         (累积偏移编码)
</code></pre><p><strong>关键</strong>：Data Runs 使用累积偏移，<code>LCN_n = LCN_{n-1} + offset_n</code></p>
<h3 id="94-xfs---128-位-extent">9.4 XFS - 128 位 Extent</h3>
<pre tabindex="0"><code>inode → 128-bit Extent (大端序)
        ├── 逻辑块偏移 (54 bits)
        ├── 物理起始块 (52 bits, 含 AG 编码)
        └── 块数 (21 bits)
</code></pre><p><strong>关键陷阱</strong>：fsblock 高位是 AG 号，必须先 AG 解码再计算物理偏移</p>
<h2 id="十目录遍历与文件查找">十、目录遍历与文件查找</h2>
<p>以查找 <code>/etc/hosts</code> 为例，看三大文件系统如何定位文件。</p>
<h3 id="101-通用流程">10.1 通用流程</h3>
<pre tabindex="0"><code>根目录 → 查找 &#34;etc&#34; → 获取 etc 的 inode → 查找 &#34;hosts&#34; → 读取数据
</code></pre><h3 id="102-三者对比">10.2 三者对比</h3>
<table>
<thead>
<tr>
<th>步骤</th>
<th>ext4</th>
<th>NTFS</th>
<th>XFS</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>根目录</strong></td>
<td>固定 inode 2</td>
<td>固定 MFT 记录 5</td>
<td>Superblock 中读取</td>
</tr>
<tr>
<td><strong>目录结构</strong></td>
<td>dirent 链表</td>
<td>B+ 树索引</td>
<td>shortform/block/btree</td>
</tr>
<tr>
<td><strong>文件名匹配</strong></td>
<td>顺序遍历</td>
<td>B+ 树查找</td>
<td>顺序或 hash</td>
</tr>
<tr>
<td><strong>inode 定位</strong></td>
<td>块组+组内索引</td>
<td>MFT 记录号×1024</td>
<td>AG 解码</td>
</tr>
</tbody>
</table>
<h3 id="103-目录格式">10.3 目录格式</h3>
<p><strong>ext4</strong>：目录是特殊文件，内容是 dirent 链表（inode + 文件名）</p>
<p><strong>NTFS</strong>：使用 B+ 树索引，小目录在 <code>$INDEX_ROOT</code>，大目录在 <code>$INDEX_ALLOCATION</code></p>
<p><strong>XFS</strong>：根据大小自动选择格式</p>
<ul>
<li>shortform：小目录，数据在 inode 内</li>
<li>block：中等目录，单个数据块</li>
<li>leaf/node：大目录，B+ 树结构</li>
</ul>
<h3 id="104-安全考虑">10.4 安全考虑</h3>
<p>从磁盘读取的文件名可能包含危险字符，必须过滤：</p>
<ul>
<li>路径分隔符：<code>\</code> <code>/</code></li>
<li>Windows 特殊字符：<code>: * ? &quot; &lt; &gt; |</code></li>
<li>Windows 保留名：<code>CON</code>, <code>PRN</code>, <code>AUX</code>, <code>NUL</code>, <code>COM1-9</code>, <code>LPT1-9</code></li>
</ul>
<h2 id="十一技术挑战与经验总结">十一、技术挑战与经验总结</h2>
<h3 id="111-字节序陷阱">11.1 字节序陷阱</h3>
<table>
<thead>
<tr>
<th>文件系统</th>
<th>字节序</th>
<th>Python struct</th>
</tr>
</thead>
<tbody>
<tr>
<td>ext4</td>
<td>小端</td>
<td><code>&lt;</code></td>
</tr>
<tr>
<td>NTFS</td>
<td>小端</td>
<td><code>&lt;</code></td>
</tr>
<tr>
<td>XFS</td>
<td><strong>大端</strong></td>
<td><code>&gt;</code></td>
</tr>
</tbody>
</table>
<h3 id="112-常见坑点">11.2 常见坑点</h3>
<table>
<thead>
<tr>
<th>问题</th>
<th>错误做法</th>
<th>正确做法</th>
</tr>
</thead>
<tbody>
<tr>
<td>XFS fsblock</td>
<td>直接乘 block_size</td>
<td>先 AG 解码</td>
</tr>
<tr>
<td>NTFS Data Runs</td>
<td>当作绝对偏移</td>
<td>累积偏移计算</td>
</tr>
<tr>
<td>ext4 inode</td>
<td>从 0 开始</td>
<td>从 1 开始（inode_num - 1）</td>
</tr>
</tbody>
</table>
<h3 id="113-实践建议">11.3 实践建议</h3>
<ol>
<li><strong>用 hexdump 验证</strong>：不要盲目相信文档，用真实数据验证</li>
<li><strong>内存管理</strong>：大文件分块读取，使用 LRU 缓存</li>
<li><strong>边界检查</strong>：所有读取操作都要检查长度</li>
<li><strong>安全处理</strong>：文件名必须过滤危险字符</li>
</ol>
<h2 id="十二高级开发者应知的文件系统常识">十二、高级开发者应知的文件系统常识</h2>
<blockquote>
<p>以下是从实际数据恢复项目中提炼的核心认知，适合技术面试或架构讨论时展示底层理解。</p>
</blockquote>
<h3 id="121-文件系统的本质">12.1 文件系统的本质</h3>
<p><strong>一句话</strong>：文件系统 = 元数据索引 + 数据块管理</p>
<p>所有文件系统都在解决同一个问题：<strong>如何从文件名找到磁盘上的字节</strong>。差异只在于索引结构和空间分配策略。</p>
<pre tabindex="0"><code>用户视角:  /etc/hosts
           ↓
文件系统:  目录索引 → inode/MFT → 数据块映射 → 物理偏移
           ↓
磁盘视角:  sector 123456, offset 0x1E240000
</code></pre><h3 id="122-三大设计哲学">12.2 三大设计哲学</h3>
<table>
<thead>
<tr>
<th>文件系统</th>
<th>设计哲学</th>
<th>典型场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ext4</strong></td>
<td>块组隔离，元数据分散</td>
<td>通用 Linux，平衡性能与可靠性</td>
</tr>
<tr>
<td><strong>NTFS</strong></td>
<td>一切皆属性，小文件内联</td>
<td>Windows，元数据密集型</td>
</tr>
<tr>
<td><strong>XFS</strong></td>
<td>AG 并行，大文件优化</td>
<td>企业存储，高吞吐场景</td>
</tr>
</tbody>
</table>
<p><strong>面试点</strong>：</p>
<ul>
<li>ext4 的 inode 2 是根目录，这是硬编码的</li>
<li>NTFS 小于 900B 的文件直接存在 MFT 记录里（驻留属性），零额外 I/O</li>
<li>XFS 全大端序，这是 SGI IRIX 的历史遗留</li>
</ul>
<h3 id="123-数据定位的两种范式">12.3 数据定位的两种范式</h3>
<p><strong>连续映射（Extent-based）</strong>：ext4、XFS</p>
<pre tabindex="0"><code>&#34;文件的第 0-99 块在磁盘的第 1000-1099 块&#34;
优点：大文件高效，元数据紧凑
</code></pre><p><strong>链式映射（Run-based）</strong>：NTFS Data Runs</p>
<pre tabindex="0"><code>&#34;从簇 1000 开始连续 50 簇，然后跳到簇 2000 连续 30 簇...&#34;
优点：碎片化场景灵活，支持稀疏文件
</code></pre><h3 id="124-必须知道的坑">12.4 必须知道的&quot;坑&quot;</h3>
<table>
<thead>
<tr>
<th>坑</th>
<th>现象</th>
<th>根因</th>
</tr>
</thead>
<tbody>
<tr>
<td>XFS 地址计算错误</td>
<td>读出来全是垃圾</td>
<td>fsblock 高位是 AG 号，不能直接乘块大小</td>
</tr>
<tr>
<td>NTFS 偏移累加错误</td>
<td>只能读第一段数据</td>
<td>Data Runs 是累积偏移，不是绝对偏移</td>
</tr>
<tr>
<td>ext4 inode 越界</td>
<td>inode 2 读成 inode 3</td>
<td>inode 从 1 开始编号，计算时要 -1</td>
</tr>
<tr>
<td>LVM 找不到文件系统</td>
<td>分区检测失败</td>
<td>要先解析 LVM 元数据找到 LV 偏移</td>
</tr>
</tbody>
</table>
<h3 id="125-架构师视角">12.5 架构师视角</h3>
<p><strong>为什么理解文件系统底层很重要？</strong></p>
<ol>
<li><strong>性能调优</strong>：知道 ext4 的 extent 树深度影响大文件随机读性能</li>
<li><strong>故障诊断</strong>：磁盘坏块时能判断影响范围（元数据区 vs 数据区）</li>
<li><strong>系统设计</strong>：设计存储系统时理解 COW、日志、快照的实现代价</li>
<li><strong>安全意识</strong>：知道&quot;删除&quot;只是解除索引，数据仍在磁盘上</li>
</ol>
<h3 id="126-一句话总结">12.6 一句话总结</h3>
<table>
<thead>
<tr>
<th>层次</th>
<th>核心问题</th>
<th>关键数据结构</th>
</tr>
</thead>
<tbody>
<tr>
<td>分区表</td>
<td>磁盘怎么分割？</td>
<td>MBR/GPT 分区条目</td>
</tr>
<tr>
<td>LVM</td>
<td>物理卷怎么组合？</td>
<td>PV → VG → LV 映射</td>
</tr>
<tr>
<td>文件系统</td>
<td>文件名怎么找到数据？</td>
<td>目录索引 + inode + extent/run</td>
</tr>
<tr>
<td>快照</td>
<td>增量数据怎么存？</td>
<td>COW + 两级索引表</td>
</tr>
</tbody>
</table>
<p><strong>终极认知</strong>：文件系统没有魔法，只有规则。掌握规则，就能在没有操作系统的情况下直接读取任何数据。</p>
<hr>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F" term="%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F" label="操作系统" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[K8S 实践-K8S 集群在线更换节点 IP 实战踩坑记]]></title>
            <link href="https://blog.yunpiao.site/post/20260123114712/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20260123114712/</id>
            
            
            <published>2026-01-23T11:47:12+08:00</published>
            <updated>2026-01-23T11:47:12+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>😵 VM 迁移后改个 IP 而已，怎么就这么多坑呢</p>
</blockquote>
<h2 id="1-背景">1. 背景</h2>
<p>周三下午，运维甩过来一句话：&ldquo;客户那边做了下 VM 迁移，IP 段变了，K8S 集群起不来了，你看看？&rdquo;</p>
<p>我心想，改个 IP 能有多难？不就是改改配置文件重启一下嘛。</p>
<p>然后我就开始了长达 30 分钟的踩坑之旅。</p>
<p><strong>环境说明</strong></p>
<table>
<thead>
<tr>
<th>项目</th>
<th>值</th>
</tr>
</thead>
<tbody>
<tr>
<td>K8S 部署工具</td>
<td>kubeasz</td>
</tr>
<tr>
<td>集群规模</td>
<td>1 master + 2 worker</td>
</tr>
<tr>
<td>网络插件</td>
<td>Flannel</td>
</tr>
<tr>
<td>存储</td>
<td>NFS</td>
</tr>
</tbody>
</table>
<p><strong>IP 变更映射</strong></p>
<table>
<thead>
<tr>
<th>节点</th>
<th>旧 IP</th>
<th>新 IP</th>
</tr>
</thead>
<tbody>
<tr>
<td>master-node</td>
<td>192.168.100.112</td>
<td>192.168.100.115</td>
</tr>
<tr>
<td>worker-node1</td>
<td>192.168.100.113</td>
<td>192.168.100.116</td>
</tr>
<tr>
<td>worker-node2</td>
<td>192.168.100.114</td>
<td>192.168.100.117</td>
</tr>
</tbody>
</table>
<h2 id="2-现象">2. 现象</h2>
<p>SSH 到新 IP 后，发现 kubelet 起不来，apiserver 也连不上，etcd 更是一片红。</p>
<pre tabindex="0"><code>kubectl get nodes
The connection to the server 192.168.100.115:6443 was refused
</code></pre><p>好家伙，全挂了。</p>
<h2 id="3-逐步排查">3. 逐步排查</h2>
<h3 id="第一反应改配置文件">第一反应：改配置文件</h3>
<p>起初我想的很简单，K8S 的配置文件里肯定写死了旧 IP，改掉就行了。</p>
<p>于是开始了 sed 大法：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span><span style="color:#75715e"># kubeasz hosts 文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112/192.168.100.115/g&#39;</span> /etc/kubeasz/clusters/itdr/hosts
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span><span style="color:#75715e"># etcd 配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112/192.168.100.115/g&#39;</span> /etc/etcd/etcd.conf
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span><span style="color:#75715e"># apiserver 服务</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112/192.168.100.115/g&#39;</span> /etc/systemd/system/kube-apiserver.service
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span><span style="color:#75715e"># kubeconfig 文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-11">11</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112/192.168.100.115/g&#39;</span> /etc/kubernetes/*.kubeconfig
</span></span></code></pre></div><p>改完重启，etcd 起来了，apiserver 也起来了。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>kubectl get cs
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span><span style="color:#75715e"># controller-manager   Healthy</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span><span style="color:#75715e"># scheduler            Healthy</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span><span style="color:#75715e"># etcd-0               Healthy</span>
</span></span></code></pre></div><p>我心想，这不挺简单的嘛。</p>
<h3 id="然后问题来了">然后问题来了</h3>
<p>kubelet 死活连不上 apiserver：</p>
<pre tabindex="0"><code>E0122 19:45:23.456789 kubelet.go:2183] &#34;Failed to connect to apiserver&#34; err=&#34;connection refused&#34;
</code></pre><p>兄弟们，这就奇怪了。apiserver 明明在 6443 端口监听着，为啥 kubelet 连不上？</p>
<h3 id="发现第一个隐藏-bosskube-lb">发现第一个隐藏 Boss：kube-lb</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span>cat /etc/kubernetes/kubelet.kubeconfig | grep server
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2">2</a></span><span><span style="color:#75715e"># server: https://127.0.0.1:6443</span>
</span></span></code></pre></div><p>等等，127.0.0.1？这是什么操作？</p>
<p>翻了下 kubeasz 的文档，原来它用了一个叫 <code>kube-lb</code> 的本地负载均衡器（基于 nginx），kubelet 连接本地的 kube-lb，kube-lb 再转发到真正的 apiserver。</p>
<p>检查 kube-lb 配置：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span>cat /etc/kube-lb/conf/kube-lb.conf
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span><span style="color:#75715e"># upstream kube_apiserver {</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3">3</a></span><span><span style="color:#75715e">#     server 192.168.100.112:6443;  # 还是旧 IP！</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4">4</a></span><span><span style="color:#75715e"># }</span>
</span></span></code></pre></div><p>真见鬼了，这个配置文件藏得够深的，官方文档压根没提。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span><span style="color:#75715e"># 修改 kube-lb 配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-2">2</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112/192.168.100.115/g&#39;</span> /etc/kube-lb/conf/kube-lb.conf
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-4">4</a></span><span><span style="color:#75715e"># 重启 kube-lb</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-5">5</a></span><span>pkill -f kube-lb
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-6">6</a></span><span>nohup /etc/kube-lb/sbin/kube-lb -c /etc/kube-lb/conf/kube-lb.conf &amp;
</span></span></code></pre></div><p>重启 kubelet，这次能连上 apiserver 了。</p>
<h3 id="第二个坑节点-ip-不更新">第二个坑：节点 IP 不更新</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1">1</a></span><span>kubectl get nodes -o wide
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-2">2</a></span><span><span style="color:#75715e"># NAME           STATUS   INTERNAL-IP</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-3">3</a></span><span><span style="color:#75715e"># master-node    Ready    192.168.100.112  # 还是旧 IP？？</span>
</span></span></code></pre></div><p>我人傻了，kubelet 都重启了，为啥节点 IP 还是旧的？</p>
<p>原来 K8S 的节点信息是存在 etcd 里的，IP 变了它不会自动更新。得手动删掉旧节点，让 kubelet 重新注册。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span><span style="color:#75715e"># 删除旧节点</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-2">2</a></span><span>kubectl delete node master-node
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-4">4</a></span><span><span style="color:#75715e"># 重启 kubelet</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-5">5</a></span><span>systemctl restart kubelet
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-6">6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-7">7</a></span><span><span style="color:#75715e"># 重新打标签</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-8">8</a></span><span>kubectl label node master-node node-role.kubernetes.io/master<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&#39;</span>
</span></span></code></pre></div><p>这下节点 IP 终于对了。</p>
<h3 id="第三个坑pod-拉不到镜像">第三个坑：Pod 拉不到镜像</h3>
<p>三个节点都 Ready 了，但是 Pod 起不来：</p>
<pre tabindex="0"><code>Failed to pull image &#34;easzlab.io.local:5000/itdr/apisix:with-auth-r2&#34;:
dial tcp 192.168.100.112:5000: connect: no route to host
</code></pre><p>又是旧 IP！这次是镜像仓库的域名解析问题。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-1">1</a></span><span>cat /etc/hosts | grep easzlab
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-2">2</a></span><span><span style="color:#75715e"># 192.168.100.112 easzlab.io.local  # 还是旧的</span>
</span></span></code></pre></div><p>每个节点都要改：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-1">1</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112.*easzlab/192.168.100.115 easzlab/g&#39;</span> /etc/hosts
</span></span></code></pre></div><h3 id="第四个坑kubectl-logs-不能用">第四个坑：kubectl logs 不能用</h3>
<p>Pod 终于起来了，但是 <code>kubectl logs</code> 报错：</p>
<pre tabindex="0"><code>Error from server: Get &#34;https://worker-node2:10250/containerLogs/...&#34;:
dial tcp 192.168.100.114:10250: connect: no route to host
</code></pre><p>注意看，它用的是 hostname <code>worker-node2</code>，但解析出来的 IP 是旧的 <code>192.168.100.114</code>。</p>
<p>又是 <code>/etc/hosts</code> 的锅，这次是节点 hostname 的映射：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-1">1</a></span><span>cat /etc/hosts | grep worker
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-2">2</a></span><span><span style="color:#75715e"># 192.168.100.113    worker-node1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-3">3</a></span><span><span style="color:#75715e"># 192.168.100.114    worker-node2</span>
</span></span></code></pre></div><p>在 master 节点改掉：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-1">1</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112.*master-node/192.168.100.115    master-node/g&#39;</span> /etc/hosts
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-2">2</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.113.*worker-node1/192.168.100.116    worker-node1/g&#39;</span> /etc/hosts
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-3">3</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.114.*worker-node2/192.168.100.117    worker-node2/g&#39;</span> /etc/hosts
</span></span></code></pre></div><h3 id="第五个坑nfs-挂载超时">第五个坑：NFS 挂载超时</h3>
<p>有几个 Pod 卡在 ContainerCreating：</p>
<pre tabindex="0"><code>Warning  FailedMount  Unable to attach or mount volumes:
mount.nfs: Connection timed out
</code></pre><p>NFS 也有旧 IP，而且有两个地方：</p>
<p><strong>1. /etc/exports 客户端白名单</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-1">1</a></span><span>cat /etc/exports
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-2">2</a></span><span><span style="color:#75715e"># /data/nfs 192.168.100.112/24(rw,sync,no_root_squash)</span>
</span></span></code></pre></div><p>改掉并重新导出：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-1">1</a></span><span>sed -i <span style="color:#e6db74">&#39;s/192.168.100.112/192.168.100.115/g&#39;</span> /etc/exports
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-2">2</a></span><span>exportfs -ra
</span></span></code></pre></div><p><strong>2. PV 里的 NFS server 地址</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-1">1</a></span><span>kubectl get pv -o yaml | grep server
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-2">2</a></span><span><span style="color:#75715e"># server: 192.168.100.112</span>
</span></span></code></pre></div><p>PV 不能直接改，得删了重建：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-1">1</a></span><span>kubectl get pv pv-nfs -o yaml &gt; /tmp/nfs-pv.yaml
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-2">2</a></span><span>sed -i <span style="color:#e6db74">&#39;s/server: 192.168.100.112/server: 192.168.100.115/g&#39;</span> /tmp/nfs-pv.yaml
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-3">3</a></span><span>kubectl delete pv pv-nfs --force --grace-period<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-4">4</a></span><span>kubectl apply -f /tmp/nfs-pv.yaml
</span></span></code></pre></div><h3 id="第六个坑pvc-变成-lost-状态">第六个坑：PVC 变成 Lost 状态</h3>
<p>PV 重建后，PVC 状态变成了 Lost，Pod 还是挂载不上。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-20-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-20-1">1</a></span><span>kubectl get pvc -n itdr
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-20-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-20-2">2</a></span><span><span style="color:#75715e"># NAME      STATUS   VOLUME</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-20-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-20-3">3</a></span><span><span style="color:#75715e"># pvc-nfs   Lost     pv-nfs</span>
</span></span></code></pre></div><p>PV 删了重建，原来的绑定关系就断了。得把 PVC 也删了重建：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-1">1</a></span><span><span style="color:#75715e"># 强制删除</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-2">2</a></span><span>kubectl delete pvc -n itdr pvc-nfs --force --grace-period<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-4">4</a></span><span><span style="color:#75715e"># 如果卡住，清理 finalizers</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-5">5</a></span><span>kubectl patch pvc -n itdr pvc-nfs -p <span style="color:#e6db74">&#39;{&#34;metadata&#34;:{&#34;finalizers&#34;:null}}&#39;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-6">6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-7">7</a></span><span><span style="color:#75715e"># 重新创建</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-21-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-21-8">8</a></span><span>kubectl apply -f pvc.yaml
</span></span></code></pre></div><p>数据不会丢，因为 PV 是 Retain 策略。</p>
<h2 id="4-定位原因">4. 定位原因</h2>
<p>回过头来看，K8S 集群改 IP 为什么这么麻烦？</p>
<p><strong>根本原因</strong>：K8S 的 IP 地址被硬编码在太多地方了：</p>
<ol>
<li><strong>TLS 证书的 SAN</strong>：证书里写死了 IP，换了 IP 就 TLS 握手失败</li>
<li><strong>etcd 成员配置</strong>：etcd 集群成员的 peer URL 写死了 IP</li>
<li><strong>各种 kubeconfig</strong>：kubelet、kube-proxy、controller-manager 都有</li>
<li><strong>systemd 服务文件</strong>：apiserver、etcd 的启动参数</li>
<li><strong>kube-lb 配置</strong>：kubeasz 特有的本地负载均衡器</li>
<li><strong>/etc/hosts</strong>：镜像仓库域名 + 节点 hostname 映射</li>
<li><strong>NFS 配置</strong>：/etc/exports + PV 资源</li>
</ol>
<p>这些配置分散在不同的地方，官方文档也没有完整列出来，只能一个一个踩坑。</p>
<h2 id="5-完整配置修改清单">5. 完整配置修改清单</h2>
<p>踩完所有坑后，整理出来的完整清单：</p>
<table>
<thead>
<tr>
<th>配置文件</th>
<th>路径</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>网卡配置</td>
<td><code>/etc/sysconfig/network-scripts/ifcfg-*</code></td>
<td>节点自身 IP</td>
</tr>
<tr>
<td>kubeasz hosts</td>
<td><code>/etc/kubeasz/clusters/&lt;cluster&gt;/hosts</code></td>
<td>所有节点 IP</td>
</tr>
<tr>
<td>etcd.conf</td>
<td><code>/etc/etcd/etcd.conf</code></td>
<td>etcd 节点 IP</td>
</tr>
<tr>
<td>etcd.service</td>
<td><code>/etc/systemd/system/etcd.service</code></td>
<td>etcd 服务配置</td>
</tr>
<tr>
<td>kube-apiserver.service</td>
<td><code>/etc/systemd/system/kube-apiserver.service</code></td>
<td>master IP</td>
</tr>
<tr>
<td><strong>kube-lb.conf</strong></td>
<td><code>/etc/kube-lb/conf/kube-lb.conf</code></td>
<td><strong>关键！</strong></td>
</tr>
<tr>
<td>kubelet.kubeconfig</td>
<td><code>/etc/kubernetes/kubelet.kubeconfig</code></td>
<td>master IP</td>
</tr>
<tr>
<td>kube-proxy.kubeconfig</td>
<td><code>/etc/kubernetes/kube-proxy.kubeconfig</code></td>
<td>master IP</td>
</tr>
<tr>
<td><strong>/etc/hosts</strong></td>
<td><code>/etc/hosts</code></td>
<td><strong>镜像仓库 + hostname</strong></td>
</tr>
<tr>
<td>/etc/exports</td>
<td><code>/etc/exports</code></td>
<td>NFS 客户端 IP</td>
</tr>
<tr>
<td>NFS PV</td>
<td><code>kubectl get pv</code></td>
<td>NFS server 地址</td>
</tr>
</tbody>
</table>
<h2 id="6-解决方法总结">6. 解决方法总结</h2>
<p>最终的操作顺序：</p>
<ol>
<li><strong>备份</strong>：etcd 数据、证书、配置文件</li>
<li><strong>改网卡</strong>：修改 ifcfg 文件，重启节点</li>
<li><strong>改 kubeasz hosts</strong>：进 kubeasz 容器改</li>
<li><strong>改 etcd 配置</strong>：etcd.conf + etcd.service</li>
<li><strong>改 kube-lb</strong>：每个节点都要改（关键！）</li>
<li><strong>改 kubeconfig</strong>：kubelet、kube-proxy 等</li>
<li><strong>重启服务</strong>：先 etcd，再 apiserver，最后 kubelet</li>
<li><strong>删旧节点</strong>：让 kubelet 重新注册</li>
<li><strong>改 /etc/hosts</strong>：镜像仓库 + 节点 hostname</li>
<li><strong>改 NFS</strong>：/etc/exports + PV + PVC</li>
</ol>
<p>总耗时：约 30 分钟</p>
<h2 id="7-经验教训">7. 经验教训</h2>
<ol>
<li><strong>节点 IP 不会自动更新</strong>，必须删旧节点让 kubelet 重新注册</li>
<li><strong>/etc/hosts 有两类映射</strong>，镜像仓库域名和节点 hostname 都要改</li>
<li><strong>NFS 要改两个地方</strong>，/etc/exports 和 PV 资源，PVC 可能变 Lost 要重建</li>
<li><strong>操作前一定要备份</strong>，etcd 数据、证书、配置文件，万一搞砸了还能回滚</li>
</ol>
<hr>
<p>改个 IP 而已，踩了 6 个坑，我是不是 bug 体质啊 😅</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/kubernetes" term="kubernetes" label="Kubernetes" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[安全实践-禁止 ClaudeCode 读取 ApiToken]]></title>
            <link href="https://blog.yunpiao.site/post/20251227143139/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20251227143139/</id>
            
            
            <published>2025-12-27T14:31:39+08:00</published>
            <updated>2025-12-27T14:31:39+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="claude-code-密钥防护如何阻止-ai-读取你的-api-token">Claude Code 密钥防护：如何阻止 AI 读取你的 API Token</h1>
<h2 id="背景">背景</h2>
<p>使用 Claude Code 的时候，我一直心里有个疑虑：这玩意儿的权限太大了。我所有的 API Token 都被它读取过，在分享的对话记录里也全都输出了。更要命的是，我用的还是第三方供应商，所以我的密钥完全是裸奔在他们的平台上。</p>
<blockquote>
<p>这让我很不安，甚至觉得自己像个傻子。我的公网项目、我的网络资产，通通暴露给别人。作为一个专业工程师，这是不能容忍的。</p>
</blockquote>
<p>所以我需要想个办法，在多端 Claude Code 上加一些限制，但又要方便执行一些需要密钥的命令——比如自动配置 DNS、自动部署 Workers。</p>
<h2 id="方案探索踩坑记录">方案探索（踩坑记录）</h2>
<h3 id="方案一bitwarden-mcp">方案一：Bitwarden MCP</h3>
<p>刚开始我想用 Bitwarden 的密钥管理 CLI。后来查了一下它还有个 MCP 集成，但仔细了解后发现，那个 MCP 是把所有密码都给 Claude Code。</p>
<p>这是个很蠢的设计。</p>
<h3 id="方案二文件权限--permissionsdeny">方案二：文件权限 + permissions.deny</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>  <span style="color:#f92672">&#34;permissions&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>    <span style="color:#f92672">&#34;deny&#34;</span>: [<span style="color:#e6db74">&#34;Read(~/.secrets/**)&#34;</span>]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">5</a></span><span>}
</span></span></code></pre></div><p><strong>问题</strong>：这只阻止了 <code>Read</code> 工具，AI 仍然可以用 <code>Bash</code> 工具执行 <code>cat ~/.secrets/xxx</code>。</p>
<p>堵了前门，人家走后门。</p>
<h3 id="方案三pretooluse-hook-拦截">方案三：PreToolUse Hook 拦截</h3>
<p>Claude Code 支持 Hooks，可以在工具调用前拦截。写个脚本检测敏感命令：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#66d9ef">if</span> (<span style="color:#e6db74">/\.secrets/i</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">command</span>)) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">error</span>(<span style="color:#e6db74">&#34;安全阻止: 禁止访问 .secrets 目录&#34;</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span>  <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span>}
</span></span></code></pre></div><p><strong>问题</strong>：AI 可以生成一个脚本文件，然后执行脚本来读取。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>echo <span style="color:#e6db74">&#39;cat ~/.secrets/token&#39;</span> &gt; /tmp/read.sh
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>sh /tmp/read.sh
</span></span></code></pre></div><p>道高一尺魔高一丈。</p>
<h3 id="方案四进程隔离--环境变量">方案四：进程隔离 + 环境变量</h3>
<p>我觉得最好的方式还是进程隔离，用环境变量的方式。但环境变量只允许通过 hook 的方式在执行 shell 命令前加载，执行完之后取消，这样才是最安全的。</p>
<p>进程隔离需要在 macOS 和 Linux 上都通用，所以考虑用 setUID 这种方式。把密钥编译成一个 Go 二进制文件，通过密钥管理工具获取后设置成环境变量，运行完再退出。</p>
<h3 id="方案五infisical最终方案">方案五：Infisical（最终方案）</h3>
<p>发现了 Infisical——一个开源的 secrets 管理平台：</p>
<ol>
<li><strong>云端存储</strong>：secrets 不在本地</li>
<li><strong>本机登录</strong>：<code>infisical login</code> 后会话缓存在 <code>~/.infisical/</code></li>
<li><strong>CLI 注入</strong>：<code>infisical run -- your-command</code> 自动注入环境变量</li>
</ol>
<p><strong>关键洞察</strong>：只要阻止 AI 调用 <code>infisical</code> 命令，就能实现完美隔离。</p>
<h2 id="最终方案架构">最终方案架构</h2>
<pre tabindex="0"><code>┌─────────────────────────────────────────────────────┐
│                 用户侧                              │
├─────────────────────────────────────────────────────┤
│  1. infisical login        (本机登录一次)           │
│  2. infisical run -- cmd   (注入 secrets 执行)      │
│  3. Secrets 存储在 Infisical 云端                   │
└─────────────────────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────┐
│                AI 隔离层                            │
├─────────────────────────────────────────────────────┤
│  Layer 1: permissions.deny                          │
│           - Read(~/.infisical/**)                   │
│           - Read(~/.secrets/**)                     │
├─────────────────────────────────────────────────────┤
│  Layer 2: PreToolUse Hook (Bash)                    │
│           - 命令中出现 &#34;infisical&#34; → 阻止           │
│           - 访问敏感目录 → 阻止                     │
├─────────────────────────────────────────────────────┤
│  Layer 3: PreToolUse Hook (Write/Edit)              │
│           - 写入包含 &#34;infisical&#34; 的脚本 → 阻止      │
└─────────────────────────────────────────────────────┘
</code></pre><h2 id="具体实现">具体实现</h2>
<h3 id="配置-permissionsdeny">配置 permissions.deny</h3>
<p>编辑 <code>~/.claude/settings.json</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2"> 2</a></span><span>  <span style="color:#f92672">&#34;permissions&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3"> 3</a></span><span>    <span style="color:#f92672">&#34;deny&#34;</span>: [
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4"> 4</a></span><span>      <span style="color:#e6db74">&#34;Read(~/.secrets/**)&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5"> 5</a></span><span>      <span style="color:#e6db74">&#34;Read(~/.infisical/**)&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6"> 6</a></span><span>      <span style="color:#e6db74">&#34;Read(./.env)&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7"> 7</a></span><span>      <span style="color:#e6db74">&#34;Read(./.env.*)&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-8"> 8</a></span><span>    ]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-9"> 9</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-10">10</a></span><span>}
</span></span></code></pre></div><h3 id="pretooluse-hook">PreToolUse Hook</h3>
<p>创建 <code>~/.cc-tool/pre-tool-security-hook.js</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1"> 1</a></span><span><span style="color:#75715e">#!/usr/bin/env node
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2"> 2</a></span><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fs</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;fs&#39;</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3"> 3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4"> 4</a></span><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">input</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-5"> 5</a></span><span><span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">stdin</span>.<span style="color:#a6e22e">setEncoding</span>(<span style="color:#e6db74">&#39;utf8&#39;</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-6"> 6</a></span><span><span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">stdin</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;data&#39;</span>, <span style="color:#a6e22e">chunk</span> =&gt; <span style="color:#a6e22e">input</span> <span style="color:#f92672">+=</span> <span style="color:#a6e22e">chunk</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-7"> 7</a></span><span><span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">stdin</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">&#39;end&#39;</span>, () =&gt; {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-8"> 8</a></span><span>  <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-9"> 9</a></span><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">hookData</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">parse</span>(<span style="color:#a6e22e">input</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-10">10</a></span><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">checkToolUse</span>(<span style="color:#a6e22e">hookData</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-11">11</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-12">12</a></span><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">blocked</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-13">13</a></span><span>      <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">error</span>(<span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">reason</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-14">14</a></span><span>      <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">2</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-15">15</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-16">16</a></span><span>    <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-17">17</a></span><span>  } <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">err</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-18">18</a></span><span>    <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">exit</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-19">19</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-20">20</a></span><span>});
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-21">21</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-22">22</a></span><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">checkToolUse</span>(<span style="color:#a6e22e">hookData</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-23">23</a></span><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">toolName</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">hookData</span>.<span style="color:#a6e22e">tool_name</span> <span style="color:#f92672">||</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-24">24</a></span><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">toolInput</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">hookData</span>.<span style="color:#a6e22e">tool_input</span> <span style="color:#f92672">||</span> {};
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-25">25</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-26">26</a></span><span>  <span style="color:#75715e">// 检查 Write/Edit 工具
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-27">27</a></span><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">toolName</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;Write&#39;</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">toolName</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;Edit&#39;</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-28">28</a></span><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">content</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">toolInput</span>.<span style="color:#a6e22e">content</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">toolInput</span>.<span style="color:#a6e22e">new_string</span> <span style="color:#f92672">||</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-29">29</a></span><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">filePath</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">toolInput</span>.<span style="color:#a6e22e">file_path</span> <span style="color:#f92672">||</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-30">30</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-31">31</a></span><span>    <span style="color:#66d9ef">if</span> (<span style="color:#e6db74">/infisical/i</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">content</span>)) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-32">32</a></span><span>      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">scriptExtensions</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/\.(sh|bash|zsh|py|rb|pl|js|ts)$/i</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-33">33</a></span><span>      <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">hasShebang</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">/^#!.*\/(bash|sh|zsh|python|ruby|perl|node)/i</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">content</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-34">34</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-35">35</a></span><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">scriptExtensions</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">filePath</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">hasShebang</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-36">36</a></span><span>        <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-37">37</a></span><span>          <span style="color:#a6e22e">blocked</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-38"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-38">38</a></span><span>          <span style="color:#a6e22e">reason</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`安全阻止: 禁止写入包含 infisical 命令的脚本`</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-39"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-39">39</a></span><span>        };
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-40"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-40">40</a></span><span>      }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-41"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-41">41</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-42"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-42">42</a></span><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">blocked</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span> };
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-43"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-43">43</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-44"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-44">44</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-45"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-45">45</a></span><span>  <span style="color:#75715e">// 检查 Bash 工具
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-46"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-46">46</a></span><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">toolName</span> <span style="color:#f92672">!==</span> <span style="color:#e6db74">&#39;Bash&#39;</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-47"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-47">47</a></span><span>    <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">blocked</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span> };
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-48"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-48">48</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-49"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-49">49</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-50"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-50">50</a></span><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">command</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">toolInput</span>.<span style="color:#a6e22e">command</span> <span style="color:#f92672">||</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-51"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-51">51</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-52"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-52">52</a></span><span>  <span style="color:#75715e">// 阻止 infisical 命令 - 任何形式
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-53"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-53">53</a></span><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">if</span> (<span style="color:#e6db74">/infisical/i</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">command</span>)) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-54"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-54">54</a></span><span>    <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-55"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-55">55</a></span><span>      <span style="color:#a6e22e">blocked</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-56"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-56">56</a></span><span>      <span style="color:#a6e22e">reason</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`安全阻止: 禁止任何涉及 infisical 的命令`</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-57"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-57">57</a></span><span>    };
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-58"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-58">58</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-59"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-59">59</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-60"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-60">60</a></span><span>  <span style="color:#75715e">// 阻止访问敏感目录
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-61"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-61">61</a></span><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">sensitivePatterns</span> <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-62"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-62">62</a></span><span>    <span style="color:#e6db74">/~\/\.secrets/i</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-63"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-63">63</a></span><span>    <span style="color:#e6db74">/~\/\.infisical/i</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-64"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-64">64</a></span><span>    <span style="color:#e6db74">/\$HOME\/\.secrets/i</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-65"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-65">65</a></span><span>    <span style="color:#e6db74">/\$HOME\/\.infisical/i</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-66"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-66">66</a></span><span>  ];
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-67"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-67">67</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-68"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-68">68</a></span><span>  <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">pattern</span> <span style="color:#66d9ef">of</span> <span style="color:#a6e22e">sensitivePatterns</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-69"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-69">69</a></span><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">pattern</span>.<span style="color:#a6e22e">test</span>(<span style="color:#a6e22e">command</span>)) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-70"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-70">70</a></span><span>      <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-71"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-71">71</a></span><span>        <span style="color:#a6e22e">blocked</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-72"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-72">72</a></span><span>        <span style="color:#a6e22e">reason</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`安全阻止: 检测到访问敏感目录的命令`</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-73"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-73">73</a></span><span>      };
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-74"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-74">74</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-75"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-75">75</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-76"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-76">76</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-77"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-77">77</a></span><span>  <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">blocked</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span> };
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-78"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-78">78</a></span><span>}
</span></span></code></pre></div><h3 id="配置-hooks">配置 Hooks</h3>
<p>在 <code>~/.claude/settings.json</code> 中添加：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-2"> 2</a></span><span>  <span style="color:#f92672">&#34;hooks&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-3"> 3</a></span><span>    <span style="color:#f92672">&#34;PreToolUse&#34;</span>: [
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-4"> 4</a></span><span>      {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-5"> 5</a></span><span>        <span style="color:#f92672">&#34;matcher&#34;</span>: <span style="color:#e6db74">&#34;Bash&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-6"> 6</a></span><span>        <span style="color:#f92672">&#34;hooks&#34;</span>: [{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-7"> 7</a></span><span>          <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;command&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-8"> 8</a></span><span>          <span style="color:#f92672">&#34;command&#34;</span>: <span style="color:#e6db74">&#34;node ~/.cc-tool/pre-tool-security-hook.js&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-9"> 9</a></span><span>        }]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-10">10</a></span><span>      },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-11">11</a></span><span>      {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-12">12</a></span><span>        <span style="color:#f92672">&#34;matcher&#34;</span>: <span style="color:#e6db74">&#34;Write&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-13">13</a></span><span>        <span style="color:#f92672">&#34;hooks&#34;</span>: [{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-14">14</a></span><span>          <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;command&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-15">15</a></span><span>          <span style="color:#f92672">&#34;command&#34;</span>: <span style="color:#e6db74">&#34;node ~/.cc-tool/pre-tool-security-hook.js&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-16">16</a></span><span>        }]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-17">17</a></span><span>      },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-18">18</a></span><span>      {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-19">19</a></span><span>        <span style="color:#f92672">&#34;matcher&#34;</span>: <span style="color:#e6db74">&#34;Edit&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-20">20</a></span><span>        <span style="color:#f92672">&#34;hooks&#34;</span>: [{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-21">21</a></span><span>          <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;command&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-22">22</a></span><span>          <span style="color:#f92672">&#34;command&#34;</span>: <span style="color:#e6db74">&#34;node ~/.cc-tool/pre-tool-security-hook.js&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-23">23</a></span><span>        }]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-24">24</a></span><span>      }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-25">25</a></span><span>    ]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-26">26</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-27">27</a></span><span>}
</span></span></code></pre></div><h2 id="验证测试">验证测试</h2>
<p>重启 Claude Code 后测试：</p>
<pre tabindex="0"><code>⏺ Bash(infisical --version)
  ⎿  Error: 安全阻止: 禁止任何涉及 infisical 的命令

⏺ Bash(echo &#34;infisical secrets list&#34; | sh)
  ⎿  Error: 安全阻止: 禁止任何涉及 infisical 的命令

⏺ Bash(python3 -c &#34;import os; os.system(&#39;infisical --version&#39;)&#34;)
  ⎿  Error: 安全阻止: 禁止任何涉及 infisical 的命令

⏺ Read(~/.infisical/credentials.json)
  ⎿  Error reading file
</code></pre><h2 id="使用方式">使用方式</h2>
<p>用户正常使用 Infisical 注入 secrets：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span><span style="color:#75715e"># 执行需要 CF_API_TOKEN 的脚本</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-2">2</a></span><span>infisical run --env<span style="color:#f92672">=</span>prod --projectId<span style="color:#f92672">=</span>xxx -- ./scripts/update-dns.sh
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-4">4</a></span><span><span style="color:#75715e"># 部署到 Cloudflare Workers</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-5">5</a></span><span>infisical run --env<span style="color:#f92672">=</span>prod --projectId<span style="color:#f92672">=</span>xxx -- wrangler deploy
</span></span></code></pre></div><p>AI 可以帮你写脚本、调试逻辑，但无法获取实际的 token 值。</p>
<h2 id="还能继续绕过吗">还能继续绕过吗？</h2>
<p>当然可以。AI 还能尝试生成绕过检测的脚本，写入后执行，还是能获取到。</p>
<p>最终极的方案还是之前想的那种：用一个二进制文件，只有特定用户、特定脚本才能执行，做 setUID 转换后才能运行命令。</p>
<p>但对于日常使用来说，当前方案已经足够了。核心原则是：</p>
<p><strong>任何时候，不要让机密明文出现在 AI 的对话框里。</strong></p>
<h2 id="总结">总结</h2>
<table>
<thead>
<tr>
<th>方案</th>
<th>问题</th>
</tr>
</thead>
<tbody>
<tr>
<td>文件权限</td>
<td>AI 可以用 Bash 读取</td>
</tr>
<tr>
<td>permissions.deny</td>
<td>只阻止 Read 工具</td>
</tr>
<tr>
<td>PreToolUse Hook</td>
<td>AI 可以写脚本绕过</td>
</tr>
<tr>
<td>Bitwarden MCP</td>
<td>直接把密码给 AI，蠢</td>
</tr>
<tr>
<td><strong>Infisical + 多层 Hook</strong></td>
<td>✅ 基本够用</td>
</tr>
</tbody>
</table>
<p>安全从来不是单点防护，而是纵深防御。每一层都可能被绕过，但层层叠加后，攻击成本会指数级上升。</p>
<p>当然，这只是针对 Claude Code 的安全。如果我自己用命令行，当然也能获取到。好处是 Claude Code 这个安全隐患已经被解除了。</p>
<p>就这。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/security" term="security" label="Security" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[作品展示-根据 Prometheus 指标生成 Grafana Dashboard]]></title>
            <link href="https://blog.yunpiao.site/post/20251217140051/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20251217140051/</id>
            
            
            <published>2025-12-17T14:00:51+08:00</published>
            <updated>2025-12-17T14:00:51+08:00</updated>
            
            
            <content type="html"><![CDATA[<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/12/2e64cc88473573bd13ffeeca91d93ca6.png" alt="image.png"  /></p>
<p>最近参加了 Kaggle 的 Gemini 3 竞赛，做了个小工具：把 Prometheus 的 /metrics 输出直接转成可导入的 Grafana Dashboard JSON。</p>
<h3 id="为什么做这个">为什么做这个</h3>
<p>手写 Grafana Dashboard 挺烦的。每次新服务上线，要对着 metrics 列表一个个配 panel、写 PromQL、调布局。重复劳动，没什么技术含量。<br>
想法很简单：把 metrics 丢给 LLM，让它帮我生成。</p>
<h3 id="实现思路">实现思路</h3>
<p>分两步走：<br>
<strong>第一步：生成 Plan</strong><br>
把 metrics 喂给 Gemini，让它分析应该建哪些分类（比如 HTTP、内存、磁盘），每个分类下放哪些 panel，用什么图表类型（timeseries、stat、gauge）。<br>
这一步输出的是一个可编辑的计划，不是最终 JSON。用户可以调整：删掉不需要的 panel、改标题、换图表类型。</p>
<p><strong>第二步：生成 Dashboard JSON</strong><br>
确认 plan 后，并行请求 Gemini 生成每个 panel 的具体配置：PromQL 查询、单位、阈值等。最后组装成完整的 Grafana Dashboard JSON。</p>
<h3 id="踩的坑">踩的坑</h3>
<p>LLM 生成的 PromQL 经常有问题：</p>
<ol>
<li><strong><code>{__name__=~&quot;metric_a|metric_b&quot;}</code></strong> —— 这种写法在 Grafana 里会报 &ldquo;vector cannot contain metrics with the same labelset&rdquo;</li>
<li><strong><code>rate(...) by (label)</code></strong> —— 语法错误，应该是 <code>sum by (label) (rate(...))</code></li>
</ol>
<p>加了一层后处理，自动修复这些常见错误。</p>
<p>LLM 生成的 json 是有问题的, 也加了自动修复</p>
<blockquote>
<p>想想还是使用 Using tools 比较方便, 直接 DSL, 之后根据这个 DSL 去生成 json, 必将让 LLM 生成 json 还是不是靠谱, 上下文一长, LLM 就智障了</p>
</blockquote>
<h3 id="功能点">功能点</h3>
<h4 id="1-智能解析">1. 智能解析</h4>
<ul>
<li>自动识别 metric 类型（counter, gauge, histogram）</li>
<li>提取所有标签</li>
<li>保留帮助文档</li>
</ul>
<h4 id="2-ai-分析">2. AI 分析</h4>
<ul>
<li>识别监控模式（RED, USE, Golden Signals）</li>
<li>规划仪表盘结构</li>
<li>建议可视化类型</li>
</ul>
<h4 id="3-精确生成">3. 精确生成</h4>
<ul>
<li><strong>Counter</strong> → <code>rate()</code> 查询</li>
<li><strong>Gauge</strong> → 直接查询</li>
<li><strong>Histogram</strong> → <code>histogram_quantile()</code> 计算百分位数</li>
<li>自动配置单位、图例、颜色</li>
</ul>
<h4 id="4-完整输出">4. 完整输出</h4>
<ul>
<li>标准 Grafana Dashboard JSON</li>
<li>自动布局</li>
<li>即导即用</li>
</ul>
<h3 id="demo">Demo</h3>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/12/2896686d10021c18e3ec0ed1dd6a1d37.png" alt="image.png"  /></p>
<ul>
<li><strong>在线体验</strong>：<a href="https://aistudio.google.com/apps/drive/1nfv58w41dTsheBGzBJ7JhrojZxoPgn-B" target="_blank" rel="noopener nofollow noreferrer" >https://aistudio.google.com/apps/drive/1nfv58w41dTsheBGzBJ7JhrojZxoPgn-B</a></li>
<li><strong>YouTube</strong>：<a href="https://youtu.be/p6b3z5gSCtY" target="_blank" rel="noopener nofollow noreferrer" >https://youtu.be/p6b3z5gSCtY</a></li>
<li><strong>源码</strong>：<a href="https://github.com/yunpiao/Grafana-Dashboard-Generator-Gemini" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/yunpiao/Grafana-Dashboard-Generator-Gemini</a></li>
</ul>
<hr>
<h3 id="顺便做了个工具">顺便做了个工具</h3>
<p>参加比赛的时候想看看别人都做了什么，发现 Kaggle 上直接翻挺费劲的，4000 多条 writeup。<br>
就爬下来整理了一下，做了个浏览器：<a href="https://kaggle-gemini3-writeups-explorer.streamlit.app/" target="_blank" rel="noopener nofollow noreferrer" >https://kaggle-gemini3-writeups-explorer.streamlit.app/</a><br>
按分类筛选、搜索、随机抽取都行。如果你也在找 AI 应用的灵感，可以翻翻</p>
<p>本次所有参赛的词云<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/12/b50c9bbae6651cf9a8b3646fa0ea82f5.png" alt="image.png"  /></p>
<hr>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B" term="%E7%BC%96%E7%A8%8B" label="编程" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E4%B8%AA%E4%BA%BA%E4%BD%9C%E5%93%81" term="%E4%B8%AA%E4%BA%BA%E4%BD%9C%E5%93%81" label="个人作品" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[作品展示-Gloss用于学习英语的小插件]]></title>
            <link href="https://blog.yunpiao.site/post/20251202115840/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20251202115840/</id>
            
            
            <published>2025-12-02T11:58:40+08:00</published>
            <updated>2025-12-02T11:58:40+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>这篇文章想简单记录一下我最近写的一个小工具：Gloss。它是一个跑在浏览器里的油猴脚本，用来给网页上的英文（或者中文）加上轻量的词汇注解，主要是为了解决我自己在看英文博客、文档时的一个小痛点。</p>
<h2 id="为什么要做这个小插件">为什么要做这个小插件？</h2>
<p>我平时看英文文章的频率挺高，但有几个很具体的烦恼：</p>
<ul>
<li>生词零零散散，要么查完就忘，要么记在各种地方，很难形成“自己的词汇本”。</li>
<li>有时候只是不太熟悉某个词，查词典又有点打断节奏，容易从网页跳到别的地方分心。</li>
<li>市面上有一些浏览器扩展可以划词翻译，但更多是“查一下就完了”，不太关注长期积累。</li>
</ul>
<p>所以我就干脆自己写了个脚本：</p>
<ul>
<li>不做“翻译整篇文章”这种重事儿。</li>
<li>专注把<strong>关键词</strong>标出来，给一个简短的释义。</li>
<li>顺手把这些词丢进一个<strong>全局词汇本</strong>，以后可以慢慢消化。</li>
</ul>
<p>Gloss 本质上是一个“给真实网页加一层学习信息”的小插件，而不是一个完整的背单词应用。</p>
<h2 id="它大概长什么样">它大概长什么样？</h2>
<p>安装脚本之后，在任意网页右下角会出现一个小控制条：</p>
<ul>
<li>显示当前状态和本次标注出的词汇数量。</li>
<li>一个「开始」按钮，用来对当前页面进行分析和标注。</li>
<li>一个下拉菜单，可以「重新分析」。</li>
<li>一个小齿轮按钮，打开设置面板。</li>
</ul>
<p>页面上如果有标注的词，会以 <code>&lt;ruby&gt;</code> 的形式出现：原文在下方，上面一行小字是译文。这样不会打乱排版，也不会像整段翻译那样破坏阅读体验。</p>
<h2 id="具体能做什么">具体能做什么？</h2>
<p>目前 Gloss 主要做了几件事：</p>
<ul>
<li><strong>智能选词</strong>：
<ul>
<li>对中文页面：挑出适合学习英文表达的中文词/短语，给出英文释义。</li>
<li>对英文页面：挑出偏难或重要的英文词，给出简短中文释义。</li>
</ul>
</li>
<li><strong>最小词汇数限制</strong>：
<ul>
<li>用分词统计页面的词数，如果太短，就不会浪费请求（也避免给一堆碎碎念加注）。</li>
</ul>
</li>
<li><strong>缓存</strong>：
<ul>
<li>同一个页面在 7 天内只会请求一次接口，下次打开直接用缓存结果，速度更快，也省钱。</li>
</ul>
</li>
<li><strong>黑名单</strong>：
<ul>
<li>可以把不想标注的网站加到黑名单里，或用通配符屏蔽一类站点。</li>
</ul>
</li>
<li><strong>全局词汇本</strong>：
<ul>
<li>所有被标注过的词，都会自动进入一个词汇本。</li>
<li>可以看到原文、翻译、来源网站、添加时间。</li>
<li>支持搜索、筛选“学习中”和“已掌握”。</li>
</ul>
</li>
<li><strong>已掌握标记</strong>：
<ul>
<li>在词汇本里点一下「✓ 掌握」，之后这个词就不会再出现在网页标注里。</li>
<li>重新分析同一篇文章时，也会尽量跳过这些词，节省 token。</li>
</ul>
</li>
<li><strong>导入 / 导出</strong>：
<ul>
<li>词汇本可以一键导出为 CSV 文件，直接丢给 Excel 或别的工具继续加工。</li>
<li>也支持从 CSV 导入，方便把以前的生词表迁移进来。</li>
</ul>
</li>
</ul>
<p>整体思路是：</p>
<blockquote>
<p>不强迫你在一个新界面背单词，而是尽量不打扰你阅读，只是顺手帮你把重要的词“记下来”，时间久了会变成你自己的词库。</p>
</blockquote>
<h2 id="怎么用">怎么用？</h2>
<p>使用方式非常简单：</p>
<ol>
<li>安装 Tampermonkey（或兼容的脚本管理扩展）。</li>
<li>在 GreasyFork 安装 Gloss 的脚本：<a href="https://greasyfork.org/zh-CN/scripts/557609-gloss-%E6%99%BA%E8%83%BD%E8%AF%8D%E6%B1%87%E6%A0%87%E6%B3%A8" target="_blank" rel="noopener nofollow noreferrer" >https://greasyfork.org/zh-CN/scripts/557609-gloss-%E6%99%BA%E8%83%BD%E8%AF%8D%E6%B1%87%E6%A0%87%E6%B3%A8</a>。<br>
源码仓库：<code>https://github.com/yunpiao/gloss</code></li>
<li>打开任意网页，右下角会出现「Gloss」小控制条。</li>
<li>第一次使用：
<ul>
<li>点击齿轮按钮，填上自己的模型 API 地址、Key 和模型名称。</li>
<li>根据习惯调整“提取词汇数量”和“最少词汇数”等配置。</li>
</ul>
</li>
<li>回到网页，点击「开始」，等一会儿就会看到文中出现小小的 ruby 标注。</li>
</ol>
<p>如果你习惯看技术博客、文档，可以只针对英文页面开启自动标注；如果是反向学习英文表达（比如看中文博客，顺手学英文对应说法），也可以只给中文页面开。</p>
<h2 id="适合谁用">适合谁用？</h2>
<p>这个小工具的设计初衷是给「已经有一点基础、想通过阅读真实内容继续提升」的人用的：</p>
<ul>
<li>能直接读英文，但常常会有一些“似懂非懂”的词。</li>
<li>不喜欢在单词 App 里做题，更喜欢在真实语境里逐渐熟悉用法。</li>
<li>接受偶尔有点“AI 的胡说八道”，但整体容忍度还行的人。</li>
</ul>
<p>如果你现在更需要的是系统性词汇课程、严格的记忆计划，那 Gloss 可能不太适合，它更偏向一个「增强现实层」——你本来就在读的东西上面，轻轻加一层信息。</p>
<h2 id="一点截图">一点截图</h2>
<h3 id="中文界面">中文界面</h3>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/12/25c7739ef19112041db546b72ee3ca3b.png" alt="image.png"  /></p>
<h3 id="英文界面">英文界面</h3>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/12/97bbb0e1fa2dfb19ef85b8f03bc4c5c6.png" alt="image.png"  /></p>
<h3 id="单词本界面">单词本界面</h3>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/12/41b7d4a501cae0e846bbfd3cc13ee9df.png" alt="image.png"  /></p>
<h2 id="一点小结">一点小结</h2>
<p>Gloss 首先是为了解决我自己的学习痛点写出来的：</p>
<ul>
<li>尽量不打扰正常浏览。</li>
<li>把“查一个词”这件事，从一次性操作变成长期积累。</li>
<li>允许你承认“这个词我已经掌握了”，之后就别再打扰你。</li>
</ul>
<p>如果你平时也有类似的需求，欢迎试试，也欢迎在 GitHub 上提 issue 或建议。这个脚本还在迭代中，我也在用它磨合自己的阅读和学习习惯。也许过一段时间回头看，还会有不少可以改进的地方。</p>
<p>仓库地址：<a href="https://github.com/yunpiao/gloss" target="_blank" rel="noopener nofollow noreferrer" ><code>https://github.com/yunpiao/gloss</code></a></p>
<p>如果你真的用上了，也可以跟我说一声它在哪些网站上帮到了你，这对我来说会是一个挺大的鼓励。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/project-retrospective" term="project-retrospective" label="Project Retrospective" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[网络知识-epoll 与零拷贝相关系统调用学习笔记]]></title>
            <link href="https://blog.yunpiao.site/post/20251113174006/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20251113174006/</id>
            
            
            <published>2025-11-13T17:40:06+08:00</published>
            <updated>2025-11-13T17:40:06+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>之前没有亲手实践过 eoll, 最近开发一个小的 c 语言的网络工具, 从阻塞到事件驱动一步一步优化 , 最后性能挺不错的. 😂 实在想不出来没有 epoll 的世界</p>
</blockquote>
<p>本文是对 Linux 下高性能网络编程核心技术点的梳理，重点围绕 <code>epoll</code> I/O 多路复用机制以及 <code>splice</code>、<code>sendfile</code> 等零拷贝（Zero-Copy）系统调用进行归纳。内容涵盖了从基本概念、API 用法、实战技巧到性能对比的各个方面，旨在为构建高性能代理（Proxy）、文件服务器等应用提供一份精炼的参考。</p>
<hr>
<h3 id="1-epoll-核心三剑客">1. Epoll 核心三剑客</h3>
<p><code>epoll</code> 是 <code>select/poll</code> 的高效替代方案，它通过内核中的红黑树和就绪链表来管理海量文件描述符，避免了每次调用时从用户态到内核态的重复数据拷贝和线性扫描。</p>
<ul>
<li>
<p><code>int epoll_create1(int flags)</code><br>
用于创建一个 <code>epoll</code> 实例。推荐使用 <code>EPOLL_CLOEXEC</code> 标志，确保在 <code>fork</code> 调用后，子进程能自动关闭继承的 <code>epoll</code> 文件描述符，避免资源泄漏。</p>
</li>
<li>
<p><code>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)</code><br>
用于在 <code>epoll</code> 实例上注册、修改或删除监听的事件。</p>
<ul>
<li><code>op</code>: 操作类型，包括 <code>EPOLL_CTL_ADD</code> (添加)、<code>EPOLL_CTL_MOD</code> (修改)、<code>EPOLL_CTL_DEL</code> (删除)。</li>
<li><code>events</code>: 事件类型掩码，常用组合如下：
<ul>
<li><code>EPOLLIN</code>: 读事件就绪（例如，socket 接收到数据）。</li>
<li><code>EPOLLOUT</code>: 写事件就绪（例如，socket 发送缓冲区有空间）。</li>
<li><code>EPOLLERR</code>: 发生错误。</li>
<li><code>EPOLLHUP</code>: 对端关闭连接。</li>
<li><code>EPOLLET</code>: <strong>边沿触发 (Edge-Triggered)</strong>。相比默认的水平触发 (Level-Triggered)，它只在状态从未就绪变为就绪时通知一次，能有效减少 wakeup 次数，但要求 I/O 操作必须是非阻塞的，并且需要循环读写直到返回 <code>EAGAIN</code>。</li>
<li><code>EPOLLEXCLUSIVE</code>: (Linux 4.5+) 用于一对多监听场景（如多个 worker 监听同一个 <code>listen_fd</code>），可避免“惊群”效应，确保只有一个线程被唤醒。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><code>int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout)</code><br>
等待已注册的事件发生。</p>
<ul>
<li><code>timeout</code>: <code>-1</code> 表示永久阻塞；<code>0</code> 表示立即返回，不阻塞；<code>&gt;0</code> 表示最长等待的毫秒数。</li>
<li>返回值是就绪的文件描述符数量，就绪事件被填充到 <code>evlist</code> 数组中。</li>
</ul>
</li>
</ul>
<p><strong>典型事件循环伪码：</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span><span style="color:#66d9ef">int</span> epfd <span style="color:#f92672">=</span> <span style="color:#a6e22e">epoll_create1</span>(EPOLL_CLOEXEC);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span><span style="color:#75715e">// 将 listen_fd 添加到 epoll 实例
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span><span style="color:#75715e"></span><span style="color:#a6e22e">epoll_ctl</span>(epfd, EPOLL_CTL_ADD, listen_fd, <span style="color:#f92672">&amp;</span>ev);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span><span style="color:#75715e">// 主循环
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span><span style="color:#75715e"></span><span style="color:#66d9ef">while</span> (<span style="color:#ae81ff">1</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>    <span style="color:#66d9ef">int</span> n <span style="color:#f92672">=</span> <span style="color:#a6e22e">epoll_wait</span>(epfd, events, MAX_EVENTS, <span style="color:#ae81ff">1000</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672">&lt;</span> n; i<span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>        <span style="color:#66d9ef">if</span> (events[i].events <span style="color:#f92672">&amp;</span> EPOLLIN) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>            <span style="color:#a6e22e">handle_read</span>(events[i].data.fd);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>        }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>        <span style="color:#66d9ef">if</span> (events[i].events <span style="color:#f92672">&amp;</span> EPOLLOUT) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>            <span style="color:#a6e22e">handle_write</span>(events[i].data.fd);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>        }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>}
</span></span></code></pre></div><h3 id="2-非阻塞-io-与-fcntl">2. 非阻塞 I/O 与 fcntl</h3>
<p><code>fcntl</code> 是一个强大的文件描述符控制工具，在高性能编程中，它主要用于设置非阻塞标志和调整管道缓冲区。</p>
<ul>
<li>
<p><strong>设置非阻塞标志 (O_NONBLOCK)</strong><br>
<code>int flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK);</code><br>
将文件描述符设置为非阻塞模式是配合 <code>epoll</code> 边沿触发（<code>EPOLLET</code>）的<strong>必要条件</strong>。</p>
</li>
<li>
<p><strong>非阻塞适用性补充</strong><br>
需要明确的是，Linux 下真正的“非阻塞”特性<strong>仅对可等待 (pollable) 的文件描述符生效</strong>。</p>
<ul>
<li><strong>常见可设置非阻塞的对象</strong>：
<ul>
<li><code>socket</code>: 网络编程的核心。</li>
<li><code>pipe</code>/<code>fifo</code>: 用于进程间通信或作为零拷贝的内核缓冲区。<code>pipe2()</code> 可在创建时直接传入 <code>O_NONBLOCK</code>。</li>
<li><code>eventfd</code> / <code>timerfd</code> / <code>signalfd</code>: 与 <code>epoll</code> 结合，用于实现事件通知、定时任务和信号处理。</li>
<li>部分字符设备，如 <code>/dev/tty*</code>。</li>
<li><code>inotify fd</code>: 用于文件系统事件通知。</li>
</ul>
</li>
<li><strong><code>O_NONBLOCK</code> 被忽略的对象</strong>：
<ul>
<li>普通磁盘文件。由于磁盘 I/O 通常被认为是始终就绪的，设置非阻塞标志会被内核忽略，因此不存在“非文件描述符的非阻塞”概念。</li>
<li>匿名内存映射、多数 <code>/proc</code> 文件系统条目。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>调整管道缓冲区大小</strong><br>
<code>fcntl(fd, F_SETPIPE_SZ, size_in_bytes);</code> (Linux 2.6.35+)<br>
在进行基于 <code>splice</code> 的大流量零拷贝转发时，增大内核管道缓冲区（默认通常为 64KB）可以显著提升吞吐量。</p>
</li>
</ul>
<h3 id="3-管道-pipepipe2">3. 管道 (pipe/pipe2)</h3>
<p>管道是零拷贝家族中不可或缺的“中间人”，它在内核中提供了一块缓冲区。</p>
<ul>
<li><code>int pipe(int fd[2])</code>: 创建一个匿名全双工管道，<code>fd[0]</code> 为读端，<code>fd[1]</code> 为写端。</li>
<li><code>int pipe2(int fd[2], int flags)</code>: (Linux 2.6.27+) <code>pipe</code> 的增强版，允许在创建时直接传入 <code>O_NONBLOCK</code> 和 <code>O_CLOEXEC</code> 标志，避免了额外的 <code>fcntl</code> 调用。</li>
</ul>
<p><strong>主要用途</strong>：</p>
<ol>
<li>传统的进程间通信（IPC）。</li>
<li>作为 <code>splice</code>/<code>tee</code>/<code>vmsplice</code> 的内核缓冲区，实现数据在不同文件描述符之间的零拷贝转发。</li>
<li><code>eventfd</code>/<code>timerfd</code> 在设计上可以看作是特定场景下对 <code>pipe</code> 的功能性替代和优化。</li>
</ol>
<h3 id="4-零拷贝家族系统调用">4. 零拷贝家族系统调用</h3>
<p>零拷贝技术通过减少 CPU 在用户态和内核态之间的数据拷贝次数，来降低 CPU 负载和内存带宽占用，从而提升数据传输效率。</p>
<table>
<thead>
<tr>
<th style="text-align:left">调用</th>
<th style="text-align:left">数据流向</th>
<th style="text-align:left">典型用途</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>splice</strong></td>
<td style="text-align:left"><code>fd</code> ↔ <code>pipe</code></td>
<td style="text-align:left">在两个文件描述符之间移动数据，核心是借助管道。</td>
</tr>
<tr>
<td style="text-align:left"><strong>tee</strong></td>
<td style="text-align:left"><code>pipe</code> → <code>pipe</code></td>
<td style="text-align:left">从一个管道中“拷贝”数据到另一个管道，原数据不消耗。</td>
</tr>
<tr>
<td style="text-align:left"><strong>vmsplice</strong></td>
<td style="text-align:left"><code>user buf</code> ↔ <code>pipe</code></td>
<td style="text-align:left">将用户空间缓冲区“映射”到管道，实现一次拷贝。</td>
</tr>
<tr>
<td style="text-align:left"><strong>sendfile</strong></td>
<td style="text-align:left"><code>file</code> → <code>socket</code></td>
<td style="text-align:left">将文件内容直接发送到套接字，HTTP 静态服务器经典优化。</td>
</tr>
</tbody>
</table>
<h4 id="41-splice">4.1 splice</h4>
<p><code>ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);</code></p>
<p><code>splice</code> 是实现通用零拷贝转发的核心。它要求 <code>fd_in</code> 或 <code>fd_out</code> 中至少有一个必须是管道。</p>
<ul>
<li>
<p><strong>flags</strong>:</p>
<ul>
<li><code>SPLICE_F_MOVE</code>: 尝试移动内存页而不是复制，是性能优化的关键。</li>
<li><code>SPLICE_F_NONBLOCK</code>: 非阻塞执行。</li>
<li><code>SPLICE_F_MORE</code>: 向内核暗示后续还有数据（类似 <code>TCP_CORK</code>），有助于数据包合并。</li>
</ul>
</li>
<li>
<p><strong>经典转发模式</strong>:<br>
<code>socket_A</code> -&gt; <code>pipe</code> -&gt; <code>socket_B</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#75715e">// 从 socket_A 读数据到管道写端
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span><span style="color:#75715e"></span><span style="color:#a6e22e">splice</span>(socket_A, NULL, pipe_w, NULL, len, flags);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span><span style="color:#75715e">// 从管道读端写数据到 socket_B
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span><span style="color:#75715e"></span><span style="color:#a6e22e">splice</span>(pipe_r, NULL, socket_B, NULL, len, flags);
</span></span></code></pre></div></li>
</ul>
<h4 id="42-tee">4.2 tee</h4>
<p><code>tee</code> 用于将管道中的数据复制一份到另一个管道，同时不影响原始数据流，适用于需要数据分发的场景，如一份数据既要发送给客户端，又要存盘记录。</p>
<h4 id="43-vmsplice">4.3 vmsplice</h4>
<p><code>vmsplice</code> 允许将用户态内存区域（通过 <code>iovec</code> 结构描述）的数据零拷贝地写入管道，或者从管道中读出，在特定场景下可替代传统的 <code>writev</code>。</p>
<h4 id="44-sendfile">4.4 sendfile</h4>
<p><code>ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);</code></p>
<p><code>sendfile</code> 是一个高度优化的接口，专门用于将文件内容 (<code>in_fd</code>) 直接传输到套接字 (<code>out_fd</code>)，数据完全在内核态流动。它是 Nginx 等 Web 服务器发送静态文件的首选方案。</p>
<h3 id="5-事件等待机制比较">5. 事件等待机制比较</h3>
<table>
<thead>
<tr>
<th style="text-align:left">接口</th>
<th style="text-align:left">可扩展性 (FD 数量)</th>
<th style="text-align:left">编程复杂度</th>
<th style="text-align:left">内核实现机制</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>select</strong></td>
<td style="text-align:left">有限 (通常 1024)</td>
<td style="text-align:left">简单</td>
<td style="text-align:left">每次调用需复制 FD 集合，线性扫描</td>
</tr>
<tr>
<td style="text-align:left"><strong>poll</strong></td>
<td style="text-align:left">无上限</td>
<td style="text-align:left">中等</td>
<td style="text-align:left">每次调用需复制 FD 集合，线性扫描</td>
</tr>
<tr>
<td style="text-align:left"><strong>epoll</strong></td>
<td style="text-align:left">无上限</td>
<td style="text-align:left">相对复杂</td>
<td style="text-align:left">内核维护红黑树，返回就绪链表</td>
</tr>
</tbody>
</table>
<p><strong>epoll 的核心优势</strong>在于，它避免了每次 <code>epoll_wait</code> 调用时都需要将整个文件描述符列表从用户态复制到内核态的开销。并且，内核直接返回就绪的 FD 列表，获取就绪事件的时间复杂度是 <strong>O(1)</strong>，与监听的总 FD 数量无关。</p>
<h3 id="6-调试与常见-errno">6. 调试与常见 errno</h3>
<ul>
<li><code>EAGAIN</code> / <code>EWOULDBLOCK</code>: 在非阻塞模式下，表示“资源暂时不可用”。读操作意味着无数据可读，写操作意味着缓冲区已满。<strong>处理方式</strong>：这是正常情况，应停止本轮读写，等待下一次 <code>EPOLLIN</code> / <code>EPOLLOUT</code> 事件通知。</li>
<li><code>ECONNRESET</code> / <code>EPIPE</code>: 对端重置或关闭了连接。</li>
<li><code>EINVAL</code>: <code>epoll_ctl</code> 参数错误，或尝试向 <code>epoll</code> 实例重复添加同一个 <code>fd</code>。</li>
</ul>
<blockquote>
<p><strong>调试建议</strong>：在日志中打印关键信息，如 <code>fd</code>、事件掩码 (<code>events</code>)、读写字节数统计以及 <code>strerror(errno)</code> 返回的错误字符串，有助于快速定位问题。</p>
</blockquote>
<h3 id="7-性能与最佳实践">7. 性能与最佳实践</h3>
<ol>
<li><strong>边沿触发 + 非阻塞</strong>：使用 <code>EPOLLET</code> 模式，并确保 <code>read/write</code> 循环执行，直到返回 <code>EAGAIN</code>，以完全耗尽内核缓冲区中的数据。</li>
<li><strong>合并写操作</strong>：使用 <code>SPLICE_F_MORE</code> 或 <code>TCP_CORK</code> 选项，鼓励内核将小的写操作合并成一个大的 TCP 包再发送，减少网络分片。</li>
<li><strong>批量处理事件</strong>：<code>epoll_wait</code> 的 <code>maxevents</code> 参数可设为 64 到 512 之间，并在单次 <code>epoll_wait</code> 返回后，循环处理所有就绪事件。</li>
<li><strong>合理设置 Pipe 大小</strong>：对于高吞吐量的零拷贝应用，可使用 <code>fcntl(fd, F_SETPIPE_SZ, ...)</code> 将管道缓冲区调大（如 1MB），但需注意这会增加内核内存占用。</li>
<li><strong>监控关键指标</strong>：监控应用的连接数、零拷贝传输字节数、回退到常规 <code>read/write</code> 的次数、<code>epoll_wait</code> 的调用频率等，以评估系统性能。</li>
</ol>
<h3 id="8-典型零拷贝转发伪码">8. 典型零拷贝转发伪码</h3>
<p>以下是使用 <code>splice</code> 实现从源 <code>src</code> 到目标 <code>dst</code> 的数据转发循环：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1"> 1</a></span><span><span style="color:#66d9ef">size_t</span> total_forwarded <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2"> 2</a></span><span><span style="color:#66d9ef">while</span> (<span style="color:#ae81ff">1</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3"> 3</a></span><span>    <span style="color:#75715e">// 1. 从源 splice 数据到管道
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4"> 4</a></span><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">ssize_t</span> n_read <span style="color:#f92672">=</span> <span style="color:#a6e22e">splice</span>(src, NULL, pipe_w, NULL, PIPE_BUFFER_SIZE,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5"> 5</a></span><span>                            SPLICE_F_NONBLOCK <span style="color:#f92672">|</span> SPLICE_F_MOVE);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6"> 6</a></span><span>    <span style="color:#66d9ef">if</span> (n_read <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-7"> 7</a></span><span>        <span style="color:#75715e">// 读取出错或结束
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-8"> 8</a></span><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-9"> 9</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-10">10</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-11">11</a></span><span>    <span style="color:#66d9ef">ssize_t</span> remaining <span style="color:#f92672">=</span> n_read;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-12">12</a></span><span>    <span style="color:#66d9ef">while</span> (remaining <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-13">13</a></span><span>        <span style="color:#75715e">// 2. 从管道 splice 数据到目标
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-14">14</a></span><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">ssize_t</span> n_written <span style="color:#f92672">=</span> <span style="color:#a6e22e">splice</span>(pipe_r, NULL, dst, NULL, remaining,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-15">15</a></span><span>                                   SPLICE_F_NONBLOCK <span style="color:#f92672">|</span> SPLICE_F_MOVE);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-16">16</a></span><span>        <span style="color:#66d9ef">if</span> (n_written <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-17">17</a></span><span>            <span style="color:#75715e">// 写入出错或阻塞
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-18">18</a></span><span><span style="color:#75715e"></span>            <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-19">19</a></span><span>        }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-20">20</a></span><span>        remaining <span style="color:#f92672">-=</span> n_written;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-21">21</a></span><span>        total_forwarded <span style="color:#f92672">+=</span> n_written;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-22">22</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-23">23</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-24">24</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-25">25</a></span><span><span style="color:#75715e">// 可以在 splice 失败或返回 0 时，实现回退到常规 recv/send 的方案。
</span></span></span></code></pre></div><h3 id="参考资料">参考资料</h3>
<ul>
<li><code>man 2 epoll_create1</code>, <code>epoll_ctl</code>, <code>epoll_wait</code>, <code>fcntl</code>, <code>splice</code>, <code>tee</code>, <code>vmsplice</code>, <code>sendfile</code></li>
<li>LWN: 《Introducing splice(2)》</li>
<li>IBM developerWorks: Zero-Copy Transfer in Linux</li>
<li>《Linux 高性能服务器编程》 - 游双 &amp; 陈硕</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[杂-为 cloudflared 增加中间代理, 优化透传服务]]></title>
            <link href="https://blog.yunpiao.site/post/20251027183710/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20251027183710/</id>
            
            
            <published>2025-10-27T18:37:10+08:00</published>
            <updated>2025-10-27T18:37:10+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-背景">1. 背景</h2>
<p>作为一个需要频繁使用内网穿透服务的开发者，我一直在使用 Cloudflare Tunnel (cloudflared) 来暴露本地服务到公网。这个工具本身非常优秀，但在国内使用时有一个让人抓狂的痛点：</p>
<p><strong>经常断联，连接不稳定，严重影响使用体验。</strong></p>
<p>具体表现：</p>
<ul>
<li>🔴 隧道连接时断时续，频繁掉线</li>
<li>🔴 重连速度慢，影响服务可用性</li>
<li>🔴 某些地区或网络环境下根本无法建立连接</li>
<li>🔴 没有任何办法绕过网络限制</li>
</ul>
<p>这种情况下，即使服务本身完全正常，用户体验也会非常糟糕。作为一个有洁癖的程序员，这种&quot;时好时坏&quot;的状态是绝对无法接受的。</p>
<h2 id="2-问题分析">2. 问题分析</h2>
<h3 id="为什么会断联">为什么会断联？</h3>
<p>Cloudflared 直连 Cloudflare 边缘节点的时候，流量路径大致是：</p>
<pre tabindex="0"><code>本地 cloudflared → 国内运营商网络 → 国际出口 → Cloudflare Edge
</code></pre><p>问题出在中间环节：</p>
<ol>
<li><strong>国际出口不稳定</strong> - 众所周知的原因</li>
<li><strong>UDP 流量容易被限制</strong> - QUIC 协议使用 UDP，某些网络会限制或丢弃 UDP 包</li>
<li><strong>连接被主动断开</strong> - 某些情况下长连接会被中断</li>
<li><strong>DNS 污染</strong> - 边缘节点地址解析可能出问题</li>
</ol>
<h3 id="理想的解决方案">理想的解决方案</h3>
<p>如果能让 cloudflared 通过一个<strong>稳定的代理服务器</strong>出去，问题就迎刃而解了：</p>
<pre tabindex="0"><code>本地 cloudflared → SOCKS5 代理 → 稳定线路 → Cloudflare Edge
</code></pre><p>优势：</p>
<ul>
<li>✅ 使用优质线路，连接稳定</li>
<li>✅ 可以绕过网络限制</li>
<li>✅ 代理失败时自动降级，不影响可用性</li>
<li>✅ 灵活配置，按需使用</li>
</ul>
<p><strong>但问题是：原版 cloudflared 不支持代理！</strong></p>
<h2 id="3-解决方案为-cloudflared-添加-socks5-代理支持">3. 解决方案：为 Cloudflared 添加 SOCKS5 代理支持</h2>
<p>既然官方不支持，那就自己动手，丰衣足食。我 fork 了 cloudflared 项目，添加了完整的 SOCKS5 代理支持。</p>
<h3 id="核心功能">核心功能</h3>
<h4 id="31-标准-socks5-协议支持">3.1 标准 SOCKS5 协议支持</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1"> 1</a></span><span><span style="color:#75715e"># 基础用法 - 通过本地代理</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2"> 2</a></span><span>cloudflared tunnel --edge-proxy-url socks5://127.0.0.1:1080 run mytunnel
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3"> 3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4"> 4</a></span><span><span style="color:#75715e"># 带认证的代理</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5"> 5</a></span><span>cloudflared tunnel --edge-proxy-url socks5://user:pass@proxy.example.com:1080 run mytunnel
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6"> 6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-7"> 7</a></span><span><span style="color:#75715e"># 使用配置文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-8"> 8</a></span><span>cat &gt; config.yml <span style="color:#e6db74">&lt;&lt;EOF
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-9"> 9</a></span><span><span style="color:#e6db74">tunnel: mytunnel
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-10">10</a></span><span><span style="color:#e6db74">credentials-file: /path/to/credentials.json
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-11">11</a></span><span><span style="color:#e6db74">edge-proxy-url: socks5://127.0.0.1:1080
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-12">12</a></span><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-13">13</a></span><span><span style="color:#e6db74">ingress:
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-14">14</a></span><span><span style="color:#e6db74">  - hostname: example.com
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-15">15</a></span><span><span style="color:#e6db74">    service: http://localhost:8080
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-16">16</a></span><span><span style="color:#e6db74">  - service: http_status:404
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-17">17</a></span><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-18">18</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-19">19</a></span><span>cloudflared tunnel run mytunnel
</span></span></code></pre></div><h4 id="32-智能降级机制">3.2 智能降级机制</h4>
<pre tabindex="0"><code>尝试代理连接
    ├─ 成功 → 使用代理连接（稳定线路）✅
    └─ 失败 → 自动降级到直连 ✅
</code></pre><p>这意味着：</p>
<ul>
<li>代理可用时，走稳定线路</li>
<li>代理挂了，自动切换直连，服务不中断</li>
<li>完全不用担心代理故障</li>
</ul>
<h4 id="33-灵活的配置方式">3.3 灵活的配置方式</h4>
<p>支持三种配置方式，随你喜欢：</p>
<p><strong>方式 1: 命令行参数</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span>cloudflared tunnel --edge-proxy-url socks5://proxy:1080 run mytunnel
</span></span></code></pre></div><p><strong>方式 2: 环境变量</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span>export TUNNEL_EDGE_PROXY_URL<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;socks5://proxy:1080&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span>cloudflared tunnel run mytunnel
</span></span></code></pre></div><p><strong>方式 3: 配置文件</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span><span style="color:#f92672">edge-proxy-url</span>: <span style="color:#ae81ff">socks5://proxy:1080</span>
</span></span></code></pre></div><h3 id="坑-1-quic-协议问题">坑 1: QUIC 协议问题</h3>
<p>QUIC 使用 UDP，标准 SOCKS5 主要是为 TCP 设计的。</p>
<p><strong>解决：</strong></p>
<ul>
<li>可以强制使用 HTTP/2: <code>--protocol http2</code></li>
<li>或者等待后续支持 UDP 转发的代理协议</li>
</ul>
<h3 id="坑-2-代理认证信息安全">坑 2: 代理认证信息安全</h3>
<p>命令行中的密码会出现在进程列表中：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1">1</a></span><span><span style="color:#75715e"># ❌ 不安全</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-2">2</a></span><span>cloudflared tunnel --edge-proxy-url socks5://user:password@proxy:1080 run mytunnel
</span></span></code></pre></div><p><strong>解决：</strong> 使用配置文件并设置权限：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span>cat &gt; config.yml <span style="color:#e6db74">&lt;&lt;EOF
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-2">2</a></span><span><span style="color:#e6db74">edge-proxy-url: socks5://user:password@proxy:1080
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-3">3</a></span><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-4">4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-5">5</a></span><span>chmod <span style="color:#ae81ff">600</span> config.yml
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-6">6</a></span><span>cloudflared tunnel run mytunnel
</span></span></code></pre></div><p><strong>完整文档：</strong></p>
<ul>
<li><a href="https://github.com/yunpiao/cloudflared/blob/master/SOCKS5_PROXY_GUIDE.md" target="_blank" rel="noopener nofollow noreferrer" >SOCKS5_PROXY_GUIDE.md</a> - 完整使用指南</li>
<li><a href="https://github.com/yunpiao/cloudflared/blob/master/TEST_PROXY.md" target="_blank" rel="noopener nofollow noreferrer" >TEST_PROXY.md</a> - 功能测试报告</li>
</ul>
<h2 id="4-编译安装">4. 编译安装</h2>
<h3 id="从源码编译">从源码编译</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-1">1</a></span><span><span style="color:#75715e"># 克隆仓库</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-2">2</a></span><span>git clone https://github.com/yunpiao/cloudflared.git
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-3">3</a></span><span>cd cloudflared
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-4">4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-5">5</a></span><span><span style="color:#75715e"># 编译（需要 go &gt;= 1.24）</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-6">6</a></span><span>make cloudflared
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-7">7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-8">8</a></span><span><span style="color:#75715e"># 编译结果</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-9">9</a></span><span>./cloudflared --version
</span></span></code></pre></div><h2 id="5-未来计划">5. 未来计划</h2>
<p>这只是第一个版本，后续可能会继续优化：</p>
<ul>
<li><input disabled="" type="checkbox"> 支持更多代理协议（HTTP CONNECT、Shadowsocks 等）</li>
<li><input disabled="" type="checkbox"> 支持 QUIC over SOCKS5 UDP 转发</li>
<li><input disabled="" type="checkbox"> 添加代理健康检查和自动切换</li>
<li><input disabled="" type="checkbox"> 代理连接的详细指标和监控</li>
<li><input disabled="" type="checkbox"> 支持代理链（多级代理）</li>
</ul>
<h2 id="6-总结">6. 总结</h2>
<p><strong>关键要点：</strong></p>
<ol>
<li>✅ 通过代理使用稳定线路，连接不再断联</li>
<li>✅ 智能降级机制，代理故障不影响服务</li>
<li>✅ 配置灵活，支持命令行、环境变量、配置文件</li>
<li>✅ 完全向后兼容，不影响原有功能</li>
<li>✅ 代码简洁，详细的中文注释</li>
</ol>
<p>如果你也遇到了类似的问题，欢迎试用这个增强版本。代码已开源，欢迎 Star 和贡献！</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li>项目仓库：https://github.com/yunpiao/cloudflared</li>
<li>上游项目：https://github.com/cloudflare/cloudflared</li>
<li>Cloudflare Tunnel 文档：https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/</li>
<li>SOCKS5 协议规范 (RFC 1928)：https://www.rfc-editor.org/rfc/rfc1928</li>
<li>SOCKS5 认证 (RFC 1929)：https://www.rfc-editor.org/rfc/rfc1929</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[网络知识-macOS 网络：当代理应用 Loon 遇上虚拟内网 Tailscale]]></title>
            <link href="https://blog.yunpiao.site/post/20251022110829/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20251022110829/</id>
            
            
            <published>2025-10-22T11:08:29+08:00</published>
            <updated>2025-10-22T11:08:29+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>折腾了好久，终于搞定了 Loon 和 Tailscale 在 macOS 上的共存问题。怎么网络工具之间的冲突也这么复杂！😧</p>
</blockquote>
<h3 id="一现象诡异的网络不通">一、现象：诡异的网络不通</h3>
<p>为了同时使用 Loon 强大的网络调试、代理能力和 Tailscale 便捷的虚拟内网功能，我尝试在我的 Mac 上同时运行它们。很快，一个诡异的现象出现了：</p>
<ol>
<li><strong>底层网络是通的</strong>：我可以 <code>ping</code> 通 Tailscale 网络中的任何一台设备，例如 <code>ping 100.64.0.1</code>，延迟极低，一切正常。</li>
<li><strong>应用层访问失败</strong>：但任何基于 TCP 的应用层访问都宣告失败。例如，使用 <code>curl</code> 访问内网服务 <code>curl https://100.64.0.1:20443</code>，命令会一直卡住，没有任何响应，最终超时。</li>
</ol>
<p><code>ping</code> 通 <code>curl</code> 不通，这通常意味着问题出在代理、防火墙或者路由上。一场漫长的排查就此开始。</p>
<h3 id="二排查">二、排查</h3>
<h4 id="第一阶段怀疑代理规则">第一阶段：怀疑代理规则</h4>
<p>我的第一反应是 Loon 的分流规则出了问题。Loon 作为系统代理，很可能错误地将发往 <code>100.64.0.1</code> 这个内网地址的请求，发送到了公网的代理服务器上。代理服务器不认识这个地址，自然无法连接。</p>
<p><strong>验证</strong>：<br>
我查看了 Loon 的请求日志，发现 <code>curl</code> 请求确实被 Loon 捕获了。于是，我在 Loon 中添加了一条<strong>本地规则</strong>，强制所有发往 Tailscale 网段 <code>100.64.0.0/10</code> 的流量都执行 <code>DIRECT</code>（直连）策略。</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/10/55ac1814d8b370b24fc5d4c6faf4a361.png" alt="image.png"  /></p>
<p>然而，添加规则后，问题依旧。<code>curl</code> 请求虽然在日志中显示为 <code>DIRECT</code>，但依然是卡死超时。这说明，问题比想象中更深层。</p>
<h4 id="第二阶段怀疑服务器与-sni">第二阶段：怀疑服务器与 SNI</h4>
<p>既然代理规则没问题，那会不会是服务器端的问题？<code>curl</code> 卡在 TLS 握手阶段，有时与 SNI（服务器名称指示）有关。即服务器要求客户端在握手时提供域名，而我用的是 IP 访问。</p>
<p><strong>验证</strong>：<br>
我使用了 <code>curl</code> 的 <code>--resolve</code> 参数，模拟了基于域名的访问：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>curl -kvv --resolve v.yunpiao.site:20443:100.64.0.1 https://v.yunpiao.site:20443
</span></span></code></pre></div><p>结果，依然是石沉大海，卡死。这基本排除了 SNI 的问题，证明问题根源就在我这台 Mac 的网络层。</p>
<h4 id="第三阶段回归本源定位路由冲突-">第三阶段：回归本源，定位路由冲突 💡</h4>
<p>此时，我回到了最基础的网络问题上：<strong>路由</strong>。数据包到底是从哪个网卡发出去的？</p>
<p>我使用 <code>netstat -nr</code> 查看系统路由表，真相瞬间大白：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>➜  ~ netstat -nr | grep <span style="color:#e6db74">&#34;^100&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>100.64/10          192.168.10.1       UGSc                  en0
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span>100.64.0.149       100.64.0.149       UH                  utun8
</span></span></code></pre></div><p><strong>这就是问题的根源！</strong></p>
<ul>
<li>第一条路由（<code>100.64/10 ... en0</code>）的优先级更高，它错误地告诉系统：“所有发往 Tailscale 网络（<code>100.64/10</code>）的数据包，都应该从 <code>en0</code> 网卡（我的 Wi-Fi）发给 <code>192.168.10.1</code>（我的家用路由器）”。数据包就这样被发向了茫茫公网，自然有去无回。</li>
<li>而正确的路由，本应是让这些数据包都通过 Tailscale 的虚拟网卡 <code>utun8</code> 发出去。</li>
</ul>
<h3 id="三原因致命的路由抢占">三、原因：致命的路由抢占</h3>
<p>根本原因在于 <strong>Loon 和 <code>tailscaled</code> 之间的路由控制权抢占</strong>。</p>
<p><code>tailscaled</code>（Tailscale 的命令行守护进程）在启动时，会向系统路由表里添加一条规则，将 <code>100.64.0.0/10</code> 的流量指向它创建的 <code>utun</code> 虚拟网卡。</p>
<p>然而，Loon 为了实现全局代理，特别是其“TUN 模式”，也会强势接管系统的路由表，添加自己的规则。不幸的是，Loon 添加的全局规则覆盖了 Tailscale 的特定规则，导致了这场冲突。</p>
<h3 id="四解决办法">四、解决办法</h3>
<h4 id="临时解决手动修正路由">临时解决：手动修正路由</h4>
<p>最直接的方法就是通过命令行，手动删除错误路由，并添加正确路由。</p>
<ol>
<li><strong>删除错误路由</strong>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>sudo route delete -net 100.64.0.0/10 192.168.10.1
</span></span></code></pre></div></li>
<li><strong>添加正确路由</strong>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span><span style="color:#75715e"># 注意: utun8 需要根据你的实际情况确定</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span>sudo route add -net 100.64.0.0/10 -interface utun8
</span></span></code></pre></div></li>
</ol>
<p>执行后，<code>curl</code> 立刻恢复正常。但这只是临时的，一旦网络环境变化或应用重启，路由可能再次被污染。</p>
<h4 id="最终解决自动化修复脚本-">最终解决：自动化修复脚本 ✅</h4>
<p>为了实现一劳永逸，我编写了一个 Shell 脚本，可以随时执行来检测并修复路由问题。记得配置到 cron 里面</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1"> 1</a></span><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2"> 2</a></span><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3"> 3</a></span><span><span style="color:#75715e"># 定义命令的绝对路径，防止 command not found</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4"> 4</a></span><span>NETSTAT_CMD<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/usr/sbin/netstat&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5"> 5</a></span><span>ROUTE_CMD<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/sbin/route&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6"> 6</a></span><span>TAILSCALE_CMD<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/usr/local/bin/tailscale&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7"> 7</a></span><span>IFCONFIG_CMD<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/sbin/ifconfig&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-8"> 8</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-9"> 9</a></span><span><span style="color:#75715e"># 错误网关和目标网络</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-10">10</a></span><span>BAD_GATEWAY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;192.168.10.1&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-11">11</a></span><span>TARGET_NET_FULL<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;100.64.0.0/10&#34;</span> <span style="color:#75715e"># 如果你要用我的脚本, 你要修改成自己的内网</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-12">12</a></span><span>TARGET_SUBNET_GREP<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;^100\.64/10&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-13">13</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-14">14</a></span><span>echo <span style="color:#e6db74">&#34;Starting Tailscale route fix script...&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-15">15</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-16">16</a></span><span><span style="color:#75715e"># 1. 检查 tailscaled 是否在运行</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-17">17</a></span><span><span style="color:#66d9ef">if</span> ! pgrep -q tailscaled; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-18">18</a></span><span>    echo <span style="color:#e6db74">&#34;Error: tailscaled is not running. Please start Tailscale first.&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-19">19</a></span><span>    exit <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-20">20</a></span><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-21">21</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-22">22</a></span><span><span style="color:#75715e"># 2. 动态获取 Tailscale 的 IP 和 utun 接口</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-23">23</a></span><span>MY_TAILSCALE_IP<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>$TAILSCALE_CMD ip -4<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-24">24</a></span><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -z <span style="color:#e6db74">&#34;</span>$MY_TAILSCALE_IP<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-25">25</a></span><span>    echo <span style="color:#e6db74">&#34;Error: Could not get Tailscale IP address.&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-26">26</a></span><span>    exit <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-27">27</a></span><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-28">28</a></span><span>echo <span style="color:#e6db74">&#34;Found my Tailscale IP: </span>$MY_TAILSCALE_IP<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-29">29</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-30">30</a></span><span>GOOD_INTERFACE<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>$IFCONFIG_CMD | grep -B1 <span style="color:#e6db74">&#34;</span>$MY_TAILSCALE_IP<span style="color:#e6db74">&#34;</span> | grep -o <span style="color:#e6db74">&#39;utun[0-9]*&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-31">31</a></span><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -z <span style="color:#e6db74">&#34;</span>$GOOD_INTERFACE<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-32">32</a></span><span>    echo <span style="color:#e6db74">&#34;Error: Could not find a utun interface for IP </span>$MY_TAILSCALE_IP<span style="color:#e6db74">.&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-33">33</a></span><span>    exit <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-34">34</a></span><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-35">35</a></span><span>echo <span style="color:#e6db74">&#34;Found correct Tailscale interface: </span>$GOOD_INTERFACE<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-36">36</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-37">37</a></span><span><span style="color:#75715e"># 3. 检查并删除错误路由</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-38"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-38">38</a></span><span><span style="color:#66d9ef">if</span> $NETSTAT_CMD -nr | grep -q <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>TARGET_SUBNET_GREP<span style="color:#e6db74">}</span><span style="color:#e6db74">.*</span><span style="color:#e6db74">${</span>BAD_GATEWAY<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-39"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-39">39</a></span><span>    echo <span style="color:#e6db74">&#34;Found and deleting incorrect route via </span>$BAD_GATEWAY<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-40"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-40">40</a></span><span>    sudo $ROUTE_CMD delete -net $TARGET_NET_FULL $BAD_GATEWAY
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-41"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-41">41</a></span><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-42"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-42">42</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-43"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-43">43</a></span><span><span style="color:#75715e"># 4. 检查并添加正确路由</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-44"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-44">44</a></span><span><span style="color:#66d9ef">if</span> ! $NETSTAT_CMD -nr | grep -q <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>TARGET_SUBNET_GREP<span style="color:#e6db74">}</span><span style="color:#e6db74">.*</span><span style="color:#e6db74">${</span>GOOD_INTERFACE<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-45"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-45">45</a></span><span>    echo <span style="color:#e6db74">&#34;Correct route is missing. Adding it now via </span>$GOOD_INTERFACE<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-46"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-46">46</a></span><span>    sudo $ROUTE_CMD add -net $TARGET_NET_FULL -interface $GOOD_INTERFACE
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-47"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-47">47</a></span><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-48"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-48">48</a></span><span>    echo <span style="color:#e6db74">&#34;Correct route already exists.&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-49"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-49">49</a></span><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-50"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-50">50</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-51"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-51">51</a></span><span>echo <span style="color:#e6db74">&#34;Route fix script finished.&#34;</span>
</span></span></code></pre></div><p>这个脚本实现了自动化检测和修复，它会自动找到正确的 <code>utun</code> 接口，并确保路由指向它。现在，每当网络出问题时，只需运行一下这个脚本，一切就都恢复了。</p>
<h3 id="五总结">五、总结</h3>
<ol>
<li><strong><code>netstat -nr</code> 是网络排查的神器</strong>：当遇到底层通（<code>ping</code>）而上层不通（<code>curl</code>）的问题时，第一时间检查路由表，往往能发现线索。</li>
<li><strong>警惕网络工具的路由控制权</strong>：多个 VPN 或代理类应用共存时，极易发生路由冲突。理解它们的工作模式（系统代理 vs TUN/虚拟网卡）至关重要。</li>
<li><strong>将解决方案脚本化</strong>：对于需要重复操作的修复步骤，编写一个脚本可以极大地提高效率和准确性，是每个工程师都应具备的好习惯。</li>
</ol>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BD%91%E7%BB%9C" term="%E7%BD%91%E7%BB%9C" label="网络" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%BF%90%E7%BB%B4%E7%BB%8F%E9%AA%8C" term="%E8%BF%90%E7%BB%B4%E7%BB%8F%E9%AA%8C" label="运维经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[K8S-bug 排查之 kubeproxy 无法正常设置 iptables 了]]></title>
            <link href="https://blog.yunpiao.site/post/20250911141844/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250911141844/</id>
            
            
            <published>2025-09-11T14:18:44+08:00</published>
            <updated>2025-09-11T14:18:44+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>😧 稀奇古怪的 bug 怎么这么多, 还好是一个前人遇到过的, 如果我是第一个发现的, 估计美好的周五晚上就没有了</p>
</blockquote>
<h3 id="1-背景">1. 背景</h3>
<p>即将下班的周五下午 5 点, 运维不合时宜的甩给我了一个图片,  这次好一些, 加了一段文字, 说用户做了下迁移, 然后发现服务启动后 redis 服务异常, 卸载重装大法也不好使,<br>
redis ha 使用 helm 部署的时候, 只有一个正常, 剩下的两个 redis pod 启动后无法连接到 master 的 redis pod.<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/09/f21e80ba47aa5ed4bb8b167000735261.png" alt="image.png"  /></p>
<p><strong>环境说明</strong></p>
<ul>
<li>公司私有化部署到客户的 k8s 环境 v1.26</li>
<li>使用 redis-ha 部署 redis, 哨兵模式</li>
</ul>
<h3 id="2-现象">2. 现象</h3>
<ul>
<li>slave 的 redis pod 连接 redis 的时候使用 dns 解析的 redis cluster ip 有问题, 还是上一版本的 redis services  ip</li>
</ul>
<h3 id="3-逐步排查">3. 逐步排查</h3>
<h4 id="起初怀疑是-dns-的问题-dns-记录没有更新">起初怀疑是 dns 的问题, dns 记录没有更新,</h4>
<p>新建 service 后 cluster ip 更新没有问题, 可以判断不是 dns 的问题</p>
<h4 id="逐步检查-k8s-组件">逐步检查 k8s 组件</h4>
<p>检查到 kube proxy 节点的日志的之后, 发现日志中有错误, 发现节点上找不到 <code>conntrack</code> <code>ipset</code> 二进制文件, 原先设置的 ipvs 模式降级到了 iptables 模式了</p>
<p><strong><code>conntrack</code> <code>ipset</code></strong> 二进制缺失</p>
<p>本来是没啥问题, iptables 就 iptables 吧, 毕竟服务没多少个, 性能是没啥问题的, 不过既然遇到了就先解决下<br>
<strong>从其他地方拷贝 conntrack ipset 二进制后重启 kube-proxy. ipvsadm 检查后发现正常了</strong></p>
<p>重启后发现报错变了, 一条日志占了半屏, 后面还有其他错误日志没发现, 这个错误消除后, 发现了可以定位 bug 的日志</p>
<blockquote>
<p>兄弟们, 有时候就是这样, 你只看到了错误日志, 你以为找到了<code>解决问题的金手指</code> 但其实你看到的是个烟雾弹</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>I1113 16:15:07.928773 <span style="color:#ae81ff">1</span> proxier.go:1464<span style="color:#f92672">]</span> <span style="color:#e6db74">&#34;Reloading service iptables data&#34;</span> numServices<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> numEndpoints<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> numFilterChains<span style="color:#f92672">=</span><span style="color:#ae81ff">4</span> numFilterRules<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> numNATChains<span style="color:#f92672">=</span><span style="color:#ae81ff">4</span> numNATRules<span style="color:#f92672">=</span><span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>E1113 16:15:07.931291 <span style="color:#ae81ff">1</span> proxier.go:1481<span style="color:#f92672">]</span> <span style="color:#e6db74">&#34;Failed to execute iptables-restore&#34;</span> err<span style="color:#f92672">=</span>&lt;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>exit status 2: ip6tables-restore v1.8.4 <span style="color:#f92672">(</span>legacy<span style="color:#f92672">)</span>: unknown option <span style="color:#e6db74">&#34;--xor-mark&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span>Error occurred at line: <span style="color:#ae81ff">16</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">5</a></span><span>Try <span style="color:#e6db74">`</span>ip6tables-restore -h<span style="color:#e6db74">&#39; or &#39;</span>ip6tables-restore --help<span style="color:#960050;background-color:#1e0010">&#39;</span> <span style="color:#66d9ef">for</span> more information.
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6">6</a></span><span>&gt;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7">7</a></span><span>I1113 16:15:07.931308 <span style="color:#ae81ff">1</span> proxier.go:858<span style="color:#f92672">]</span> <span style="color:#e6db74">&#34;Sync failed&#34;</span> retryingTime<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;30s&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8">8</a></span><span>I1113 16:15:07.931317 <span style="color:#ae81ff">1</span> proxier.go:820<span style="color:#f92672">]</span> <span style="color:#e6db74">&#34;SyncProxyRules complete&#34;</span> elapsed<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;22.67239ms&#34;</span>
</span></span></code></pre></div><p>看到第一反应, <code>unknown option &quot;--xor-mark&quot; </code> iptables 版本太老的, 不支持这个命令, 或者 mod 没加载,  检查了一遍, 发现版本也是新的, ko mod 也加载了, 神奇, 而且日志中提示使用的确实是 1.8.4 版本的 iptables. <strong>真见鬼了 兄弟们</strong></p>
<p>随后我想着会不会就这样的呢? 这个看着是个 error, 其实是个 warning 呢? 毕竟服务连接 es 这些是正常的啊</p>
<h4 id="反思-不正常-十分有十二分的不正常">反思, 不正常, 十分有十二分的不正常</h4>
<p>回到问题原点</p>
<ol>
<li>部署 redis</li>
<li>redis-master 启动没有问题</li>
<li>redis-slave1 启动没有问题</li>
<li>redis-slave2 启动故障, 连接 master 提示超时,</li>
<li>登录到 slave2 机器上发现 telnet 确实不通, 查看 ipvs 确实没有条目, 确实是这个直接原因</li>
</ol>
<blockquote>
<p>分析, slave1 可以通过 cluster 连接到 redis master, slave2 发现连接不上, 首先证明 master 是没问题的, 还是 ipvs 条目创建的问题, ipvs 是由 kube proxy 直接管理的, 问题还是在 kubeproxy</p>
</blockquote>
<ol start="6">
<li>尝试将业务服务移动到 master 试一下, 因为出现问题的都在两台 worker 节点上,</li>
<li>将这两台机器 cordon 后, 删了业务 pod</li>
<li>原先启动报错连接不到 redis, 移动后可以正常启动了</li>
<li>检查现在移动到的这个节点和之前有问题的节点有什么不一致的</li>
<li>部署服务全部都是一致的, 但是正常机器是没有报 ip6tables-restore 这个错误的</li>
<li>不经意对比到内核版本的时候发现问题了</li>
</ol>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/09/4c10a8a3e0168c4fc4348f51428c5a81.png" alt="image.png"  /><br>
<strong>找到一个十分像问题点的不同, 内核版本有差别</strong></p>
<h3 id="4-定位原因">4. 定位原因</h3>
<h4 id="google-大法">google 大法</h4>
<p><strong>unknown option &ldquo;&ndash;set-mark&rdquo;</strong> 发现确实好像有些眉目额, 难道就是内核的 bug 吗 ?</p>
<blockquote>
<p>确实是的, 内核的锅, 上一年 12 月, 也算是一个新鲜的 bug, 这种事为什么发生在我身上, 我是易 bug 体质吗?</p>
</blockquote>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/09/8d8295c7a5bd517627cbbc8197d990bd.png" alt="image.png"  /></p>
<h4 id="根本原因root-cause一个有缺陷的内核提交">根本原因（Root Cause）：一个有缺陷的内核提交</h4>
<p>报告中明确指出了导致这个问题的“罪魁祸首”：一个特定的内核提交（commit）。</p>
<ul>
<li><strong>Commit ID:</strong> 862c95d9859f</li>
<li><strong>Commit 标题:</strong> netfilter: xt_mark: reject MARK target with mask<br>
<strong>这个提交的意图本来是好的，但实现是错误的。</strong></li>
<li><strong>原本的意图：</strong> 该提交的作者可能想要增加一个检查，以防止用户设置一个无效的 mark/mask 组合。例如，&ndash;set-mark 0x10/0x1 就是无效的，因为 mark 的值 (0x10) 中包含的位超出了掩码 (0x1) 的范围。</li>
<li><strong>错误的结果：</strong> 然而，代码的实现过于“激进”和简单粗暴，它直接<strong>禁止了所有使用掩码的 MARK 目标</strong>，无论这个组合是否有效。这破坏了一个已经存在了很长时间并且被广泛使用的合法功能。<br>
这是一个典型的<strong>回归 Bug (Regression Bug)</strong>：为了修复或改进某个东西而引入的新代码，却意外地破坏了另一个原本正常工作的功能</li>
</ul>
<h4 id="影响范围">影响范围</h4>
<p>这个 Bug 的影响非常广泛，因为它从内核层面直接禁用了 MARK 目标的一个核心功能。</p>
<ul>
<li><strong>Kubernetes (K8s):</strong> kube-proxy 在 IPVS 模式下严重依赖 &ndash;set-mark 0x4000/0x4000 这样的语法来标记需要做 SNAT 的流量。当内核拒绝这条规则时，kube-proxy 的同步循环就会失败，导致其陷入 CrashLoopBackOff。</li>
<li><strong>Tailscale:</strong> 正如报告中提到的，Tailscale 也使用类似的规则来标记流量，因此也会在这个有问题的内核版本上运行失败。</li>
<li><strong>其他网络软件:</strong> 任何依赖 iptables mark 掩码功能的防火墙脚本、网络工具或 CNI 插件都会受到影响。</li>
<li><strong>受影响的系统:</strong>
<ul>
<li><strong>Ubuntu 24.04 (Noble Numbat)</strong>，因为它默认搭载了受影响的 Linux 6.8 内核。</li>
<li><strong>Ubuntu 22.04 LTS (Jammy Jellyfish)</strong> 等旧版本，如果用户安装了 <strong>HWE (Hardware Enablement) 内核</strong>，内核版本也会升级到受影响的版本，从而引入这个问题。</li>
</ul>
</li>
</ul>
<h3 id="5-分析日志出错点">5. 分析日志出错点</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span>	<span style="color:#75715e">// --------------------------------------------------------------------------
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span><span style="color:#75715e"></span>	<span style="color:#75715e">// 应用变更到系统
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span><span style="color:#75715e"></span>	<span style="color:#75715e">// --------------------------------------------------------------------------
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span>	<span style="color:#75715e">// 将内存中 `activeEntries` 的变更同步到 ipset 内核中。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span><span style="color:#75715e"></span>	<span style="color:#75715e">// 这个函数会计算差异，并调用 `ipset` 命令来添加或删除条目。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span><span style="color:#75715e"></span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">set</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">ipsetList</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span>		<span style="color:#a6e22e">set</span>.<span style="color:#a6e22e">syncIPSetEntries</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span>	}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-11">11</a></span><span>	<span style="color:#75715e">// 为 ipset 生成相关的 iptables 规则 (例如，匹配 ipset 的规则)。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-12">12</a></span><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">writeIptablesRules</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-13">13</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-14">14</a></span><span>	<span style="color:#75715e">// 准备最终的 iptables-restore 数据。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-15">15</a></span><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Reset</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-16">16</a></span><span>	<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">natChains</span>.<span style="color:#a6e22e">Bytes</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-17">17</a></span><span>	<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">natRules</span>.<span style="color:#a6e22e">Bytes</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-18">18</a></span><span>	<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">filterChains</span>.<span style="color:#a6e22e">Bytes</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-19">19</a></span><span>	<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">filterRules</span>.<span style="color:#a6e22e">Bytes</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-20">20</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-21">21</a></span><span>	<span style="color:#a6e22e">klog</span>.<span style="color:#a6e22e">V</span>(<span style="color:#ae81ff">5</span>).<span style="color:#a6e22e">Infof</span>(<span style="color:#e6db74">&#34;Restoring iptables rules: %s&#34;</span>, <span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Bytes</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-22">22</a></span><span>	<span style="color:#75715e">// 这是整个 iptables 同步过程中最核心的原子操作。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-23">23</a></span><span><span style="color:#75715e"></span>	<span style="color:#75715e">// 所有之前在内存中构建好的 iptables 规则（存储在 proxier.iptablesData 中）
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-24">24</a></span><span><span style="color:#75715e"></span>	<span style="color:#75715e">// 通过 `iptables-restore` 命令一次性、原子地应用到系统中。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-25">25</a></span><span><span style="color:#75715e"></span>	<span style="color:#75715e">// `utiliptables.NoFlushTables` 选项告诉 `iptables-restore` 不要清除表中已有的、非 kube-proxy 管理的规则。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-26">26</a></span><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptables</span>.<span style="color:#a6e22e">RestoreAll</span>(<span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Bytes</span>(), <span style="color:#a6e22e">utiliptables</span>.<span style="color:#a6e22e">NoFlushTables</span>, <span style="color:#a6e22e">utiliptables</span>.<span style="color:#a6e22e">RestoreCounters</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-27">27</a></span><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-28">28</a></span><span>		<span style="color:#a6e22e">klog</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;Failed to execute iptables-restore: %v\nRules:\n%s&#34;</span>, <span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">iptablesData</span>.<span style="color:#a6e22e">Bytes</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-29">29</a></span><span>		<span style="color:#75715e">// 如果 iptables-restore 失败，需要回滚新打开的端口，以避免资源泄漏。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-30">30</a></span><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">utilproxy</span>.<span style="color:#a6e22e">RevertPorts</span>(<span style="color:#a6e22e">replacementPortsMap</span>, <span style="color:#a6e22e">proxier</span>.<span style="color:#a6e22e">portsMap</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-31">31</a></span><span>		<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-32">32</a></span><span>	}
</span></span></code></pre></div><h3 id="6-解决方法">6. 解决方法</h3>
<p>由于我没有直接接触客户, 不过用脚趾头想了下, 用户说的迁移, 其实是偷偷升级了 linux 内核, 但是升级操作还不统一, 小版本不一致, 所以造成了 master 的内核是好的, worker 节点的内核是 bug 版本的<br>
反馈给客户了, 让用户升级内核解决了</p>
<h3 id="参考链接">参考链接</h3>
<p><a href="https://github.com/rancher/rke2/issues/7438" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/rancher/rke2/issues/7438</a><br>
<a href="https://bugs.launchpad.net/ubuntu/&#43;source/linux/&#43;bug/2091990" target="_blank" rel="noopener nofollow noreferrer" >https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2091990</a><br>
<a href="https://github.com/kubernetes/kubernetes/blob/v1.14.10/pkg/proxy/ipvs/proxier.go#L721" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/kubernetes/kubernetes/blob/v1.14.10/pkg/proxy/ipvs/proxier.go#L721</a></p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/kubernetes" term="kubernetes" label="Kubernetes" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5" term="%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5" label="问题排查" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/k8s" term="k8s" label="k8s" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BD%91%E7%BB%9C" term="%E7%BD%91%E7%BB%9C" label="网络" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[K8S-bug 排查之 longhorn 的 rpm 命令]]></title>
            <link href="https://blog.yunpiao.site/post/20250911112456/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250911112456/</id>
            
            
            <published>2025-09-11T11:24:56+08:00</published>
            <updated>2025-09-11T11:24:56+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>怎么稀奇古怪的问题都被我遇到啊 😧</p>
</blockquote>
<h3 id="一现象诡异的内存增长">一、现象：诡异的内存增长</h3>
<p>最近，观察到一个奇怪的现象：在用户私有化部署环境中, <code>longhorn-manager</code> pod 所在的节点内存使用率会持续飙升，最终达到 100%，导致节点和 <code>longhorn-manager</code> 本身无响应。</p>
<p>通过 <code>top</code> 和 <code>ps</code> 等工具深入排查，发现节点上出现了大量的 <code>rpm -q iscsi-initiator-utils</code> 进程。这些进程不断累积，似乎在等待某个锁或资源，最终耗尽了所有可用内存。</p>
<p>从 <code>longhorn-manager</code> 的日志中，可以看到类似以下的错误信息，显示 <code>rpm</code> 命令执行超时或失败，并伴随着数据库损坏的错误。</p>
<pre tabindex="0"><code class="language-log" data-lang="log">time=&#34;2024-12-19T12:35:56Z&#34; level=debug msg=&#34;Package nfs-utils is not found in node xxx&#34; func=&#34;controller.(*NodeController).syncPackagesInstalled&#34; file=&#34;node_controller.go:997&#34; controller=longhorn-node error=&#34;failed to execute: /usr/bin/nsenter [nsenter --mount=/host/proc/3013255/ns/mnt --net=/host/proc/3013255/ns/net rpm -q nfs-utils], output package nfs-utils is not installed\n, stderr error: rpmdb: BDB0113 Thread/process 667765/140606332983168 failed: BDB1507 Thread died in Berkeley DB library\nerror: db5 error(-30973) from dbenv-&gt;failchk: BDB0087 DB_RUNRECOVERY: Fatal error, run database recovery\nerror: cannot open Packages index using db5 - (-30973)\nerror: cannot open Packages database in /var/lib/rpm\nerror: rpmdb: BDB0113 Thread/process 667765/140606332983168 failed: BDB1507 Thread died in Berkeley DB library\nerror: db5 error(-30973) from dbenv-&gt;failchk: BDB0087 DB_RUNRECOVERY: Fatal error, run database recovery\nerror: cannot open Packages index using db5 - (-30973)\nerror: cannot open Packages database in /var/lib/rpm\n: exit status 1&#34; node=xxx
</code></pre><p>显然，<code>longhorn-manager</code> 正在持续、高频地调用 <code>rpm -q</code> 来检查软件包的安装状态，而这个操作在高并发下导致了宿主机的 RPM 数据库损坏和进程挂起。</p>
<h3 id="二排查追根溯源">二、排查：追根溯源</h3>
<p>问题的关键在于定位 <code>longhorn-manager</code> 为何如此频繁地执行 <code>rpm</code> 查询。</p>
<p>通过分析 GitHub 上的相关 Issue 和社区讨论，发现 Longhorn 的 <code>node_controller</code> 中有一个名为 <code>syncPackagesInstalled</code> 的环境检查函数。 这个函数的设计初衷是定期确认每个节点上是否安装了 Longhorn 运行所必需的依赖包，例如 <code>iscsi-initiator-utils</code>, <code>nfs-utils</code>, <code>cryptsetup</code> 等。</p>
<p>在之前的版本中，它的实现方式是直接调用宿主机系统的包管理器（如 <code>rpm</code> 或 <code>dpkg</code>）来查询包是否存在。 这种方式虽然直接，但存在几个致命缺陷：</p>
<ol>
<li><strong>性能开销大</strong>：每次查询都需要与包管理器的数据库进行交互，这是一个相对较重的操作。</li>
<li><strong>锁竞争</strong>：当 <code>longhorn-manager</code> 高频发起检查，或者宿主机上同时有其他进程（如 <code>dnf-automatic</code>）在操作 RPM 数据库时，很容易引发锁竞争，导致进程挂起。</li>
<li><strong>可靠性差</strong>：一旦 RPM 数据库因任何原因出现问题，这种检查机制就会完全失效，并可能加剧数据库的损坏，形成恶性循环。</li>
</ol>
<p>在的案例中，正是这种“重量级”的检查机制，导致了大量 <code>rpm</code> 进程堆积，最终耗尽系统资源。</p>
<h3 id="三原因不恰当的依赖检查方式">三、原因：不恰当的依赖检查方式</h3>
<p>根本原因在于 Longhorn 使用了一种不恰当的方式来验证依赖项的可用性。</p>
<p>Longhorn 实际上关心的不是“软件包是否被 RPM 数据库记录为已安装”，而是“其功能所需的二进制文件是否存在且可执行”。例如，它需要的是 <code>iscsiadm</code> 这个命令，而不是 <code>iscsi-initiator-utils</code> 这个包名本身。</p>
<p>直接查询包管理器是一种间接且脆弱的验证方法。更健壮、更轻量的方式应该是直接检查所需命令是否存在于系统的 <code>PATH</code> 中。</p>
<p>还有其他原因<br>
longhorn 里面没有设置默认的资源限制, 私有化部署的时候也没有限制, 倒是一个 pod 打垮一个节点<br>
rpm 的问题, rpm 的调用并不是特别高频, 但是 rpm 的 db 出现了问题, 说明 rpm 太脆弱了</p>
<h3 id="四解决办法">四、解决办法</h3>
<h4 id="社区解决办法">社区解决办法</h4>
<p>社区和开发人员也意识到了这个问题，并迅速给出了解决方案。在最新的代码提交中，Longhorn 彻底改变了依赖检查的逻辑。</p>
<p>新的实现方式摒弃了对 <code>rpm</code>, <code>dpkg</code> 等包管理器的直接调用，转而使用更简单、更高效的 <code>command -v &lt;executable&gt;</code> 命令来检查所需的可执行文件是否存在。</p>
<p>这个修复已经通过 Pull Request 合并，并计划在后续版本（如 v1.7.3 和 v1.8.0）中提供。</p>
<hr>
<h4 id="线上解决方法">线上解决方法</h4>
<p>客户那边是私有化部署, 所以并不能直接升级</p>
<h5 id="临时解决">临时解决</h5>
<p><code>rpm --rebuilddb</code> 修复受损的 rpm db 数据</p>
<h5 id="后续优化">后续优化</h5>
<p>部署包升级到 longhorn 版本到 1.7.3</p>
<h3 id="五总结">五、总结</h3>
<ul>
<li>不要相信任何的开源包, 一定要设置资源的内存限制</li>
<li>遇到的技术问题, 大部分前人已经遇到过了, 出了问题先找 issue, 可能就是一个已知 bug</li>
<li>即使是小的命令 hang 泄露, 也会堆积占用大量的内存</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/kubernetes" term="kubernetes" label="Kubernetes" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/k8s" term="k8s" label="k8s" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/longhorn" term="longhorn" label="Longhorn" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5" term="%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5" label="问题排查" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[大模型时代，重新定义“程序员”-理解 Transformer]]></title>
            <link href="https://blog.yunpiao.site/post/20250731103921/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250731103921/</id>
            
            
            <published>2025-07-31T10:39:21+08:00</published>
            <updated>2025-07-31T10:39:21+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>整天 vibe coding , 怎么能不稍微了解下这个神奇的生成是怎么实现的呢</p>
</blockquote>
<p>如果一个程序员不再亲自编写每一行代码，他还是程序员吗？这个问题在 AI 生成内容（AIGC）浪潮下显得尤为尖锐。然而，一个长期存在于软件工程领域的共识是：<strong>编写代码本身，从来都不是项目的真正瓶颈</strong>。真正的瓶颈过去是，现在依然是，代码审查、知识转移、测试调试，以及由此带来的巨大沟通协调开销。</p>
<p>大语言模型（LLM）的出现并没有消除这些工作，而是将开发者的核心工作负载，<strong>从“编写”这个动作，戏剧性地转移到了“审查与维护”之上</strong>。正如行业一句名言所说：<strong>“代码最大的成本在于理解它，而不是编写它。”</strong> 本文将深入拆解驱动这一切的 Transformer 架构，剖析其工作原理，并探讨开发者在新时代下必须掌握的生存法则。</p>
<h2 id="1-工作模式的转变从创作者到审查官">1. 工作模式的转变：从创作者到审查官</h2>
<p>长期以来，程序员的价值被粗略地等同于代码产出量。但所有资深工程师都明白，真正的挑战在于构建一个易于理解、维护和扩展的系统。LLM 作为代码生成器，极大地加速了“写”的过程，但这仅仅是第一步。</p>
<p>开发者的新角色，更像是一个经验丰富的<strong>项目总监</strong>或<strong>首席审查官</strong>。我们的任务变成了：</p>
<ol>
<li><strong>精确定义问题</strong>：向 AI 清晰地描述需求和边界条件。</li>
<li><strong>审查生成结果</strong>：快速甄别 AI 生成代码中的逻辑漏洞、安全隐患和性能瓶颈。</li>
<li><strong>调试与整合</strong>：将 AI 生成的模块放入现有系统中，并解决由此产生的兼容性与集成问题。</li>
<li><strong>指导与优化</strong>：通过反馈和调整（例如对模型进行微调或打补丁），持续提升 AI 的产出质量。</li>
</ol>
<p>本质上，AI 成为了一个能力极强但经验不足的初级程序员，而我们的价值，则体现在利用自身深厚的经验和系统性思维来驾驭它。</p>
<h2 id="2-技术基石深入理解-transformer-架构">2. 技术基石：深入理解 Transformer 架构</h2>
<p>要理解这场变革的底层逻辑，我们必须深入拆解驱动现代 LLM 的核心技术——由 Google 在 2017 年提出的 <strong>Transformer</strong> 架构。</p>
<h3 id="21-历史性跨越从-rnn-到-transformer">2.1 历史性跨越：从 RNN 到 Transformer</h3>
<p>在 Transformer 出现之前，RNN（循环神经网络）在处理序列数据时存在一个致命缺陷：<strong>长期依赖问题</strong>。随着序列变长，梯度在反向传播中容易消失或爆炸，导致模型“遗忘”掉早期的重要上下文。</p>
<p>Transformer 架构用一个看似暴力的方式解决了这个问题。它放弃了 RNN 的顺序处理模式，通过 <strong>Attention（注意力）机制</strong>让模型可以直接关注输入序列中的任意部分。代价是什么？<strong>一个巨大的、昂贵的二次方复杂度计算问题 (O(N2))</strong>。这意味着上下文窗口的长度（<code>N</code>）每增加一倍，计算量和内存占用就会增加四倍。</p>
<p>尽管代价高昂，但这次权衡是革命性的：<strong>Transformer 用一个巨大的计算问题，换掉了 RNN 那个难以解决的“记忆衰退问题”。</strong></p>
<h3 id="22-第一步将语言转化为向量-tokenization--embedding">2.2 第一步：将语言转化为向量 (Tokenization &amp; Embedding)</h3>
<p>模型无法直接理解文本。它的第一步是将文本转化为数字。</p>
<ol>
<li><strong>分词 (Tokenization)</strong>：GPT 等模型采用**字节对编码（Byte Pair Encoding, BPE）**算法。该算法迭代地将语料库中出现频率最高的相邻字符对合并成一个新的、更长的单元（token），直到达到预设的词汇表大小。最终，GPT 看到的不是一句话，而是一个由数字 ID 组成的序列，例如 <code>[1234, 56, 789]</code>。</li>
<li><strong>词嵌入 (Embedding)</strong>：每个 token ID 都对应一个高维向量，即“词嵌入”。这个向量（在 GPT-3 中高达 12288 维）从一个巨大的嵌入矩阵中查找得到。初始时，它只包含该 token 的孤立语义信息，就像字典里的词条。著名的 <code>women - man ≈ queen - king</code> 关系，就体现在这些向量的方向与距离上。</li>
<li><strong>位置编码 (Positional Encoding)</strong>：为了让模型知道词的顺序，一个代表位置信息的向量会被加到词嵌入上。巧妙的是，这个位置编码是固定生成的，在模型训练时不会被反向传播更新。</li>
</ol>
<h3 id="23-核心引擎注意力机制-attention-is-all-you-need">2.3 核心引擎：注意力机制 (Attention is All You Need)</h3>
<p>词嵌入本身是静态的、无上下文的。例如，“mole”可以是“鼹鼠”，也可以是“间谍”。<strong>Transformer 的核心目标，就是通过注意力机制，让这些静态的词嵌入动态地融入上下文信息。</strong></p>
<p>注意力机制的本质，是一个信息筛选和加权的过程，其核心是为每个输入向量生成三个新的、维度更小的向量：</p>
<ul>
<li><strong>Query (Q) 查询向量</strong>：由每个词的嵌入（乘以一个权重矩阵 <code>Wq</code>）生成。它代表当前词“希望寻找”什么样的上下文信息。可以理解为它发出的一个问题：“为了明确我的含义，谁和我最相关？”</li>
<li><strong>Key (K) 键向量</strong>：同样由每个词的嵌入（乘以 <code>Wk</code>）生成。它代表该词能够“提供”什么样的信息以响应查询。可以理解为每个词给自己贴的“内容标签”。</li>
<li><strong>Value (V) 值向量</strong>：同样由每个词的嵌入（乘以 <code>Wv</code>）生成。它代表该词在被关注后，实际要贡献出的信息内容。</li>
</ul>
<p>计算过程如下：</p>
<ol>
<li>用一个词的 <strong>Query</strong> 向量与序列中<strong>所有</strong>词的 <strong>Key</strong> 向量进行点积计算，得到一组相关性分数。</li>
<li>这些分数通过 <strong>Softmax</strong> 函数，被归一化为一个总和为 1 的概率分布，即“注意力权重”。这决定了当前词应该对其他词投入多少“注意力”。</li>
<li>最后，将这些权重与每个词对应的 <strong>Value</strong> 向量相乘再求和，得到一个加权平均后的向量。</li>
<li>将这个融合了全局上下文信息的向量，加回到原始的词嵌入上，就完成了一次信息的更新。</li>
</ol>
<p>通过这个过程，词与词之间可以直接传递信息，即便相隔很远，从而解决了多义性问题，让词向量从静态变为动态。</p>
<h3 id="24-多维视角多头注意力机制-multi-head-attention">2.4 多维视角：多头注意力机制 (Multi-Head Attention)</h3>
<p>单次注意力计算（单头）只能学习一种类型的上下文依赖关系。为了让模型能同时捕捉多种不同类型的关系（如语法结构、语义关联、指代关系等），Transformer 采用了<strong>多头注意力机制</strong>。</p>
<p>它并行运行多个独立的注意力“头”，每个头都拥有自己独立的 <code>WQ</code>, <code>WK</code>, <code>WV</code> 权重矩阵，在不同的表示子空间中学习。例如，GPT-3 在每个注意力模块中使用了 <strong>96 个</strong>独立的注意力头。所有头的输出结果最终会被合并，并通过一个输出矩阵 <code>Wo</code> 整合成一个单一的向量，传递给下一层。</p>
<h3 id="25-完整架构堆叠层与前馈网络">2.5 完整架构：堆叠层与前馈网络</h3>
<p>一个完整的 Transformer 模块（或称“层”）由两部分组成：</p>
<ol>
<li>一个多头注意力模块。</li>
<li>一个简单的全连接前馈神经网络（Feed-Forward Network, FFN/MLP）。</li>
</ol>
<p>数据流经注意力模块进行信息融合，再流经 FFN 进行非线性变换。GPT-3 这样的庞大模型，就是将这样的层级结构堆叠了 <strong>96 次</strong>。数据在其中反复流动，层层深入地提炼和编码信息。模型的绝大部分参数（约三分之二）都位于 FFN 中，而注意力机制的参数约占三分之一，但其计算量是核心瓶颈。</p>
<h2 id="3-从训练到推理揭秘-gpt-的工作流">3. 从训练到推理：揭秘 GPT 的工作流</h2>
<p>理解了底层架构，我们再来看看模型是如何学习和工作的。</p>
<h3 id="31-训练在海量数据中涌现智能">3.1 训练：在海量数据中“涌现”智能</h3>
<p>模型的训练目标极其单纯：<strong>基于前面的文本，预测下一个最有可能出现的词</strong>。</p>
<p>其训练过程是一个不断循环的闭环：</p>
<ol>
<li><strong>前向传播 (Forward Pass)</strong>：将一段文本输入模型，计算出下一个词在整个词汇表上的概率分布。</li>
<li><strong>计算误差 (Compute Loss)</strong>：将模型预测的概率分布与真实文本中的下一个词进行比较，计算出“误差”或“损失”。</li>
<li><strong>反向传播 (Backward Pass)</strong>：根据误差，通过链式法则计算出每个参数（<code>WQ, WK, WV</code> 等）对误差的贡献程度（梯度）。</li>
<li><strong>微调参数 (Update Parameters)</strong>：沿着梯度的反方向，微小地调整所有参数，使模型下一次的预测更接近真实答案。</li>
</ol>
<p>这个“<strong>前向传播 → 计算误差 → 反向传播 → 微调参数</strong>”的循环，会在一个巨大的文本语料库上重复<strong>数万亿次</strong>。<strong>最终，词向量的“语义”不是被设计出来的，而是在完成“预测下一个词”这个简单任务的过程中，作为副产品，自动地、统计地“涌现”出来的。</strong></p>
<h3 id="32-推理自回归式的文本生成">3.2 推理：自回归式的文本生成</h3>
<p>训练完成后，模型就进入了只进行前向传播的<strong>推理（Inference</strong> 阶段。其连续生成文本的方式是<strong>自回归（Autoregressive</strong>的：</p>
<blockquote>
<p><strong>模型进行一次完整的计算来预测一个词，然后把这个新预测的词当作已知信息“吃”回去，并扩充自己的输入，再进行下一次完整的计算来预测下一个词。这个过程就像一个不断向前滚动的雪球，一步步地构建出完整的句子。</strong></p>
</blockquote>
<p>具体来说，预测下一个词的关键，是使用输入序列中<strong>最后一个词</strong>的最终输出向量。这个向量经过最终的变换层，会输出一个覆盖整个词汇表的概率分布，模型从中选择一个词作为输出。</p>
<h3 id="33-输出控制温度-temperature-与-top-k">3.3 输出控制：温度 (Temperature) 与 Top-K</h3>
<p>为了控制生成文本的创造性，通常会引入两个参数：</p>
<ul>
<li><strong>温度 (Temperature)</strong>：在应用 Softmax 之前，用一个温度值来缩放原始的概率分数。
<ul>
<li>温度趋近 0：放大最高概率词的优势，生成结果确定性高，但可能重复乏味。</li>
<li>温度 &gt; 1：使概率分布更平缓，增加随机性，生成结果更有创造性，但也更容易出现逻辑错误。</li>
</ul>
</li>
<li><strong>Top-K / Top-P (Nucleus) 采样</strong>：限制模型只能从概率最高的 K 个词或累积概率达到 P 的词中进行抽样，以此截断长尾，防止选到不合逻辑的低概率词。</li>
</ul>
<h3 id="34-记忆的实现多轮对话">3.4 记忆的实现：多轮对话</h3>
<p>实现多轮对话的原理非常直接：<strong>将上一轮的问答（Q&amp;A）内容，全部拼接到当前这一轮的输入中</strong>，形成一个更长的上下文，然后让模型基于这个完整的历史记录进行预测。这再次凸显了**上下文窗口（Context Window）**作为模型“工作记忆”的核心地位和局限性。</p>
<h2 id="4-新时代的生存法则开发者的新技能图谱">4. 新时代的生存法则：开发者的新技能图谱</h2>
<p>面对如此强大的工具，与其焦虑，不如主动升级自己的技能树。</p>
<h3 id="1-专注于-ai-难以替代的领域">1. 专注于 AI 难以替代的领域</h3>
<p>将精力投入到高层次、创造性的工作中：</p>
<ul>
<li><strong>复杂系统设计与架构</strong>：权衡各种约束、设计可扩展、高可用的系统。</li>
<li><strong>技术创新</strong>：提出全新的解决方案和范式。</li>
<li><strong>底层原理理解</strong>：深入掌握操作系统、网络、Go 底层等，进行极致的性能优化和问题排查。</li>
</ul>
<h3 id="2-掌握上下文工程context-engineering">2. 掌握上下文工程（Context Engineering）</h3>
<p>AI 领域的新核心技能不是提示词，而是<strong>上下文工程</strong>。这意味着开发者需要像架构师一样，精心设计提供给模型的整个信息环境。用好 AI Agent 本身就是一个复杂的工程项目。</p>
<h3 id="3-提升代码阅读与调试的优先级">3. 提升代码阅读与调试的优先级</h3>
<p>既然“写”的比例下降，“审”的比例上升，那么<strong>快速读懂、理解并调试陌生代码（尤其是 AI 生成的代码）的能力</strong>就变得至关重要。</p>
<h3 id="4-建立批判性思维信任理解质疑推理">4. 建立批判性思维：信任理解，质疑推理</h3>
<p>这是一个重要的心智模型：<strong>信任大模型对输入信息的理解和概括能力，但必须严格质疑它的多步逻辑推理和最终结论</strong>。必须警惕“会说话就能解决所有问题”的误区，工程实践的严谨性与逻辑深度无可替代。</p>
<h2 id="5-总结重新定义程序员">5. 总结：重新定义“程序员”</h2>
<p>回到最初的问题：一个不怎么写代码的程序员，还是程序员吗？<br>
答案是肯定的，但“程序员”的定义正在被重塑。<br>
未来的程序员，将不再是单纯的代码工匠，而是<strong>手持 AI 这把神兵利器的领域专家和问题解决者</strong>。我们的核心价值，在于利用人类独有的系统性思维、批判性分析能力和对最终产品质量的绝对责任感，去引导、驾驭和整合 AI 的强大生产力。<br>
与其担心被替代，不如拥抱这场变革。从“写”到“审”的转变，不是价值的削弱，而是认知价值的升华。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B" term="%E5%A4%A7%E6%A8%A1%E5%9E%8B" label="大模型" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/ai%E6%9C%8D%E5%8A%A1" term="ai%E6%9C%8D%E5%8A%A1" label="AI服务" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%A8%8B%E5%BA%8F%E5%91%98" term="%E7%A8%8B%E5%BA%8F%E5%91%98" label="程序员" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B" term="%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B" label="软件工程" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/transformer" term="transformer" label="Transformer" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/attention" term="attention" label="Attention" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%8A%80%E8%83%BD%E8%BD%AC%E5%9E%8B" term="%E6%8A%80%E8%83%BD%E8%BD%AC%E5%9E%8B" label="技能转型" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[团队建设-技术文档体系与最佳实践]]></title>
            <link href="https://blog.yunpiao.site/post/20250715155939/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250715155939/</id>
            
            
            <published>2025-07-15T15:59:39+08:00</published>
            <updated>2025-07-15T15:59:39+08:00</updated>
            
            
            <content type="html"><![CDATA[<h3 id="构建高效研发团队一套可落地的技术文档体系与最佳实践"><strong>构建高效研发团队：一套可落地的技术文档体系与最佳实践</strong></h3>
<blockquote>
<p><strong>核心理念：文档是写给别人看的，更是为了未来的自己。</strong></p>
</blockquote>
<p>在任何一个技术团队中，文档的重要性不言而喻。它不仅仅是项目的记录，更是知识传承、团队协作和效率提升的基石。缺乏规范的文档管理，会导致知识孤岛、新人上手困难、项目交接混乱、问题排查耗时等一系列问题。</p>
<p>本文将分享一套我们团队在实践中总结出的技术文档管理体系，旨在为不同规模的研发团队提供一个清晰、可落地的参考框架。</p>
<hr>
<h3 id="一-文档分类总览为知识建立索引"><strong>一、 文档分类总览：为知识建立索引</strong></h3>
<p>我们将团队的所有技术文档归纳为五大类，每一类都有其明确的定位和价值。</p>
<h4 id="11-how-to-指南-tutorials"><strong>1.1 How-To 指南 (Tutorials)</strong></h4>
<p>这类文档是团队的“快速上手手册”，旨在帮助成员快速搭建环境、使用公共组件。它们是消除重复性提问、提升团队自服务能力的利器。</p>
<ul>
<li><strong>示例</strong>：
<ul>
<li>《本地开发环境搭建指南 (Go/Python/Node.js)》</li>
<li>《公司内部私有仓库 (Registry) 使用方法》</li>
<li>《如何一键部署个人测试环境》</li>
<li>《K8s 集群基础入门与配置》</li>
</ul>
</li>
</ul>
<h4 id="12-业务介绍文档-business-context"><strong>1.2 业务介绍文档 (Business Context)</strong></h4>
<p>技术最终服务于业务。这部分文档帮助研发人员理解“我们为什么要做”，建立业务全局观，确保技术方案与业务目标对齐。通常由产品经理（PM）或技术负责人（TL）主导维护。</p>
<ul>
<li><strong>目标</strong>：同步业务背景，拉齐团队认知。</li>
<li><strong>示例</strong>：
<ul>
<li>《核心产品 X v2.0 介绍》</li>
<li>《核心产品 X 业务白皮书》</li>
<li>《某某解决方案商业价值分析》</li>
</ul>
</li>
</ul>
<h4 id="13-功能与系统设计文档-core-engineering-docs"><strong>1.3 功能与系统设计文档 (Core Engineering Docs)</strong></h4>
<p>这是研发团队最核心、最高频维护的文档库。它记录了一个功能从需求到落地、再到维护的全过程。我们建议按<strong>功能模块</strong>建立子目录，形成结构化的知识库。</p>
<ul>
<li><strong>目标</strong>：沉淀技术细节，支撑开发、迭代与维护。</li>
<li><strong>示例</strong>：
<ul>
<li><code>系统设计/用户身份认证模块</code></li>
<li><code>系统设计/数据处理引擎</code></li>
<li><code>系统设计/全局配置中心</code></li>
<li><code>系统设计/API 网关接入方案</code></li>
</ul>
</li>
</ul>
<h4 id="14-服务部署文档-deployment"><strong>1.4 服务部署文档 (Deployment)</strong></h4>
<p>这类文档关注服务的生命周期管理，确保软件可以被顺畅、可靠地发布和运维。</p>
<ul>
<li><strong>目标</strong>：标准化发布流程，降低部署风险。</li>
<li><strong>示例</strong>：
<ul>
<li>《v1.3.0 版本发布操作手册 (Release Note &amp; SOP)》</li>
<li>《服务容器化打包规范》</li>
</ul>
</li>
</ul>
<h4 id="15-问题排查知识库-troubleshooting-kb"><strong>1.5 问题排查知识库 (Troubleshooting KB)</strong></h4>
<p>将线上线下遇到的典型问题及其解决方案记录下来，形成团队的“集体记忆”。这是从被动救火到主动预防的关键一步。</p>
<ul>
<li><strong>目标</strong>：加速问题定位，共享排查经验。</li>
<li><strong>示例</strong>：
<ul>
<li><code>2023-10-24-某功能白名单不生效问题排查复盘</code></li>
<li><code>2023-11-15-客户端 Agent 离线问题分析指南</code></li>
</ul>
</li>
</ul>
<hr>
<h3 id="二-深入剖析如何写好一份功能与系统设计文档"><strong>二、 深入剖析：如何写好一份“功能与系统设计文档”</strong></h3>
<p>“功能与系统设计文档”是整个体系的重中之重。一份好的设计文档，能让团队成员在任何时候都能快速理解模块的全貌。我们将其进一步细分为七个子文档，每个子文档面向不同读者，侧重点各不相同。</p>
<h4 id="21-需求理解文档"><strong>2.1 需求理解文档</strong></h4>
<ul>
<li><strong>面向对象</strong>：研发（RD）、技术负责人（TL）、产品经理（PM）</li>
<li><strong>核心内容</strong>：
<ul>
<li><strong>背景介绍</strong>：回答“为什么要做这个功能？” 它的业务价值和要解决的核心痛点是什么？</li>
<li><strong>关联文档</strong>：附上相关的原始需求文档、客户标书、第三方 API 文档等。</li>
<li><strong>功能列表 (Feature List)</strong>：清晰地列出本功能/系统包含的所有功能点。</li>
</ul>
</li>
</ul>
<h4 id="22-技术难点分析文档"><strong>2.2 技术难点分析文档</strong></h4>
<ul>
<li><strong>面向对象</strong>：研发（RD）、技术负责人（TL）</li>
<li><strong>核心内容</strong>：这份文档用于识别和攻克技术瓶颈，是项目排期和风险评估的重要依据。
<ul>
<li><strong>技术预研</strong>：针对团队成员不熟悉的技术或场景，进行的前置调研和论证。</li>
<li><strong>PoC (Proof of Concept)</strong>：记录关键技术点的原型验证过程与结论。</li>
<li><strong>示例</strong>：
<ul>
<li>《调研：如何在 K8s Pod 内获取真实的客户端 IP》</li>
<li>《方案对比：单端口复用多种四层协议的技术实现》</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="23-方案设计文档--核心基石"><strong>2.3 方案设计文档 (⭐️⭐️⭐️ 核心基石)</strong></h4>
<ul>
<li><strong>面向对象</strong>：研发（RD）、技术负责人（TL）</li>
<li><strong>核心内容</strong>：这是最重要、最需要频繁维护的文档，详细记录了系统的技术实现方案。对于复杂系统，建议将以下各项拆分为独立文档。
<ol>
<li><strong>数据库设计</strong>：
<ul>
<li>详细的表结构定义（字段、类型、索引、注释）。</li>
<li>非关系型数据库（如 Redis, MongoDB）的 Key 设计、文档结构等。</li>
</ul>
</li>
<li><strong>API 接口设计</strong>：
<ul>
<li>可以使用 Swagger/OpenAPI 等工具管理，但建议在此记录每次迭代的<strong>接口变更日志</strong>，方便协作方快速了解变化。</li>
<li>文件名示例：<code>2023-10-25-导出接口字段更新-v1.3.md</code></li>
</ul>
</li>
<li><strong>服务流程说明</strong>：
<ul>
<li><strong>架构图/流程图</strong>：使用图表清晰地展示数据流、控制流和模块间的交互关系。</li>
<li><strong>详细逻辑阐述</strong>：用文字对流程图中的关键路径和复杂逻辑进行补充说明。</li>
</ul>
</li>
<li><strong>监控与告警设计 (Metrics)</strong>：
<ul>
<li>服务需要暴露哪些可观测性指标 (Metrics)？</li>
<li>如何定义告警规则和阈值？</li>
</ul>
</li>
<li><strong>日志设计</strong>：
<ul>
<li>定义结构化日志的格式和关键字段。</li>
<li>说明日志的存储策略、生命周期等。</li>
</ul>
</li>
</ol>
</li>
</ul>
<h4 id="24-功能使用说明文档"><strong>2.4 功能使用说明文档</strong></h4>
<ul>
<li><strong>面向对象</strong>：产品经理（PM）、客户、测试（QA）、运维（Ops）</li>
<li><strong>核心内容</strong>：用通俗易懂的语言，一步步地指导用户如何使用本功能。<strong>避免使用过多的技术术语</strong>。
<ul>
<li><strong>示例</strong>：
<ul>
<li>《如何在 Windows Server 上安装 Agent》</li>
<li>《三步教你创建一条全局白名单规则》</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="25-问题排查指南-faq"><strong>2.5 问题排查指南 (F.A.Q)</strong></h4>
<ul>
<li><strong>面向对象</strong>：研发（RD）、技术负责人（TL）、技术支持</li>
<li><strong>核心内容</strong>：以“现象-原因-解决方案”的结构，列出该模块可能出现的常见问题及其处理方法。
<ul>
<li><strong>示例</strong>：
<ul>
<li><strong>现象</strong>：无法创建“蜜罐账户”。</li>
<li><strong>可能原因 1</strong>：目标域控服务器的 LDAP 端口未开放。</li>
<li><strong>解决方案 1</strong>：在服务器防火墙上添加入站规则，放行该端口。</li>
<li><strong>可能原因 2</strong>：网络策略限制了应用服务器到域控的连接。</li>
<li><strong>解决方案 2</strong>：联系网络管理员，申请开放相关网络访问策略。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="26-环境搭建与配置文档"><strong>2.6 环境搭建与配置文档</strong></h4>
<ul>
<li><strong>面向对象</strong>：研发（RD）、技术负责人（TL）、运维（Ops）</li>
<li><strong>核心内容</strong>：说明本模块/服务如何独立编译、启动和配置。
<ul>
<li>编译构建命令。</li>
<li>配置文件（<code>config.yaml</code>）中每个参数的详细说明。</li>
<li>服务启动/停止脚本。</li>
</ul>
</li>
</ul>
<h4 id="27-系统压测与性能分析文档"><strong>2.7 系统压测与性能分析文档</strong></h4>
<ul>
<li><strong>面向对象</strong>：研发（RD）、技术负责人（TL）、运维（Ops）</li>
<li><strong>核心内容</strong>：展示系统的性能基线，为容量规划和性能优化提供数据支持。
<ul>
<li>压测报告：包括 QPS、延迟、CPU/内存使用率等关键指标。</li>
<li>压测工具和脚本的使用说明。</li>
<li>性能瓶颈分析与调优记录。</li>
</ul>
</li>
</ul>
<hr>
<h3 id="三-文档编写的最佳实践-golden-rules"><strong>三、 文档编写的最佳实践 (Golden Rules)</strong></h3>
<p>好的工具和框架需要好的习惯来配合。以下是一些我们推荐的文档编写建议：</p>
<ol>
<li><strong>简洁至上</strong>：语言力求简洁明了，避免直接复制粘贴大段网络文章，应提炼和总结。</li>
<li><strong>善用图表</strong>：一张清晰的流程图、架构图胜过千言万语。</li>
<li><strong>版本化思维</strong>：
<ul>
<li>文档标题前缀建议加上日期（如 <code>2023-10-25-</code>），方便识别时效性。</li>
<li>针对迭代的功能更新，建议<strong>新建文档</strong>，而不是直接在旧文档上修改，以保留历史版本。</li>
</ul>
</li>
<li><strong>建立连接</strong>：在文档末尾附上相关链接，将分散的文档串联成知识网络。</li>
<li><strong>代码片段辅助</strong>：在解释核心逻辑时，可以贴入关键的伪代码或真实代码片段，但要避免大段代码刷屏。</li>
<li><strong>契约精神</strong>：对外（前端、其他团队）提供的文档（尤其是 API 文档）是团队间的“契约”，任何修改都必须及时通知所有相关方。</li>
<li><strong>定期回顾</strong>：文档的价值在于其准确性。定期回顾和更新自己负责的文档，使其与代码和业务保持同步。</li>
</ol>
<hr>
<h3 id="结语"><strong>结语</strong></h3>
<p>建立一套完善的文档体系并非一朝一夕之功，它需要团队负责人推动，并融入到每个成员的日常工作中，最终形成一种文化。这套体系看似增加了“额外”的工作，但从长远来看，它带来的<strong>沟通效率提升、知识有效沉淀、团队稳定性增强</strong>等收益，将远远超过投入的成本。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/project-retrospective" term="project-retrospective" label="Project Retrospective" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%A1%B9%E7%9B%AE" term="%E9%A1%B9%E7%9B%AE" label="项目" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%B7%A5%E4%BD%9C" term="%E5%B7%A5%E4%BD%9C" label="工作" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%96%87%E6%A1%A3" term="%E6%96%87%E6%A1%A3" label="文档" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[回顾项目-用PySpark和KMeans挖掘海量Web日志中的攻击模式]]></title>
            <link href="https://blog.yunpiao.site/post/20250715152828/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250715152828/</id>
            
            
            <published>2025-07-15T15:28:28+08:00</published>
            <updated>2025-07-15T15:28:28+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>现在想起这个项目, 当时的我真是个天才, 对机器学习和深度学习分类出来的恶意模型, 进行自动分类, 再也不用一条一条分析日志了</p>
</blockquote>
<hr>
<h2 id="详细步骤">详细步骤</h2>
<ol>
<li><strong>数据源</strong>:
<ul>
<li>原始流量日志。</li>
</ul>
</li>
<li><strong>初级检测 (深度学习)</strong>:
<ul>
<li>原始数据首先经过一个<strong>深度学习模型</strong>进行快速筛选。</li>
<li>输出包括：所有流量的预测结果、仅恶意流量、以及初步报告。</li>
</ul>
</li>
<li><strong>综合检测 (混合模型)</strong>:
<ul>
<li>原始数据被一个更复杂的模型处理，该模型融合了<strong>机器学习 + 传统规则 + 深度学习</strong>的预测结果。</li>
<li>这是系统的主力检测环节，产出更精确的综合性结果。</li>
</ul>
</li>
<li><strong>无监督聚类与模式发现</strong>:
<ul>
<li>综合检测后的恶意流量，会进入一个<strong>聚类分析</strong>流程（这与您之前提供的PySpark代码高度吻合）。</li>
<li>目的是将相似的攻击自动归类，发现未知的攻击模式。</li>
</ul>
</li>
<li><strong>行为分析 (扫描与爬虫检测)</strong>:
<ul>
<li>这是一个并行的分析流水线，它不只看单条请求，而是分析<strong>连续流量特征</strong>。</li>
<li>专门用于识别大规模扫描、爬虫、以及特定时间段内的恶意活动。</li>
</ul>
</li>
<li><strong>人工校验与模型优化</strong>:
<ul>
<li>聚类结果会由安全分析师进行<strong>人工校验</strong>。</li>
<li>校验结果（manual_report）和修正集（rec）会反哺给模型，形成一个闭环，让模型越来越准。</li>
</ul>
</li>
<li><strong>最终报告</strong>:
<ul>
<li>系统最终产出两类核心报告：给技术分析师的<strong>聚类报告</strong>和给管理层的<strong>态势感知报告</strong>。</li>
</ul>
</li>
</ol>
<hr>
<h3 id="项目结构与流程总结"><strong>项目结构与流程总结</strong></h3>
<h4 id="一-项目概述"><strong>一、 项目概述</strong></h4>
<p>本项目旨在构建一个智能化的恶意流量检测与分析系统。该系统不仅能识别已知的攻击模式，更核心的是具备<strong>增量学习和自我演进</strong>的能力，能够应对不断变种的恶意流量。项目通过结合<strong>机器学习</strong>（聚类分析）和<strong>数据挖掘</strong>（关联规则）技术，实现从海量流量中自动化地发现新攻击模式、提取检测规则，并最终反哺检测引擎，形成一个闭环优化的生态系统。</p>
<p>同时，项目还深入到<strong>连续流量行为分析</strong>层面，通过定义攻击生命周期的不同阶段，从宏观和动态的视角对扫描、注入、Webshell、僵尸网络等复杂攻击行为进行态势感知和深度识别。</p>
<h4 id="二-核心流程与方法论"><strong>二、 核心流程与方法论</strong></h4>
<p>项目的核心流程可概括为一个**“发现-分析-提取-增强”**的闭环。</p>
<ol>
<li>
<p><strong>模式发现 (聚类分析)</strong></p>
<ul>
<li><strong>挑战</strong>: 恶意流量的Payload（攻击载荷）会不断产生变体，但其核心模式通常在一定范围内。</li>
<li><strong>方法</strong>: 首先，利用 <code>word2vec</code> 模型将文本化的流量数据转换为高维向量。然后，采用 <code>Kmeans</code> 等聚类算法对这些向量进行无监督聚类。</li>
<li><strong>产出</strong>: 将海量的、看似杂乱的恶意流量划分为若干个高度内聚的类别。每个类别代表一种或一类相似的攻击模式集合。</li>
</ul>
</li>
<li>
<p><strong>规则提取 (关联性分析)</strong></p>
<ul>
<li><strong>目标</strong>: 从每个聚类簇中，自动化地提取出能够精确描述该类攻击的“最小特征集”或“频繁项规则”。</li>
<li><strong>方法</strong>: 在每个聚类簇内部，应用频繁项挖掘算法（如Apriori或FP-Growth），找出Payload中频繁出现的组合模式。</li>
<li><strong>产出</strong>: 一系列高置信度的攻击规则，可直接用于检测。</li>
</ul>
</li>
<li>
<p><strong>引擎增强与模型迭代</strong></p>
<ul>
<li><strong>流程</strong>: 将新提取的频繁项规则加入到现有的检测引擎中，提高检测系统的覆盖面和准确率。</li>
<li><strong>闭环</strong>: 同时，将每个簇中的典型攻击样本回传给上游的分析模型，作为新的训练数据，从而增强模型对新变种的识别能力，完成一次<strong>增量更新</strong>。</li>
</ul>
</li>
</ol>
<h4 id="三-当前进展与数据洞察"><strong>三、 当前进展与数据洞察</strong></h4>
<p><strong>1. 流量态势分析:</strong><br>
对约 <strong>6xxx万</strong> 条总流量的初步分析显示：</p>
<ul>
<li>恶意流量总量为 <strong>3xx万</strong>，整体占比为 <strong>5.34%</strong>。</li>
<li>不同节点（如S1, S4, S7）的恶意流量占比存在显著差异（从2.99%到7.65%不等），表明攻击流量在网络中分布不均，可能存在重点攻击目标或来源。</li>
</ul>
<p><strong>2. 恶意流量构成分析 (以S1, S7节点为例):</strong></p>
<ul>
<li><strong>SQL注入</strong>是绝对主流的攻击类型，在S1和S7节点的恶意流量中占比分别高达 <strong>57.7%</strong> 和 <strong>52.7%</strong>。</li>
<li><strong>Struct2漏洞</strong> 和 <strong>XSS攻击</strong> 同样占据重要比例，是需要重点关注的攻击向量。</li>
<li>该数据明确了当前分析和防御的优先级。</li>
</ul>
<p><strong>3. 聚类效果验证:</strong><br>
已成功将流量样本聚类成50个类别，初步观察显示，各类内部的流量样本在攻击手法上表现出高度相似性，证明了**“word2vec + Kmeans”**技术路径在自动化发现攻击模式上的有效性。</p>
<h4 id="四-连续流量行为分析-重点分析方向"><strong>四、 连续流量行为分析 (重点分析方向)</strong></h4>
<p>为实现更深层次的威胁识别，项目将分析视角从“单条流量”扩展到“连续行为”，并定义了两类特征集：</p>
<ul>
<li><strong>单条特征集 (静态特征)</strong>: Payload的静态特点，用于识别攻击类型（如SQL注入、Webshell）。</li>
<li><strong>统计特征集 (动态特征)</strong>: 基于时间序列、流量大小、响应码分布等统计数据，用于识别攻击所处的<strong>阶段</strong>（如扫描、利用、潜伏、通信）。</li>
</ul>
<p><strong>各攻击类型的分析计划如下：</strong></p>
<table>
<thead>
<tr>
<th style="text-align:left">攻击类型</th>
<th style="text-align:left">攻击阶段划分</th>
<th style="text-align:left">当前分析基础</th>
<th style="text-align:left">下一步工作计划</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>SQL注入</strong></td>
<td style="text-align:left">1. 攻击尝试<!-- raw HTML omitted -->2. 攻击成功</td>
<td style="text-align:left">- 以 <code>sqlmap</code> 攻击流程为蓝本。<!-- raw HTML omitted -->- 识别到攻击成功时Response长度会发生显著变化。</td>
<td style="text-align:left">- <strong>深入分析 <code>sqlmap</code></strong>: 研究其在盲注、注入点判断、数据库指纹识别等成功判定上的具体实现方式和流量特征。</td>
</tr>
<tr>
<td style="text-align:left"><strong>Webshell</strong></td>
<td style="text-align:left">1. 上传尝试<!-- raw HTML omitted -->2. 上传成功<!-- raw HTML omitted -->3. 客户端连接<!-- raw HTML omitted -->4. Webshell通信</td>
<td style="text-align:left">- 已有初步特征分析。</td>
<td style="text-align:left">- <strong>搭建靶机复现</strong>: 利用“中国菜刀”等多种工具实施攻击，捕捉并分析完整的攻击流量。<!-- raw HTML omitted -->- <strong>细化研究</strong>: 重点分析“上传漏洞发掘”及不同工具（菜刀、御剑等）在“Webshell通信”阶段的流量差异。</td>
</tr>
<tr>
<td style="text-align:left"><strong>HTTP僵尸网络</strong></td>
<td style="text-align:left">1. 病毒植入<!-- raw HTML omitted -->2. 病毒激活<!-- raw HTML omitted -->3. 连接C&amp;C服务器<!-- raw HTML omitted -->4. C&amp;C通信<!-- raw HTML omitted -->5. 发起攻击</td>
<td style="text-align:left">- 已有理论框架和参考资料。</td>
<td style="text-align:left">- <strong>搭建靶机复现</strong>: 在实验室环境中模拟完整的Botnet生命周期，理解各阶段产生的流量特征。<!-- raw HTML omitted -->- <strong>分析已有流量</strong>: 结合复现经验，对现有流量数据池进行特征挖掘。</td>
</tr>
<tr>
<td style="text-align:left"><strong>CMS漏洞利用</strong></td>
<td style="text-align:left">- （根据具体漏洞）</td>
<td style="text-align:left">- 已有理论框架。</td>
<td style="text-align:left">- <strong>构建漏洞知识库</strong>: 收集公开的CMS漏洞信息，整理其攻击载荷（Exploit）和产生的流量特征，形成一个可用于检测的特征库。</td>
</tr>
<tr>
<td style="text-align:left"><strong>扫描/爬虫</strong></td>
<td style="text-align:left">- （行为单一）</td>
<td style="text-align:left">- 已有理论框架。</td>
<td style="text-align:left">- <strong>细化统计特征</strong>: 通过分析连接频率、请求间隔方差、User-Agent一致性、响应码分布（如大量404）等来精确识别。</td>
</tr>
</tbody>
</table>
<h4 id="五-总结"><strong>五、 总结</strong></h4>
<p>本项目核心优势在于<strong>将机器学习的自动化模式发现能力与专家知识驱动的行为分析相结合</strong>，构建了一个能够自我演进的智能检测体系。当前项目已在数据分析和技术验证上取得初步成果，后续工作的关键在于<strong>深入复现和分析各类攻击的完整生命周期</strong>，将行为阶段识别的“动态特征”与Payload分析的“静态特征”有效融合，从而实现对高级、持续性网络攻击的精准感知与防御。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/project-retrospective" term="project-retrospective" label="Project Retrospective" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%AE%89%E5%85%A8" term="%E5%AE%89%E5%85%A8" label="安全" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%A1%B9%E7%9B%AE" term="%E9%A1%B9%E7%9B%AE" label="项目" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[项目回顾-检测恶意 http 流量时的词向量选型]]></title>
            <link href="https://blog.yunpiao.site/post/20250715151121/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250715151121/</id>
            
            
            <published>2025-07-15T15:11:21+08:00</published>
            <updated>2025-07-15T15:11:21+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>当时做完安全项目后, 计划升级下特征, 用词向量来进行特征转化, 但是实际下来, 效果很差, 当时使用的 300 维, 现在 gpt 都几万维了</p>
</blockquote>
<hr>
<h2 id="fasttext-vs-word2vec一场关于词向量的深度对决">FastText vs. Word2Vec：一场关于词向量的深度对决</h2>
<p>不久前，Facebook Research 开源了一个令人振奋的项目——<a href="https://github.com/facebookresearch/fastText" target="_blank" rel="noopener nofollow noreferrer" >fastText</a>。它号称是一种既快速又高效的词向量学习和文本分类方法。作为一名NLP爱好者，我立刻燃起了好奇心：它的词向量（Embedding）与我们熟知的经典模型 Word2Vec 相比，究竟孰优孰劣？</p>
<p>考虑到 fastText 的设计深受 Word2Vec 启发，将两者直接比较再合适不过了。于是，我进行了一系列实验，希望通过数据揭示它们各自的优势和内在机理。</p>
<h3 id="两位选手简介">两位选手简介</h3>
<p>在开始对决之前，让我们先简单了解一下两位“选手”。</p>
<ol>
<li>
<p><strong>Word2Vec (Gensim 实现)</strong>：由 Google 的 Tomas Mikolov 团队提出，是词向量领域的奠基之作。它将每个词视为一个独立的、不可分割的原子单位，通过上下文来学习其向量表示（“一个词的含义由其周围的词决定”）。例如，<code>apple</code> 这个词，在 Word2Vec 眼中就是一个整体。</p>
</li>
<li>
<p><strong>fastText</strong>：可以看作是 Word2Vec 的一个巧妙扩展。它最大的创新在于引入了<strong>子词信息 (Subword Information)</strong>。fastText 不仅为整个词学习向量，还为词内部的字符 n-gram（例如，对于 <code>apple</code>，可能会有 <code>app</code>, <code>ppl</code>, <code>ple</code> 等）学习向量。一个词的最终向量是其所有子词向量的总和。</p>
</li>
</ol>
<p>这个看似微小的改动，却是 fastText 的“秘密武器”，我们稍后会看到它如何发挥巨大作用。</p>
<h3 id="实验场数据与方法">实验场：数据与方法</h3>
<p>为了进行公平且全面的比较，我设计了两个实验场景：</p>
<ol>
<li>
<p><strong>标准基准测试 (Quantitative)</strong>：使用NLP领域公认的 <code>text8</code> 数据集（维基百科文本的集合）进行训练，并通过 <code>questions-words.txt</code> 词汇类比任务来评估词向量的质量。这个任务包含两类问题：</p>
<ul>
<li><strong>语义类比 (Semantic)</strong>：例如 &ldquo;king - man + woman ≈ queen&rdquo; (国王 - 男人 + 女人 ≈ 王后)，考验模型对概念关系的理解。</li>
<li><strong>句法类比 (Syntactic)</strong>：例如 &ldquo;amazing - amazingly ≈ calm - calmly&rdquo; (形容词 - 副词)，考验模型对词形、语法结构的理解。</li>
</ul>
</li>
<li>
<p><strong>定性观察 (Qualitative)</strong>：在我的一个中文项目（星座 vs. IT文章）中，我也分别训练了两个模型，直观地感受它们在特定领域对词义的捕捉能力。</p>
</li>
</ol>
<p>我使用了 <code>gensim</code> 库来训练 Word2Vec 模型，并使用 fastText 的官方命令行工具进行训练，两者都尽量采用相似的默认超参数（如向量维度=100，窗口大小=5）以保证公平性。</p>
<h3 id="结果揭晓数据不会说谎">结果揭晓：数据不会说谎</h3>
<p>在 <code>text8</code> 数据集上运行词汇类比任务后，结果非常清晰：</p>
<table>
<thead>
<tr>
<th style="text-align:left">任务类型</th>
<th style="text-align:center">Word2Vec 准确率</th>
<th style="text-align:center">fastText 准确率</th>
<th style="text-align:center">优胜者</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>语义类比</strong></td>
<td style="text-align:center">表现优异</td>
<td style="text-align:center"><strong>更胜一筹</strong></td>
<td style="text-align:center"><strong>fastText</strong></td>
</tr>
<tr>
<td style="text-align:left"><strong>句法类比</strong></td>
<td style="text-align:center">表现尚可</td>
<td style="text-align:center"><strong>压倒性优势</strong></td>
<td style="text-align:center"><strong>fastText</strong></td>
</tr>
</tbody>
</table>
<p><strong>核心发现：</strong></p>
<ol>
<li><strong>句法任务上的绝对王者</strong>：fastText 在句法类比任务上的表现远超 Word2Vec。这完全符合预期。</li>
<li><strong>语义任务上的强大挑战者</strong>：在较小的数据集上，Word2Vec 的语义表现可能与 fastText 相当甚至略优。但随着 <code>text8</code> 这样的大语料库的引入，fastText 的语义准确率也显著提升，并最终超越了 Word2Vec。</li>
</ol>
<h3 id="深度剖析fasttext-为何在句法上如此强大">深度剖析：fastText 为何在句法上如此强大？</h3>
<p>答案就在于我们前面提到的<strong>子词信息</strong>。</p>
<p>让我们回到那个句法类比的例子：<code>amazing amazingly calm calmly</code>。</p>
<p>这个任务的正确逻辑是：<br>
<code>vector(&quot;amazingly&quot;) - vector(&quot;amazing&quot;) ≈ vector(&quot;calmly&quot;) - vector(&quot;calm&quot;)</code></p>
<ul>
<li>
<p><strong>对于 fastText</strong>：</p>
<ul>
<li><code>vector(&quot;amazing&quot;)</code> 是 <code>a, am, ma, ...</code> 等子词向量的和。</li>
<li><code>vector(&quot;amazingly&quot;)</code> 是 <code>a, am, ma, ...</code> <strong>加上</strong> <code>-ly</code> 相关子词（如 <code>ly</code>, <code>gly</code>, <code>ngly</code>）向量的和。</li>
<li>当两者相减时，<code>amazing</code> 共同的子词部分被抵消，留下的向量很大程度上代表了<strong>从形容词变为副词的“-ly”后缀</strong>的语义。这个“后缀向量”是通用的，因此它与 <code>vector(&quot;calmly&quot;) - vector(&quot;calm&quot;)</code> 的结果高度相似。</li>
</ul>
</li>
<li>
<p><strong>对于 Word2Vec</strong>：</p>
<ul>
<li><code>amazing</code> 和 <code>amazingly</code> 是两个完全独立、无内在联系的词。模型必须从海量语料中偶然学到它们之间的关系。它无法像 fastText 那样，从结构上“理解”这种词形变化。</li>
</ul>
</li>
</ul>
<p>这个机制也解释了为什么 fastText 对<strong>未登录词 (Out-of-Vocabulary, OOV)</strong> 和<strong>罕见词</strong>特别友好。即使一个词没在训练集中出现过，fastText 依然能通过其已知的字符 n-gram 来“拼凑”出一个合理的向量。</p>
<h3 id="实践指南我该如何选择">实践指南：我该如何选择？</h3>
<p>通过这次实验，我们可以得出一些非常实用的结论：</p>
<ol>
<li>
<p><strong>优先选择 fastText 的场景</strong>：</p>
<ul>
<li><strong>处理形态丰富的语言</strong>：对于英语、德语、俄语等拥有大量前缀、后缀和词形变化的语言，fastText 的子词模型优势巨大。</li>
<li><strong>需要处理拼写错误或未登录词</strong>：在用户生成内容（UGC）、社交媒体文本等场景，fastText 的鲁棒性更强。</li>
<li><strong>重视句法信息</strong>：如果你的下游任务（如词性标注、依存句法分析）依赖于词的结构信息，fastText 是不二之选。</li>
<li><strong>追求速度</strong>：fastText 的训练速度极快，尤其在文本分类任务上，它能在几分钟内处理完数百万条数据，效果媲美深度学习模型。</li>
</ul>
</li>
<li>
<p><strong>Word2Vec 依然有价值的场景</strong>：</p>
<ul>
<li><strong>作为强大的基线模型</strong>：Word2Vec 简单、直观，效果稳健，是任何 NLP 项目起步时一个绝佳的 baseline。</li>
<li><strong>处理形态简单的语言</strong>：正如我的分析中提到的，对于中文这类词汇相对固定、缺少复杂形态变化的语言，fastText 的子词优势可能不那么明显。在这种情况下，Word2Vec 依然是一个极具竞争力的选择。</li>
</ul>
</li>
</ol>
<h3 id="结语">结语</h3>
<p>这次对比实验清晰地展示了 fastText 相较于 Word2Vec 的演进和优势，尤其是在利用子词信息增强对词汇形态的理解方面。它不仅在句法任务上取得了压倒性胜利，并且在语料充足时，其语义表示能力也同样出色。</p>
<p>当然，衡量词向量好坏的最终标准是它们在<strong>下游任务（如文本分类、情感分析）中的表现</strong>。我的下一步计划，就是将这两种方法生成的词向量应用到我的 PySpark 分类流程中，看看它们谁能带来更大的性能提升。</p>
<p>技术的演进永无止境，但通过亲手实验来理解其背后的原理，总能让我们在选择工具时更加从容和自信。</p>
<p><strong>参考文献</strong></p>
<ol>
<li><a href="https://arxiv.org/pdf/1607.04606v1.pdf" target="_blank" rel="noopener nofollow noreferrer" >Enriching Word Vectors with Subword Information (fastText 论文)</a></li>
<li><a href="https://arxiv.org/pdf/1301.3781v3.pdf" target="_blank" rel="noopener nofollow noreferrer" >Efficient Estimation of Word Representations in Vector Space (Word2Vec 论文)</a></li>
</ol>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/project-retrospective" term="project-retrospective" label="Project Retrospective" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%A1%B9%E7%9B%AE" term="%E9%A1%B9%E7%9B%AE" label="项目" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%AE%89%E5%85%A8" term="%E5%AE%89%E5%85%A8" label="安全" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[回顾项目-用机器学习检测恶意流量]]></title>
            <link href="https://blog.yunpiao.site/post/20250715145913/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250715145913/</id>
            
            
            <published>2025-07-15T14:59:13+08:00</published>
            <updated>2025-07-15T14:59:13+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>翻出来之前在一个安全中心做的恶意流量识别项目的代码, 做下总结</p>
</blockquote>
<hr>
<h2 id="使用-pyspark-和-mllib-构建-web-攻击检测模型从-n-gram-到逻辑回归">使用 PySpark 和 MLlib 构建 Web 攻击检测模型：从 N-gram 到逻辑回归</h2>
<p>在网络安全领域，检测恶意 Web 请求（如 SQL 注入、跨站脚本攻击 XSS）是一项至关重要的任务。本文将深入探讨如何使用 PySpark 和 MLlib 构建一个高效的 Web 攻击检测模型。我们将从数据预处理、特征工程（N-gram 分词）、模型训练到最终的预测和评估，一步步揭示其背后的技术细节。</p>
<h3 id="技术栈概览">技术栈概览</h3>
<p>我们的解决方案基于以下核心技术：</p>
<ul>
<li><strong>PySpark</strong>: 用于大规模数据处理和分布式计算。</li>
<li><strong>MLlib</strong>: PySpark 的机器学习库，提供丰富的算法和工具，包括特征转换、模型训练和评估。</li>
<li><strong>N-gram 分词</strong>: 一种文本特征提取技术，将 URL 和 Payload 切分成连续的子字符串，用于捕捉恶意模式。</li>
<li><strong>逻辑回归 (Logistic Regression)</strong>: 一种常用的分类算法，用于预测 Web 请求是否为恶意。</li>
</ul>
<h3 id="数据准备与特征工程">数据准备与特征工程</h3>
<p>首先，我们需要准备用于训练和预测的数据。通常，这些数据来源于 Web 服务器的访问日志，包含 URL、POST 数据、HTTP 请求头等信息。</p>
<p><strong>1. 加载数据</strong></p>
<p>我们使用 PySpark 从 Parquet 格式的文件中读取数据：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>data <span style="color:#f92672">=</span> spark<span style="color:#f92672">.</span>read<span style="color:#f92672">.</span>parquet(<span style="color:#e6db74">&#34;data/new_data&#34;</span>)<span style="color:#f92672">.</span>fillna(<span style="color:#e6db74">&#34;&#34;</span>)<span style="color:#f92672">.</span>repartition(<span style="color:#ae81ff">1024</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>data<span style="color:#f92672">.</span>cache()
</span></span></code></pre></div><p><code>fillna(&quot;&quot;)</code> 用于处理缺失值，<code>repartition(1000)</code> 用于将数据分成多个分区，以便并行处理。<code>cache()</code> 则将数据缓存到内存中，加速后续操作。</p>
<p><strong>2. 特征工程：N-gram 分词</strong>(可以用现有的大模型分词代替, o200k )</p>
<p>特征工程是机器学习的关键步骤。在本例中，我们使用 N-gram 分词技术从 URL 和 POST 数据中提取特征。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#f92672">from</span> pyspark.sql.functions <span style="color:#f92672">import</span> udf
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span><span style="color:#f92672">from</span> pyspark.sql.types <span style="color:#f92672">import</span> StringType, ArrayType
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span><span style="color:#f92672">import</span> pyspark.sql.functions <span style="color:#66d9ef">as</span> F
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5">5</a></span><span><span style="color:#75715e"># 自定义 UDF 函数，用于 N-gram 分词</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6">6</a></span><span>udf_ngram_split <span style="color:#f92672">=</span> udf(ngram_split, ArrayType(StringType()))
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7">7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8">8</a></span><span>sel_data <span style="color:#f92672">=</span> data<span style="color:#f92672">.</span>select(<span style="color:#e6db74">&#34;url&#34;</span>,<span style="color:#e6db74">&#34;post&#34;</span>,udf_ngram_split(<span style="color:#e6db74">&#34;url&#34;</span>,<span style="color:#e6db74">&#34;post&#34;</span>)<span style="color:#f92672">.</span>alias(<span style="color:#e6db74">&#34;data&#34;</span>),F<span style="color:#f92672">.</span>lit(<span style="color:#ae81ff">1</span>)<span style="color:#f92672">.</span>alias(<span style="color:#e6db74">&#34;label&#34;</span>))
</span></span></code></pre></div><p><code>ngram_split</code> 是一个自定义的 Python 函数，它将输入的字符串（URL 和 POST 数据）切分成 N-gram 序列。例如，对于字符串 &ldquo;path&rdquo;，3-gram 分词的结果是 <code>[&quot;pat&quot;, &quot;ath&quot;]</code>。</p>
<p><strong>3. 其他特征</strong></p>
<p>除了 N-gram 分词，我们还可以提取其他有用的特征，例如：</p>
<ul>
<li>HTTP 请求方法 (GET, POST, HEAD)</li>
<li>HTTP 响应状态码 (2xx, 3xx, 4xx, 5xx)</li>
<li>URL 的信息熵</li>
<li>User-Agent 是否为已知恶意 UA</li>
<li>请求路径是否为常见的扫描器路径</li>
</ul>
<p>这些特征可以通过自定义 UDF 函数提取，并添加到 DataFrame 中。</p>
<h3 id="模型训练与预测">模型训练与预测</h3>
<p>在完成特征工程后，我们可以使用 MLlib 训练一个逻辑回归模型。</p>
<p><strong>1. 构建 Pipeline</strong></p>
<p>MLlib 的 Pipeline 提供了一种方便的方式来组织机器学习流程。一个典型的 Pipeline 包含以下步骤：</p>
<ul>
<li><strong>Tokenizer</strong>: 将文本数据切分成单词或 N-gram。</li>
<li><strong>HashingTF 或 CountVectorizer</strong>: 将文本特征转换为数值型向量。</li>
<li><strong>IDF (可选)</strong>: 计算逆文档频率，进行特征加权。</li>
<li><strong>Logistic Regression</strong>: 训练逻辑回归模型。</li>
</ul>
<p><strong>2. 加载预训练模型</strong></p>
<p>由于篇幅限制，本文不详细介绍模型训练过程。我们假设已经训练好了一个模型，并将其保存到磁盘。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#f92672">from</span> pyspark.ml <span style="color:#f92672">import</span> PipelineModel
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>model <span style="color:#f92672">=</span> PipelineModel<span style="color:#f92672">.</span>load(<span style="color:#e6db74">&#34;cv_lr_model&#34;</span>)
</span></span></code></pre></div><p><strong>3. 模型预测</strong></p>
<p>加载模型后，我们可以使用它来预测新的 Web 请求是否为恶意。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>test <span style="color:#f92672">=</span> model<span style="color:#f92672">.</span>transform(sel_data)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span>test<span style="color:#f92672">.</span>groupby(<span style="color:#e6db74">&#34;prediction&#34;</span>)<span style="color:#f92672">.</span>count()<span style="color:#f92672">.</span>show()
</span></span></code></pre></div><p><code>transform</code> 方法将 Pipeline 应用于输入数据，生成包含预测结果的 DataFrame。<code>groupby(&quot;prediction&quot;).count().show()</code> 用于统计预测结果，例如：</p>
<pre tabindex="0"><code>+----------+------+
|prediction| count|
+----------+------+
|       0.0| 11580|
|       1.0|831150|
+----------+------+
</code></pre><p><strong>4. 模型评估</strong></p>
<p>为了评估模型的性能，我们需要使用一些指标，例如准确率、召回率、F1 值等。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span><span style="color:#75715e"># 假设所有数据都是正样本</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span>TP <span style="color:#f92672">=</span> <span style="color:#ae81ff">831150</span>  <span style="color:#75715e"># True Positives</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3">3</a></span><span>FN <span style="color:#f92672">=</span> <span style="color:#ae81ff">11580</span>  <span style="color:#75715e"># False Negatives</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4">4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-5">5</a></span><span>recall <span style="color:#f92672">=</span> TP <span style="color:#f92672">/</span> (TP <span style="color:#f92672">+</span> FN)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-6">6</a></span><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Recall: </span><span style="color:#e6db74">{</span>recall<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>) <span style="color:#75715e"># 召回率</span>
</span></span></code></pre></div><p>在本例中，我们假设所有输入数据都是已知的恶意样本，因此我们主要关注模型的召回率。召回率越高，说明模型能够识别出更多的恶意请求。</p>
<h3 id="总结与展望">总结与展望</h3>
<p>本文介绍了如何使用 PySpark 和 MLlib 构建一个 Web 攻击检测模型。通过 N-gram 分词和逻辑回归，我们可以有效地识别恶意 Web 请求。</p>
<p>未来，我们可以进一步优化模型，例如：</p>
<ul>
<li>使用更复杂的特征工程技术，如 Word2Vec 或 BERT。</li>
<li>使用更大的数据集进行训练，提高模型的泛化能力。</li>
</ul>
<p>希望本文能够帮助读者了解如何使用 PySpark 和 MLlib 构建 Web 攻击检测模型，并为网络安全领域的研究和实践提供一些思路。</p>
<hr>
<p>希望这篇博客文章对你有所帮助！</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/project-retrospective" term="project-retrospective" label="Project Retrospective" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%AE%89%E5%85%A8" term="%E5%AE%89%E5%85%A8" label="安全" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%A1%B9%E7%9B%AE" term="%E9%A1%B9%E7%9B%AE" label="项目" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[杂-如何修复io异常的 wsl 系统]]></title>
            <link href="https://blog.yunpiao.site/post/20250711172444/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250711172444/</id>
            
            
            <published>2025-07-11T17:24:44+08:00</published>
            <updated>2025-07-11T17:24:44+08:00</updated>
            
            
            <content type="html"><![CDATA[<h3 id="如何安全修复wsl2虚拟磁盘的文件系统错误"><strong>如何安全修复WSL2虚拟磁盘的文件系统错误</strong></h3>
<p>当在WSL2的 <code>dmesg</code> 日志中发现 <code>EXT4-fs warning</code> 或 <code>Filesystem error</code> 等信息时，通常表明虚拟磁盘 (<code>ext4.vhdx</code>) 存在文件系统层面的问题。直接在运行中的WSL发行版内执行 <code>e2fsck</code> 会因磁盘正在使用而失败。</p>
<p>最安全、最专业的解决方法是：安装一个临时的“救援”发行版，将有问题的虚拟磁盘作为外部设备挂载到该环境中进行修复。</p>
<h4 id="核心原则"><strong>核心原则</strong></h4>
<p>文件系统检查工具 (<code>e2fsck</code>) 必须在**目标文件系统完全卸载（unmounted）**的状态下运行。</p>
<hr>
<h3 id="操作步骤"><strong>操作步骤</strong></h3>
<h4 id="第1步彻底关闭所有wsl实例"><strong>第1步：彻底关闭所有WSL实例</strong></h4>
<p>此步骤至关重要，确保所有虚拟磁盘都已卸载且未被锁定。在Windows PowerShell或CMD中执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>wsl --shutdown
</span></span></code></pre></div><h4 id="第2步定位目标虚拟磁盘文件-ext4vhdx"><strong>第2步：定位目标虚拟磁盘文件 (<code>ext4.vhdx</code>)</strong></h4>
<p>找到需要修复的WSL发行版（例如Ubuntu）的虚拟硬盘文件。其路径通常位于：</p>
<pre tabindex="0"><code>%localappdata%\Packages\
</code></pre><p>在该目录下，找到对应发行版的文件夹（如 <code>CanonicalGroupLimited.Ubuntu_...</code>），进入 <code>LocalState</code> 目录，即可找到 <code>ext4.vhdx</code> 文件。复制其完整路径以备后用。</p>
<p><strong>示例路径</strong>: <code>C:\Users\YourUser\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\LocalState\ext4.vhdx</code></p>
<h4 id="第3步安装一个轻量级救援发行版"><strong>第3步：安装一个轻量级“救援”发行版</strong></h4>
<p>推荐使用Alpine，因为它安装迅速、占用空间小。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1"> 1</a></span><span>wsl --install Alpine```
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2"> 2</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3"> 3</a></span><span><span style="color:#75715e">#### **第4步：将目标磁盘挂载到救援系统**</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4"> 4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5"> 5</a></span><span>以裸盘<span style="color:#960050;background-color:#1e0010">（</span>bare<span style="color:#960050;background-color:#1e0010">）</span>模式将目标虚拟磁盘挂载到Alpine<span style="color:#960050;background-color:#1e0010">。</span>这种模式只附加设备<span style="color:#960050;background-color:#1e0010">，</span>不挂载文件系统<span style="color:#960050;background-color:#1e0010">。</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6"> 6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-7"> 7</a></span><span>**以管理员权限**打开PowerShell<span style="color:#960050;background-color:#1e0010">，</span>执行以下命令<span style="color:#960050;background-color:#1e0010">，</span>注意替换为自己的`ext4.vhdx`文件路径<span style="color:#960050;background-color:#1e0010">。</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-8"> 8</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-9"> 9</a></span><span>```powershell
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-10">10</a></span><span><span style="color:#75715e"># --vhd: 指明挂载的是VHD文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-11">11</a></span><span><span style="color:#75715e"># -d Alpine: 指定由Alpine发行版处理挂载</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-12">12</a></span><span><span style="color:#75715e"># --bare: 关键参数，只附加设备而不挂载文件系统</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-13">13</a></span><span>wsl --mount <span style="color:#e6db74">&#34;C:\Path\To\Your\ext4.vhdx&#34;</span> --vhd -d Alpine --bare
</span></span></code></pre></div><h4 id="第5步在救援系统中执行文件系统检查"><strong>第5步：在救援系统中执行文件系统检查</strong></h4>
<ol>
<li>
<p>启动并进入Alpine环境：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>wsl -d Alpine
</span></span></code></pre></div></li>
<li>
<p>进入Alpine后，使用 <code>lsblk</code> 命令查找新挂载的设备名称（如 <code>/dev/sdb</code>, <code>/dev/sdc</code> 等）。</p>
</li>
<li>
<p>使用 <code>e2fsck</code> 对找到的设备进行检查和修复。</p>
<ul>
<li>使用 <code>sudo</code> 获取权限。</li>
<li>使用 <code>-f</code> 参数强制进行检查。</li>
<li>将 <code>/dev/sdX</code> 替换为上一步找到的真实设备名。</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span><span style="color:#75715e"># 在Alpine终端内执行</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2">2</a></span><span>sudo e2fsck -f /dev/sdX
</span></span></code></pre></div><p>根据提示输入 <code>y</code> 同意修复所有发现的错误。</p>
</li>
</ol>
<h4 id="第6步清理并恢复"><strong>第6步：清理并恢复</strong></h4>
<ol>
<li>检查完成后，退出Alpine终端。
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span>exit
</span></span></code></pre></div></li>
<li>回到Windows PowerShell，卸载之前挂载的虚拟磁盘。
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span>wsl --unmount <span style="color:#e6db74">&#34;C:\Path\To\Your\ext4.vhdx&#34;</span>
</span></span></code></pre></div></li>
</ol>
<p>至此，原WSL发行版的虚拟磁盘已修复完毕，可以正常启动和使用。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[经验-cursor 如何有效使用]]></title>
            <link href="https://blog.yunpiao.site/post/20250604150126/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250604150126/</id>
            
            
            <published>2025-06-04T15:01:26+08:00</published>
            <updated>2025-06-04T15:01:26+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>目前使用 cursor 做程序的实践, cursor 版本: 0.50.7</p>
</blockquote>
<p>三大件 MCP + Doc + Rules</p>
<h2 id="1-mcp-使用">1. MCP 使用</h2>
<p>MCP（Model Context Protocol）是 Cursor 提供的一种机制，允许 AI 助手通过外部服务器获取实时、准确的文档或数据，以增强代码生成的质量。以下是你提到的三个 MCP 的使用实践：</p>
<h3 id="11-deepwiki">1.1 deepwiki</h3>
<p>Deepwiki MCP 是一个开源工具，用于从 GitHub 仓库或 Deepwiki 网站（deepwiki.com）获取最新的文档或代码库内容，特别适合需要爬取 GitHub 仓库文档的场景。它通过 MCP 协议将爬取的内容转换为 Markdown 格式，供 Cursor 使用<br>
<strong>使用方法：</strong></p>
<ul>
<li>在 Cursor 中输入类似 use deepwiki <a href="https://deepwiki.com/shadcn-ui/ui" target="_blank" rel="noopener nofollow noreferrer" >https://deepwiki.com/shadcn-ui/ui</a> 的提示，获取指定 Deepwiki 页面内容。</li>
<li>支持单页或多页爬取，例如 use deepwiki multiple pages <a href="https://deepwiki.com/shadcn-ui/ui" target="_blank" rel="noopener nofollow noreferrer" >https://deepwiki.com/shadcn-ui/ui</a>。</li>
<li>可指定爬取深度和输出格式（单一文档或按页面分割）。</li>
</ul>
<h3 id="12-context7">1.2 context7</h3>
<p>Context7 MCP 是 Upstash 提供的服务，专注于为 LLMs 提供版本特定的最新文档和代码示例，解决 AI 模型生成过时或错误代码的问题。它与 Cursor、Windsurf 等 MCP 兼容客户端无缝集成。<br>
<strong>使用方法：</strong></p>
<ul>
<li>在 Cursor 提示中添加 use context7，例如：<code>Create a Next.js API route with Hono. use context7</code> Context7 将从官方文档拉取最新代码示例，注入到 LLM 的上下文。</li>
<li>支持版本特定的文档查询，例如指定 Next.js 或 React Query 的具体版本</li>
</ul>
<h3 id="13-mcp-feedback-enhanced">1.3 mcp-feedback-enhanced</h3>
<p>在AI辅助开发工具中实现人机协作工作流。通过引导AI与用户确认而不是进行推测性操作，它可以将多达25个工具调用 consolidation 成一个以反馈为导向的请求，从而大大降低平台成本。<br>
<strong>使用方法:</strong></p>
<ul>
<li>配合 rules 使用, llm 制定多个计划, 调用该工具与用户进行确认, 确认后继续执行, 节省请求数</li>
</ul>
<h3 id="14-示例">1.4 示例</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span>{  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span>    <span style="color:#f92672">&#34;mcpServers&#34;</span>: {  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>        <span style="color:#f92672">&#34;deepwiki&#34;</span>: {  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>            <span style="color:#f92672">&#34;command&#34;</span>: <span style="color:#e6db74">&#34;npx&#34;</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>            <span style="color:#f92672">&#34;args&#34;</span>: [  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>                <span style="color:#e6db74">&#34;-y&#34;</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>                <span style="color:#e6db74">&#34;mcp-deepwiki@latest&#34;</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>            ]  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>        },  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>        <span style="color:#f92672">&#34;context7&#34;</span>: {  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>            <span style="color:#f92672">&#34;command&#34;</span>: <span style="color:#e6db74">&#34;npx&#34;</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>            <span style="color:#f92672">&#34;args&#34;</span>: [  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>                <span style="color:#e6db74">&#34;-y&#34;</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>                <span style="color:#e6db74">&#34;@upstash/context7-mcp&#34;</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>            ],  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>            <span style="color:#f92672">&#34;env&#34;</span>: {  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17">17</a></span><span>                <span style="color:#f92672">&#34;DEFAULT_MINIMUM_TOKENS&#34;</span>: <span style="color:#e6db74">&#34;6000&#34;</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18">18</a></span><span>            }  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19">19</a></span><span>        },  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20">20</a></span><span>        <span style="color:#f92672">&#34;mcp-feedback-enhanced&#34;</span>: {  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21">21</a></span><span>            <span style="color:#f92672">&#34;command&#34;</span>: <span style="color:#e6db74">&#34;uvx&#34;</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22">22</a></span><span>            <span style="color:#f92672">&#34;args&#34;</span>: [  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23">23</a></span><span>                <span style="color:#e6db74">&#34;--python&#34;</span>,  <span style="color:#960050;background-color:#1e0010">如果你是远程连接的</span>, <span style="color:#960050;background-color:#1e0010">最好加上这个参数</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-24">24</a></span><span>                <span style="color:#e6db74">&#34;/home/yunpiao/anaconda3/bin/python&#34;</span>,  <span style="color:#960050;background-color:#1e0010">如果你是远程连接的</span>, <span style="color:#960050;background-color:#1e0010">最好加上这个参数</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-25">25</a></span><span>                <span style="color:#e6db74">&#34;mcp-feedback-enhanced@latest&#34;</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-26">26</a></span><span>            ],  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-27">27</a></span><span>            <span style="color:#f92672">&#34;env&#34;</span>: {  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-28">28</a></span><span>                <span style="color:#f92672">&#34;FORCE_WEB&#34;</span>: <span style="color:#e6db74">&#34;true&#34;</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-29">29</a></span><span>                <span style="color:#f92672">&#34;MCP_DEBUG&#34;</span>: <span style="color:#e6db74">&#34;false&#34;</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-30">30</a></span><span>            },  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-31">31</a></span><span>            <span style="color:#f92672">&#34;timeout&#34;</span>: <span style="color:#ae81ff">600</span>,  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-32">32</a></span><span>            <span style="color:#f92672">&#34;autoApprove&#34;</span>: [  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-33">33</a></span><span>                <span style="color:#e6db74">&#34;interactive_feedback&#34;</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-34">34</a></span><span>            ]  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-35">35</a></span><span>        }  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-36">36</a></span><span>    }  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-37">37</a></span><span>}<span style="color:#960050;background-color:#1e0010">}</span>
</span></span></code></pre></div><h2 id="2-doc">2. Doc</h2>
<p>Cursor 0.50.7 支持多种方式获取文档，以增强 AI 助手的上下文准确性。以下是你提到的三种方式：</p>
<h3 id="21-自带的-docs-文档">2.1 自带的 docs 文档</h3>
<p>Cursor 内置了 @ 语法，可直接调用特定库或框架的官方文档。例如，@nginx 会从官方来源获取 Nginx 的最新文档。</p>
<h3 id="22-增加网页文档爬虫方式-官方支持">2.2 增加网页文档(爬虫方式-官方支持)</h3>
<p>Cursor 0.50.7 支持通过爬虫方式从指定网页获取文档，官方提供了集成工具，允许用户指定 URL 并将内容注入 LLM 上下文。<br>
cursor 命令输入框(crtl+shift+p 打开)中填写 <code>&gt;Add New Custom Docs</code>, 之后填写需要进行爬取的网站, 例如<code>https://nginx.org/en/docs/</code></p>
<h3 id="23-增加-github-文档-使用-deepwiki-方式">2.3 增加 github 文档, 使用 deepwiki 方式</h3>
<p>对于一些没有文档的 github\gitlab 项目可以使用 deepwiki 转换成 wiki, 用于添加 doc</p>
<blockquote>
<p>最简单方法, 更改域名中的 github 为 deepwiki , 然后添加到 custom doc 里面</p>
</blockquote>
<h2 id="3-rules">3. Rules</h2>
<h3 id="31-针对项目设置user-cursor-rules">3.1 针对项目设置user cursor rules</h3>
<p>我现在使用的 rules, 找不出出处了, 好像在一个回复里扒出来的, 如果这个有版权的话, 可以联系我删除</p>
<pre tabindex="0"><code>你是Cursor IDE的AI编程助手，遵循核心工作流（研究-&gt;构思-&gt;计划-&gt;执行-&gt;评审）用中文协助用户，面向专业程序员，交互应简洁专业，避免不必要解释。

[沟通守则]
1.  响应以模式标签 `[模式：X]` 开始，初始为 `[模式：研究]`。
2.  核心工作流严格按 `研究-&gt;构思-&gt;计划-&gt;执行-&gt;评审` 顺序流转，用户可指令跳转。

[核心工作流详解]
1.  `[模式：研究]`：理解需求。
2.  `[模式：构思]`：提供至少两种可行方案及评估（例如：`方案1：描述`）。
3.  `[模式：计划]`：将选定方案细化为详尽、有序、可执行的步骤清单（含原子操作：文件、函数/类、逻辑概要；预期结果；新库用`Context7`查询）。不写完整代码。完成后用`interactive-feedback`请求用户批准。
4.  `[模式：执行]`：必须用户批准方可执行。严格按计划编码执行。计划简要（含上下文和计划）存入`./issues/任务名.md`。关键步骤后及完成时用`interactive-feedback`反馈。
5.  `[模式：评审]`：对照计划评估执行结果，报告问题与建议。完成后用`interactive-feedback`请求用户确认。

[快速模式]
`[模式：快速]`：跳过核心工作流，快速响应。完成后用`interactive-feedback`请求用户确认。

[主动反馈与MCP服务]
* **通用反馈**：研究/构思遇疑问时，使用 `interactive_feedback` 征询意见。任务完成（对话结束）前也需征询。
* **MCP服务**：
    * `interactive_feedback`: 用户反馈。
    * `Context7`: 查询最新库文档/示例。
    * 优先使用MCP服务。
</code></pre><h3 id="32-自动生成-cursor-rules">3.2 自动生成 cursor rules</h3>
<p>llm 对话框中输入<code>/Generate Cursor Rules</code>, 会生成一份 project rules, 记得设置这个 rules 属性为 always, 这种 rules 可以让 llm 保持一致性, 不至于每次对话都跟换了一个新手一样</p>
<h3 id="33-设置项目背景-cursor-rules">3.3. 设置项目背景 cursor rules</h3>
<p>可以将自己的编程习惯再新建一个 rules, 可以帮助 llm 生成符合你要求的代码</p>
<pre tabindex="0"><code>### 微信小程序最佳实践规则集 

```yaml
version: 1
rules:
  # ========== 性能优化规则 ==========
  # 图片优化
  - name: &#34;optimize-images&#34;
    pattern: |
      &lt;image\s+src=&#34;(.+?)&#34;\s*/&gt;
    replacement: |
      &lt;image src=&#34;{{_match_1}}&#34; mode=&#34;widthFix&#34; lazy-load=&#34;true&#34; /&gt;
    message: &#34;图片应启用懒加载和适当模式&#34;
    severity: warning
    scope: 
      files: [&#34;*.wxml&#34;]

  # 减少setData数据量
  - name: &#34;minimal-setdata&#34;
    pattern: |
      this\.setData\(\s*\{[^}]*[^}]{100,}\s*\}\s*\)
    message: &#34;setData数据不应超过100字符，仅更新必要字段&#34;
    severity: error

  # 防抖节流
  - name: &#34;debounce-events&#34;
    pattern: |
      (bindtap|bindinput|bindscroll)=&#34;(\w+)\&#34;
    replacement: |
      {{_match_1}}=&#34;debounce({{_match_2}})&#34;
    message: &#34;高频事件(如滚动、输入)应添加防抖/节流&#34;
    severity: warning

  # ========== 代码规范规则 ==========
  # 组件命名规范
  - name: &#34;component-naming&#34;
    pattern: |
      Component\(\{\s*properties:\s*\{\s*([a-z]\w+)\s*:
    replacement: |
      properties: {
        {{ regex_replace(_match_1, &#39;^.&#39;, _match_1 | lower) }}: 
    message: &#34;组件属性名应使用小驼峰命名法&#34;
    severity: warning

### 使用效果验证
1. 在WXML中写`&lt;image src=&#34;...&#34;&gt;`会触发优化建议
2. 在JS中写`this.setData({ largeData })`会检查数据量
3. API调用不加错误处理会显示错误提示
4. 敏感信息检测会阻止提交含`token=&#39;...&#39;`的代码

&gt; 最佳实践：结合微信开发者工具的&#34;代码质量扫描&#34;和&#34;性能分析&#34;功能，定期检查规则执行效果，根据项目需求调整规则强度。
</code></pre>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/backend" term="backend" label="Backend" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[网络知识-iptables 的原子笔记]]></title>
            <link href="https://blog.yunpiao.site/post/20250529115159/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250529115159/</id>
            
            
            <published>2025-05-29T11:51:59+08:00</published>
            <updated>2025-05-29T11:51:59+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="netfilter-与-iptables">**Netfilter 与 iptables：</h2>
<ul>
<li><strong>Netfilter</strong>：Linux 内核中的包过滤框架，提供 5 个关键的“<strong>hook 点</strong>”（流量检查点），允许在数据包传输路径中进行干预。
<ul>
<li><code>NF_IP_PRE_ROUTING</code>：数据包刚进入系统，路由前。</li>
<li><code>NF_IP_LOCAL_IN</code>：数据包目的地是本机。</li>
<li><code>NF_IP_FORWARD</code>：数据包需要转发。</li>
<li><code>NF_IP_LOCAL_OUT</code>：本机生成的数据包。</li>
<li><code>NF_IP_POST_ROUTING</code>：数据包离开系统前。</li>
</ul>
</li>
<li><strong>iptables</strong>：用户空间的工具，用于配置 Netfilter 规则。它将规则组织成<strong>表 (tables)</strong> 和<strong>链 (chains)</strong>。</li>
</ul>
<hr>
<h2 id="iptables-的五大tables"><strong>iptables 的五大（Tables）</strong></h2>
<p>iptables 将规则按功能分类到不同的“表”中：</p>
<ul>
<li><strong><code>filter</code> 表</strong>：最常用，用于<strong>过滤</strong>（ACCEPT/DROP）数据包，实现防火墙功能。</li>
<li><strong><code>nat</code> 表</strong>：用于<strong>网络地址转换 (NAT)</strong>，如修改源/目的 IP/端口。</li>
<li><strong><code>mangle</code> 表</strong>：用于<strong>修改 IP 头</strong>，如 TTL 值，或给数据包打<strong>内部标记 (mark)</strong>。</li>
<li><strong><code>raw</code> 表</strong>：用于<strong>绕过连接跟踪 (conntrack)</strong>，通常在数据包进入连接跟踪系统前处理。</li>
<li><strong><code>security</code> 表</strong>：用于集成 <strong>SELinux</strong> 安全策略，给数据包打 SELinux 标记。</li>
</ul>
<hr>
<h2 id="链-chains数据包的流水线"><strong>链 (Chains)：数据包的“流水线”</strong></h2>
<p>每个表内部包含一个或多个“链”，这些链与 Netfilter 的 Hook 点对应。数据包流经不同的链，规则按序匹配：</p>
<ul>
<li><code>PREROUTING</code>：对应 <code>NF_IP_PRE_ROUTING</code>。</li>
<li><code>INPUT</code>：对应 <code>NF_IP_LOCAL_IN</code>。</li>
<li><code>FORWARD</code>：对应 <code>NF_IP_FORWARD</code>。</li>
<li><code>OUTPUT</code>：对应 <code>NF_IP_LOCAL_OUT</code>。</li>
<li><code>POSTROUTING</code>：对应 <code>NF_IP_POST_ROUTING</code>。</li>
</ul>
<p><strong>不同表在同一 Hook 点的优先级：</strong> 数据包触发某个 Hook 点时，多个表的链可能会被调用。调用顺序遵循特定优先级（例如，<code>raw</code> &gt; <code>mangle</code> &gt; <code>nat</code> &gt; <code>filter</code> &gt; <code>security</code>）。</p>
<hr>
<h2 id="iptables-规则匹配与动作"><strong>iptables 规则：匹配与动作</strong></h2>
<p>每条规则由两部分组成：</p>
<ul>
<li><strong>匹配 (Matching)</strong>：定义数据包必须满足的条件（如协议、IP/端口、接口、连接状态等）。</li>
<li><strong>目标 (Target)</strong>：数据包匹配成功后执行的动作。
<ul>
<li><strong>终止目标</strong>：执行后停止当前链的匹配（如 <code>ACCEPT</code>, <code>DROP</code>, <code>DNAT</code>, <code>REDIRECT</code>）。</li>
<li><strong>非终止目标</strong>：执行后继续当前链的匹配（如 <code>LOG</code>）。</li>
<li><strong>跳转目标 (Jump Target)</strong>：一种特殊的非终止目标，可跳转到<strong>用户自定义链</strong>，实现规则的模块化管理。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="连接跟踪-conntrack有状态的防火墙"><strong>连接跟踪 (Conntrack)：有状态的防火墙</strong></h2>
<ul>
<li><strong>功能</strong>：Netfilter 上的一个子系统，使得 iptables 能将数据包视为<strong>连接或会话的一部分</strong>，而非独立个体。</li>
<li><strong>连接状态</strong>：
<ul>
<li><code>NEW</code>：新连接的第一个合法包。</li>
<li><code>ESTABLISHED</code>：已建立的连接。</li>
<li><code>RELATED</code>：与已有连接相关的包（如 FTP 数据连接）。</li>
<li><code>INVALID</code>：非法或无法识别的包。</li>
<li><code>UNTRACKED</code>：被 <code>raw</code> 表标记为不跟踪的包。</li>
<li><code>SNAT</code>/<code>DNAT</code>：NAT 转换后的虚拟状态。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="透明代理客户端无感知的流量重定向"><strong>透明代理：客户端无感知的流量重定向</strong></h2>
<p>透明代理的核心在于在<strong>客户端无需配置</strong>的情况下，将流量重定向到代理服务器。</p>
<h3 id="1-基于-dnat-的透明代理"><strong>1. 基于 DNAT 的透明代理</strong></h3>
<ul>
<li><strong>原理</strong>：利用 <code>nat</code> 表的 <code>PREROUTING</code> 链，将数据包的<strong>目的 IP 和端口</strong>修改为代理服务器的本地 IP 和端口。</li>
<li><strong>客户端感知</strong>：无感知。</li>
<li><strong>代理服务器感知</strong>：收到数据包的目的地是自身，需要通过 <code>SO_ORIGINAL_DST</code> 等方式获取原始目的地。</li>
<li><strong>优点</strong>：实现相对简单。</li>
<li><strong>缺点</strong>：修改了数据包目的地，代理程序需要额外处理。</li>
</ul>
<h3 id="2-基于-tproxy-的透明代理"><strong>2. 基于 TPROXY 的透明代理</strong></h3>
<ul>
<li><strong>原理</strong>：利用 <code>mangle</code> 表的 <code>PREROUTING</code> 链给数据包打上<strong>标记 (mark)</strong>，并结合 <strong>策略路由 (Policy Routing)</strong> 将数据包重路由到本地代理程序，<strong>不修改数据包的原始目的 IP 和端口</strong>。
<ul>
<li><strong>iptables TPROXY 目标</strong>：在 <code>mangle</code> 表中将符合条件的数据包打上 <code>fwmark</code> 标记，并指定 <code>on-port</code> (代理监听端口)。</li>
<li><strong>策略路由</strong>：
<ul>
<li><code>ip rule add fwmark &lt;mark_value&gt; lookup &lt;table_id&gt;</code>：告诉内核，带有指定 <code>fwmark</code> 的数据包应查询特定的路由表。</li>
<li><code>ip route add local 0.0.0.0/0 dev lo table &lt;table_id&gt;</code>：在指定路由表中，将所有流量路由到本地回环接口 (<code>lo</code>)，从而被本地代理程序捕获。</li>
</ul>
</li>
</ul>
</li>
<li><strong>客户端感知</strong>：无感知。</li>
<li><strong>代理服务器感知</strong>：收到数据包的目的 IP 和端口就是客户端最初想要访问的真实 IP 和端口，简化代理逻辑。</li>
<li><strong>优点</strong>：保留原始目的地址，对代理程序更透明，支持 TCP 和 UDP 等多种协议。</li>
<li><strong>缺点</strong>：配置相对复杂，需要内核支持 TPROXY 功能，代理程序也需支持 TPROXY 模式。</li>
</ul>
<hr>
<h3 id="透明代理的共性要求"><strong>透明代理的共性要求</strong></h3>
<ul>
<li><strong>IP 转发</strong>：<code>net.ipv4.ip_forward</code> 必须开启。</li>
<li><strong>代理程序支持</strong>：代理程序需要支持透明代理模式，并能正确处理重定向的流量。</li>
<li><strong>安全性</strong>：作为流量枢纽，需确保代理服务器自身的安全。</li>
<li><strong>HTTPS 流量</strong>：对于 HTTPS，透明代理通常涉及 SSL/TLS 解密（MITM），需额外配置证书。</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[经验-windows 下权限 ace 的限制]]></title>
            <link href="https://blog.yunpiao.site/post/20250523151024/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250523151024/</id>
            
            
            <published>2025-05-23T15:10:24+08:00</published>
            <updated>2025-05-23T15:10:24+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="问题">问题</h2>
<p>当 一个对象设置 dacl (1. DACL 定义了&quot;谁&quot;可以对资源做&quot;什么&quot;操作) 的时候, 出现参数错误</p>
<h2 id="分析">分析</h2>
<blockquote>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/error-add-user-to-security-permissions" target="_blank" rel="noopener nofollow noreferrer" >https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/error-add-user-to-security-permissions</a></p>
<ol>
<li>在Windows Server中，向对象的安全权限中添加用户或组时，可能会因ACL大小限制而收到错误消息。❌</li>
<li>错误提示“超出操作系统对用户和组数量的限制”或“参数不正确”。🚫</li>
<li>ACL的最大尺寸为64KB，约1820个ACE。📈</li>
<li>实际使用中，ACL的最大尺寸因性能问题而不实用。🚫</li>
<li>ACL的大小取决于其访问控制条目（ACE）的数量和大小。🔍</li>
<li>文章提供了关于Active Directory Domain Services中访问控制工作原理的链接，供进一步了解。🔗</li>
<li>使用Cacls.exe工具时也可能触发该问题。🛠️</li>
<li>解决方法包括移除一些用户或组后再尝试操作。🔄</li>
</ol>
</blockquote>
<p>当达到访问控制列表 (ACL) 的最大大小时，就会出现这个问题。ACL 的大小随访问控制条目 (ACE) 的数量和大小而变化。ACL 的最大大小为 64 KB，即大约 1820 个 ACE。不过，出于性能考虑，最大大小并不适用。<br>
命令将返回 <code>&gt; The parameter is incorrect.</code></p>
<h2 id="修复">修复</h2>
<p>使用 <code>dsacls &quot;CN=Deleted Objects,DC=a,DC=com&quot; /resetDefaultDACL</code> 将 dacl 恢复, 就会正常, 同时之前的数据也会丢失, 但功能正常了</p>
<h2 id="优化">优化</h2>
<p>在设置 dacl 的时候, 之前的账户可以使用 <code>dsacls &quot;CN=Deleted Objects,DC=a,DC=com&quot; /r a/user</code>  删除 user 用户的权限, 防止用户被删除了, 这个数据项还在, 一直累计, 导致最终超过大小限制</p>
<h2 id="反思">反思</h2>
<p>在一个系统中, 如果一个资源最终会被删除, 则应当设计完整的清理策略, 即对任意一个可能造成持久化的操作都应该配备相应的删除策略</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/security" term="security" label="Security" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[PostgreSQL 日志轮训的最佳实践]]></title>
            <link href="https://blog.yunpiao.site/post/20250515173913/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250515173913/</id>
            
            
            <published>2025-05-15T17:39:13+08:00</published>
            <updated>2025-05-15T17:39:13+08:00</updated>
            
            
            <content type="html"><![CDATA[<ul>
<li>文件变量名用作日志轮转时进行清理历史文件的作用, 比如 ‘postgresql-%H.log’ 每小时一个文件, 第二天会对前一天的日志进行覆盖</li>
<li>日志轮转时机可以配置根据时间和大小, log_rotation_age log_rotation_size 参数</li>
<li>log_truncate_on_rotation 仅对“时间轮转”有效，新的同名文件会覆盖旧文件, 用于清理旧日志</li>
<li>这种限制理论最大文件数量限制将会是 log_rotation_age * 文件名循环中的一个环中的文件数</li>
<li>这种方法不完美, 没有办法硬限制, 最优方案是使用 logrotate  优化表达, 注意还是原格式, 只修改少量词语</li>
</ul>
<h2 id="logrotate-限制日志大小的-6-个关键指令">logrotate 限制日志大小的 6 个关键指令</h2>
<table>
<thead>
<tr>
<th>指令</th>
<th>版本</th>
<th>作用</th>
<th>常见写法</th>
</tr>
</thead>
<tbody>
<tr>
<td>size &lt;N&gt;</td>
<td>所有</td>
<td>当当前文件 ≥ N 时立即轮转</td>
<td>size 100M</td>
</tr>
<tr>
<td>maxsize &lt;N&gt;</td>
<td>≥3.8</td>
<td>若文件 ≥ N 则轮转，否则即使到周期也不轮转</td>
<td>maxsize 500M</td>
</tr>
<tr>
<td>minsize &lt;N&gt;</td>
<td>≥3.8</td>
<td>文件 ≥ N 且到周期才轮转</td>
<td>minsize 20M</td>
</tr>
<tr>
<td>rotate &lt;N&gt;</td>
<td>所有</td>
<td>仅保留 N 份旧日志（配合压缩可近似控制总量）</td>
<td>rotate 7</td>
</tr>
<tr>
<td>maxage &lt;N&gt;</td>
<td>所有</td>
<td>旧日志达 N 天即淘汰</td>
<td>maxage 30</td>
</tr>
<tr>
<td>compress / delaycompress</td>
<td>所有</td>
<td>压缩旧档、延迟一天再压缩</td>
<td>compress</td>
</tr>
</tbody>
</table>
<blockquote>
<p>size 与 maxsize 的区别：<br>
• size 触发轮转后，接下来仍按周期继续；<br>
• maxsize 触发轮转后，会重置周期计时，直到再次满足 maxsize 或到下次周期。<br>
PostgreSQL 日志的推荐模板（单文件 ≤100 MB，总量 ≈2.4 GB/天）</p>
</blockquote>
<h2 id="配置示例">配置示例</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span>/var/log/pg_log/postgresql-*.log <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span>    daily                  <span style="color:#75715e"># 每天检查一次</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>    size 100M              <span style="color:#75715e"># 单文件 ≥100 MB 就轮转</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>    rotate <span style="color:#ae81ff">24</span>              <span style="color:#75715e"># 最多保留 24 份（≈1 天内文件）</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>    compress               <span style="color:#75715e"># 旧日志 gzip</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>    delaycompress          <span style="color:#75715e"># 延迟一天再压缩，便于排查</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>    dateext                <span style="color:#75715e"># 追加时间戳防止重名</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>    missingok              <span style="color:#75715e"># 无日志也不报错</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>    notifempty             <span style="color:#75715e"># 空文件不轮转</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>    su postgres postgres   <span style="color:#75715e"># 切换用户，保持属主</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>    postrotate
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>        /usr/bin/pg_ctl -D /storage/db/vpostgres reload
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>    endscript
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p><strong>效果</strong></p>
<p>硬约束：任何单个日志不超过 100 MB。<br>
软约束：若日志写入 &lt;100 MB/小时，则一天 ≤2.4 GB；峰值高于 100 MB/小时时可能略超，但依旧可控。<br>
24 份之外的文件自动删除；配合 compress 实际磁盘占用更小。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/#%E8%BF%90%E7%BB%B4%E7%BB%8F%E9%AA%8C" term="#%E8%BF%90%E7%BB%B4%E7%BB%8F%E9%AA%8C" label="#运维经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[中国企业域内安全现状研究报告]]></title>
            <link href="https://blog.yunpiao.site/post/20250508181500/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250508181500/</id>
            
            
            <published>2025-05-08T18:15:00+08:00</published>
            <updated>2025-05-08T18:15:00+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="中国企业域内安全现状研究">中国企业域内安全现状研究</h1>
<h2 id="一域安全定义及其关键性">一、域安全定义及其关键性</h2>
<ol>
<li>定义：
<ul>
<li>域安全是指在企业明确定义的“可信域”内，对用户、系统、应用、数据等资源实施统一的身份、访问、网络及数据保护。</li>
<li>其核心是“边界内可信、边界外不信任”，传统依赖 Windows AD/DC，现代演进至跨本地、云端和混合环境的统一管理。</li>
</ul>
</li>
<li>关键性：
<ul>
<li>保护数字资产、确保业务连续、维护客户与社会信任；</li>
<li>合规驱动：在“数字中国”战略和日益严格的网络安全、数据安全、个人信息保护法规下，企业域安全已上升为战略高度；</li>
<li>若域安全失陷，不仅经济损失，更可能影响社会稳定与国家利益。</li>
</ul>
</li>
</ol>
<h2 id="二演进中的it环境与安全新挑战">二、演进中的IT环境与安全新挑战</h2>
<ol>
<li>数字化转型与边界模糊：
<ul>
<li>云计算、大数据、IoT、移动办公扩展攻击面；</li>
<li>传统“城堡”边界失效，安全护城河转向“身份+数据”为中心。</li>
</ul>
</li>
<li>远程与混合办公：
<ul>
<li>员工个人设备、公共网络访问增多，VPN/RDP等成为APT和勒索软件的主要靶点。</li>
</ul>
</li>
<li>云安全责任模型：
<ul>
<li>与云厂商共担安全责任，CSPM、CWPP、CASB 等工具纳入域安全体系。</li>
</ul>
</li>
<li>供应链风险：
<ul>
<li>第三方或上游厂商漏洞可植入后门，供应链攻击日益主流。</li>
</ul>
</li>
</ol>
<h2 id="三中国企业面临的主要安全威胁">三、中国企业面临的主要安全威胁</h2>
<ol>
<li>高级持续性威胁（APT）：
<ul>
<li>国家及有组织犯罪支持，长期潜伏、目标明确，常借0day与社会工程入侵政府、科研、能源、制造等高价值行业。</li>
</ul>
</li>
<li>勒索软件：
<ul>
<li>日益专业化，结合漏洞利用与定向渗透，制造、医疗、政府等因业务中断成本高付赎金。</li>
</ul>
</li>
<li>数据泄露与窃取：
<ul>
<li>技术防护不足、权限滥用、管理缺失及内部人员误操作均可导致海量敏感数据外泄。</li>
</ul>
</li>
<li>内部威胁：
<ul>
<li>离职报复、特权滥用或安全意识薄弱带来严重风险，需要严格IAM与行为监控。</li>
</ul>
</li>
<li>其他：网络钓鱼、弱口令、远程访问滥用、云配置错误、安全产品自身漏洞等。</li>
</ol>
<h2 id="四安全技术与解决方案现状">四、安全技术与解决方案现状</h2>
<ol>
<li>基础防护：
<ul>
<li>NGFW、IDS/IPS：内外部边界及东西向流量分段。</li>
<li>IAM/PAM：多因素认证、最小权限与生命周期管理。</li>
<li>EPP/EDR：特征库＋行为分析，提升终端可见性与响应。</li>
<li>DLP：监控敏感数据全生命周期，满足《数据安全法》《个人信息保护法》合规要求。</li>
</ul>
</li>
<li>先进范式：
<ul>
<li>零信任架构：持续验证、动态准入，已在银行、工业互联网等场景试点。</li>
<li>NDR：分析内部东西向网络流量，补足EDR端点视角。</li>
<li>XDR：跨端点、网络、云、邮件等多源数据融合，实现联动检测与响应。</li>
<li>SIEM/SOAR：日志集中分析＋自动化编排，提高SOC效率、告警处理速度。</li>
</ul>
</li>
<li>人工智能助力：
<ul>
<li>UEBA、恶意样本自动分析、漏洞优先级排序、风险预测；</li>
<li>挑战：模型安全、训练数据质量、人才缺口。</li>
</ul>
</li>
</ol>
<h2 id="五安全管理运营与成熟度">五、安全管理、运营与成熟度</h2>
<ol>
<li>SOC现状与挑战：
<ul>
<li>告警疲劳、人力成本高、数据孤岛、多工具集成难；</li>
<li>发展趋势：SOAR自动化、MDR托管服务、MITRE ATT&amp;CK框架、云原生SOC、智能化威胁狩猎、开放式XDR。</li>
</ul>
</li>
<li>安全模型：PDR、P2DR、PDRR 提供保护—检测—响应（—恢复）闭环思路。</li>
<li>事件响应与准备：
<ul>
<li>完善书面应急预案、定期演练、复盘持续改进；</li>
<li>建立专门团队或引入外部服务。</li>
</ul>
</li>
<li>数据安全能力成熟度模型（DSMM）：
<ul>
<li>从组织、制度、技术、人员四维度评估并提升数据全生命周期安全能力；</li>
<li>已有363家企业通过认证。</li>
</ul>
</li>
</ol>
<h2 id="六监管与合规要求">六、监管与合规要求</h2>
<ol>
<li>核心法规：
<ul>
<li>《网络安全法》：网络运营者安全责任、日志留存、等级保护；</li>
<li>《数据安全法》：数据分类分级、风险评估、应急处置；</li>
<li>《个人信息保护法》：合法必要、告知同意、PIPIA、跨境传输规则。</li>
</ul>
</li>
<li>等级保护 2.0（MLPS）：
<ul>
<li>五级定级、通用与扩展技术及管理要求，覆盖云、IoT、工业控制等新型系统。</li>
</ul>
</li>
<li>行业规范：金融、医疗、工业控制等领域的专项指引与法规；</li>
<li>监管机构：网信办、工信部、公安部、CNCERT 等发布检查、通报和执法案例。</li>
</ol>
<h2 id="七安全投资趋势与挑战">七、安全投资趋势与挑战</h2>
<ol>
<li>投资规模：
<ul>
<li>市场规模预计从2023年110亿美元增至2028年171亿美元；</li>
<li>企业预算增长积极，73%中国高管计划提高安全投入，多基于量化风险评估。</li>
</ul>
</li>
<li>重点领域：
<ul>
<li>云安全、零信任、AI 安全、XDR、SOC 现代化；</li>
<li>安全软件与服务、中高端硬件、托管安全服务。</li>
</ul>
</li>
<li>主要挑战：
<ul>
<li>人才短缺（327万缺口），技术更新与遗留系统共存，威胁复杂度提升，ROI难衡量，合规压力多重、核心技术依赖、业务与安全平衡、数据治理复杂。</li>
</ul>
</li>
</ol>
<h2 id="八典型案例与最佳实践">八、典型案例与最佳实践</h2>
<ol>
<li>事件剖析：
<ul>
<li>勒索软件（Travelex、CPI、MSI）揭示网络分段、权限管理与备份策略缺失；</li>
<li>APT（高校、政府机构）长期潜伏、横向移动；</li>
<li>数据泄露（高校、企业罚款案例）强调合规与技术双缺失；</li>
<li>内部威胁（离职员工恶意操作）暴露权限回收与监控不足。</li>
</ul>
</li>
<li>成功实践：
<ul>
<li>零信任在银行、工业互联网场景的分阶段落地；</li>
<li>DSMM 认证企业系统化数据治理；</li>
<li>主动漏洞管理、强IAM、网络分段、日志集中、员工培训、IR演练等基础控防。</li>
</ul>
</li>
<li>经验启示：
<ul>
<li>安全方案须与业务创新场景深度融合；</li>
<li>合规与合规外提升并重，避免清单式应付；</li>
<li>监管执法案例警示合规缺失后果。</li>
</ul>
</li>
</ol>
<h2 id="九战略性提升建议">九、战略性提升建议</h2>
<ol>
<li>技术与架构：
<ul>
<li>夯实 NGFW/IDS、IAM/MFA、EDR、DLP 基础；</li>
<li>分阶段拥抱零信任（SDP、微隔离、IAP）；</li>
<li>部署 NDR/XDR 以增强东西向可见性；</li>
<li>构建数据安全中台，参考 DSMM；</li>
<li>AI/ML 辅助威胁检测与 SOAR 自动化响应；</li>
<li>混合多云环境安全一体化管理；</li>
<li>强化供应链安全审计与 SCA。</li>
</ul>
</li>
<li>管理与运营：
<ul>
<li>建设智能 SOC，或引入 MDR；</li>
<li>常态化漏洞管理及风险排序；</li>
<li>全周期 DevSecOps 开发与运营；</li>
<li>定期 IR 演练与复盘；</li>
<li>高层与董事会定期风险汇报。</li>
</ul>
</li>
<li>合规与治理：
<ul>
<li>持续跟踪法规动态，定期内部自查与第三方审计；</li>
<li>明确重要数据和 CII 特殊保护；</li>
<li>完善 PIPL“告知-同意”及 PIPIA 流程；</li>
<li>主动与监管机构沟通，及时应报事件。</li>
</ul>
</li>
</ol>
<h2 id="十结论">十、结论</h2>
<p>中国企业正处于数字化快速演进与网络安全威胁持续升级的交汇点。唯有以合规为底线、以风险为导向，持续强化基础、拥抱新范式、智能化运营、数据驱动治理，并在技术、管理和文化层面协同发力，方能构建动态韧性强的大域安全体系，保障企业在数字经济时代行稳致远。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/security" term="security" label="Security" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[经验-openresty 相关优化手法]]></title>
            <link href="https://blog.yunpiao.site/post/20250208150956/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250208150956/</id>
            
            
            <published>2025-02-08T15:09:56+08:00</published>
            <updated>2025-02-08T15:09:56+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-lua-层面">1. LUA 层面</h2>
<h3 id="11-尽量使用局部变量-减少垃圾回收开销">1.1 尽量使用局部变量, 减少垃圾回收开销</h3>
<ul>
<li><code>local var = value</code> 替代 <code>var = value</code></li>
<li>外部函数在包初始化的时候直接 <code>local a = c.a</code></li>
</ul>
<h3 id="12-使用-tableconcat-而不是--字符串拼接">1.2 使用 table.concat 而不是 .. 字符串拼接</h3>
<p><code>table.concat({&quot;a&quot;, &quot;b&quot;, &quot;c&quot;}, &quot;&quot;)</code></p>
<h3 id="13-预编译正则表达式">1.3 预编译正则表达式</h3>
<p><code>local re = ngx.re.compile(&quot;pattern&quot;)</code>。</p>
<h3 id="14-确定重复数据的域">1.4 确定重复数据的域</h3>
<p>将全局可以用到的保存为全局变量, 在初始化的时候加载好, 并确定好最方便查找的结构, 如 ip 地址库</p>
<h3 id="15-在耗时函数上加一层缓存">1.5 在耗时函数上加一层缓存</h3>
<p>如计算 hash 函数, 或者 json decode 后的对象, 空间换时间</p>
<h3 id="16-使用-luajit-ffi">1.6 使用 LuaJIT FFI</h3>
<p>openresty lua-resty-core 计划使用 ffi 完全替换掉之前的 nginx-lua-module, 性能会有所提升</p>
<h3 id="17-采样执行一些不重要的函数">1.7 采样执行一些不重要的函数</h3>
<p>如打印错误日志, 当逾期错误日志会频繁打印, 增加一层限制, 固定 100/秒等策略</p>
<h3 id="18-非必要不-io">1.8 非必要不 io</h3>
<p>不要轻易读写文件和网络, 除非必要, 数据可以缓存本地一份就缓存本地, 不要任何请求都缓存 redis</p>
<h3 id="19-异步化操作">1.9 异步化操作</h3>
<p>如有功能耗时严重, 可以异步请求, 如异步写入日志, 异步请求 redis</p>
<h3 id="110-压缩数据-批次执行-适当增大-buffer">1.10 压缩数据, 批次执行, 适当增大 buffer</h3>
<p>批次是效果最好的优化手段, 发送数据或者执行逻辑, 如果能够批次尽量批次执行</p>
<h3 id="111-lua-有-jit-可以即使编译使用-pcre_jit">1.11 lua 有 JIT, 可以即使编译,使用 pcre_jit</h3>
<p>函数中尽量不要有 pcall 等无法 JIT 的操作</p>
<h3 id="112-cosocket-非阻塞-io">1.12 cosocket 非阻塞 io</h3>
<h3 id="113-使用-ecc-证书而不是-rsa">1.13 使用 ECC 证书而不是 RSA</h3>
<p>压测发现, RCC 证书比 RSA 具有更好的性能</p>
<h3 id="114-发现攻击-尽早关闭">1.14 发现攻击, 尽早关闭</h3>
<p>ngx.exit(444)  连接数多 直接 RST, 缺点没有日志记录</p>
<h2 id="2-nginx-层面">2. Nginx 层面</h2>
<h3 id="21-参数优化">2.1 参数优化</h3>
<pre tabindex="0"><code>worker_processes auto;             # 匹配 CPU 核心数
worker_rlimit_nofile 102400;       # 提高文件描述符限制
events {
    worker_connections 20480;      # 根据系统最大 fd 调整
    use epoll;                     # 高并发场景使用 epoll
}
lua_shared_dict my_cache 100m;     # 缓存配置、限流计数器等
keepalive_timeout 60;              # 保持长连接
client_header_timeout 15s;         # 避免慢客户端攻击
proxy_buffers 8 16k;               # 减少磁盘 IO 使用
sendfile on;                       # 零拷贝传输静态文件
tcp_nopush on;                     # 合并数据包发送
gzip on;                           # 对文本内容启用压缩
</code></pre><h2 id="3-linux-层面">3. Linux 层面</h2>
<h3 id="31-系统参数">3.1 系统参数</h3>
<pre tabindex="0"><code># 调整 TCP 参数
sysctl -w net.core.somaxconn=65535
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_fin_timeout=30
ulimit -n 102400                   # 修改 Nginx 进程的 fd 限制
# 使用大页内存（需 Nginx 支持）
sysctl -w vm.nr_hugepages=1024
# 调整磁盘调度策略（SSD 推荐 noop）
echo noop &gt; /sys/block/sda/queue/scheduler
</code></pre><h2 id="4-监控与分析">4. 监控与分析</h2>
<ul>
<li>火焰图 <a href="https://github.com/openresty/openresty-systemtap-toolkit" target="_blank" rel="noopener nofollow noreferrer" >SystemTap</a> 分析 Lua/C 代码瓶颈。</li>
<li><strong>Lua 内存分析</strong>：<code>collectgarbage(&quot;count&quot;)</code> 监控内存泄漏。</li>
<li>压测工具 <a href="https://github.com/wg/wrk" target="_blank" rel="noopener nofollow noreferrer" >wrk</a></li>
<li>指标监控 <a href="https://github.com/knyar/nginx-lua-prometheus" target="_blank" rel="noopener nofollow noreferrer" >prometheus</a></li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/backend" term="backend" label="Backend" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/lua" term="lua" label="lua" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[杂-记录新的分布式存储(iomesh)使用指南]]></title>
            <link href="https://blog.yunpiao.site/post/20250123113047/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250123113047/</id>
            
            
            <published>2025-01-23T11:30:47+08:00</published>
            <updated>2025-01-23T11:30:47+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>最近公司产品部署到客户公司使用的一款小众些的分布式存储-iomesh, 做下流水账记录</p>
</blockquote>
<h2 id="一环境准备"><strong>一、环境准备</strong></h2>
<ol>
<li>
<p><strong>硬件要求</strong></p>
<ul>
<li>每个节点至少需：
<ul>
<li>1块缓存盘（Cache Disk，推荐SSD）</li>
<li>1块数据盘（Partition Disk）</li>
</ul>
</li>
<li>网络分离（建议存储网络独立，如10.234.1.0/24）</li>
</ul>
</li>
<li>
<p><strong>系统配置</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span><span style="color:#75715e"># 所有节点安装 open-iscsi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>apt install open-iscsi -y
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span><span style="color:#75715e"># 修改iSCSI配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">5</a></span><span>sudo sed -i <span style="color:#e6db74">&#39;s/^node.startup = automatic$/node.startup = manual/&#39;</span> /etc/iscsi/iscsid.conf
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6">6</a></span><span>sudo modprobe iscsi_tcp
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7">7</a></span><span>echo <span style="color:#e6db74">&#34;iscsi_tcp&#34;</span> | sudo tee /etc/modprobe.d/iscsi-tcp.conf
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8">8</a></span><span>systemctl enable --now iscsid
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 id="二kubernetes集群部署"><strong>二、Kubernetes集群部署</strong></h2>
<ol>
<li>
<p><strong>要求</strong></p>
<ul>
<li>Kubernetes 1.24+</li>
<li>Containerd作为容器运行时</li>
</ul>
</li>
<li>
<p><strong>节点准备</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#75715e"># Master节点参与调度（如需）</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>kubectl taint nodes --all node-role.kubernetes.io/control-plane- node-role.kubernetes.io/master-
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 id="三iomesh离线安装"><strong>三、IOMesh离线安装</strong></h2>
<ol>
<li>
<p><strong>安装包准备</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>tar -xvf iomesh-offline-v0.11.1.tgz
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>cd iomesh-offline
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>ctr --namespace k8s.io image import ./images/iomesh-offline-images.tar
</span></span></code></pre></div></li>
<li>
<p><strong>生成配置文件</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>./helm show values charts/iomesh &gt; iomesh.yaml
</span></span></code></pre></div></li>
<li>
<p><strong>关键配置项</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1"> 1</a></span><span><span style="color:#f92672">iomesh</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2"> 2</a></span><span>  <span style="color:#f92672">chunk</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3"> 3</a></span><span>    <span style="color:#f92672">dataCIDR</span>: <span style="color:#e6db74">&#34;10.234.1.0/24&#34;</span>  <span style="color:#75715e"># 存储专用网络</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4"> 4</a></span><span>  <span style="color:#f92672">diskDeploymentMode</span>: <span style="color:#e6db74">&#34;hybridFlash&#34;</span>  <span style="color:#75715e"># 或 &#34;allFlash&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5"> 5</a></span><span>  <span style="color:#f92672">deviceMap</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6"> 6</a></span><span>    <span style="color:#f92672">cacheWithJournal</span>:   <span style="color:#75715e"># Hybrid模式配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7"> 7</a></span><span>      <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-8"> 8</a></span><span>        <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-9"> 9</a></span><span>          <span style="color:#f92672">iomesh.com/bd-driverType</span>: <span style="color:#ae81ff">SSD</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-10">10</a></span><span>    <span style="color:#f92672">dataStoreWithJournal</span>:  <span style="color:#75715e"># All-Flash模式配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-11">11</a></span><span>      <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-12">12</a></span><span>        <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-13">13</a></span><span>          <span style="color:#f92672">iomesh.com/bd-driverType</span>: <span style="color:#ae81ff">SSD</span>
</span></span></code></pre></div></li>
<li>
<p><strong>安装IOMesh</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span>./helm install iomesh ./charts/iomesh <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span><span style="color:#ae81ff"></span>  --create-namespace <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3">3</a></span><span><span style="color:#ae81ff"></span>  --namespace iomesh-system <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4">4</a></span><span><span style="color:#ae81ff"></span>  --values iomesh.yaml <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-5">5</a></span><span><span style="color:#ae81ff"></span>  --wait
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 id="四磁盘管理"><strong>四、磁盘管理</strong></h2>
<ol>
<li>
<p><strong>查看磁盘状态</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span>kubectl -n iomesh-system get blockdevice
</span></span></code></pre></div></li>
<li>
<p><strong>标记磁盘类型</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1">1</a></span><span><span style="color:#75715e"># 标记缓存盘</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-2">2</a></span><span>kubectl label blockdevice &lt;blockdevice-name&gt; iomesh-system/disk<span style="color:#f92672">=</span>SSD -n iomesh-system
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-4">4</a></span><span><span style="color:#75715e"># 标记数据盘</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-5">5</a></span><span>kubectl label blockdevice &lt;blockdevice-name&gt; iomesh-system/disk<span style="color:#f92672">=</span>HDD -n iomesh-system
</span></span></code></pre></div></li>
<li>
<p><strong>申领磁盘</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span>./helm -n iomesh-system upgrade iomesh charts/iomesh -f iomesh.yaml
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 id="五存储配置"><strong>五、存储配置</strong></h2>
<ol>
<li>
<p><strong>StorageClass示例</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-1">1</a></span><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">storage.k8s.io/v1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-2">2</a></span><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">StorageClass</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-3">3</a></span><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-4">4</a></span><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">iomesh-sc</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-5">5</a></span><span><span style="color:#f92672">provisioner</span>: <span style="color:#ae81ff">com.iomesh.csi-driver</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-6">6</a></span><span><span style="color:#f92672">reclaimPolicy</span>: <span style="color:#ae81ff">Retain</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-7">7</a></span><span><span style="color:#f92672">parameters</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-8">8</a></span><span>  <span style="color:#f92672">replicaFactor</span>: <span style="color:#e6db74">&#34;2&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-9">9</a></span><span>  <span style="color:#f92672">thinProvision</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span></code></pre></div></li>
<li>
<p><strong>创建PVC</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-1"> 1</a></span><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">v1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-2"> 2</a></span><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">PersistentVolumeClaim</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-3"> 3</a></span><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-4"> 4</a></span><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">demo-pvc</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-5"> 5</a></span><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-6"> 6</a></span><span>  <span style="color:#f92672">storageClassName</span>: <span style="color:#ae81ff">iomesh-sc</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-7"> 7</a></span><span>  <span style="color:#f92672">accessModes</span>: [<span style="color:#ae81ff">ReadWriteOnce]</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-8"> 8</a></span><span>  <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-9"> 9</a></span><span>    <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-10">10</a></span><span>      <span style="color:#f92672">storage</span>: <span style="color:#ae81ff">100Gi</span>
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 id="六运维监控"><strong>六、运维监控</strong></h2>
<ol>
<li>
<p><strong>查看集群状态</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-1">1</a></span><span>kubectl get iomeshclusters.iomesh.com -n iomesh-system -o yaml
</span></span></code></pre></div></li>
<li>
<p><strong>关键指标</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-1">1</a></span><span><span style="color:#f92672">status</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-2">2</a></span><span>  <span style="color:#f92672">summary</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-3">3</a></span><span>    <span style="color:#f92672">clusterSummary</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-4">4</a></span><span>      <span style="color:#f92672">spaceInfo</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-5">5</a></span><span>        <span style="color:#f92672">totalDataCapacity</span>: <span style="color:#ae81ff">24.</span><span style="color:#ae81ff">66Ti </span> <span style="color:#75715e"># 总存储容量</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-6">6</a></span><span>        <span style="color:#f92672">usedDataSpace</span>: <span style="color:#ae81ff">11.</span><span style="color:#ae81ff">50Gi     </span> <span style="color:#75715e"># 已用空间</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-7">7</a></span><span>    <span style="color:#f92672">metaSummary</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-8">8</a></span><span>      <span style="color:#f92672">leader</span>: <span style="color:#ae81ff">10.211.5.11</span>:<span style="color:#ae81ff">10100</span>     <span style="color:#75715e"># Meta服务Leader节点</span>
</span></span></code></pre></div></li>
</ol>
<hr>
<h2 id="七故障排查"><strong>七、故障排查</strong></h2>
<ol>
<li>
<p><strong>Pod挂载PVC失败</strong></p>
<pre tabindex="0"><code class="language-log" data-lang="log">Error: Failed to load module tcp
</code></pre><p><strong>解决方案</strong>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-1">1</a></span><span><span style="color:#75715e"># 确认iscsi_tcp模块加载</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-2">2</a></span><span>lsmod | grep iscsi_tcp
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-3">3</a></span><span>modprobe iscsi_tcp
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-4">4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-5">5</a></span><span><span style="color:#75715e"># 检查iscsid服务状态</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-6">6</a></span><span>systemctl status iscsid
</span></span></code></pre></div></li>
<li>
<p><strong>磁盘未Claimed</strong></p>
<ul>
<li>检查<code>iomesh.yaml</code>设备选择器配置</li>
<li>确认磁盘标签是否正确</li>
</ul>
</li>
</ol>
<hr>
<h2 id="八卸载iomesh"><strong>八、卸载IOMesh</strong></h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-1">1</a></span><span>./helm uninstall -n iomesh-system iomesh
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-2">2</a></span><span><span style="color:#75715e"># 清理残留资源（谨慎操作！）</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-3">3</a></span><span>kubectl delete ns iomesh-system
</span></span></code></pre></div><hr>
<h2 id="九-参考">九、 参考</h2>
<ul>
<li><strong>官方文档</strong>：<a href="https://docs.iomesh.com/" target="_blank" rel="noopener nofollow noreferrer" >IOMesh Documentation</a></li>
</ul>
<h2 id="十-和-longhorn-的对比">十、 和 longhorn 的对比</h2>
<table>
<thead>
<tr>
<th><strong>维度</strong></th>
<th><strong>IOMesh 优势</strong></th>
<th><strong>Longhorn 优势</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>性能</strong></td>
<td>高 IOPS、低延迟</td>
<td>轻量级，适合中等负载</td>
</tr>
<tr>
<td><strong>集成性</strong></td>
<td>企业级功能丰富</td>
<td>与 Kubernetes 深度集成</td>
</tr>
<tr>
<td><strong>成本</strong></td>
<td>高（商业授权+硬件）</td>
<td>低（开源免费）</td>
</tr>
<tr>
<td><strong>易用性</strong></td>
<td>复杂，需专业运维</td>
<td>简单，适合云原生新手</td>
</tr>
<tr>
<td><strong>适用场景</strong></td>
<td>核心生产系统</td>
<td>实验环境、边缘计算</td>
</tr>
</tbody>
</table>
<p><strong>稳定性与性能优先选 IOMesh，灵活性与成本优先选 Longhorn</strong>。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[经验-docker 相关使用实践]]></title>
            <link href="https://blog.yunpiao.site/post/20250106142544/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250106142544/</id>
            
            
            <published>2025-01-06T14:25:44+08:00</published>
            <updated>2025-01-06T14:25:44+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="一harbor-仓库-ssl-证书问题解决依赖加证书文件实在解决不了的情况下">一、Harbor 仓库 SSL 证书问题解决(依赖加证书文件实在解决不了的情况下)</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span><span style="color:#75715e"># 添加自签名证书到系统信任链</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span>echo <span style="color:#e6db74">&#34;-----BEGIN CERTIFICATE-----
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span><span style="color:#e6db74">MIID... (证书内容) ...
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span><span style="color:#e6db74">-----END CERTIFICATE-----&#34;</span> | sudo tee -a /etc/ssl/certs/harbor.crt &gt; /dev/null
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span><span style="color:#75715e"># CentOS/RHEL 更新证书信任库</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>sudo update-ca-trust force-enable
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>sudo update-ca-trust extract
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span><span style="color:#75715e"># Ubuntu/Debian 更新证书</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>sudo update-ca-certificates
</span></span></code></pre></div><p><strong>验证方法</strong>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>curl -v https://your-harbor-domain
</span></span></code></pre></div><h2 id="二镜像优化分析工具">二、镜像优化分析工具</h2>
<p>使用 Dive 进行镜像层分析：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#75715e"># 安装 Dive</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>wget https://github.com/wagoodman/dive/releases/download/v0.11.0/dive_0.11.0_linux_amd64.deb
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>sudo dpkg -i dive*.deb
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5">5</a></span><span><span style="color:#75715e"># 分析镜像</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6">6</a></span><span>dive your-image:tag
</span></span></code></pre></div><p><strong>关键功能</strong>：</p>
<ul>
<li>可视化每层文件变化</li>
<li>计算浪费的空间（重复/删除的文件）</li>
</ul>
<h2 id="三dockerfile-最佳实践">三、Dockerfile 最佳实践</h2>
<ol>
<li>
<p><strong>基础规范</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span><span style="color:#66d9ef">FROM</span><span style="color:#e6db74"> bitnami/python:3.7.17-debian-11-r0 AS builder</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">WORKDIR</span><span style="color:#e6db74"> /app</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3">3</a></span><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">COPY</span> . .<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div></li>
<li>
<p><strong>健康检查配置</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span><span style="color:#960050;background-color:#1e0010">HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2">2</a></span><span><span style="color:#960050;background-color:#1e0010">  </span><span style="color:#66d9ef">CMD</span> curl -f http://localhost:8080/health <span style="color:#f92672">||</span> exit <span style="color:#ae81ff">1</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div></li>
<li>
<p><strong>安全增强</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span><span style="color:#66d9ef">RUN</span> groupadd -r appuser <span style="color:#f92672">&amp;&amp;</span> useradd -r -g appuser appuser<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">USER</span><span style="color:#e6db74"> appuser</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div></li>
<li>
<p><strong>构建优化</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span><span style="color:#75715e"># 使用 BuildKit 缓存加速</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-2">2</a></span><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#66d9ef">RUN</span> --mount<span style="color:#f92672">=</span>type<span style="color:#f92672">=</span>cache,target<span style="color:#f92672">=</span>/go/pkg/mod <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-3">3</a></span><span><span style="color:#ae81ff"></span>    go mod download<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div></li>
<li>
<p>编写 .dockerignore 文件, 将不相关的文件忽略, 不发送到 docker 环境</p>
</li>
<li>
<p>一个容器运行一个应用</p>
</li>
<li>
<p>镜像不要在生产环境中不要使用  latest 标签</p>
</li>
<li>
<p>优先使用 copy 而不是 add</p>
</li>
<li>
<p>设置默认的环境变量</p>
</li>
<li>
<p>使用 label 设置镜像元数据</p>
</li>
</ol>
<h2 id="四多架构镜像构建">四、多架构镜像构建</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1"> 1</a></span><span><span style="color:#75715e"># 创建构建器实例</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-2"> 2</a></span><span>docker buildx create --use --name multiarch-builder
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-3"> 3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-4"> 4</a></span><span><span style="color:#75715e"># 构建并推送多平台镜像</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-5"> 5</a></span><span>docker buildx build --platform linux/arm64,linux/amd64 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-6"> 6</a></span><span><span style="color:#ae81ff"></span>  -t your-registry/image:tag --push .
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-7"> 7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-8"> 8</a></span><span><span style="color:#75715e"># 创建统一 manifest</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-9"> 9</a></span><span>docker manifest create your-registry/image:tag <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-10">10</a></span><span><span style="color:#ae81ff"></span>  your-registry/image:tag-arm64 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-11">11</a></span><span><span style="color:#ae81ff"></span>  your-registry/image:tag-amd64
</span></span></code></pre></div><h2 id="五网络与安全管控">五、网络与安全管控</h2>
<ol>
<li>
<p><strong>动态防火墙管理</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span><span style="color:#75715e"># 监控 Docker 端口并更新 iptables 规则</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-2">2</a></span><span>*/5 * * * * root /path/to/monitor_docker_ports.sh<span style="color:#f92672">(</span>一个检查 docker 开发了哪些端口的工具<span style="color:#f92672">)</span>
</span></span></code></pre></div></li>
<li>
<p><strong>安全端口暴露策略</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-1">1</a></span><span><span style="color:#75715e"># 仅允许指定 IP 段访问</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-2">2</a></span><span>iptables -A DOCKER-USER -p tcp --dport <span style="color:#ae81ff">8080</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-3">3</a></span><span><span style="color:#ae81ff"></span>  -s 10.0.0.0/8 -j ACCEPT
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-4">4</a></span><span>iptables -A DOCKER-USER -p tcp --dport <span style="color:#ae81ff">8080</span> -j DROP
</span></span></code></pre></div></li>
</ol>
<h2 id="六cicd-集成">六、CI/CD 集成</h2>
<ol>
<li>
<p><strong>GitLab Runner 配置</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-1">1</a></span><span>gitlab-runner install --user<span style="color:#f92672">=</span>gitlab-runner --working-directory<span style="color:#f92672">=</span>/home/gitlab-runner
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-2">2</a></span><span>gitlab-runner register --url https://gitlab.com --registration-token $REG_TOKEN
</span></span></code></pre></div></li>
<li>
<p><strong>高效缓存配置</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-1">1</a></span><span><span style="color:#f92672">variables</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-2">2</a></span><span>  <span style="color:#f92672">DOCKER_BUILDKIT</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-3">3</a></span><span><span style="color:#f92672">build</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-4">4</a></span><span>  <span style="color:#f92672">script</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-5">5</a></span><span>    - <span style="color:#ae81ff">docker build --cache-from=your-image:cache --tag=your-image:latest .</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-6">6</a></span><span>    - <span style="color:#ae81ff">docker push your-image:latest</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="七高级技巧">七、<strong>高级技巧</strong></h2>
<ul>
<li><strong>Root 权限进入容器</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-1">1</a></span><span>   docker exec -it --user root &lt;container&gt; /bin/bash
</span></span></code></pre></div><ul>
<li><strong>代理网络配置</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-1">1</a></span><span>   <span style="color:#75715e"># 创建代理配置文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-2">2</a></span><span>   cat <span style="color:#e6db74">&lt;&lt;EOF | sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-3">3</a></span><span><span style="color:#e6db74">   [Service]
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-4">4</a></span><span><span style="color:#e6db74">   Environment=&#34;HTTP_PROXY=http://proxy.example.com:8080&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-5">5</a></span><span><span style="color:#e6db74">   Environment=&#34;NO_PROXY=localhost,.internal&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-6">6</a></span><span><span style="color:#e6db74">   EOF</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-7">7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-8">8</a></span><span>   systemctl daemon-reload
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-13-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-13-9">9</a></span><span>   systemctl restart docker
</span></span></code></pre></div><ul>
<li><strong>镜像压缩优化</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-1">1</a></span><span>   <span style="color:#75715e"># 多线程压缩加速</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-2">2</a></span><span>   docker save your-image | pigz -9 &gt; image.tar.gz
</span></span></code></pre></div><ul>
<li><strong>多阶段构建</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-1">1</a></span><span>   FROM golang:1.19 AS build<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-2">2</a></span><span><span style="color:#960050;background-color:#1e0010"></span>   COPY . .<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-3">3</a></span><span><span style="color:#960050;background-color:#1e0010"></span>   RUN go build -o /app<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-4">4</a></span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-5">5</a></span><span><span style="color:#960050;background-color:#1e0010"></span>   FROM alpine:3.15<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-6">6</a></span><span><span style="color:#960050;background-color:#1e0010"></span>   COPY --from<span style="color:#f92672">=</span>build /app /app<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-7">7</a></span><span><span style="color:#960050;background-color:#1e0010"></span>   CMD <span style="color:#f92672">[</span><span style="color:#e6db74">&#34;/app&#34;</span><span style="color:#f92672">]</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><ul>
<li><strong>标签管理策略</strong>
<ul>
<li>使用语义化版本标签（v1.2.3）</li>
<li>为最新稳定版本维护 latest 标签</li>
<li>包含构建元数据（commit hash + 日期）</li>
</ul>
</li>
<li><strong>日志管理, 防止磁盘撑爆</strong>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-1">1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-2">2</a></span><span>  <span style="color:#f92672">&#34;log-driver&#34;</span>: <span style="color:#e6db74">&#34;json-file&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-3">3</a></span><span>  <span style="color:#f92672">&#34;log-opts&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-4">4</a></span><span>    <span style="color:#f92672">&#34;max-size&#34;</span>: <span style="color:#e6db74">&#34;10m&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-5">5</a></span><span>    <span style="color:#f92672">&#34;max-file&#34;</span>: <span style="color:#e6db74">&#34;3&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-6">6</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-7">7</a></span><span>}
</span></span></code></pre></div></li>
<li><strong>资源限制</strong>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-1">1</a></span><span>docker run -it --cpus<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span> --memory<span style="color:#f92672">=</span>512m --blkio-weight<span style="color:#f92672">=</span><span style="color:#ae81ff">500</span> your-image
</span></span></code></pre></div></li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%90%8E%E7%AB%AF" term="%E5%90%8E%E7%AB%AF" label="后端" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/docker" term="docker" label="docker" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[后端-常见身份认证协议详解与应用场景]]></title>
            <link href="https://blog.yunpiao.site/post/20250102181730/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250102181730/</id>
            
            
            <published>2025-01-02T18:17:30+08:00</published>
            <updated>2025-01-02T18:17:30+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="basic-鉴权">Basic 鉴权</h2>
<p>在 header 的 auth 字段中 base64 编码过后的账户密码, 安全性较差<br>
格式: <code>Authorization: Basic YWRtaW46MTIzNA==</code></p>
<h2 id="摘要认证">摘要认证</h2>
<p>使用哈希算法, 对账户,密码,随机字符串等进行摘要, 不直接将密码传递给后端<br>
格式: <code>Authorization: Digest username=&quot;admin&quot;, realm=&quot;example&quot;, nonce=&quot;xyz&quot;, uri=&quot;/resource&quot;, response=&quot;digest-hash&quot;</code></p>
<h2 id="bearer-token">Bearer Token</h2>
<p>用于 api 或者 Oauth 认证, 通过使用 token 来访问受限资源<br>
token , 可以由认证服务器 或者 后端服务直接返回, 一般为 set-cookie 的方式<br>
令牌中一般具有过期时间, 或者会持续更新 token<br>
格式:  <code>Authorization: Bearer &lt;token&gt;</code></p>
<h2 id="oauth">OAuth</h2>
<p>场景有 单点登录, api 授权, 第三方登录等</p>
<p>使用第三方授权服务, 分为多种授权模式</p>
<ul>
<li>授权码模式, 重定向到第三方授权服务器, 获取授权码, 通过授权码从授权服务器获取对应资源的 token</li>
<li>隐式模式, 授权直接返回, 不用授权码</li>
<li>密码模式, 直接将用户密码透传给授权服务器</li>
<li>客户端模式 ,机器对机器通信, 服务之间交互</li>
</ul>
<h2 id="令牌认证-token-base-auth">令牌认证 token-base auth</h2>
<p>首次登录提供账户密码. 验证后会生成令牌, 后续直接使用  token 进行访问, 令牌泄露会有风险, 一般会有过期时间和强制过期功能<br>
一般格式:  <code>Authorization: Bearer &lt;token&gt;</code></p>
<h2 id="双向-tls-认证">双向 TLS 认证</h2>
<p>客户端和服务端需要双向 tls 认证才能访问<br>
一般客户端会检查服务端的证书情况, 服务器也可以检查客户端证书情况, 数据传输系统中, 经常使用这种技术, 客户端证书认证通过后才能访问服务端资源</p>
<h2 id="ntlm-微软开发的认证协议-逐步被-kerberos-协议替代">NTLM 微软开发的认证协议, 逐步被 kerberos 协议替代</h2>
<p>基于 MD4 或者 MD5 进行认证<br>
流程:</p>
<ol>
<li>服务端返回一个字符串,</li>
<li>客户端使用 NTLM Hash（固定）和 返回的随机字符串生成一个 hash,</li>
<li>使用这个 hash 进行认证</li>
</ol>
<h2 id="kerberos-认证">Kerberos 认证</h2>
<p>引入一个第三方 KDC(密钥分发中心), 进行身份验证</p>
<ol>
<li>身份认证: 将账户密码加密后发送给 KDC 的 AS(验证服务), 返回加密的票据(TGT, 票据授权票据)和加密的会话密钥(用于和 TGS 通信), 客户端解密(使用密码派生密钥, 密码对称加密后的密钥)后保存票据</li>
<li>请求服务票据: 向 KDC 的 TGS (颁发服务票据)申请服务的票据 , 同时生成会话密钥(用于目标服务通信)</li>
<li>访问服务: 客户端使用服务票据和会话密钥的认证信息到目标服务器进行访问</li>
</ol>
<h2 id="oidc-openid-connect">OIDC (openid connect)</h2>
<p>基于 OAuth2 认证协议, 用于单点登录和身份认证, 扩展 OAuth, 不仅支持授权还支持身份认证</p>
<ol>
<li>客户端向 idP(身份提供者)发起认证</li>
<li>身份提供者要求用户登录</li>
<li>登录成功返回 id token 和 access token</li>
<li>客户端使用 id token (JWT) 验证用户身份(包含用户信息, 防止被篡改, 利用其他人的身份)</li>
<li>使用 access token 进行资源访问</li>
</ol>
<h2 id="cas-中央认证服务">CAS (中央认证服务)</h2>
<p>主要用于单点登录, 集中式的认证机制</p>
<ol>
<li>用户通过应用程序访问受保护资源</li>
<li>应用程序重定向到 CAS</li>
<li>用户在 CAS 中认证</li>
<li>CAS 返回服务票据</li>
<li>用户将服务票据发往应用程序</li>
<li>应用程序使用票据访问 CAS 验证票据</li>
<li>验证成功,应用返回受保护资源</li>
</ol>
<h2 id="webauthh">WebAuthh</h2>
<p>使用本地设备的生物识别进行无密码登录, 辅助认证手段, 不能保证生物识别在每一个机器上都拥有, 所以不能用作登录认证场景</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/security" term="security" label="Security" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%90%8E%E7%AB%AF" term="%E5%90%8E%E7%AB%AF" label="后端" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[K8S-longhorn RWX 卷出现连接卡死,无法读取问题解决]]></title>
            <link href="https://blog.yunpiao.site/post/20241211171937/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20241211171937/</id>
            
            
            <published>2024-12-11T17:19:37+08:00</published>
            <updated>2024-12-11T17:19:37+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="背景">背景</h2>
<p>公司产品私有化部署原本是用 nfs-provisioner  RWX 卷, 用于多个 pod 间的数据同步, 但是由于 <code>nfs</code> 也部署在产品内部, 还是有单点问题, 所以增加了使用 longhorn 作为分布式存储组件, 在兼容适配后, 在交付第一次给用户部署的时候, 遇到了一个奇怪的问题</p>
<h2 id="现象">现象</h2>
<ol>
<li>刚安装完毕后数据, 系统一切正常, 后来过段时间发现当 ls 挂载的 RWX 卷的时候, 程序会卡死在这个 ls 命令下</li>
<li>share-manager-pvc pod 负载不断升高, 日志不断打印客户端建立 session 和 断开 session</li>
<li>ganesha.nfsd cpu 不断升高, 一直上升到 100%</li>
</ol>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/12/23b722085cd31e1206866b3a233b56c3.png" alt="image.png"  /></p>
<h2 id="尝试解决">尝试解决</h2>
<h3 id="1-定位是哪里出现了问题">1. 定位是哪里出现了问题</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span><span style="color:#f92672">[</span>root@centos7-node-1 itdr-base<span style="color:#f92672">]</span><span style="color:#75715e"># kubectl get pod -n longhorn-system </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span>NAME                                                     READY   STATUS    RESTARTS       AGE
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>csi-attacher-6c46cf9fb4-2tnvn                            1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>csi-attacher-6c46cf9fb4-7r4b5                            1/1     Running   <span style="color:#ae81ff">0</span>              5d3h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>csi-attacher-6c46cf9fb4-pg92t                            1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>csi-provisioner-569985c57c-295sc                         1/1     Running   <span style="color:#ae81ff">1</span> <span style="color:#f92672">(</span>9h ago<span style="color:#f92672">)</span>     5d3h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>csi-provisioner-569985c57c-m2rmq                         1/1     Running   <span style="color:#ae81ff">1</span> <span style="color:#f92672">(</span>30h ago<span style="color:#f92672">)</span>    5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>csi-provisioner-569985c57c-w6srv                         1/1     Running   <span style="color:#ae81ff">2</span> <span style="color:#f92672">(</span>11h ago<span style="color:#f92672">)</span>    5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>csi-resizer-7597448bf6-2gvqw                             1/1     Running   <span style="color:#ae81ff">2</span> <span style="color:#f92672">(</span>5d3h ago<span style="color:#f92672">)</span>   5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>csi-resizer-7597448bf6-gj8pf                             1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>csi-resizer-7597448bf6-kvl7z                             1/1     Running   <span style="color:#ae81ff">2</span> <span style="color:#f92672">(</span>8h ago<span style="color:#f92672">)</span>     5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>csi-snapshotter-549bbf4ffb-b4pqv                         1/1     Running   <span style="color:#ae81ff">1</span> <span style="color:#f92672">(</span>8h ago<span style="color:#f92672">)</span>     5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>csi-snapshotter-549bbf4ffb-drwfg                         1/1     Running   <span style="color:#ae81ff">2</span> <span style="color:#f92672">(</span>5d3h ago<span style="color:#f92672">)</span>   5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>csi-snapshotter-549bbf4ffb-h2r45                         1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>engine-image-ei-6c7d6635-cxpm9                           1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>engine-image-ei-6c7d6635-hmv4s                           1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17">17</a></span><span>engine-image-ei-6c7d6635-n9sd7                           1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18">18</a></span><span>engine-image-ei-6c7d6635-r7t2w                           1/1     Running   <span style="color:#ae81ff">1</span> <span style="color:#f92672">(</span>5d1h ago<span style="color:#f92672">)</span>   5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19">19</a></span><span>engine-image-ei-6c7d6635-rd8s7                           1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20">20</a></span><span>engine-image-ei-6c7d6635-v7svn                           1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21">21</a></span><span>instance-manager-14bf79e1fe244326ec6b6f5072c3b245        1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22">22</a></span><span>instance-manager-394ba9e4c3dac77955f37f65a0782ac8        1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23">23</a></span><span>instance-manager-7b604f3d0af1d43d33a1106ad3c58252        1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-24">24</a></span><span>instance-manager-cee2e92722813c9b0a6659f3ae7aecac        1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-25">25</a></span><span>instance-manager-d0e16589d6085f69d9b92962ed07e74f        1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-26">26</a></span><span>instance-manager-dd8c32539c1d24364789b50b2ea7d9ec        1/1     Running   <span style="color:#ae81ff">0</span>              5d1h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-27">27</a></span><span>longhorn-csi-plugin-4zwwv                                3/3     Running   <span style="color:#ae81ff">3</span> <span style="color:#f92672">(</span>5d1h ago<span style="color:#f92672">)</span>   5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-28">28</a></span><span>longhorn-csi-plugin-5xl2w                                3/3     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-29">29</a></span><span>longhorn-csi-plugin-csmsj                                3/3     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-30">30</a></span><span>longhorn-csi-plugin-hqgj6                                3/3     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-31">31</a></span><span>longhorn-csi-plugin-t2x77                                3/3     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-32">32</a></span><span>longhorn-csi-plugin-wg69z                                3/3     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-33">33</a></span><span>longhorn-driver-deployer-7f95dbdb75-s7w7s                1/1     Running   <span style="color:#ae81ff">0</span>              5d3h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-34">34</a></span><span>longhorn-manager-4hs2b                                   2/2     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-35">35</a></span><span>longhorn-manager-9mmg2                                   2/2     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-36">36</a></span><span>longhorn-manager-lr49l                                   2/2     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-37">37</a></span><span>longhorn-manager-lr9zd                                   2/2     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-38"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-38">38</a></span><span>longhorn-manager-rlvtp                                   2/2     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-39"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-39">39</a></span><span>longhorn-manager-vdh24                                   2/2     Running   <span style="color:#ae81ff">2</span> <span style="color:#f92672">(</span>5d1h ago<span style="color:#f92672">)</span>   5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-40"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-40">40</a></span><span>longhorn-ui-7b7dcc9589-mnjks                             1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-41"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-41">41</a></span><span>share-manager-pvc-824e52b5-1390-4b73-886a-248e65c445ed   1/1     Running   <span style="color:#ae81ff">0</span>              5d3h
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-42"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-42">42</a></span><span>share-manager-pvc-b0a20829-e360-4562-9c56-0693bc6ae6cd   1/1     Running   <span style="color:#ae81ff">0</span>              5d5h
</span></span></code></pre></div><ol>
<li>nfs 卡死, 那就一层一层往上找</li>
<li>检查发现 longhorn 是使用 share manager pvc 这一 pod 提供 nfs 服务的</li>
<li>直接通过这个 pvc 上层的 svc 进行手动挂载
<ol>
<li><code>mount -vvv -t nfs4 -o vers=4.2 -o nolock -o noresvport -o hard -o timeo=1 100.96.37.116:/pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f /mnt/longhorn-nfs</code></li>
</ol>
</li>
<li>检查现象一样, 同样无法挂载</li>
<li>重复创建一个 pvc, 发现刚开始可以, 过段时间现象依旧, 还是卡死</li>
<li>登录到 pod, 查看挂载的文件目录是否正常(文件目录通过 open-iscsi 提供目录挂载, 之后使用 nfs 将这一目录进行共享)</li>
</ol>
<p>至此, 可以确定是 <code>share-manager-pvc</code> 这个 pod 内的问题</p>
<h3 id="2-分析这个-share-manager-pvc--pod-是怎么工作的">2. 分析这个 <code>share-manager-pvc</code>  pod 是怎么工作的</h3>
<pre tabindex="0"><code>Longhorn Volume (存储卷)
       |
       v
Share-Manager Pod
(NFS Server 容器运行中)
       |
       v
Kubernetes Worker Node
(NFS Client 挂载 RWX 卷)
       |
       v
多个应用 Pod
(访问共享数据)
</code></pre><p>检查发现这个 pod 运行一个 <code># nfs-ganesha</code> 服务, 该程序是一个 <strong>NFS Server</strong> 容器，用于将存储卷的内容通过 NFS 协议共享出去</p>
<h3 id="3-首先尝试-strace-查看是不是因为一些系统调用卡死-导致无法处理请求">3. 首先尝试 strace 查看是不是因为一些系统调用卡死, 导致无法处理请求</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#f92672">[</span>root@localhost ~<span style="color:#f92672">]</span><span style="color:#75715e"># strace -p  2502828</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>strace: Process <span style="color:#ae81ff">2502828</span> attached
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>futex<span style="color:#f92672">(</span>0x7ffa45ffb990, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, 46, NULL, FUTEX_BITSET_MATCH_ANY
</span></span></code></pre></div><p>检查发现卡在了一个 轻量级的锁下面, 通过 google 发现并没有什么关于这个的 bug, 或者解决方法</p>
<h3 id="4-退回到最原始的解决方法-通过查看日志来排查完全依赖代码开发者的编程素养">4. 退回到最原始的解决方法, 通过查看日志来排查(完全依赖代码开发者的编程素养)</h3>
<p>查看日志如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1"> 1</a></span><span>11/12/2024 02:01:51 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.2.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e2 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2"> 2</a></span><span>11/12/2024 02:01:51 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.2.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e7 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3"> 3</a></span><span>11/12/2024 02:01:51 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa28000d80 name<span style="color:#f92672">=(</span>19:Linux NFSv4.2 bogon<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa2001dfc0 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa2001b440
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-4"> 4</a></span><span>11/12/2024 02:01:51 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> longhorn_rm_clid :CLIENT ID :EVENT :Remove client <span style="color:#e6db74">&#39;19%3ALinux%20NFSv4.2%20bogon&#39;</span> from recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f <span style="color:#f92672">(</span>19%3ALinux%20NFSv4.2%20bogon<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-5"> 5</a></span><span>11/12/2024 02:01:51 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> longhorn_add_clid :CLIENT ID :EVENT :Add client <span style="color:#e6db74">&#39;19:Linux NFSv4.2 bogon&#39;</span> to recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-6"> 6</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_32<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.4.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e3 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-7"> 7</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_32<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.4.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e8 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-8"> 8</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_32<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa28000d80 name<span style="color:#f92672">=(</span>19:Linux NFSv4.2 bogon<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa2001b440 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa3001a8c0
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-9"> 9</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_32<span style="color:#f92672">]</span> longhorn_rm_clid :CLIENT ID :EVENT :Remove client <span style="color:#e6db74">&#39;19%3ALinux%20NFSv4.2%20bogon&#39;</span> from recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f <span style="color:#f92672">(</span>19%3ALinux%20NFSv4.2%20bogon<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-10">10</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_32<span style="color:#f92672">]</span> longhorn_add_clid :CLIENT ID :EVENT :Add client <span style="color:#e6db74">&#39;19:Linux NFSv4.2 bogon&#39;</span> to recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-11">11</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.5.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e4 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-12">12</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.5.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e9 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-13">13</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa28000d80 name<span style="color:#f92672">=(</span>19:Linux NFSv4.2 bogon<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa3001a8c0 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa2001dfc0
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-14">14</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> longhorn_rm_clid :CLIENT ID :EVENT :Remove client <span style="color:#e6db74">&#39;19%3ALinux%20NFSv4.2%20bogon&#39;</span> from recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f <span style="color:#f92672">(</span>19%3ALinux%20NFSv4.2%20bogon<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-15">15</a></span><span>11/12/2024 02:01:53 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_33<span style="color:#f92672">]</span> longhorn_add_clid :CLIENT ID :EVENT :Add client <span style="color:#e6db74">&#39;19:Linux NFSv4.2 bogon&#39;</span> to recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-16">16</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.0.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008df -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-17">17</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.0.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008ea -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-18">18</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa34004420 name<span style="color:#f92672">=(</span>23:Linux NFSv4.2 localhost<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa3c01db30 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa2001b440
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-19">19</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> longhorn_rm_clid :CLIENT ID :EVENT :Remove client <span style="color:#e6db74">&#39;23%3ALinux%20NFSv4.2%20localhost&#39;</span> from recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f <span style="color:#f92672">(</span>23%3ALinux%20NFSv4.2%20localhost<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-20">20</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> longhorn_add_clid :CLIENT ID :EVENT :Add client <span style="color:#e6db74">&#39;23:Linux NFSv4.2 localhost&#39;</span> to recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-21">21</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_28<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.3.1 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e5 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-22">22</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.3.1 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008eb -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-23">23</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa34004420 name<span style="color:#f92672">=(</span>23:Linux NFSv4.2 localhost<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa2001b440 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa0c015b50
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-24">24</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> longhorn_rm_clid :CLIENT ID :EVENT :Remove client <span style="color:#e6db74">&#39;23%3ALinux%20NFSv4.2%20localhost&#39;</span> from recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f <span style="color:#f92672">(</span>23%3ALinux%20NFSv4.2%20localhost<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-25">25</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> longhorn_add_clid :CLIENT ID :EVENT :Add client <span style="color:#e6db74">&#39;23:Linux NFSv4.2 localhost&#39;</span> to recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-26">26</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.1.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008e6 -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-27">27</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.1.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008ec -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-28">28</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa28000d80 name<span style="color:#f92672">=(</span>19:Linux NFSv4.2 bogon<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa2001dfc0 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa0c021c50
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-29">29</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> longhorn_rm_clid :CLIENT ID :EVENT :Remove client <span style="color:#e6db74">&#39;19%3ALinux%20NFSv4.2%20bogon&#39;</span> from recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f <span style="color:#f92672">(</span>19%3ALinux%20NFSv4.2%20bogon<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-30">30</a></span><span>11/12/2024 02:01:54 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_24<span style="color:#f92672">]</span> longhorn_add_clid :CLIENT ID :EVENT :Add client <span style="color:#e6db74">&#39;19:Linux NFSv4.2 bogon&#39;</span> to recovery backend share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-31">31</a></span><span>11/12/2024 02:01:56 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_28<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.0.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008ea -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-32">32</a></span><span>11/12/2024 02:01:56 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_28<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :CREATE_SESSION client addr<span style="color:#f92672">=</span>::ffff:100.97.0.0 clientid<span style="color:#f92672">=</span>Epoch<span style="color:#f92672">=</span>0x6758f134 Counter<span style="color:#f92672">=</span>0x000008ed -------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-33">33</a></span><span>11/12/2024 02:01:56 : epoch 6758f134 : share-manager-pvc-d14f1a3a-7dfe-4684-b650-6736cdd7623f : nfs-ganesha-33<span style="color:#f92672">[</span>svc_28<span style="color:#f92672">]</span> nfs4_op_create_session :CLIENT ID :INFO :Client Record 0x7ffa34004420 name<span style="color:#f92672">=(</span>23:Linux NFSv4.2 localhost<span style="color:#f92672">)</span> refcount<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span> cr_confirmed_rec<span style="color:#f92672">=</span>0x7ffa0c015b50 cr_unconfirmed_rec<span style="color:#f92672">=</span>0x7ffa3c01dec0
</span></span></code></pre></div><p>发现不对劲, 十分有十二分的不对劲, 日志中显示, 程序似乎陷入到了 <code>longhorn_rm_clid</code> 和 <code>longhorn_add_clid</code> 的循环中, 十分频繁,而正常环境是每个主机加入后, 之后就不再重建. 检查发现不通的 <code>client addr</code> 却拥有这相同的 <code>clientid</code>, 怀疑是不是因为 <code>localhost</code>  这个的问题(hostname 的问题)</p>
<h3 id="5-google-关键词-nfs-ganesha-hostname">5. google 关键词 <code>nfs-ganesha hostname</code></h3>
<p>发现有一个 <a href="https://lists.nfs-ganesha.org/archives/list/devel@lists.nfs-ganesha.org/thread/4H2JRMKNA7P2GCJ6UC5HISOEEBJXSYLP/" target="_blank" rel="noopener nofollow noreferrer" >讨论</a> , 这个 nfs 服务器在<code>Multi-client Same Hostname</code> 有 <code>issue</code>, 所以到这里几乎就可以判断出来了, 因为 <code>k8s node </code>的 <code>hostname</code> 相同导致的, 登录到节点上查看发现果然是的, 所有的 <code>hostname</code> 都是 <code>localhost</code></p>
<h2 id="最终解决方法-一般看似很稀奇的问题-背后都是一个很沙雕的原因导致的">最终解决方法 (一般看似很稀奇的问题, 背后都是一个很沙雕的原因导致的)</h2>
<p>根据 <a href="https://github.com/nfs-ganesha/nfs-ganesha/issues/676" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/nfs-ganesha/nfs-ganesha/issues/676</a> 描述, 前置条件是需要每一个主机有唯一的 hostname, 解决方法</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1"> 1</a></span><span>- <span style="color:#f92672">hosts</span>: <span style="color:#ae81ff">kube_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2"> 2</a></span><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">设置主机名为 inventory_hostname</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3"> 3</a></span><span>  <span style="color:#f92672">tasks</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4"> 4</a></span><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">设置主机名</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5"> 5</a></span><span>      <span style="color:#f92672">command</span>: <span style="color:#ae81ff">hostnamectl set-hostname {{ k8s_nodename }}</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6"> 6</a></span><span>      <span style="color:#f92672">become</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7"> 7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-8"> 8</a></span><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">确认主机名设置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-9"> 9</a></span><span>      <span style="color:#f92672">command</span>: <span style="color:#ae81ff">hostname</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-10">10</a></span><span>      <span style="color:#f92672">register</span>: <span style="color:#ae81ff">hostname_output</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-11">11</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-12">12</a></span><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">输出主机名设置结果</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-13">13</a></span><span>      <span style="color:#f92672">debug</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-14">14</a></span><span>        <span style="color:#f92672">msg</span>: <span style="color:#e6db74">&#34;{{ hostname_output.stdout }}&#34;</span>
</span></span></code></pre></div><h2 id="反思">反思</h2>
<h3 id="在-longhorn-安装前进行了-env-check-的检查-但是一切正常-为什么没有检查出来主机名不唯一env-check-中有关于主机名的检查">在 longhorn 安装前进行了 env check 的检查, 但是一切正常, 为什么没有检查出来主机名不唯一(env check 中有关于主机名的检查)</h3>
<p><code>env check </code>中使用 <code>kubectl get node</code> 中的 <code>name</code> 获取 <code>hostname</code> 查看是否唯一, 但实际上的 hostname 并不是 k8s 中记录的</p>
<p>kubeasz 中创建 kubelet 服务文件的时候使用了一个小 <code>tricks</code>, 重写了 <code>hostname</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span>ExecStart<span style="color:#f92672">={{</span> bin_dir <span style="color:#f92672">}}</span>/kubelet <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span><span style="color:#ae81ff"></span>  --config<span style="color:#f92672">=</span>/var/lib/kubelet/config.yaml <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3">3</a></span><span><span style="color:#ae81ff"></span>  --container-runtime-endpoint<span style="color:#f92672">=</span>unix:///run/containerd/containerd.sock <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4">4</a></span><span><span style="color:#ae81ff"></span>  --hostname-override<span style="color:#f92672">={{</span> K8S_NODENAME <span style="color:#f92672">}}</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-5">5</a></span><span><span style="color:#ae81ff"></span>  --kubeconfig<span style="color:#f92672">=</span>/etc/kubernetes/kubelet.kubeconfig <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-6">6</a></span><span><span style="color:#ae81ff"></span>  --root-dir<span style="color:#f92672">={{</span> KUBELET_ROOT_DIR <span style="color:#f92672">}}</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-7">7</a></span><span><span style="color:#ae81ff"></span>  --v<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span>
</span></span></code></pre></div><h2 id="其他命令">其他命令</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span><span style="color:#75715e"># 查看挂载卷</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-2">2</a></span><span>kubectl -n longhorn-system get volumes 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-3">3</a></span><span><span style="color:#75715e"># 删除挂载卷</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-4">4</a></span><span>kubectl -n longhorn-system delete volume pvc-a99e2813-f30c-4562-87f6-3e49b568f8fe 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-5">5</a></span><span><span style="color:#75715e"># 获取卷的副本数</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-6">6</a></span><span>kubectl -n longhorn-system get replicas
</span></span></code></pre></div>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/kubernetes" term="kubernetes" label="Kubernetes" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/k8s" term="k8s" label="k8s" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/longhorn" term="longhorn" label="longhorn" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[编程-code review 的检查清单]]></title>
            <link href="https://blog.yunpiao.site/post/20241114150556/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20241114150556/</id>
            
            
            <published>2024-11-14T11:05:56+08:00</published>
            <updated>2024-11-14T11:05:56+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="代码审核清单">代码审核清单</h1>
<h2 id="1-代码结构">1. 代码结构</h2>
<ul>
<li><strong>代码长度与复杂度</strong>：检查是否有过长的函数或方法，过于复杂的嵌套逻辑，或是入参过多的情况。尽量将长函数拆分成多个小函数，保持代码简洁易懂。</li>
<li><strong>重复代码</strong>：是否存在重复代码片段？重复代码应该提取成公共方法或类，减少维护成本。</li>
<li><strong>死循环与异常处理</strong>：确保没有不必要的死循环，所有可能的异常都应得到适当的处理和记录。</li>
</ul>
<h2 id="2-代码注释">2. 代码注释</h2>
<ul>
<li><strong>注释规范</strong>：确保注释简洁明了，特别是在复杂的逻辑部分。注释应清晰地描述为什么这么做，而不仅仅是代码的实现细节。</li>
<li><strong>注释位置</strong>：注释应放在合适的位置，避免过于冗长。函数头部、复杂条件判断前、特殊逻辑部分等地方尤为重要。</li>
<li><strong>缩进与格式化</strong>：代码的缩进和格式应统一，保持一致的空格键、制表符（tab）使用，符合团队的代码规范。</li>
</ul>
<h2 id="3-安全性">3. 安全性</h2>
<ul>
<li><strong>资源关闭</strong>：所有IO操作后，确保资源（如文件、数据库连接等）都被及时关闭，避免资源泄漏。</li>
<li><strong>数据类型使用</strong>：避免在不合适的场合使用 <code>double</code> 和 <code>float</code>，例如在涉及金额计算时，推荐使用 <code>BigDecimal</code>。</li>
<li><strong>池大小与并发</strong>：合理配置对象池的大小，并发操作中确保线程安全，避免死锁。</li>
<li><strong>输入验证与异常处理</strong>：对所有外部输入进行严格检查，避免注入攻击。异常处理时要确保不会泄露敏感信息。</li>
<li><strong>日志与敏感信息</strong>：日志记录应避免暴露敏感数据，且日志文件大小和保留天数要有控制。</li>
</ul>
<h2 id="4-性能">4. 性能</h2>
<ul>
<li><strong>SQL 优化</strong>：关注 SQL 执行时间，避免复杂查询，尤其是对大数据集的操作。确保数据库查询已优化并使用预处理语句以防注入。</li>
<li><strong>正则表达式性能</strong>：正则表达式可能会影响性能，特别是在处理大量数据时。确保使用合适的正则表达式并进行优化。</li>
<li><strong>对象复用与内存管理</strong>：尽量复用对象，避免频繁创建和销毁，特别是在高并发环境下。考虑使用对象池来管理资源。</li>
<li><strong>异步处理与锁优化</strong>：使用异步 I/O 来提高性能，锁的粒度应尽可能小，以减少竞争。</li>
</ul>
<h2 id="5-单元测试">5. 单元测试</h2>
<ul>
<li><strong>可测试性</strong>：确保代码能够被有效单元测试，关键模块应覆盖所有功能，包括异常场景。</li>
<li><strong>异常测试</strong>：单元测试不仅要覆盖正常流程，还应验证各种异常场景，确保代码的健壮性。</li>
</ul>
<h2 id="6-优化">6. 优化</h2>
<ul>
<li><strong>替代自定义方案</strong>：考虑使用标准库或已有的高效解决方案，避免重新造轮子。</li>
<li><strong>异步处理与缓存</strong>：长时间运行的遍历操作是否可以异步执行？耗时函数是否可以使用缓存（如 LRU 缓存）来减少负载？</li>
<li><strong>IO 请求合并</strong>：多个小的 IO 请求是否可以合并成一次请求，以减少通信开销？</li>
</ul>
<h2 id="7-运维">7. 运维</h2>
<ul>
<li><strong>监控与指标暴露</strong>：服务和系统应该有完善的监控，关键指标（如 CPU、内存、磁盘 I/O）要暴露出来。确保 SQL 查询时间、外部调用等重要性能指标可以监控。</li>
<li><strong>告警系统</strong>：合理配置告警规则，确保及时发现问题，并能快速响应。告警级别、负责人和通知方式要清晰明确。</li>
<li><strong>探活与应急预案</strong>：确保系统有探活机制，能够在出现问题时及时发现，并有预案处理故障。</li>
</ul>
<h2 id="8-其他">8. 其他</h2>
<ul>
<li><strong>业务逻辑与需求一致性</strong>：确认业务实现符合需求，避免功能实现上的偏差。</li>
<li><strong>代码可读性与可维护性</strong>：代码应具有良好的可读性，修改点要考虑到对整体系统的影响，尤其是在工具函数和公共方法的修改上。</li>
<li><strong>兼容性与异常演练</strong>：不同操作系统、不同网络环境下的兼容性要做好测试。考虑到已知的风险和潜在故障，做好应急演练。</li>
<li><strong>代码审查的频率与规模</strong>：每次代码审查的代码行数应控制在200行以内，过多代码的审查容易导致疏漏。</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/backend" term="backend" label="Backend" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%80%BB%E7%BB%93" term="%E6%80%BB%E7%BB%93" label="总结" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[数据-redis 使用经验总结]]></title>
            <link href="https://blog.yunpiao.site/post/20241106103955/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20241106103955/</id>
            
            
            <published>2024-11-06T10:39:55+08:00</published>
            <updated>2024-11-06T10:39:55+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="记录工作中总结的-redis-相关知识">记录工作中总结的 redis 相关知识</h1>
<h2 id="redis-底层数据接口对应关系">redis 底层数据接口对应关系</h2>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230920094241.png" alt="image.png"  /></p>
<h2 id="redis-如何保存数据">redis 如何保存数据</h2>
<p>采用了哈希表来保存所有的键值对 一个哈希表对应了多个哈希桶 哈希桶中的entry元素中保存了<em>key和</em>value指针，分别指向了实际的键和值 通过链式冲突解决冲突</p>
<p>为了避免链式冲突解决方法上的不足 rehash ， 增加哈希桶数量</p>
<ol>
<li>两个 hash 操作， a 需要扩容， b 申请两倍的空间</li>
<li>a 重新映射并赋值拷贝</li>
<li>释放 a 的空间</li>
</ol>
<p>问题 复制的时候需要时间 可能会阻塞线程 无法服务请求</p>
<h2 id="为什么采用-单线程">为什么采用 单线程</h2>
<p>指的是 网络 io 和 键值对读写  持久化和集群数据是由额外的进程完成<br>
避免并发带来的数据共享问题， redis 性能瓶颈不在于读写， 网络、cpu 才是瓶颈</p>
<p>6.0 引入多进程， 网络 io 到实际的读写还是单线程，但是网络性能提升， 性能瓶颈出现在了网络 io 上， 多 io 处理网络请求， 提高网络处理并行度， 但是读写命令还是单线程</p>
<h2 id="为什么快">为什么快</h2>
<p>io 多路复用 并发处理客户端请求， 提升吞吐率<br>
一个进程多个 io 流， select epoll 机制， 内核会一直监听多个套接字请求， 有请求到来， 交给 redis 处理<br>
基于内存 操作迅速<br>
底层数据结构 高效， 单进程 无竞争</p>
<h2 id="重启后数据丢失问题">重启后数据丢失问题</h2>
<p>AOF RDB<br>
AOF 先执行命令 在写入内存 再记录日志</p>
<p>什么时候写入磁盘<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230920100522.png" alt="image.png"  /></p>
<p>每条数据都会记录， 数据会越来越大，</p>
<h3 id="aof-重写机制">AOF 重写机制</h3>
<p>主线程 fork 后台 bgrewriteof<br>
根据所有键值创建一个新的 AOF 文件，<br>
重写前 拷贝主线程内存 拷贝页表， 不影响主线程的情况下， 逐一拷贝数据到重写， 如果有新的写命令 先放入缓存区， 完成后写入日志</p>
<h3 id="rdb-快照">RDB 快照</h3>
<p>日志比较多的时候， 启动会比较慢， 全量快照， 两个命令生成， save 和 bgsave， 主线程中执行， 导致阻塞， bgsave， 子进程 避免阻塞，<br>
借用操作系统的写时复制， 快照期间还是可以正常写入，</p>
<p>主要流程为：</p>
<ul>
<li>bgsave子进程是由主线程fork出来的，可以共享主线程的所有内存数据。</li>
<li>bgsave子进程运行后，开始读取主线程的内存数据，并把它们写入RDB文件中。</li>
<li>如果主线程对这些数据都是读操作，例如A，那么主线程和bgsave子进程互不影响。</li>
<li>如果主线程需要修改一块数据，如C，这块数据会被复制一份，生成数据的副本，然主线程在这个副本上进行修改；bgsave子进程可以把原来的数据C写入RDB文件。</li>
</ul>
<h2 id="写时复制">写时复制</h2>
<blockquote>
<p>通过 <code>fork()</code> 来创建一个子进程时，操作系统需要将父进程虚拟内存空间中的大部分内容全部复制到子进程中（主要是数据段、堆、栈；代码段共享）。这个操作不仅非常耗时，而且会浪费大量物理内存。引入了写时复制技术。内核不会复制进程的整个地址空间，而是只复制其页表，<code>fork</code> 之后的父子进程的地址空间指向同样的物理内存页 然而只要有一个进程试图写入共享区域的某个页面，那么就会为这个进程创建该页面的一个新副本。写时复制技术将内存页的复制延迟到第一次写入时，更重要的是，在很多情况下不需要复制。这节省了大量时间，充分使用了稀有的物理内存。<br>
原理<br>
<code>fork()</code> 之后，内核会把父进程的所有内存页都标记为<strong>只读</strong>。一旦其中一个进程尝试写入某个内存页，就会触发一个保护故障（缺页异常），此时会陷入内核。<br>
内核将拦截写入，并为尝试写入的进程创建这个页面的一个<strong>新副本</strong>，恢复这个页面的<strong>可写权限</strong>，然后重新执行这个写操作，这时就可以正常执行了。<br>
内存页本来被父子进程应用， 两个引用， 创建新副本后， 引用 -1 ， 如果页面只有一个引用， 则直接修改数据</p>
</blockquote>
<h3 id="优缺点">优缺点</h3>
<p>优点：减少不必要的资源分配，节省宝贵的物理内存。<br>
缺点：如果在子进程存在期间发生了大量写操作，那么会频繁地产生页面错误，不断陷入内核，复制页面。这反而会降低效率。</p>
<blockquote>
<p>golang 中也有这样的使用， string，array 也是写时复制<br>
快照全量有压力，Redis采用了增量快照，在做一次全量快照后，后续的快照只对修改的数据进行记录，需要记住哪些数据被修改了<br>
在Redis4.0提出了混合使用AOF和RDB快照的方法，也就是两次RDB快照期间的所有命令操作由AOF日志文件进行记录。这样的好处是RDB快照不需要很频繁的执行，可以避免频繁fork对主线程的影响，而且AOF日志也只记录两次快照期间的操作，不用记录所有操作，</p>
</blockquote>
<h2 id="混合持久化">混合持久化</h2>
<ul>
<li>在Redis 4.0及以上版本中，可以同时使用RDB和AOF进行持久化。</li>
<li><strong>优化</strong>:
<ul>
<li>结合RDB的快速启动和AOF的数据完整性优势。</li>
<li>在发生故障时，可以使用RDB快速恢复大部分数据，然后使用AOF重放最近的写入操作。</li>
</ul>
</li>
</ul>
<h2 id="redis-数据同步">redis 数据同步</h2>
<p>第一次数据同步， 发送 rdb 文件<br>
级联的“主-从-从”模式<br>
手动选择一个从库，用来同步其他从库的数据，以减少主库生成RDB文件和传输RDB文件的压力；<br>
这样从库就可以知道在进行数据同步的时候，不需要和主库直接交互，只需要和选择的从库进行写操作同步就可以了，从而减少主库的压力</p>
<p>==redis 执行 全量同步的时候, 会推迟 bgsave 的执行==</p>
<h2 id="redis-的增量同步">redis 的增量同步</h2>
<p>在 redis 中 .offsetrepl_backlog_buffer是一个环形的缓冲区，如果从库断开时间太长导致自己的offset被覆盖了，就只能再次全量同步了。<br>
全量同步是自动的</p>
<h3 id="数据一致性问题"><strong>数据一致性问题</strong></h3>
<p>当从服务器已经完成和主服务的数据同步之后，再新增的命令会以异步的方式发送至从服务器，在这个过程中主从同步会有短暂的数据不一致，如在这个异步同步发生之前主服务器宕机了，会造成数据不一致。</p>
<h2 id="redis-事务">redis 事务</h2>
<p>使用 lua 脚本是简单直接的方法, 但是用户程序中需要对 lua 脚本进行校验, 避免出现注入的情况</p>
<p>正常方式是使用</p>
<ul>
<li>MULTI 开启</li>
<li>EXEC commit</li>
<li>WATCH 查看是否变更, 变更事务会打断</li>
<li>DISCARD 取消事务</li>
<li>UNWATCH 取消所有监视</li>
</ul>
<blockquote>
<p>注意 事务中的命令 redis 都会执行, 不管对错, Redis 不会停止命令的处理<br>
注意 redis 不算满足了原子性</p>
</blockquote>
<h2 id="大-key-问题">大 key 问题</h2>
<p><code>redis-cli -h b.redis -p 1959 --bigkeys</code> 使用该命令查看都有哪些大 key , 程序频繁请求大 key 会出现问题, 一个是内存问题, 一个是网络问题, 同时会阻塞其他请求, 这种大 key 需要尽量避免, 防止出现问题, 可以通过加 lru 或者拆分去解决</p>
<h2 id="redis-的高可用">redis 的高可用</h2>
<p>常用的有<br>
redis cluster, 好像问题较多, 没有具体使用过<br>
twemproxy 不算集群方案, 属于前置加了一层 hash 分片处理,传输 ==lua 脚本的时候会出现问题, 一个 lua 脚本 同时操作的 key 如果不在同一个 redis,  会尝试分发, 可能会失败==<br>
哨兵模式, 客户端程序容易出问题, 主从切换的时候, 客户端需要及时切换, 不是所有客户端都支持, 可以把这一层切换到 haproxy 中去执行, 其他客户端直联 haproxy, 故障后 haproxy 自动切换连接的 redis</p>
<h2 id="哨兵模式下的从服务器只读性">哨兵模式下的从服务器只读性</h2>
<p>默认在情况下，处于复制模式的主服务器既可以执行写操作也可以执行读操作，而从服务器则只能执行读操作。</p>
<p>可以在从服务器上执行 <code>config set replica-read-only no</code> 命令，使从服务器开启写模式，但需要注意以下几点：</p>
<ul>
<li>在从服务器上写的数据不会同步到主服务器；</li>
<li>当键值相同时主服务器上的数据可以覆盖从服务器；</li>
<li>在进行完整数据同步时，从服务器数据会被清空。</li>
</ul>
<h2 id="redis-哨兵模式的风险点">redis 哨兵模式的风险点</h2>
<ol>
<li>三个节点请情况下, 哨兵挂了两个， 也会有问题，是指至少两个哨兵认为需要切换才切换主备， 则两台哨兵挂了 则不会切换</li>
<li>redis 哨兵模式， 三个机器全量数据， 数据过多则会消耗内存</li>
</ol>
<h2 id="redis-scan">redis scan</h2>
<p>scan 如何实现的, 参考: <a href="https://my.oschina.net/liboware/blog/5371977" target="_blank" rel="noopener nofollow noreferrer" >https://my.oschina.net/liboware/blog/5371977</a><br>
生产环境用 scan 禁止 keys</p>
<h3 id="scan-用法">scan 用法</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span><span style="color:#66d9ef">local</span> cursor <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;0&#34;</span>  <span style="color:#75715e">-- 初始化游标</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span><span style="color:#66d9ef">repeat</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>    <span style="color:#75715e">-- 扫描匹配的键</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>    <span style="color:#66d9ef">local</span> resp <span style="color:#f92672">=</span> redis.call(<span style="color:#e6db74">&#39;SCAN&#39;</span>, cursor, <span style="color:#e6db74">&#39;MATCH&#39;</span>, <span style="color:#e6db74">&#39;authToken*&#39;</span>, <span style="color:#e6db74">&#39;COUNT&#39;</span>, <span style="color:#ae81ff">10000</span>) <span style="color:#75715e">-- count 表示 hash 槽遍历的数量, 不是返回的 key 的数量</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>    cursor <span style="color:#f92672">=</span> tonumber(resp[<span style="color:#ae81ff">1</span>])  <span style="color:#75715e">-- 获取新的游标</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>    <span style="color:#66d9ef">local</span> dataList <span style="color:#f92672">=</span> resp[<span style="color:#ae81ff">2</span>]  <span style="color:#75715e">-- 获取扫描到的键</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>    <span style="color:#75715e">-- 遍历所有符合条件的键</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>    <span style="color:#66d9ef">for</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>, <span style="color:#f92672">#</span>dataList <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>        <span style="color:#66d9ef">local</span> d <span style="color:#f92672">=</span> dataList[i]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>        <span style="color:#66d9ef">local</span> ttl <span style="color:#f92672">=</span> redis.call(<span style="color:#e6db74">&#39;TTL&#39;</span>, d)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>        <span style="color:#75715e">-- 如果 TTL 为 -1（没有过期时间），则删除该键</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>        <span style="color:#66d9ef">if</span> ttl <span style="color:#f92672">==</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span> <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>            redis.call(<span style="color:#e6db74">&#39;DEL&#39;</span>, d)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>        <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17">17</a></span><span>    <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18">18</a></span><span><span style="color:#66d9ef">until</span> cursor <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>  <span style="color:#75715e">-- 如果游标为 0，表示扫描完成</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19">19</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20">20</a></span><span><span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#39;all finished&#39;</span>
</span></span></code></pre></div><h2 id="redis-lua-使用时的最佳实践">redis lua 使用时的最佳实践</h2>
<h4 id="显式的参数传递">显式的参数传递</h4>
<p><strong>无论单机还是集群模式，对于 key 的操作必须通过参数列表，显示的传进去，而不能依赖脚本或是随机逻辑</strong></p>
<blockquote>
<p><a href="https://mytechshares.com/2022/10/07/dive-redis-lua/" target="_blank" rel="noopener nofollow noreferrer" >https://mytechshares.com/2022/10/07/dive-redis-lua/</a></p>
</blockquote>
<p>使用 keys传入, 不能随机或者直接硬编码, 更不能依赖 hgetall 获取 key<br>
解释: 对于集群模式, 主从节点同时执行相同的 lua 脚本(3.2 版本原生模式下), 但是 hgetall 得到的数据在主从是不一致的, 最终导致主从数据的不一致</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#75715e"># Python 示例，使用 redis-py 库</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>script <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span><span style="color:#e6db74">local value1 = redis.call(&#39;get&#39;, KEYS[1])
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span><span style="color:#e6db74">local value2 = redis.call(&#39;get&#39;, KEYS[2])
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5">5</a></span><span><span style="color:#e6db74">return value1 .. &#39; &#39; .. value2
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6">6</a></span><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7">7</a></span><span>keys <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#39;user:1000&#39;</span>, <span style="color:#e6db74">&#39;user:1001&#39;</span>]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8">8</a></span><span>result <span style="color:#f92672">=</span> redis<span style="color:#f92672">.</span>eval(script, len(keys), <span style="color:#f92672">*</span>keys)
</span></span></code></pre></div><h4 id="集群模式下的命令传递">集群模式下的命令传递</h4>
<p>Redis 集群（Cluster）模式下，当你使用 Lua 脚本操作多个键时，这些键必须位于同一个 <strong>slot</strong>（哈希槽）上，并且必须在同一个 <strong>shard</strong>（分片）内。<br>
使用 tempproxy 的时候不能执行复杂的 redis keys 操作, 需要保证数据在同一个 redis 实例, 不然会存在非预期错误</p>
<h4 id="lua-脚本在主从同步上的改进">lua 脚本在主从同步上的改进</h4>
<p>3.2 版本前, 脚本原生模式, 主从都执行相同的 lua 脚本, 但是结果可能不同, 导致不一致<br>
3.2 后 出现了效果复制, 复制 lua 实际的执行命令</p>
<p>lua 脚本为了数据一致性做的改进</p>
<ul>
<li><strong>不允许获取系统时间或外部状态</strong>：Lua 脚本不允许直接调用 <code>TIME</code> 或访问外部变量，这保证了脚本的执行不受外部状态变化的影响，避免了主从节点之间的差异。</li>
<li><strong>修改伪随机函数 <code>math.random</code></strong>：Redis 对 <code>math.random</code> 函数做了修改，确保每次调用时，若没有重新设置种子（<code>math.randomseed</code>），返回的随机数序列是确定的。这样，即使在不同节点上执行相同的 Lua 脚本，也能保证返回相同的随机结果。</li>
<li><strong>命令返回顺序的统一</strong>：在 Redis 4.0 版本之前，像 <code>SMEMBERS</code> 这类命令返回的结果是有序的（按字典顺序），但从 Redis 5.0 版本开始，这种排序行为被移除了。因此，Lua 脚本不能假设任何返回结果的顺序，除非文档明确说明该命令有顺序保证。</li>
<li><strong>禁止调用随机命令并修改数据库</strong>：为了保持脚本的确定性，Redis 不允许在 Lua 脚本中调用 <code>RANDOMKEY</code>、<code>SRANDMEMBER</code>、<code>TIME</code> 等不确定性命令后，尝试修改数据库。如果 Lua 脚本调用这些命令，且后续试图进行写操作，Redis 会报错。这是为了避免基于随机数的操作影响到主从一致性。</li>
</ul>
<h4 id="lua-缓存">lua 缓存</h4>
<p>lua 每次执行解析性能有消耗, 可以优化, 使用 cache 将脚本缓存下来</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>script load <span style="color:#e6db74">&#34;redis.call(&#39;incr&#39;, KEYS[1])&#34;</span> <span style="color:#75715e">-- 加载后返回 hash </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>EVALSHA da0bf4095ef4b6f337f03ba9dcd326dbc5fc8ace <span style="color:#ae81ff">1</span> testkey <span style="color:#75715e">-- 直接使用 hash 执行 lua 脚本</span>
</span></span></code></pre></div><h2 id="redis-分布式锁的最佳实践">redis 分布式锁的最佳实践</h2>
<h4 id="使用-set-命令实现分布式锁">使用 <code>SET</code> 命令实现分布式锁</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>SET lock_key unique_lock_value NX PX <span style="color:#ae81ff">10000</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span>- <span style="color:#e6db74">`</span>NX<span style="color:#e6db74">`</span>：只有在 <span style="color:#e6db74">`</span>lock_key<span style="color:#e6db74">`</span> 不存在时才设置成功。
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3">3</a></span><span>- <span style="color:#e6db74">`</span>PX<span style="color:#e6db74">`</span>：设置键的过期时间（单位：毫秒），防止锁被永久占用，避免死锁。
</span></span></code></pre></div><p>通过这种方式，==一次原子操作==就可以完成锁的获取和过期时间的设置，减少了潜在的竞争风险。</p>
<h2 id="redis-删除大-key">redis 删除大 key</h2>
<ol>
<li>逐渐删除, 使用 hscan 等小批量删除</li>
<li>使用 unlink , 异步删除</li>
</ol>
<h2 id="redis-压测">redis 压测</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span>redis  和 tewmproxy 极限性能
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2">2</a></span><span>redis-benchmark -h node -p <span style="color:#ae81ff">8985</span> -t set,lpush -n <span style="color:#ae81ff">10000</span> -q
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3">3</a></span><span>redis-benchmark -h node -p <span style="color:#ae81ff">8985</span> -t set, INCR, hincrby,expireat -n <span style="color:#ae81ff">300000</span> -q
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4">4</a></span><span>redis-benchmark -h node -p <span style="color:#ae81ff">8985</span> -n <span style="color:#ae81ff">100000</span> -q script load <span style="color:#e6db74">&#34;redis.call(&#39;hincrby&#39;,&#39;foo&#39;,&#39;foo&#39;,&#39;1&#39;) redis.call(&#39;expire&#39;,&#39;foo&#39;,10)&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5">5</a></span><span>redis-benchmark -h node -p <span style="color:#ae81ff">8099</span> -t set,lpush -n <span style="color:#ae81ff">10000</span> -q
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6">6</a></span><span>redis-benchmark -s ./twemproxy/data/yjsv5-rate-limit-twemproxy.sock -t set,lpush -n <span style="color:#ae81ff">10000</span> -q
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7">7</a></span><span>redis-benchmark  -s ./twemproxy/data/yjsv5-rate-limit-twemproxy.sock -n <span style="color:#ae81ff">100000</span> -q script load <span style="color:#e6db74">&#34;redis.call(&#39;hincrby&#39;,&#39;foo&#39;,&#39;foo&#39;,&#39;1&#39;) redis.call(&#39;expire&#39;,&#39;foo&#39;,10)&#34;</span>
</span></span></code></pre></div><h2 id="redis-内部-key-删除策略">redis 内部 key 删除策略</h2>
<h3 id="不超过设置最大内存">不超过设置最大内存</h3>
<p>主库</p>
<ol>
<li>每次访问 key 检查是否过期 (惰性删除)</li>
<li>启动时注册定期函数, 每周期循环 随机获取一批 key 检查是否过期 (定期删除 默认每 100 ms 扫描一次, 每次随机扫描 20 个 key, 默认优先 key 是否过期,  如果默认超过 25% 的 key 都是过期 key , 则直接再重复一次定期删除, 直到最后小于 25%)<br>
从库</li>
<li>从库如果可写, 从库自己维护自己的过期 key, 也没惰性删除策略, 但是不会从从库获取到已经过期的 key, 从库得知 key 过期了不会删除, 之后等待主库 key 下发 del 删除过期 key</li>
</ol>
<blockquote>
<p>过期 key 的自动删除机制。它是 Redis 用来回收内存空间的常用机制，应用广泛，本身就会引起 Redis 操作阻塞，导致性能变慢，所以，你必须要知道该机制对性能的影响。<br>
<a href="https://mp.weixin.qq.com/s/v_Wnrvf25UyKzRgvDziMeA" target="_blank" rel="noopener nofollow noreferrer" >https://mp.weixin.qq.com/s/v_Wnrvf25UyKzRgvDziMeA</a></p>
</blockquote>
<p>对上述描述的说明: Redis 4.0 之前是阻塞的, 4.0 后 <strong>过期 key 的自动删除机制是后台进程, 不会直接造成 redis 阻塞, 但是极端情况下会占用 cpu 资源, 导致 redis 的响应变慢, 上述是不准确的</strong></p>
<h3 id="超过设置最大内存">超过设置最大内存</h3>
<p>超过了 maxmemory 配置的值，就会触发新的内存淘汰了</p>
<p>数据删除后, 内存可能还是会很高, 不会主动归还给操作系统 因为存在内存碎片, 可以手动也会自动进行碎片整理</p>
<h4 id="内存淘汰策略">内存淘汰策略</h4>
<table>
<thead>
<tr>
<th>策略类型</th>
<th>策略名称</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>内存淘汰策略</strong></td>
<td><strong>noeviction</strong></td>
<td>当内存不足时，Redis 不会淘汰任何键，<strong>而是返回错误, 不在进行数据的写入</strong>（<code>OOM command not allowed when used memory &gt; 'maxmemory'</code>）。适用于不希望丢失任何数据的场景。</td>
</tr>
<tr>
<td></td>
<td><strong>allkeys-lru</strong></td>
<td>使用 <strong>LRU</strong>（最近最少使用）策略来淘汰任意键，优先淘汰最近最少使用的键。如果所有键都有过期时间，则会按过期时间来删除；否则按 LRU 进行淘汰。</td>
</tr>
<tr>
<td></td>
<td><strong>volatile-lru</strong></td>
<td>只淘汰设置了过期时间的键，并使用 LRU 策略删除最少使用的键。该策略只作用于设置了过期时间的键，对于没有过期时间的键不会进行淘汰。</td>
</tr>
<tr>
<td></td>
<td><strong>allkeys-random</strong></td>
<td>使用随机策略淘汰任意键，选择随机的键进行删除，适用于需要保证内存空间的场景，但不关心哪个键被删除。</td>
</tr>
<tr>
<td></td>
<td><strong>volatile-random</strong></td>
<td>只淘汰设置了过期时间的键，使用随机策略随机删除其中的一些键。</td>
</tr>
<tr>
<td></td>
<td><strong>volatile-ttl</strong></td>
<td>只淘汰设置了过期时间的键，并根据剩余的过期时间（TTL）选择即将过期的键进行淘汰，优先删除那些 TTL 较短的键。</td>
</tr>
</tbody>
</table>
<p>说明 如果 <strong>缓冲区</strong> 占用了大量内存，淘汰策略就会失效，无法自动清理缓冲区的 内存,导致 Redis 无法继续工作。<br>
Redis 中每个客户端都有输入缓冲区和输出缓冲区。若出现大量请求，缓冲区可能被填满，进而引发内存问题。<strong>特别是大 Key 操作（如大对象的写入和读取），可能导致输出缓冲区迅速膨胀，占用过多内存</strong>。<br>
可以通过设置 client-output-buffer-limit 默认值, 强制断开 buffer 过大的 client , 关闭该客户端连接，从而防止内存被过度占用</p>
<h2 id="redis-的-rehash-机制">redis 的 rehash 机制</h2>
<p>Redis 的 rehash 过程是为了扩展或收缩哈希表，以保持高效的查找、插入和删除操作。为了避免一次性 rehash 带来的性能开销，Redis 采用渐进式 rehash<br>
redis 还在处理请求 每处理一次请求 将该 entry 拷贝到 b hash ， 为了避免一直没有请求 周期性的搬移一些数据到 hash b</p>
<h3 id="1-触发-rehash">1. 触发 rehash</h3>
<p>rehash 通常在以下两种情况下触发：</p>
<ol>
<li><strong>哈希表负载因子过高</strong>：哈希表中的键值对数量接近或超过哈希表容量。</li>
<li><strong>哈希表负载因子过低</strong>：在大量键值对被删除后，哈希表的利用率下降到一定程度。</li>
</ol>
<h3 id="2-哈希表结构">2. 哈希表结构</h3>
<p>在 Redis 中，每个 <code>dict</code>（字典）包含两个哈希表实例 <code>ht[0]</code> 和 <code>ht[1]</code>，rehash 过程中会使用这两个哈希表：</p>
<ul>
<li><code>ht[0]</code>：现有的哈希表。</li>
<li><code>ht[1]</code>：rehash 过程中使用的新哈希表。</li>
</ul>
<h3 id="3-渐进式-rehash-过程">3. 渐进式 rehash 过程</h3>
<p>渐进式 rehash 通过在常规操作（如插入、删除、查找）中逐步迁移键值对，以避免一次性 rehash 导致的性能抖动。</p>
<h2 id="redigo--并发使用">redigo  并发使用</h2>
<p>不使用连接池的话 redigo 并发（redigo 执行 Do Send 命令的时候不能并发）下会 redis conn 出错断开。</p>
<h2 id="redis-问题排查">redis 问题排查</h2>
<p><strong>info Clients</strong></p>
<ul>
<li>connected_clients  当前正在连接数量<br>
<strong>redis 抓包</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span>sudo tcpdump -i any tcp and port <span style="color:#ae81ff">6379</span> -n -nn -s0 -tttt -w redis.cap
</span></span></code></pre></div><p><strong>查看当前连接</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span>redis-cli  -a <span style="color:#e6db74">&#34;password&#34;</span>  CLIENT LIST|grep -v watch | sort -t <span style="color:#e6db74">&#34; &#34;</span> -k <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-2">2</a></span><span>netstat -na | grep <span style="color:#ae81ff">6379</span> | grep TIME_WAIT | wc -l
</span></span></code></pre></div><p><strong>空闲连接 自动断开时间</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1">1</a></span><span>config get timeout
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-2">2</a></span><span>config set timeout <span style="color:#ae81ff">600</span>
</span></span></code></pre></div>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/backend" term="backend" label="Backend" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%90%8E%E7%AB%AF" term="%E5%90%8E%E7%AB%AF" label="后端" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/redis" term="redis" label="redis" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ELK 架构中 ES 性能优化]]></title>
            <link href="https://blog.yunpiao.site/post/20240828133844/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240828133844/</id>
            
            
            <published>2024-08-28T13:38:44+08:00</published>
            <updated>2024-08-28T13:38:44+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-背景">1. 背景</h2>
<p>由于目前日志采集流程中, 经常遇到用户磁盘 IO 占用超过 90% 以上的场景, 但是观察其日志量大约在 2k~5k 之间, 整体数据量不大, 所以针对该问题进行了一系列的压测和实验验证,最后得出这篇优化建议文档</p>
<h2 id="2-压测前期准备">2. 压测前期准备</h2>
<h3 id="21-制造大量日志">2.1 制造大量日志</h3>
<p>该阶段为数据源输入阶段, 为了避免瓶颈在数据制造侧, 所以需要保证 filebeat 具有足够的日志制造能力<br>
最后效果, filebeat 可以达到 70k QPS 的数据发往logstash . (真实数据可以更高, 70k qps 是因为目前单实例 logstash 的 CPU 计算瓶颈, logstash 配置的 output 为空的情况下)</p>
<h3 id="22-logstash-侧降低副本-调整资源限制">2.2 logstash 侧降低副本, 调整资源限制</h3>
<p>Logstash 主要是 cpu 密集型服务, 数据传输到 es 的时候为了观察方便, 设置 logstash 副本数为 1, 并将 cpu 的 limit 开放到 12 core. 同时为了避免影响到 es, 将 该单实例 logstash 部署到 master 节点<br>
<strong>验证性能方案</strong><br>
不断调节 logstash 配置, 找出性能消耗较大的配置项</p>
<h3 id="23-es-侧配置等保持不变">2.3 es 侧配置等保持不变</h3>
<p>根据之前的推断, 磁盘 io 主要由于 es index 写入进行的消耗, 所以 es 是本次调节的重点, 暂时保持配置不变, 在验证过程中不断调节 es 配置</p>
<h3 id="24-其他调整">2.4 其他调整</h3>
<ol>
<li>其他无关服务关闭, 卸载 mongo, Redis .  等无关服务</li>
</ol>
<h2 id="3-压测步骤">3. 压测步骤</h2>
<ol>
<li>关闭 logstash 到 es 的输出, 通过 logstash 的 metrics 观察不同配置对 logstash 处理能力的影响</li>
<li>Logstash 配置不变情况下, 调节 es index 生命周期策略等配置, 通过 es 的 metrics 观察不同配置对 doc 数量和 磁盘 io 的影响</li>
</ol>
<h2 id="4-优化建议">4. 优化建议</h2>
<h3 id="41-logstash-优化">4.1 logstash 优化</h3>
<h4 id="411-删除-logstsh-大量字段删除操作-移动到-filebeat-侧进行">4.1.1 删除 logstsh 大量字段删除操作, 移动到 filebeat 侧进行</h4>
<p>mutate remove_field 处理消耗大量 cpu, 可以将部分操作移动到 filebeat 中</p>
<h4 id="412-优化-logstash-配置参数-提升每批次处理量-优先">4.1.2 优化 logstash 配置参数, 提升每批次处理量 (优先)</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>pipeline.batch.size: <span style="color:#ae81ff">500</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>pipeline.batch.delay: <span style="color:#ae81ff">200</span>
</span></span></code></pre></div><p>空间换时间, 内存会有小幅上升, 但是会提升 cpu 处理效率</p>
<h4 id="413-优化-if-else-处理逻辑暂缓">4.1.3 优化 if else 处理逻辑(暂缓)</h4>
<p>由于 logstash 没有 switch 等语句, 只能嵌套多个 if else. 所以可以将中日志频率高的 event code 放在前面</p>
<h4 id="414-使用-logstash-pipeline-避免多个数据源的相互影响暂缓">4.1.4 使用 logstash pipeline, 避免多个数据源的相互影响(暂缓)</h4>
<p>Logstash 提供了 logstash pipeline 机制, 避免 filter 之间的相互影响<br>
参考: <a href="https://www.elastic.co/guide/en/logstash/current/configuration.html" target="_blank" rel="noopener nofollow noreferrer" >https://www.elastic.co/guide/en/logstash/current/configuration.html</a></p>
<h3 id="42-es-优化">4.2 es 优化</h3>
<h4 id="421-index-segment分段设置-等参数优化-优先">4.2.1 index segment分段设置 等参数优化 (优先)</h4>
<p>索引模版增加 合并相关参数</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span>  <span style="color:#f92672">&#34;index&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span>    <span style="color:#f92672">&#34;codec&#34;</span>: <span style="color:#e6db74">&#34;best_compression&#34;</span>, 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span>    <span style="color:#f92672">&#34;mapping&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span>      <span style="color:#f92672">&#34;total_fields&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span>        <span style="color:#f92672">&#34;limit&#34;</span>: <span style="color:#e6db74">&#34;10000&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span>      }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span>    },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span>    <span style="color:#f92672">&#34;refresh_interval&#34;</span>: <span style="color:#e6db74">&#34;30s&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span>    <span style="color:#f92672">&#34;translog&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-11">11</a></span><span>      <span style="color:#f92672">&#34;flush_threshold_size&#34;</span>: <span style="color:#e6db74">&#34;512mb&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-12">12</a></span><span>      <span style="color:#f92672">&#34;sync_interval&#34;</span>: <span style="color:#e6db74">&#34;10s&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-13">13</a></span><span>      <span style="color:#f92672">&#34;durability&#34;</span>: <span style="color:#e6db74">&#34;async&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-14">14</a></span><span>    },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-15">15</a></span><span>    <span style="color:#f92672">&#34;merge&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-16">16</a></span><span>      <span style="color:#f92672">&#34;scheduler&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-17">17</a></span><span>        <span style="color:#f92672">&#34;max_thread_count&#34;</span>: <span style="color:#e6db74">&#34;1&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-18">18</a></span><span>      },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-19">19</a></span><span>      <span style="color:#f92672">&#34;policy&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-20">20</a></span><span>        <span style="color:#f92672">&#34;floor_segment&#34;</span>: <span style="color:#e6db74">&#34;2mb&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-21">21</a></span><span>        <span style="color:#f92672">&#34;max_merge_at_once&#34;</span>: <span style="color:#e6db74">&#34;5&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-22">22</a></span><span>        <span style="color:#f92672">&#34;max_merged_segment&#34;</span>: <span style="color:#e6db74">&#34;5gb&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-23">23</a></span><span>      }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-24">24</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-25">25</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-26">26</a></span><span>}
</span></span></code></pre></div><p>将各个索引模版增加以上参数, 各参数解释</p>
<ul>
<li>&ldquo;codec&rdquo;: &ldquo;best_compression&rdquo;,  // 使用最佳压缩方式, 节省磁盘和 io 性能, 会稍微影响读取性能, 但是目前场景为写多读少</li>
<li>&ldquo;refresh_interval&rdquo;: &ldquo;30s&rdquo;,  // 增加刷新间隔, 避免频繁刷新, 占用磁盘 io, 数据最大 30s 后可被检索</li>
<li>&ldquo;translog.flush_threshold_size&rdquo;: &ldquo;128mb&rdquo;, // 设置事务日志（translog）达到多大时进行刷新（flush）</li>
<li>&ldquo;translog.sync_interval&rdquo;: &ldquo;10s&rdquo;,// 定事务日志同步到磁盘的频率, 10s 内的数据可能在宕机时候丢失</li>
<li>&ldquo;translog.durability&rdquo;: &ldquo;async&rdquo;, // async 表示异步持久化，事务日志不会立即同步到磁盘，而是根据 translog.sync_interval 的设置进行同步</li>
<li>&ldquo;merge.scheduler.max_thread_count&rdquo;: 1, // 较高的线程数可以加快合并速度，但也会增加磁盘 IO。设置为 1 表示只使用一个线程进行合并</li>
<li>&ldquo;merge.policy.floor_segment&rdquo;: &ldquo;2mb&rdquo;, // 小于此大小的段将被优先合并，以减少段的数量并提高查询性能。</li>
<li>&ldquo;merge.policy.max_merge_at_once&rdquo;: &ldquo;5&rdquo;,  // 较高的值可以减少段的数量，但也会增加合并操作的 I/O 负载</li>
<li>&ldquo;merge.policy.max_merged_segment&rdquo;: &ldquo;5gb&rdquo;  // 较大的段可以减少段的数量，但也会增加单个段的大小，可能影响查询性能。</li>
</ul>
<h4 id="422-生命周期策略关闭强制合并-优先">4.2.2 生命周期策略关闭强制合并 (优先)</h4>
<p>关闭强制合并, 避免在 index 读写较高时, 进行合并操作, 可以在空闲时段离线进行合并操作</p>
<h4 id="423-增加温阶段-在温阶段中-合并-segement-和降低副本数暂缓">4.2.3 增加温阶段, 在温阶段中, 合并 segement 和降低副本数(暂缓)</h4>
<p>将临近删除的数据, 增加温阶段, 合并 segement 和降低副本数, 以此达到降低磁盘使用量的目的</p>
<h2 id="5-最终效果">5. 最终效果</h2>
<p>硬盘: 机械硬盘, vmware 虚拟盘<br>
Logstash 配置: 单实例 12 核 8g<br>
ES data 配置: 双实例 12 核 8g<br>
极限情况下(磁盘 io 成为瓶颈的情况下)<br>
峰值: 12k qps<br>
平均值: 9k qps</p>
<p><strong>收益</strong><br>
优化前 峰值 5k qps<br>
优化后 峰值 12k qps</p>
<h2 id="6--关于这次优化的问题解答">6.  关于这次优化的问题解答</h2>
<ol>
<li>为什么之前 io 负载高
<ol>
<li>频繁的 merge 造成, io 进程多, 同时还有强制合并</li>
</ol>
</li>
<li>为什么 logstash 内存高
<ol>
<li>jvm 直接占用内存, 不是实际使用的内存</li>
<li>实际要看 jvm 内的指标量</li>
</ol>
</li>
<li>什么是 segement, 为什么要合并, 什么时候合并
<ol>
<li> Elasticsearch 中，段是一个倒排索引的基本单元</li>
<li>es 会自动和手动合并 segement</li>
<li>segement 不变, 只能被合并, 合并可以通过参数调节</li>
<li>数据首先写入内存缓冲区，然后定期刷新到新的段中</li>
<li>分配和段的管理是由底层的 Lucene 库自动处理的</li>
</ol>
</li>
<li>ingest 是什么, pipeline 是做什么的
<ol>
<li>用于在数据被索引之前对其进行预处理, 与处理数据用的</li>
<li>pipeline 是一系列处理器（processors）的集合</li>
</ol>
</li>
</ol>
<h2 id="7-最佳实践">7. 最佳实践</h2>
<ul>
<li>尽量批量发送数据到 Elasticsearch。</li>
<li>数据预处理尽可能在数据产生侧完成。</li>
<li>每个 Elasticsearch shard 的建议大小为 50GB。</li>
<li>不重要的数据可不保留副本。</li>
<li>Elasticsearch 的 segment 合并会严重占用磁盘 IO，对于不需要实时处理的数据，可以减缓合并频率。</li>
<li>Logstash 是 CPU 密集型应用，一般不需要大量内存。</li>
<li>Filebeat 功能强大，可以处理一些数据缓存等任务。</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/infra/ops" term="infra/ops" label="Infra/Ops" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/elastic" term="elastic" label="elastic" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96" term="%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96" label="性能优化" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[实战-mongo 从单个副本修复整个副本集]]></title>
            <link href="https://blog.yunpiao.site/post/20240710170729/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240710170729/</id>
            
            
            <published>2024-07-10T17:07:29+08:00</published>
            <updated>2024-07-10T17:07:29+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="背景">背景</h2>
<p>Mongo 目前部署方式为三个 mongo data 节点, 一个选举节点, 目前可以容忍四个 pod 中一个 pod 异常后, 服务仍然可以正常运行(原因: 至少三个节点投票才能选举主节点), 但是如果出现有两台 pod 异常后, 整个服务将不可用, 本文档用于记录如何从剩余的一或者两个可用的mongo 节点恢复整个 mongo 集群</p>
<h2 id="情境说明-以-mongodb-0-mongodb-2-异常为例">情境说明, 以 mongodb-0 mongodb-2 异常为例</h2>
<p><strong>情境描述</strong></p>
<p>mongodb-0 mongodb-2 由于各种原因, 已经不可正常服务</p>
<p>原因包括</p>
<ul>
<li>
<p>Oplog 相差太大, 需要数天时间才能追上现有数据</p>
</li>
<li>
<p>节点 pvc 异常</p>
</li>
<li>
<p>Mongo 卸载后, 重新安装时候先启动 mongo 0, 但是 mongo 0 一直在 oplog 修复数据</p>
</li>
<li>
<p>Mongo 重新安装, mongo 0 的 pvc 删除后, mongo 0 优先启动, mongo 0 自己选举自己为了 master 节点, 后续启动节点与 mongo 0 分裂为了两个集群</p>
</li>
</ul>
<h2 id="修复步骤-1-将异常节点-pod-删除-暂时不启动该节点">修复步骤 1. 将异常节点 pod 删除, 暂时不启动该节点</h2>
<p>因为 pod 不支持临时停止, 临时设置定时脚本强制删除 pod</p>
<p>可以使用 tmux 启动两个 shell ,执行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bash" data-lang="Bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span><span style="color:#75715e"># 每隔0.1s 执行一次删除命令</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>watch -n 0.1 kubectl delete pods mongodb-0 --force
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span>watch -n 0.1 kubectl delete pods mongodb-2 --force 
</span></span></code></pre></div><p>新启动一个 shell , 删除 pvc, 可以避免启动时候读取 pvc</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bash" data-lang="Bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>kubectl delete pvc datadir-mongodb-0
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>kubectl delete pvc datadir-mongodb-2
</span></span></code></pre></div><h2 id="修复步骤-2-登录可以正常启动节点-pod-更新-mongo-副本集配置">修复步骤 2. 登录可以正常启动节点 pod, 更新 mongo 副本集配置</h2>
<p>登录 mongo 正常节点</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bash" data-lang="Bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#75715e"># 登录正常 pod, mongodb-1 为例, 仅存的正常节点</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>kubectl exec -it mongodb-1 bash
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span><span style="color:#75715e"># 登录 mongo root 账户</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5">5</a></span><span>mongo -u root -p 4NXcr1rv3zwCsxLugLDw60i8R
</span></span></code></pre></div><p>登录后, 大概率会看到当前节点不是主节点, 是从节点</p>
<p>rs0:SECONDARY&gt;</p>
<p>执行下面指令, 修复 mongo</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bash" data-lang="Bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1"> 1</a></span><span><span style="color:#75715e"># 获取配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2"> 2</a></span><span>var config <span style="color:#f92672">=</span> rs.conf<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3"> 3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-4"> 4</a></span><span><span style="color:#75715e">## 删除不可用节点,删除的原因,  默认情况下需要三个节点投票才能成为主节点, 由于目前健康节点最多是 2 个, 所需需要更新选举为主节点的投票阈值</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-5"> 5</a></span><span><span style="color:#75715e">## 删除后, 投票阈值会变成 2, 会选举出该正常节点为主节点, 后续新加节点, 将从这个节点进行同步数据</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-6"> 6</a></span><span>config.members <span style="color:#f92672">=</span> config.members.filter<span style="color:#f92672">(</span>member <span style="color:#f92672">=</span>&gt; member.host !<span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;mongodb-0.mongodb-headless.itdr.svc.cluster.local:27017&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-7"> 7</a></span><span>config.members <span style="color:#f92672">=</span> config.members.filter<span style="color:#f92672">(</span>member <span style="color:#f92672">=</span>&gt; member.host !<span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;mongodb-2.mongodb-headless.itdr.svc.cluster.local:27017&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-8"> 8</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-9"> 9</a></span><span><span style="color:#75715e">## 强制更新配置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-10">10</a></span><span>rs.reconfig<span style="color:#f92672">(</span>config, <span style="color:#f92672">{</span>force: true<span style="color:#f92672">})</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-11">11</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-12">12</a></span><span><span style="color:#75715e"># 查看是否重新选举当前的节点为主节点</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-13">13</a></span><span>rs.status<span style="color:#f92672">()</span>
</span></span></code></pre></div><h2 id="修复步骤-3-重建异常节点">修复步骤 3. 重建异常节点</h2>
<p>将修复步骤 1 , 一直循环删除 pod 的命令停止, ctrl + c 停止</p>
<p>等改 mongodb-0 mongodb-2 pod 新建成功, pod 新建后, 由于没有 pvc, 会新建 pvc. Pvc 为空之后, mongo pod 会按照新加副本节的流程加入 mongo 集群, 查看是否重建完成</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/mongo" term="mongo" label="mongo" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[后端-golang max proc 默认设置在 k8s 中的坑]]></title>
            <link href="https://blog.yunpiao.site/post/20240605152941/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240605152941/</id>
            
            
            <published>2024-06-05T15:29:41+08:00</published>
            <updated>2024-06-05T15:29:41+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="问题">问题</h2>
<p>对于在物理机上, golang 程序对于 GMP 中 P 的数量是由 cpu 的核心数来确定的, 但是这种行为在 k8s pod 中会出现问题,  pod 中一般会设置 pod 的 limit 信息, 距离 一个 pods 的最大 cpu 使用量为 4 个核心, 但是由于这台物理机是 16 核的, 所以在 GMP 初始化的时候, P 的数量会变成 16.</p>
<p>这种异常会对 cpu 密集的应用程序造成大量的 cpu 切换成本.</p>
<p>pod 资源配置</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>    <span style="color:#f92672">Limits</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>      <span style="color:#f92672">cpu</span>:     <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>      <span style="color:#f92672">memory</span>:  <span style="color:#ae81ff">8Gi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span>    <span style="color:#f92672">Requests</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">5</a></span><span>      <span style="color:#f92672">cpu</span>:        <span style="color:#ae81ff">500m</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6">6</a></span><span>      <span style="color:#f92672">memory</span>:     <span style="color:#ae81ff">500Mi</span>
</span></span></code></pre></div><p>测试使用 golang 运行得到的 GOMAXPROCS 参数, 这里发现是 16, 而不是 pod 上的 4</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>/tmp <span style="color:#75715e"># ./t1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>GOMAXPROCS: <span style="color:#ae81ff">16</span>
</span></span></code></pre></div><h2 id="解决">解决</h2>
<p>这种常见问题, 一般都有现成的方案</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>	<span style="color:#a6e22e">_</span> <span style="color:#e6db74">&#34;go.uber.org/automaxprocs&#34;</span> <span style="color:#75715e">// 根据容器配额设置 maxprocs
</span></span></span></code></pre></div><p>在 main 函数中引入该库就可以解决该问题,</p>
<h2 id="分析">分析</h2>
<p>该库使用 <code>/proc/$PID/cgroup</code> 这个文件解析当前的 cpu 信息, 再通过该信息设置最大 P 数量.</p>
<p>研发同学在进行云原生或者微服务改造的时候, 如果对于 k8s 机制不了解, 是不会进行以上修改的, 甚至如果没有分析过性能, 问题都发现不了. 所以我这边始终认为研发和运维不应该太割裂, 研发应该具备基本的运维能力, 且对于自身服务部署的环境相当熟悉.</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/go" term="go" label="go" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/k8s" term="k8s" label="k8s" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[杂-epub书籍导入微信读书无法显示图片]]></title>
            <link href="https://blog.yunpiao.site/post/20240605112841/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240605112841/</id>
            
            
            <published>2024-06-05T11:28:41+08:00</published>
            <updated>2024-06-05T11:28:41+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="太长不看">太长不看</h2>
<p>mac  可以执行下列脚本</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span><span style="color:#75715e"># 指定文件夹路径</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>folder_path<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;./epub 书籍&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span><span style="color:#75715e"># 初始化 file 变量</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span><span style="color:#75715e"># 遍历文件夹下的所有文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span><span style="color:#66d9ef">for</span> f in <span style="color:#e6db74">&#34;</span>$folder_path<span style="color:#e6db74">&#34;</span>/*; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>  <span style="color:#75715e"># 检查是否是文件</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>  <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -f <span style="color:#e6db74">&#34;</span>$f<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>    <span style="color:#75715e"># 获取文件名（不带路径）</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>    filename<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>basename <span style="color:#e6db74">&#34;</span>$f<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>    <span style="color:#75715e"># 去掉文件后缀</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>    file<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>filename%.*<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>    <span style="color:#75715e"># 将文件名添加到 file 变量中</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>    unzip -d ./$file ./$file.epub
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17">17</a></span><span>    grep <span style="color:#e6db74">&#39;data-savepage-src=&#39;</span> -rl ./$file/* --include <span style="color:#e6db74">&#34;*.html&#34;</span> | xargs sed -i <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#e6db74">&#39;s|data-savepage-src=&#34;[^&#34;]*&#34;||g&#39;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18">18</a></span><span>    cd ./$file
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19">19</a></span><span>    zip -r ../format/$file.epub *
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20">20</a></span><span>    cd ../
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21">21</a></span><span>    rm -fr ./$file
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22">22</a></span><span>  <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23">23</a></span><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><h2 id="删除-html-文件中的-data-savepage-src-属性">删除 HTML 文件中的 &lsquo;data-savepage-src&rsquo; 属性</h2>
<p>检查发现是因为图片具有 data-savepage-src 和 src 两个属性, 对于这两个属性, 只要删除 data-savepage-src 属性, 就可以导入微信读书后正常显示图片.<br>
<code>grep 'data-savepage-src=' -rl &quot;./$file&quot; --include &quot;*.html&quot; | xargs sed -i '' 's|data-savepage-src=&quot;[^&quot;]*&quot;||g'</code></p>
<h2 id="总结">总结</h2>
<p>究其原因还是 html 是由 chrome 或者 其他插件导出的, epub 导入到微信读书后, 微信读书没有正确解析, 或者是优先解析 data-savepage-src. 导致失败. 属于是 微信读书的 bug</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9D%82" term="%E6%9D%82" label="杂" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%B0%8F%E8%AF%B4" term="%E5%B0%8F%E8%AF%B4" label="小说" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/blog" term="blog" label="blog" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[实战-线上 golang windows 下的内存泄露]]></title>
            <link href="https://blog.yunpiao.site/post/20240425152836/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240425152836/</id>
            
            
            <published>2024-04-25T00:00:00+08:00</published>
            <updated>2024-04-25T00:00:00+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-背景">1. 背景</h2>
<p>今天遇到了一个客户问题, 用户说我们这边的程序占用的内存越来越多, 我们的程序是通过 windows server 托管的, 使用 golang 编译成 exe 后, 使用安装器安装到用户 windows 服务器上的, 理论上只是一个心跳 + 信息收集用的, 不会占用太多内存.</p>
<p>不过当看到用户 64G 内存马上满了之后, 意识到肯定是哪里出了问题 😱</p>
<h2 id="2-复现步骤">2. 复现步骤</h2>
<p>找了公司里面几台安装的比较久的 exe 看了下, 运行一两个月后, 内存确实到了 1 G 左右. 😂 这个时候只能一步一步排查哪里出问题了</p>
<h3 id="排查第一步">排查第一步</h3>
<p>首先想到的是 Pprof 神器, <code>fmt.Println(http.ListenAndServe(&quot;0.0.0.0:6063&quot;, nil))</code> 一把梭之后, dump 查看内存占用<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/8a504070cec1b40230b963234e228b9c.png" alt="image.png"  /><br>
一顿操作猛如虎, 一看内存几百K, 这什么都没有</p>
<p>想到可能是使用了 CGO, 这部分内存是没办法在 pprof 中观察到的, 于是开启了第二步</p>
<h3 id="排查第二步">排查第二步</h3>
<p>神器 windbg, 想到可以转储内存, 之后通过分析内存中的内容, 就可以知道都是那些内容占据的内存了<br>
首先转储内存, 直接在任务管理器中操作转储<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/2d0917cc85ea0215e3df0fbcdc9682ab.png" alt="image.png"  /></p>
<p>下载 windbg, 并打开转储文件<br>
<a href="https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/" target="_blank" rel="noopener nofollow noreferrer" >https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/</a> 可以从微软这里下载, 不过这个版本比较新了, 一些命令可能与旧版本不兼容, 我是 google 下载的之前版本</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/8804be6b3c0fae9403f3cc50a8bf0c4b.png" alt="image.png"  /></p>
<h4 id="分析前的-windbg-配置工作">分析前的 windbg 配置工作</h4>
<p>分析前, 需要将 window 的符号配置路径, 可以在 windbg 中输入下面的命令进行加载</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>.sympath SRV*c:\mySymbols*http<span style="color:#960050;background-color:#1e0010">:</span>//msdl.microsoft.com/download/symbols
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>!sym noisy
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>.reload
</span></span></code></pre></div><p>然后加载转储文件使用如下的命令进行分析</p>
<p>常用的 windbg 命令</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span>!analyse -v 先大致分析下
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span>!heap -s 查看内存分布
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span>0:000&gt; !heap -s
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span>LFH Key                   : 0x311c1eaa657dc8ce
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span>Termination on corruption : ENABLED
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span>          Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span>                            <span style="color:#f92672">(</span>k<span style="color:#f92672">)</span>     <span style="color:#f92672">(</span>k<span style="color:#f92672">)</span>    <span style="color:#f92672">(</span>k<span style="color:#f92672">)</span>     <span style="color:#f92672">(</span>k<span style="color:#f92672">)</span> length      blocks cont. heap 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span>-------------------------------------------------------------------------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-11">11</a></span><span><span style="color:#ae81ff">0000000001980000</span> <span style="color:#ae81ff">00000002</span>   <span style="color:#ae81ff">32552</span>  <span style="color:#ae81ff">20420</span>  <span style="color:#ae81ff">32552</span>    <span style="color:#ae81ff">479</span>   <span style="color:#ae81ff">896</span>     <span style="color:#ae81ff">6</span>    <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">2</span>   LFH
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-12">12</a></span><span><span style="color:#ae81ff">0000000000010000</span> <span style="color:#ae81ff">00008000</span>      <span style="color:#ae81ff">64</span>      <span style="color:#ae81ff">4</span>     <span style="color:#ae81ff">64</span>      <span style="color:#ae81ff">2</span>     <span style="color:#ae81ff">1</span>     <span style="color:#ae81ff">1</span>    <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-13">13</a></span><span><span style="color:#ae81ff">0000000000320000</span> <span style="color:#ae81ff">00001002</span>    <span style="color:#ae81ff">7216</span>   <span style="color:#ae81ff">3236</span>   <span style="color:#ae81ff">7216</span>     <span style="color:#ae81ff">37</span>     <span style="color:#ae81ff">6</span>     <span style="color:#ae81ff">4</span>    <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">6</span>   LFH
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-14">14</a></span><span><span style="color:#ae81ff">0000000028670000</span> <span style="color:#ae81ff">00001002</span>      <span style="color:#ae81ff">60</span>     <span style="color:#ae81ff">16</span>     <span style="color:#ae81ff">60</span>      <span style="color:#ae81ff">5</span>     <span style="color:#ae81ff">2</span>     <span style="color:#ae81ff">1</span>    <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-15">15</a></span><span>00000000287f0000 <span style="color:#ae81ff">00001002</span>      <span style="color:#ae81ff">60</span>      <span style="color:#ae81ff">8</span>     <span style="color:#ae81ff">60</span>      <span style="color:#ae81ff">5</span>     <span style="color:#ae81ff">1</span>     <span style="color:#ae81ff">1</span>    <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">0</span>      
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-16">16</a></span><span>-------------------------------------------------------------------------------------
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-17">17</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-18">18</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-19">19</a></span><span>!heap -stat -h <span style="color:#ae81ff">0000000001980000</span> 参看Heap
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-20">20</a></span><span>    size     <span style="color:#75715e">#blocks     total     ( %) (percent of total busy bytes)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-21">21</a></span><span>    <span style="color:#ae81ff">118</span> d - e38  <span style="color:#f92672">(</span>20.12<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-22">22</a></span><span>    8e4 <span style="color:#ae81ff">1</span> - 8e4  <span style="color:#f92672">(</span>12.58<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-23">23</a></span><span>    <span style="color:#ae81ff">800</span> <span style="color:#ae81ff">1</span> - <span style="color:#ae81ff">800</span>  <span style="color:#f92672">(</span>11.32<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-24">24</a></span><span>    <span style="color:#ae81ff">400</span> <span style="color:#ae81ff">2</span> - <span style="color:#ae81ff">800</span>  <span style="color:#f92672">(</span>11.32<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-25">25</a></span><span>    <span style="color:#ae81ff">782</span> <span style="color:#ae81ff">1</span> - <span style="color:#ae81ff">782</span>  <span style="color:#f92672">(</span>10.62<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-26">26</a></span><span>    <span style="color:#ae81ff">50</span> f - 4b0  <span style="color:#f92672">(</span>6.63<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-27">27</a></span><span>    <span style="color:#ae81ff">410</span> <span style="color:#ae81ff">1</span> - <span style="color:#ae81ff">410</span>  <span style="color:#f92672">(</span>5.75<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-28">28</a></span><span>    <span style="color:#ae81ff">238</span> <span style="color:#ae81ff">1</span> - <span style="color:#ae81ff">238</span>  <span style="color:#f92672">(</span>3.14<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-29">29</a></span><span>    1e0 <span style="color:#ae81ff">1</span> - 1e0  <span style="color:#f92672">(</span>2.65<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-30">30</a></span><span>    1b0 <span style="color:#ae81ff">1</span> - 1b0  <span style="color:#f92672">(</span>2.39<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-31">31</a></span><span>    <span style="color:#ae81ff">20</span> a - <span style="color:#ae81ff">140</span>  <span style="color:#f92672">(</span>1.77<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-32">32</a></span><span>    <span style="color:#ae81ff">100</span> <span style="color:#ae81ff">1</span> - <span style="color:#ae81ff">100</span>  <span style="color:#f92672">(</span>1.41<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-33">33</a></span><span>    <span style="color:#ae81ff">68</span> <span style="color:#ae81ff">2</span> - d0  <span style="color:#f92672">(</span>1.15<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-34">34</a></span><span>    3e <span style="color:#ae81ff">3</span> - ba  <span style="color:#f92672">(</span>1.03<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-35">35</a></span><span>    <span style="color:#ae81ff">98</span> <span style="color:#ae81ff">1</span> - <span style="color:#ae81ff">98</span>  <span style="color:#f92672">(</span>0.84<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-36">36</a></span><span>    <span style="color:#ae81ff">30</span> <span style="color:#ae81ff">3</span> - <span style="color:#ae81ff">90</span>  <span style="color:#f92672">(</span>0.80<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-37">37</a></span><span>    <span style="color:#ae81ff">44</span> <span style="color:#ae81ff">2</span> - <span style="color:#ae81ff">88</span>  <span style="color:#f92672">(</span>0.75<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-38"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-38">38</a></span><span>    <span style="color:#ae81ff">42</span> <span style="color:#ae81ff">2</span> - <span style="color:#ae81ff">84</span>  <span style="color:#f92672">(</span>0.73<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-39"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-39">39</a></span><span>    <span style="color:#ae81ff">40</span> <span style="color:#ae81ff">2</span> - <span style="color:#ae81ff">80</span>  <span style="color:#f92672">(</span>0.71<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-40"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-40">40</a></span><span>    3c <span style="color:#ae81ff">2</span> - <span style="color:#ae81ff">78</span>  <span style="color:#f92672">(</span>0.66<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-41"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-41">41</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-42"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-42">42</a></span><span>!heap -flt s <span style="color:#ae81ff">118</span> 查看 <span style="color:#ae81ff">118</span> 大小的数据里面有什么, 什么都没看出来
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-43"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-43">43</a></span><span>    _HEAP @ <span style="color:#ae81ff">1980000</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-44"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-44">44</a></span><span>              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-45"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-45">45</a></span><span>        00000000019819d0 <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0000</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   00000000019819e0    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-46"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-46">46</a></span><span>        0000000001981b50 <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   0000000001981b60    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-47"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-47">47</a></span><span>        0000000001981fc0 <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   0000000001981fd0    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-48"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-48">48</a></span><span>        <span style="color:#ae81ff">0000000001982140</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   <span style="color:#ae81ff">0000000001982150</span>    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-49"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-49">49</a></span><span>        <span style="color:#ae81ff">0000000001982380</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   <span style="color:#ae81ff">0000000001982390</span>    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-50"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-50">50</a></span><span>        <span style="color:#ae81ff">0000000001983090</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   00000000019830a0    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-51"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-51">51</a></span><span>        00000000019832c0 <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   00000000019832d0    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-52"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-52">52</a></span><span>        <span style="color:#ae81ff">0000000001983520</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   <span style="color:#ae81ff">0000000001983530</span>    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-53"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-53">53</a></span><span>        <span style="color:#ae81ff">0000000001983730</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   <span style="color:#ae81ff">0000000001983740</span>    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-54"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-54">54</a></span><span>        <span style="color:#ae81ff">0000000001984650</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   <span style="color:#ae81ff">0000000001984660</span>    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-55"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-55">55</a></span><span>        0000000001984cf0 <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   0000000001984d00    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-56"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-56">56</a></span><span>        0000000001984fa0 <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   0000000001984fb0    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-57"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-57">57</a></span><span>        <span style="color:#ae81ff">0000000001987640</span> <span style="color:#ae81ff">0012</span> <span style="color:#ae81ff">0012</span>  <span style="color:#f92672">[</span>00<span style="color:#f92672">]</span>   <span style="color:#ae81ff">0000000001987650</span>    <span style="color:#ae81ff">00118</span> - <span style="color:#f92672">(</span>busy<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-58"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-58">58</a></span><span>    _HEAP @ <span style="color:#ae81ff">10000</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-59"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-59">59</a></span><span>    _HEAP @ <span style="color:#ae81ff">320000</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-60"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-60">60</a></span><span>    _HEAP @ <span style="color:#ae81ff">28670000</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-61"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-61">61</a></span><span>    _HEAP @ 287f0000
</span></span></code></pre></div><p>🥲 看了下 分布很均匀, 看不出来到底是那种类型数据, 似乎没有有用的特征信息, 推断可能是内核态的进程了, 太费事了, 于是想到了万能的笨方法</p>
<h3 id="排查第三步-逐步尝试--查看哪个函数消耗的内存">排查第三步 逐步尝试,  查看哪个函数消耗的内存</h3>
<p>由于代码量不大, 相关的 cgo 函数就那么几个. 所以可以直接遍历, 定位内存泄露函数</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">10000000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;TestXXX: %#v\n&#34;</span>, <span style="color:#a6e22e">getXXX</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;TestXXX: %#v\n&#34;</span>, <span style="color:#a6e22e">getXXX1</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;TestXXX: %#v\n&#34;</span>, <span style="color:#a6e22e">getXXX2</span>())
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5">5</a></span><span>}
</span></span></code></pre></div><p>写一个单测, 直接运行, 查看内存变化<br>
最后确实定位到了问题点,  程序中使用了 <code># DsGetDomainControllerInfoW</code> 函数, 使用了系统调用去获取了 windows server 的信息, 但是信息是 dll 中申请的, golang 这边管理不了, 后续要手动使用 <code># DsFreeDomainControllerInfoW</code> 系统调用手动释放, 😒 真是 fuck, 不过好在这部分代码不是我写的, 可以甩锅 😁</p>
<p>具体的函数使用说明 <a href="https://learn.microsoft.com/zh-cn/windows/win32/api/ntdsapi/nf-ntdsapi-dsgetdomaincontrollerinfoa" target="_blank" rel="noopener nofollow noreferrer" >https://learn.microsoft.com/zh-cn/windows/win32/api/ntdsapi/nf-ntdsapi-dsgetdomaincontrollerinfoa</a></p>
<h2 id="3-windows-下进程内存的观测方法">3. windows 下进程内存的观测方法</h2>
<h3 id="31-windows-自带性能监视器">3.1. windows 自带性能监视器</h3>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/c92d19e2f758f5279d15cc45dacbd45c.png" alt="image.png"  /><br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/d5122413822424eb971b279002d3fecd.png" alt="image.png"  /><br>
一般来说, 自带的已经可以了, 但是不足之处是, 无法查看具体的数值, 只有统计值</p>
<h3 id="32-使用-windows-exporter-查看进程状态">3.2. 使用 windows exporter 查看进程状态</h3>
<p><a href="https://github.com/prometheus-community/windows_exporter" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/prometheus-community/windows_exporter</a><br>
直接从 release 下载最新版本, 在 windows 上运行, 注意默认情况下, 是不会监控进程相关的指标的, 需要在启动命令中增加相关参数</p>
<p>启动命令</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>.<span style="color:#ae81ff">\w</span>indows_exporter.exe --collectors.enabled <span style="color:#e6db74">&#34;process&#34;</span> --collector.process.include<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;firefox.+&#34;</span>
</span></span></code></pre></div><p>默认的采集端是 9182, 在 prometheus 中配置采集对象</p>
<p>prometheus.yml</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1"> 1</a></span><span><span style="color:#f92672">global</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2"> 2</a></span><span>  <span style="color:#f92672">scrape_interval</span>:     <span style="color:#ae81ff">60s</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3"> 3</a></span><span>  <span style="color:#f92672">evaluation_interval</span>: <span style="color:#ae81ff">60s</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4"> 4</a></span><span> 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5"> 5</a></span><span><span style="color:#f92672">scrape_configs</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6"> 6</a></span><span>  - <span style="color:#f92672">job_name</span>: <span style="color:#ae81ff">prometheus</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7"> 7</a></span><span>    <span style="color:#f92672">static_configs</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-8"> 8</a></span><span>      - <span style="color:#f92672">targets</span>: [<span style="color:#e6db74">&#39;localhost:9182&#39;</span>]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-9"> 9</a></span><span>        <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-10">10</a></span><span>          <span style="color:#f92672">instance</span>: <span style="color:#ae81ff">prometheus</span>
</span></span></code></pre></div><p>最后在 grafana 中观察就可以看到指定进程的内存占用情况<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/beedd3c83ac4b8311a922d34038c1f00.png" alt="image.png"  /></p>
<p>具体的指标项为 <code>windows_process_working_set_private_bytes</code>, 通过 grafana 可以只管查看到具体的内存占用数据</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BA%BF%E4%B8%8A%E5%AE%9E%E6%88%98" term="%E7%BA%BF%E4%B8%8A%E5%AE%9E%E6%88%98" label="线上实战" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/go" term="go" label="go" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/cgo" term="cgo" label="cgo" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Obsidian 使用 obsidian-copy-url-in-preview 插件时, CF R2 提示 CORS 错误]]></title>
            <link href="https://blog.yunpiao.site/post/20240429143707/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240429143707/</id>
            
            
            <published>2024-04-01T14:37:07+08:00</published>
            <updated>2024-04-01T14:37:07+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-起因">1. 起因</h2>
<p>我用的 cloudFlare  r2 做图床, 但是发现 Obsidian 复制 oss 图片比较麻烦, 每次复制都需要打开网址, 然后复制, 直接复制的话, 只是复制了 url, 没有办法粘贴到聊天记录中.<br>
所以开始了折腾之旅</p>
<h2 id="2-开始找插件">2. 开始找插件</h2>
<p>使用了 images-tools-kit, 功能挺多,但是发现复制图片提示错误, 弹出不能 copy 图片到剪切板, 直接 打开控制台, 一查看发现<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/5ad0b1c603b8335d9e1d175d2282a8d0.png" alt="网上找的图"  /><br>
==cors 跨域==问题, 想着不应该啊, 这么多人用的东西, 有问题应该早改了, 所以试了下其他的网上图片, 发现是可以的 😂</p>
<p>于是看了 issue, 发现 <a href="https://github.com/NomarCub/obsidian-copy-url-in-preview" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/NomarCub/obsidian-copy-url-in-preview</a> 这个插件, 更加简单, 直接右键可以复制, 比 kit 还简单些, 又安装这个 插件试用, 发现还是同样的错误, 于是开始了上班的摸鱼之旅.</p>
<h2 id="3-开始排查">3. 开始排查</h2>
<h3 id="31-查看-cf-的-r2-中的跨域配置">3.1 查看 cf 的 r2 中的跨域配置</h3>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/9df0b0b58b834e213daee6055bac28ee.png" alt="image.png"  /><br>
默认是空的, 我在里面添加了如上的策略配置, curl 了下, 发现返回的 header 中已经有了</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/607e982348c1fe9442dcdfe7d0c208cc.png" alt="image.png"  /></p>
<h3 id="32-接下来再复制的时候-发现问题还是存在">3.2 接下来再复制的时候, 发现问题还是存在</h3>
<p>于是开始查看了下请求, 发现没有红框中的这一条, 于是猜想, 可能是之前的请求已经缓存到磁盘了, 这些请求是没有 Access 的请求字段的<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/1eb84c60af397aeca64fe33691e9e654.png" alt="image.png"  /></p>
<p><strong>解决办法也是很简单的, 勾选上停用缓存, 之后直接复制是可以的</strong></p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2024/04/a6645c8b320ad4c1332d01a5b239a624.png" alt="image.png"  /></p>
<p>网上也有一些骚操作, 在 url 下加 get 的, 不过不太体面, 我 blog 中太多 url, 还得一个一个改</p>
<h2 id="4-结语">4. 结语</h2>
<p>总的来说是个小问题, 问题的原因还是 cf 的 r2, 以为 r2 默认有 header 头接受跨域请求的, 没想到还需要自己配</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%BC%80%E6%BA%90%E5%B7%A5%E5%85%B7" term="%E5%BC%80%E6%BA%90%E5%B7%A5%E5%85%B7" label="开源工具" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/blog" term="blog" label="blog" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[hugo blog 经常出现的诡异的字符空格新增问题]]></title>
            <link href="https://blog.yunpiao.site/post/20240416151029/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240416151029/</id>
            
            
            <published>2024-03-15T15:10:29+08:00</published>
            <updated>2024-03-15T15:10:29+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>hugo 主题相关问题</p>
<h1 id="1-现象">1. 现象</h1>
<p>网页刚打开时候的现象<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/202404161513483.png" alt="image.png"  /><br>
等待几秒后, 突然变化了, 自动加了空格, 这种变化很是恼人</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/202404161512326.png" alt="image.png"  /></p>
<h1 id="2-分析">2. 分析</h1>
<p>看了下 blog 的源文档, 中英文之间是没有空格的, 所以这里肯定是什么工具自动加的 空格, 虽然是好事, 但是这种体验, 真的是垃圾</p>
<h1 id="3-解决">3. 解决</h1>
<p>google 后看到, 大部分前端是引入的 <code>pangu.min.js</code> 进行的自动化, 所以解决办法也很简单</p>
<ol>
<li>ctrl + f  pangu.min.js</li>
<li>找到主题中的 <!-- raw HTML omitted --><!-- raw HTML omitted -->, 直接删除</li>
<li>世界清净</li>
</ol>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/hugo" term="hugo" label="hugo" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/blog" term="blog" label="blog" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9D%82" term="%E6%9D%82" label="杂" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[实战-mongo 在线调整缓存大小]]></title>
            <link href="https://blog.yunpiao.site/post/20240418181803/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240418181803/</id>
            
            
            <published>2024-02-18T18:18:03+08:00</published>
            <updated>2024-02-18T18:18:03+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>记录线上解决 oom 问题</p>
<p>由于之前的版本没有增加内存限制, mongo 会使用(节点内存-1)/2 内存用 cache, 于是造成了线上环境的 mongo, 内存一直在增加, 隔几天 oom 一次, 由于是副本集的模式, 所以暂时没有什么影响, 这里记录下当时的操作过程</p>
<p>admin 登录 mongo 查看当前 cache 大小</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>db.serverStatus<span style="color:#f92672">()</span>.wiredTiger.cache<span style="color:#f92672">[</span><span style="color:#e6db74">&#39;maximum bytes configured&#39;</span><span style="color:#f92672">]</span>/1024/1024/1024
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span><span style="color:#75715e"># 13</span>
</span></span></code></pre></div><p>设置 wiredTiger cache 大小</p>
<p>db.adminCommand( { &ldquo;setParameter&rdquo;: 1, &ldquo;wiredTigerEngineRuntimeConfig&rdquo;: &ldquo;cache_size=8G&rdquo;})</p>
<p>由于直接更改这个参数,并不能减少当前节点的内存占用, 这里还是需要重启节点, 好在这一节点是副本节点, 所以使用了最简单粗暴的方法</p>
<p><code>kubectl delete pods mongodb-2</code></p>
<p>重启后内存降低, 再看内存, 已经不会再涨了</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BA%BF%E4%B8%8A%E5%AE%9E%E6%88%98" term="%E7%BA%BF%E4%B8%8A%E5%AE%9E%E6%88%98" label="线上实战" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[经验-如何使用 golang 写单测]]></title>
            <link href="https://blog.yunpiao.site/post/20240418170927/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240418170927/</id>
            
            
            <published>2023-08-07T17:09:27+08:00</published>
            <updated>2023-08-07T17:09:27+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>很多程序员写了几年的代码, 连最基本的单测都不会写, 属实有点说不过去</p>
<p>生成二进制文件<br>
<code>go test -c</code></p>
<p>指定二进制名称<br>
<code>go test -c -o </code></p>
<p>执行某一个函数测试<br>
<code>./xx.test -test.run &quot;TestUnitReport$&quot; -test.v</code></p>
<h2 id="意义">意义</h2>
<p>提前发现问题<br>
边界问题<br>
方便在代码出现 bug 时及时调试<br>
为了写单测，会督促大家减少复杂代码的耦合性（代码可单测）</p>
<p>副作用<br>
需要投入时间和精力<br>
测试数据需要频繁维护<br>
一般研发人员不喜欢单测</p>
<h2 id="单测在-golang-中的地位">单测在 golang 中的地位</h2>
<p>Go语言在工具链和标准库中提供对测试的原生支持，<br>
TestMain函数包含了测试用例的初始化和主函数的测试逻辑。</p>
<h2 id="最简单示例-如何写单测">最简单示例-如何写单测</h2>
<p>文件目录结构</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>➜  util git:<span style="color:#f92672">(</span>feature/1.3.0<span style="color:#f92672">)</span> ✗ tree
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>.
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>├── utils.go
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span>└── utils_test.go
</span></span></code></pre></div><p>utils.go 的被测函数</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#75715e">// In returns whether item is in array, like keyword `in` in python.
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">In</span>(<span style="color:#a6e22e">item</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">array</span> []<span style="color:#66d9ef">string</span>) <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">array</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">item</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">t</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5">5</a></span><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6">6</a></span><span>		}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7">7</a></span><span>	}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8">8</a></span><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9">9</a></span><span>}
</span></span></code></pre></div><p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/202404181708718.png" alt="image.png"  /></p>
<blockquote>
<p>手动执行命令为 go test -v -run ^TestIn$</p>
</blockquote>
<h3 id="自动生成方法---goland">自动生成方法 - goland</h3>
<p>使用 goland 的生成功能， 生成单测函数<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230706114853.png" alt="image.png"  /><br>
模式模板为表驱动测试方式<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230706114933.png" alt="image.png"  /></p>
<blockquote>
<p>表驱动测试<br>
表驱动测试本身是编程语言无关的。Go核心团队和Go早期开发者在实践过程中发现表驱动测试十分适合Go代码测试并在标准库和第三方项目中大量使用此种测试设计，这样表驱动测试也就逐渐成为Go的一个惯用法。就像我们从上面的示例中看到的那样，表驱动测试有着诸多优点<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230705181049.png" alt=""  /></p>
</blockquote>
<h3 id="常用的-testing-函数">常用的 testing 函数</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1"> 1</a></span><span>Fail : 测试失败<span style="color:#960050;background-color:#1e0010">，</span>测试继续<span style="color:#960050;background-color:#1e0010">，</span>也就是之后的代码依然会执行
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2"> 2</a></span><span>FailNow : 测试失败<span style="color:#960050;background-color:#1e0010">，</span>测试中断
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3"> 3</a></span><span>Log : 输出信息
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4"> 4</a></span><span>Logf : 输出格式化的信息
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5"> 5</a></span><span>Skip : 相当于 Log <span style="color:#f92672">+</span> SkipNow
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6"> 6</a></span><span>Skipf : 相当于 Logf <span style="color:#f92672">+</span> SkipNow
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-7"> 7</a></span><span>SkipNow : 跳过测试<span style="color:#960050;background-color:#1e0010">，</span>测试中断
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-8"> 8</a></span><span>Error : 相当于 Log <span style="color:#f92672">+</span> Fail
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-9"> 9</a></span><span>Errorf : 相当于 Logf <span style="color:#f92672">+</span> Fail
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-10">10</a></span><span>Fatal : 相当于 Log <span style="color:#f92672">+</span> FailNow
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-11">11</a></span><span>Fatalf : 相当于 Logf <span style="color:#f92672">+</span> FailNow
</span></span></code></pre></div><h3 id="单测形式">单测形式</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span><span style="color:#a6e22e">TestXxxx</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>)    <span style="color:#75715e">// 基本测试用例 以验证某个功能是否正常工作。该函数通常包含一个或多个测试用例，用于测试代码的不同方面。测试用例可以使用断言检查程序的预期输出是否与实际输出一致。
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span><span style="color:#75715e"></span><span style="color:#a6e22e">BenchmarkXxxx</span>(<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) <span style="color:#75715e">// 压力测试的测试用例,验证代码在高负载下的性能表现。压力测试可以使用不同的测试负载来测试代码在不同负载下的响应时间、吞吐量和资源使用情况等
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3">3</a></span><span><span style="color:#75715e"></span><span style="color:#a6e22e">Example_Xxx</span>()  <span style="color:#75715e">// 测试控制台输出的例子,编写测试示例，以输出预期的控制台输出。这些输出通常用于验证程序是否按照预期的行为执行
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-4">4</a></span><span><span style="color:#75715e"></span><span style="color:#a6e22e">TestMain</span>(<span style="color:#a6e22e">m</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">M</span>) <span style="color:#75715e">// 测试 Main 函数 TestMain函数包含了测试用例的初始化和主函数的测试逻辑。
</span></span></span></code></pre></div><h3 id="其他测试形式">其他测试形式</h3>
<ul>
<li>包外测试<br>
与包内测试本质是面向实现的白盒测试不同，包外测试的本质是一种面向接口的黑盒测试<br>
Go标准库中包内测试和包外测试的使用情况</li>
</ul>
<p>包内测试<br>
优点，<br>
内部函数可以直接使用<br>
不足，<br>
测试代码自身需要经常性的维护<br>
包的循环依赖 使用包外测试 或者调整结构</p>
<p>外部测试<br>
优点<br>
没有循环依赖<br>
不需要经常维护<br>
缺点<br>
访问有限  解决方法， 通过外部暴露的方式 使用内部包的 _test 文件给外部使用</p>
<h3 id="单测组织结构">单测组织结构</h3>
<ol>
<li>
<p>平铺<br>
简单<br>
独立</p>
</li>
<li>
<p>基于测试套件和测试用例的xUnit实践模式进行组织<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230705155338.png" alt="image.png"  /></p>
</li>
</ol>
<h4 id="对比">对比</h4>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230705155359.png" alt="image.png"  /></p>
<h3 id="测试数据准备">测试数据准备</h3>
<p>测试固件是指一个人造的、确定性的环境，一个测试用例或一个测试套件（下的一组测试用例）在这个环境中进行测试，其测试结果是可重复的（多次测试运行的结果是相同的）。我们一般使用setUp和tearDown来代表测试固件的创建/设置与拆除/销毁的动作</p>
<p>下面是一些使用测试固件的常见场景：<br>
◦  将一组已知的特定数据加载到数据库中，测试结束后清除这些数据；<br>
◦  复制一组特定的已知文件，测试结束后清除这些文件；<br>
◦  创建伪对象（fake object）或模拟对象（mock object），并为这些对象设定测试时所需的特定数据和期望结果。<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230705160127.png" alt="image.png"  /></p>
<p>testdata 文件夹用于存放测试需要的数据</p>
<blockquote>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230707095813.png" alt="image.png"  /></p>
</blockquote>
<p>测试代码对外部文件数据的依赖之外，还会经常面对被测代码对外部业务组件或服务的依赖。此外，越是接近业务层，被测代码对外部组件或服务依赖的可能性越大。</p>
<p>使用辅助函数</p>
<p>你不需要一个真实的数据库来满足运行单元测试的需求。<br>
测试代码对外部文件数据的依赖之外，还会经常面对被测代码对外部业务组件或服务的依赖。此外，越是接近业务层，被测代码对外部组件或服务依赖的可能性越大。比如：<br>
◦  被测代码需要连接外部Redis服务；<br>
◦  被测代码依赖一个外部邮件服务器来发送电子邮件；<br>
◦  被测代码需与外部数据库建立连接并进行数据操作；<br>
◦  被测代码使用了某个外部RESTful服务。</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230705181409.png" alt="image.png"  /><br>
替身概念<br>
在GitHub上有一个名为gostub（https://github.com/prashantv/gostub）的第三方包可以用于简化stub替身的管理和编写。以上面的例子为例，如果改写为使用gostub的测试，代码如下</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/ob/20230705182354.png" alt="image.png"  /></p>
<p>模糊测试 go-fuzz</p>
<p>单测要做的事就是针对各个函数，构造好运行环境、输入数据、依赖的api、断言好结果</p>
<h4 id="构造运行环境">构造运行环境</h4>
<p>依赖的redis、api 这些，可以直接通过 测试配置文件 的形式 启动起来，在开发机上搭建好redis、api服务，从真实服务上获取数据，也可以mock掉返回结果</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/backend" term="backend" label="Backend" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" term="%E7%BC%96%E7%A8%8B%E7%BB%8F%E9%AA%8C" label="编程经验" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[实战-Windows 下 golang 进程无法被 kill 排查]]></title>
            <link href="https://blog.yunpiao.site/post/20230707211004/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20230707211004/</id>
            
            
            <published>2023-07-07T21:10:04+08:00</published>
            <updated>2023-07-07T21:10:04+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-背景">1. 背景</h2>
<p>这次遇到的是一个很恶心的 Windows 现场问题：</p>
<ul>
<li>业务进程长时间不再上报心跳</li>
<li>监控 goroutine 也不再推进</li>
<li>任务管理器里进程无法结束，提示“拒绝访问”</li>
<li>服务重启后，新的 updater 进程还能继续拉起来，但旧进程并没有真正退出</li>
<li>卸载流程也会被卡住</li>
</ul>
<p>第一眼看上去像是用户态程序写炸了：死循环、锁没释放、goroutine 卡死、或者 panic 被吞了。</p>
<p>但这条路越查越不对。最后定位到的根因并不在普通 Go 逻辑里，而是在 <code>WinPcap</code> 的 <code>NPF</code> 驱动路径上。</p>
<p>更准确地说：<strong>结合复现实验、用户态/内核态堆栈、以及公开讨论，可以把问题收敛到旧版 <code>NPF</code> 驱动在并发枚举网卡时卡死，进而把调用线程拖进内核态等待。</strong></p>
<p>这篇文章把完整排查路径整理一下，顺便把当时用到的一套 Windows 调试工具链也记下来。</p>
<h2 id="2-现象">2. 现象</h2>
<p>现场现象非常稳定：</p>
<ul>
<li>心跳日志停止</li>
<li>monitor 日志停止</li>
<li>相关采集组件因为配置错误无法正常启动</li>
<li>在任务管理器中手动结束进程失败</li>
<li>服务重启后出现多个 updater 实例</li>
<li>卸载失败</li>
</ul>
<p>这里最容易误判的一点是：<strong>“无法结束进程”不一定是用户态没处理退出信号，也可能是线程已经陷进内核态等待，导致用户态 kill 不了。</strong></p>
<h2 id="3-复现条件">3. 复现条件</h2>
<p>复现方式并不复杂，核心是把问题逼到“并发枚举网卡 + 异常重试”这个路径：</p>
<ul>
<li>同时打开多个依赖抓包/网卡枚举的模块</li>
<li>人为制造配置错误，让相关组件持续启动失败</li>
<li>让 supervisor/updater 不断重试</li>
</ul>
<p>复现后可以看到一个明显的时间线：</p>
<ol>
<li>初期心跳、监控都正常</li>
<li>一段时间后心跳先停</li>
<li>再过一段时间 monitor 也停</li>
<li>最后任务管理器已经无法结束进程</li>
</ol>
<p>这个顺序很关键：它说明<strong>不是程序瞬间整体崩掉</strong>，而是某条关键执行链路先卡死，之后连带把其他逻辑慢慢拖死。</p>
<h2 id="4-第一轮误判以为是-goroutine-自己死了">4. 第一轮误判：以为是 goroutine 自己死了</h2>
<p>一开始很自然会怀疑：</p>
<ul>
<li>心跳 goroutine panic 了</li>
<li>某个死循环把 CPU 吃满了</li>
<li>某个锁没放，导致心跳和 monitor 都在等</li>
</ul>
<p>但实际现象和这种判断对不上：</p>
<ul>
<li>没看到 panic 日志</li>
<li>不是 CPU 飙升型问题</li>
<li>不是所有 goroutine 同时消失，而是“有的停了，有的还活着”</li>
</ul>
<p>这时候要把问题拆开看：</p>
<ol>
<li><strong>业务逻辑有没有退出？</strong></li>
<li><strong>线程有没有卡在 syscall/cgo？</strong></li>
<li><strong>调用栈有没有进入内核驱动？</strong></li>
</ol>
<p>如果第三个成立，那就不是普通 Go 代码层面能解释干净的事了。</p>
<h2 id="5-先用-pprof-看用户态再决定要不要下内核态">5. 先用 pprof 看用户态，再决定要不要下内核态</h2>
<p>按照 Go 官方文档，挂 <code>pprof</code> 最快的方式就是直接引入 <code>net/http/pprof</code>，然后起一个 HTTP 服务：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span>	<span style="color:#a6e22e">_</span> <span style="color:#e6db74">&#34;net/http/pprof&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>	<span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewProfileHTTPServer</span>(<span style="color:#a6e22e">addr</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>	<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#a6e22e">addr</span>, <span style="color:#66d9ef">nil</span>))
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>	}()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>}
</span></span></code></pre></div><p>我现场一般会直接挂一个只在内网开放的端口，然后重点看：</p>
<ul>
<li><code>/debug/pprof/goroutine</code></li>
<li><code>/debug/pprof/heap</code></li>
<li><code>/debug/pprof/profile</code></li>
</ul>
<p>这一步的作用不是“直接证明驱动死锁”，而是先回答两个问题：</p>
<ol>
<li>Go 进程是不是还活着</li>
<li>哪些 goroutine 停在 syscall/IO/等待链路上</li>
</ol>
<p>如果 <code>pprof</code> 还能打开，但业务 goroutine 不再推进，这通常意味着：<strong>进程还没完全死，只是关键执行链路已经卡住。</strong></p>
<h2 id="6-为什么进入死锁后有的协程会卡死有的不会">6. 为什么进入死锁后，有的协程会卡死，有的不会</h2>
<p>这是这类问题里最容易让人困惑的点。</p>
<p>先说结论：<strong>进入内核态卡死的，不是整个 Go runtime 瞬间“全停”，而是某些 goroutine 所在的执行链路先停。其他 goroutine 是否还能继续推进，取决于它们是不是依赖这条链路、有没有可用的 <code>P/M</code>、以及是否被共享锁/共享状态串住。</strong></p>
<p>可以这样理解：</p>
<ul>
<li>调用 <code>pcapFindAllDevs</code> 这类路径时，Go goroutine 最终会落到 syscall/cgo 边界</li>
<li>如果对应线程长期阻塞在内核态，运行时不一定会把所有 goroutine 一起拖死</li>
<li>但如果这个 goroutine 持有关键锁、占着 supervisor 流程、或者后续很多逻辑都依赖它返回，那业务表象就会变成“越来越多的协程停摆”</li>
</ul>
<p>所以你会看到一种典型现象：</p>
<ul>
<li><code>pprof</code> 还能通</li>
<li>某些 HTTP/日志协程还活着</li>
<li>心跳/monitor/拉起流程已经不动了</li>
</ul>
<p>这不是矛盾，而是<strong>阻塞发生在关键链路，而不是所有 goroutine 同时被 runtime 停掉</strong>。</p>
<h2 id="7-把可疑点收敛到-pcapfindalldevs">7. 把可疑点收敛到 <code>pcapFindAllDevs</code></h2>
<p>当我把视角从“业务 goroutine 为什么停了”切到“哪些系统调用可能把线程拖进内核态”以后，嫌疑点很快收敛到了网卡枚举逻辑。</p>
<p>因为现场里有一段路径会反复获取本机网卡列表，而这条路径最终会走到 WinPcap/Npcap 的设备枚举接口。</p>
<p>下面这张图就是当时直接定位到的关键调用点：<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2026/03/ba05b4ea6e9827d5a5318986752014ca.png" alt="image.png"  /></p>
<p>重点不是这段 Go 封装本身，而是它后面调用的 <code>pcapFindAllDevs</code> 最终会进入 <code>NPF</code> 驱动路径。</p>
<p>为了把猜测变成证据，我又单独写了最小复现程序，只保留：</p>
<ul>
<li>启动 <code>pprof</code></li>
<li>调一次网卡枚举</li>
<li>输出设备信息</li>
</ul>
<p>如果把业务逻辑剥干净后仍然能复现“进程无法结束”，那基本可以排除“上层业务代码”的锅。</p>
<h2 id="8-用户态证据不够就下到内核态">8. 用户态证据不够，就下到内核态</h2>
<p>这类问题只看普通日志已经不够了，后面我主要用了这几个工具：</p>
<ul>
<li><code>Process Explorer</code>：快速看线程、句柄、基本堆栈</li>
<li><code>LiveKD</code>：不重启机器，直接对在线系统做内核态查看</li>
<li><code>WinDbg/CDB</code>：看线程栈、IRP、符号、反汇编</li>
</ul>
<p>其中 <code>LiveKD</code> 很适合这种“现场还活着，但不好重启”的问题。它可以把当前系统状态直接切进去看。</p>
<h2 id="9-livekd--windbg-看到的关键信号">9. LiveKD / WinDbg 看到的关键信号</h2>
<p>一旦把线程栈拉出来，问题的味道就完全变了。</p>
<p>下面这张图里，关键调用链已经非常清楚：<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2026/03/4f03d1f998ef3be6897af8d5ed20a698.png" alt="image.png"  /></p>
<p>核心信号有三个：</p>
<ol>
<li>线程处于 <code>KernelMode</code> 等待态</li>
<li>调用链里出现了 <code>ndis!ndisOpenAdapterLegacyProtocol</code></li>
<li>调用栈继续往下能看到 <code>npf</code> 路径</li>
</ol>
<p>如果再看 <code>IRP</code>，可以进一步看到请求挂在 <code>\Driver\npf</code> 上。</p>
<p>这时候问题已经不太像“用户态进程结束不掉”，而更像：</p>
<p><strong>线程在尝试打开/绑定网卡相关对象时，卡在了 <code>NDIS + NPF</code> 这条驱动链路里。</strong></p>
<h2 id="10-一个很容易踩的坑符号没配好windbg-会直接误导你">10. 一个很容易踩的坑：符号没配好，WinDbg 会直接误导你</h2>
<p>如果 <code>WinDbg</code> 符号不对，很多分析命令会直接给你一堆假信息，典型报错就是：</p>
<ul>
<li><code>OS symbols are WRONG</code></li>
<li><code>Either you specified an unqualified symbol...</code></li>
</ul>
<p>我现场是这么配的：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>.sympath SRV*c:\mySymbols*http<span style="color:#960050;background-color:#1e0010">:</span>//msdl.microsoft.com/download/symbols
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>!sym noisy
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span>.reload
</span></span></code></pre></div><p>如果你用 <code>cdb</code> 或 <code>windbg</code>，也可以先把公共符号服务器环境变量配上：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>set _NT_SYMBOL_PATH=srv*DownstreamStore*https<span style="color:#960050;background-color:#1e0010">:</span>//msdl.microsoft.com/download/symbols
</span></span></code></pre></div><p>这一步看起来很基础，但在实际排障里非常关键。<strong>符号不对，后面所有 <code>!analyze -v</code>、线程栈、模块分析都会跑偏。</strong></p>
<h2 id="11-为什么怀疑是旧版-npf-驱动而不是普通业务-bug">11. 为什么怀疑是旧版 <code>NPF</code> 驱动，而不是普通业务 bug</h2>
<p>这里我没有把话说成“官方已经 100% 盖棺定论”，因为能拿到的最硬证据来自三部分：</p>
<ol>
<li><strong>复现实验</strong>：最小化程序也能复现</li>
<li><strong>内核态调用栈</strong>：线程稳定挂在 <code>ndisOpenAdapterLegacyProtocol</code> 和 <code>npf</code> 路径</li>
<li><strong>公开讨论</strong>：旧 WinPcap/legacy NDIS 路径确实长期有兼容性和维护问题，Npcap 官方也明确说明它是 WinPcap 的更新/替代实现，且基于更新的 NDIS 6 LWF 架构</li>
</ol>
<p>Npcap 官方站点给出的口径很明确：</p>
<ul>
<li><code>WinPcap</code> 已长期不再维护</li>
<li><code>Npcap</code> 是 Nmap Project 维护的替代实现</li>
<li><code>Npcap</code> 在 Windows 7 及以后使用的是 <code>NDIS 6</code> LWF，而不是 WinPcap 的旧 <code>NDIS 5</code> 路径</li>
</ul>
<p>另外，公开社区里也能找到针对旧 NDIS/legacy bind 路径的讨论。下面这张图就是当时排查时对上的一条公开信息：<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/2026/03/314550165bdd9fd0cabf7ea31a19d73e.png" alt="网卡讨论"  /><br>
所以更稳妥的表述是：</p>
<blockquote>
<p>结合复现、线程栈和公开资料，问题高度怀疑与旧版 WinPcap <code>NPF</code> 驱动在并发枚举网卡时卡在 legacy bind/open adapter 路径有关；迁移到 <code>Npcap</code> 或避免并发枚举网卡，是合理且可验证的修复方向。</p>
</blockquote>
<h2 id="12-最终结论">12. 最终结论</h2>
<p>这次问题的根因可以收敛成一句话：</p>
<p><strong>多个执行路径并发调用网卡枚举接口，最终进入旧版 WinPcap <code>NPF</code> 驱动路径；线程卡在内核态等待后，相关进程表现为无法结束，依赖这条链路的心跳、monitor 和拉起逻辑也随之停摆。</strong></p>
<p>这里最坑的点在于：</p>
<ul>
<li>现象看起来像“Go 协程死了”</li>
<li>实际上根因落在驱动路径</li>
<li>用户态日志只能看到结果，看不到真正的阻塞点</li>
</ul>
<h2 id="13-修复方向">13. 修复方向</h2>
<p>我认为有两条修复路线，优先级很明确。</p>
<h3 id="131-优先方案迁移到-npcap">13.1 优先方案：迁移到 <code>Npcap</code></h3>
<p>这是我更推荐的方案，原因很直接：</p>
<ul>
<li><code>WinPcap</code> 已经是停止维护状态</li>
<li><code>Npcap</code> 仍在持续更新</li>
<li><code>Npcap</code> 官方明确提供 <code>WinPcap API-compatible mode</code></li>
<li>从架构上看，Npcap 也已经不是老 WinPcap 那条陈旧驱动路径</li>
</ul>
<p>如果你的程序当前只是用 <code>wpcap.dll</code>/<code>Packet.dll</code> 兼容接口，迁移成本通常比重写业务逻辑低。</p>
<h3 id="132-保守方案避免并发枚举网卡">13.2 保守方案：避免并发枚举网卡</h3>
<p>如果短期内没法替换驱动，那至少要先把触发条件拆掉：</p>
<ul>
<li>不要让多个模块并发调用网卡枚举</li>
<li>把枚举网卡做成单飞或全局串行化</li>
<li>updater 不要重复去探测同一类设备状态</li>
<li>在 supervisor 层面对异常重试做节流，避免错误配置把驱动路径持续打爆</li>
</ul>
<p>说白了，这类问题在旧驱动路径上<strong>不是“多试几次总会成功”</strong>，而是越重试越容易把系统带进更糟糕的状态。</p>
<h2 id="14-我会保留的一套现场排障顺序">14. 我会保留的一套现场排障顺序</h2>
<p>下次再遇到“Windows 进程无法结束 + Go 程序部分逻辑停摆 + 怀疑抓包驱动”这类问题，我会直接按这套顺序走：</p>
<ol>
<li><strong>先挂 <code>pprof</code></strong>：确认用户态还有哪些 goroutine 在推进</li>
<li><strong>再看 Process Explorer</strong>：确认线程、句柄、基础堆栈</li>
<li><strong>必要时上 LiveKD</strong>：把线程状态、IRP、内核栈拉出来</li>
<li><strong>最后用 WinDbg/CDB + 公共符号</strong>：确认模块、符号、调用路径</li>
<li><strong>把业务代码最小化复现</strong>：隔离出是不是单纯的驱动/系统调用问题</li>
</ol>
<p>这条链路的价值在于：<strong>先用低成本工具快速排除业务 bug，再用内核态证据确认是否已经掉进驱动层。</strong></p>
<h2 id="15-参考链接">15. 参考链接</h2>
<h3 id="官方文档">官方文档</h3>
<ul>
<li><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer" target="_blank" rel="noopener nofollow noreferrer" >Process Explorer</a></li>
<li><a href="https://docs.microsoft.com/en-us/sysinternals/downloads/livekd" target="_blank" rel="noopener nofollow noreferrer" >LiveKD</a></li>
<li><a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/" target="_blank" rel="noopener nofollow noreferrer" >Windows Debugger</a></li>
<li><a href="https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/microsoft-public-symbols" target="_blank" rel="noopener nofollow noreferrer" >Microsoft public symbols</a></li>
<li><a href="https://npcap.com/" target="_blank" rel="noopener nofollow noreferrer" >Npcap 官网</a></li>
<li><a href="https://npcap.com/guide/npcap-users-guide.html" target="_blank" rel="noopener nofollow noreferrer" >Npcap User&rsquo;s Guide</a></li>
<li><a href="https://npcap.com/vs-winpcap.html" target="_blank" rel="noopener nofollow noreferrer" >Npcap vs WinPcap</a></li>
<li><a href="https://www.winpcap.org/misc/faq.htm" target="_blank" rel="noopener nofollow noreferrer" >WinPcap FAQ</a></li>
<li><a href="https://pkg.go.dev/net/http/pprof" target="_blank" rel="noopener nofollow noreferrer" >Go net/http/pprof</a></li>
</ul>
<h3 id="辅助资料">辅助资料</h3>
<ul>
<li><a href="https://blog.csdn.net/raiven2008/article/details/82142186" target="_blank" rel="noopener nofollow noreferrer" >Process Explorer 使用示例</a></li>
<li><a href="https://techcommunity.microsoft.com/t5/windows-blog-archive/unkillable-processes/ba-p/723389" target="_blank" rel="noopener nofollow noreferrer" >不可杀进程的经典文章</a></li>
<li><a href="https://darjun.github.io/2021/06/09/youdontknowgo/pprof/" target="_blank" rel="noopener nofollow noreferrer" >pprof 调试入门</a></li>
<li><a href="https://blog.csdn.net/flyingleo1981/article/details/17349393" target="_blank" rel="noopener nofollow noreferrer" >windump 文件分析</a></li>
<li><a href="https://www.cnblogs.com/fwycmengsoft/p/4214580.html" target="_blank" rel="noopener nofollow noreferrer" >Windows 驱动/进程分析案例</a></li>
<li><a href="https://www.cnblogs.com/ziwuge/archive/2011/07/28/2120446.html" target="_blank" rel="noopener nofollow noreferrer" >NPF 驱动开发笔记</a></li>
<li><a href="https://blog.51cto.com/shayi1983/1710861" target="_blank" rel="noopener nofollow noreferrer" >内核态切换说明</a></li>
<li><a href="https://www.cnblogs.com/wt645631686/p/13915625.html" target="_blank" rel="noopener nofollow noreferrer" >Go GMP 调度补充 1</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1442315?from=article.detail.1848155" target="_blank" rel="noopener nofollow noreferrer" >Go GMP 调度补充 2</a></li>
</ul>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/windows" term="windows" label="windows" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/go" term="go" label="go" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5" term="%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5" label="问题排查" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%BA%BF%E4%B8%8A%E5%AE%9E%E6%88%98" term="%E7%BA%BF%E4%B8%8A%E5%AE%9E%E6%88%98" label="线上实战" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Ja3 介绍]]></title>
            <link href="https://blog.yunpiao.site/post/20240416155744/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240416155744/</id>
            
            
            <published>2023-04-16T15:57:42+08:00</published>
            <updated>2023-04-16T15:57:42+08:00</updated>
            
            
            <content type="html"><![CDATA[<h3 id="tlsssl-指纹">TLS/SSL 指纹</h3>
<p>客户端和 server 端进行三次握手后,会进行 TLS 握手阶段, 同时恶意流量也会通过这种方式绕过检测</p>
<h3 id="ja3">ja3</h3>
<p>我们知道，不仅&quot;良性的&quot;应用程序会使用TLS及其前身SSL对其流量进行加密，而且恶意软件也常常这样做；前者这样做的目的是确保数据安全，而后者这样做的目的则是将其流量隐藏在噪声中。为了启动TLS会话，客户端将在<code>TCP 3次握手</code>后发送TLS客户端的<code>Hello数据包</code>。这个数据包及其生成方式取决于构建客户端应用程序时所使用的软件包和方法。如果接受TLS连接，服务器将使用基于服务器端库和配置以及Client Hello消息中的详细信息创建的<code>TLS Server Hello数据包</code>进行响应。由于<code>TLS协商是以明文的方式传输</code>的，所以，我们可以使用TLS Client Hello数据包中的详细信息对<code>客户端应用程序进行指纹识别</code>。</p>
<p><a href="https://xz.aliyun.com/t/3889" target="_blank" rel="noopener nofollow noreferrer" >https://xz.aliyun.com/t/3889</a></p>
<p>JA3和JA3S是一种基于TLS指纹的安全分析方法。JA3指纹能够指示客户端应用程序通过TLS通信的方式，而JA3S指纹能够指示服务器响应。如果两者结合起来，实质上就生成了客户端和服务器之间的加密协商的指纹。虽然基于TLS的检测方法不一定是灵丹妙药，也不一定能保证映射到客户端应用程序，但它们始终是安全分析的轴心所在。</p>
<p><a href="http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html</a></p>
<p>ssl 函数 clienthello<a href="https://manpages.debian.org/experimental/libssl-doc/SSL_client_hello_get0_ext.3ssl.en.html" target="_blank" rel="noopener nofollow noreferrer" >https://manpages.debian.org/experimental/libssl-doc/SSL_client_hello_get0_ext.3ssl.en.html</a></p>
<p>ja3 算法<a href="https://xz.aliyun.com/t/3889" target="_blank" rel="noopener nofollow noreferrer" >https://xz.aliyun.com/t/3889</a></p>
<p>增强 ja3<a href="http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=18951" target="_blank" rel="noopener nofollow noreferrer" >http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=18951</a></p>
<p>bfe 读取 clienthello</p>
<p><code>local ngx_lua_ffi_ssl_get_client_hello_extffi.cdef[[int ngx_http_lua_ffi_ssl_get_client_hello_ext1(ngx_http_request_t *r,unsigned int type, const unsigned char **out, size_t *outlen,char **err);]]</code></p>
<p>ngx_lua_ffi_ssl_get_client_hello_ext = C.ngx_http_lua_ffi_ssl_get_client_hello_ext1</p>
<p>nginx: log -&gt; socketgoflume: socket -&gt; tls_sink</p>
<h2 id="3-支持-https-ja3-指纹识别和防护策略">3. 支持 https ja3 指纹识别和防护策略</h2>
<p><a href="https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/clienthello.md</a></p>
<p><a href="https://github.com/bfenetworks/bfe/blob/70ed4a0b11b6521ddef3e861cb854eacc5d89e16/bfe_tls/handshake_messages.go" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/bfenetworks/bfe/blob/70ed4a0b11b6521ddef3e861cb854eacc5d89e16/bfe_tls/handshake_messages.go</a></p>
<p><a href="https://github.com/jbremer/ja3/blob/v1.0.1/parser.go" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/jbremer/ja3/blob/v1.0.1/parser.go</a></p>
<p><a href="http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=18951" target="_blank" rel="noopener nofollow noreferrer" >http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=18951</a><a href="https://github.com/fooinha/nginx-ssl-ja3" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/fooinha/nginx-ssl-ja3</a> nginx module ja3</p>
<p><a href="https://github.com/bfenetworks/bfe/blob/70ed4a0b11b6521ddef3e861cb854eacc5d89e16/bfe_tls/handshake_messages.go" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/bfenetworks/bfe/blob/70ed4a0b11b6521ddef3e861cb854eacc5d89e16/bfe_tls/handshake_messages.go</a></p>
<h3 id="引用">引用</h3>
<p><a href="https://www.ruanyifeng.com/blog/2014/02/ssl_tls.html" target="_blank" rel="noopener nofollow noreferrer" >https://www.ruanyifeng.com/blog/2014/02/ssl_tls.html</a></p>
<p><a href="https://www.guildhab.top/2021/04/%E9%80%9A%E8%BF%87-ja3s-%E5%AE%9E%E7%8E%B0-tls-%E6%8C%87%E7%BA%B9%E8%AF%86%E5%88%AB/" target="_blank" rel="noopener nofollow noreferrer" >https://www.guildhab.top/2021/04/%E9%80%9A%E8%BF%87-ja3s-%E5%AE%9E%E7%8E%B0-tls-%E6%8C%87%E7%BA%B9%E8%AF%86%E5%88%AB/</a></p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/linux" term="linux" label="linux" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%BF%90%E7%BB%B4" term="%E8%BF%90%E7%BB%B4" label="运维" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9D%82" term="%E6%9D%82" label="杂" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[mac air 体验 k3s]]></title>
            <link href="https://blog.yunpiao.site/post/20230412103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20230412103946/</id>
            
            
            <published>2023-04-12T10:39:46+08:00</published>
            <updated>2023-04-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>multipass launch -n master -c <span style="color:#ae81ff">1</span> -m 1G -d 10G
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>multipass launch -n node1 -c <span style="color:#ae81ff">1</span> -m 1G -d 10G
</span></span></code></pre></div><p><code>multipass shell master</code></p>
<p><code>curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -</code></p>
<p><code>sudo cat /var/lib/rancher/k3s/server/node-token</code></p>
<p><code>multipass shell node1</code></p>
<p>安装k3s，将master的ip和token替换到下面<br>
<code>curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://【master的ip】:6443 K3S_TOKEN=【master的token】 sh -</code></p>
<p>连接master虚拟机，确认节点<br>
连接master的shell终端<br>
<code>multipass shell master</code></p>
<p>查看k3s节点<br>
<code>sudo kubectl get nodes</code><br>
至此安装完成</p>
<p>部署nginx测试<br>
连接master终端<br>
创建一个deployment，名字是demo1，80端口，2个pod<br>
sudo kubectl create deployment demo1 &ndash;image=nginx &ndash;port=80 &ndash;replicas=2</p>
<p>给demo1创建一个负载均衡的service<br>
sudo kubectl expose deployment demo1 &ndash;type=LoadBalancer &ndash;port=80</p>
<p>查看所有service<br>
sudo kubectl get svc</p>
<p>c1581c4030.zicp.fun</p>
<p>这里的端口号是31886<br>
在master中，执行 curl http://master:31886，输出html字符代表nginx访问成功。</p>
<p>或者在本地电脑执行 multipass list 查看master虚拟机ip，浏览器访问http://虚拟机ip:31886。</p>
<p>加拉取仓库</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span>cat &gt; /etc/rancher/k3s/registries.yaml <span style="color:#e6db74">&lt;&lt;EOF
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span><span style="color:#e6db74">mirrors:
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span><span style="color:#e6db74">  docker.io:
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span><span style="color:#e6db74">    endpoint:
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span><span style="color:#e6db74">      - &#34;http://hub-mirror.c.163.com&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span><span style="color:#e6db74">      - &#34;https://docker.mirrors.ustc.edu.cn&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span><span style="color:#e6db74">      - &#34;https://registry.docker-cn.com&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span>systemctl restart k3s
</span></span></code></pre></div>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/kubernetes" term="kubernetes" label="Kubernetes" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%BF%90%E7%BB%B4" term="%E8%BF%90%E7%BB%B4" label="运维" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[解决 mac 无法被 ssh 免密登录问题]]></title>
            <link href="https://blog.yunpiao.site/post/20230112103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20230112103946/</id>
            
            
            <published>2023-01-12T10:39:46+08:00</published>
            <updated>2023-01-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>今天遇到的问题, 我远程登录加中的 mac mini, 虽然有密码管理器, 但是还是没有免密登录快捷. 所以设置免密登录出现了问题, 这里做下记录</p>
<h2 id="现象">现象</h2>
<p><code>ssh-copy-id a@itx</code> 执行成功后, 发现执行成功后, 还是需要输入密码<br>
报错如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>debug1: Next authentication method: publickey
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>debug1: Offering public key: /Users/xxx/.ssh/id_rsa RSA SHA256:l9F/xxxxx
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>debug1: Authentications that can <span style="color:#66d9ef">continue</span>: publickey,password,keyboard-interactive
</span></span></code></pre></div><h2 id="分析">分析</h2>
<p>client 端检查咩什么问题, 文件也正确, 所以去服务端排查, 查看 sshd 日志输出, 检查发现</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>Authentication refused: bad ownership or modes <span style="color:#66d9ef">for</span> directory /Users/xxx
</span></span></code></pre></div><p>所以, 看起来是由于被登录的 mac 主目录权限不正确, 导致么有读取到 authenticationed file 的原因</p>
<h2 id="解决">解决</h2>
<p>知道问题后, 一切都迎刃而解<br>
神之一手</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>chmod <span style="color:#ae81ff">755</span> ~
</span></span></code></pre></div>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/mac" term="mac" label="mac" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9D%82" term="%E6%9D%82" label="杂" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%BF%90%E7%BB%B4" term="%E8%BF%90%E7%BB%B4" label="运维" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[docker-docker容器安全]]></title>
            <link href="https://blog.yunpiao.site/post/20221201103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20221201103946/</id>
            
            
            <published>2022-12-01T10:39:46+08:00</published>
            <updated>2022-12-01T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<h3 id="1安全策略-cgroup">1.安全策略-Cgroup</h3>
<p>1.限制Cpu</p>
<blockquote>
<p>docker run &ndash;rm -ti -c 2000 ubuntu bash</p>
</blockquote>
<p>2.限制内存</p>
<blockquote>
<p>docker run &ndash;rm -ti -m 200M ubuntu bash</p>
</blockquote>
<p>3.限制快设备io</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span> 	 docker run --rm -ti --name container1 ubuntu bash
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span> 	 dd <span style="color:#66d9ef">if</span><span style="color:#f92672">=</span>/dev/zero of<span style="color:#f92672">=</span>testfile0 bs<span style="color:#f92672">=</span>8k mount<span style="color:#f92672">=</span><span style="color:#ae81ff">5000</span> oflag<span style="color:#f92672">=</span>direct
</span></span></code></pre></div><p>修改 Cgroup文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span><span style="color:#75715e"># 查找容器挂载的文件系统“/dev/mapper”的位置</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span>$ mount|grep ContainerID
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span><span style="color:#75715e"># 查看容器挂载的文件系统中的文件(Path为上条命令取得的返回结果)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span><span style="color:#75715e"># 返回结果“-&gt;”标记后会有一个新的路径，我们先称其为DeviceFilePath</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span>$ ls -l Path
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span><span style="color:#75715e"># 查询容器挂载的设备号</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span>$ ls DeviceFilePath -l
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span><span style="color:#75715e"># 返回结果中“root disk”后面会有一串用“,”隔开的数字，假设是128和256</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span><span style="color:#75715e"># 限制容器的写速度</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span>$ sudo echo <span style="color:#e6db74">&#39;128:256 10240000&#39;</span> &gt;/sys/fs/cgroup/blkio/system.slice/docker-DockerID.scope/blkio.throttle.write_bps_device
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-11">11</a></span><span><span style="color:#75715e"># 10240000是每秒可写入的最多的字节数。</span>
</span></span></code></pre></div><h3 id="2ulimit">2.ulimit</h3>
<p>在Docker1.6之后，可以设置全局默认的ulimit，如设置CPU时间</p>
<blockquote>
<p>sudo docker daemon &ndash;default-ulimit cpu=1200<br>
或者在启动容器时，单独对其ulimit进行设置</p>
</blockquote>
<blockquote>
<p>docker run &ndash;rm -ti &ndash;ulimit cpu=1200 ubuntu bash<br>
进入容器后可以查看</p>
</blockquote>
<blockquote>
<p>ulimit -t<br>
返回结果：1200</p>
</blockquote>
<h3 id="3-容器组网">3. 容器组网</h3>
<p>在接入容器隔离不足的情况下，将受信任的和不受信任的容器组网在不同的网络中，可以降低风险。</p>
<h3 id="4-容器--全虚拟化">4. 容器 + 全虚拟化</h3>
<p>如果将容器运行在全虚拟化环境中(如在虚拟机中运行容器)，这样就算容器被攻破，虚拟机还具有保护作用。目前一些安全需求很高的应用场景采用的就是这种方式，如公有云场景。</p>
<h3 id="5-镜像签名">5. 镜像签名</h3>
<p>Docker可信镜像及升级框架(The Update Framework，TUF)是Docker 1.8所提供的一个新功能，可以校验镜像的发布者。当发布者将镜像push到远程仓库时，Docker会对镜像用私钥进行签名，之后其他人pull该镜像的时候，Docker就会用发布者的公钥来校验该镜像是否和发布者所发布的镜像一致，是否被篡改过，是否是最新版。</p>
<h3 id="6-日志审核">6. 日志审核</h3>
<blockquote>
<p>docker run &ndash;rm -ti &ndash;log-driver=&ldquo;syslog&rdquo; ubuntu bash<br>
通过docker inspect ContainerID可以看到容器使用了哪种日志驱动。另外，只有json-file支持docker logs命令，docker logs ContainerID。</p>
</blockquote>
<h3 id="7-监控">7. 监控</h3>
<blockquote>
<p>容器的资源使用情况主要指容器对内存、网络I/O、CPU、磁盘I/O的使用情况等，命令：docker stats ContainerID。<br>
查看容器的运行状态，命令：docker ps -a。</p>
</blockquote>
<h3 id="8-文件系统级防护">8. 文件系统级防护</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>可读写挂载：
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>$ docker run --rm -ti ubuntu bash
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span><span style="color:#75715e"># echo &#34;hello&#34; &gt;/home/test.txt</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span><span style="color:#75715e"># cat /home/test.txt</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5">5</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6">6</a></span><span>只读挂载：
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-7">7</a></span><span>$ docker run --rm -ti --read-only ubuntu bash
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-8">8</a></span><span><span style="color:#75715e"># echo &#34;hello&#34; &gt;/home/test.txt</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-9">9</a></span><span><span style="color:#75715e"># 返回结果：bash:/home/test.txt:Read-only file system</span>
</span></span></code></pre></div><h3 id="9-capability">9. capability</h3>
<p>Capabilities 链接 <a href="https://wiki.archlinux.org/index.php/Capabilities_%28%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%29" target="_blank" rel="noopener nofollow noreferrer" >https://wiki.archlinux.org/index.php/Capabilities_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)</a></p>
<p>从2.2版开始，Linux有了capability的概念，它打破了Linux操作系统中超级用户/普通用户的概念，让普通用户也可以做只有超级用户才能完成的工作。capability可以作用在进程上，也可以作用在程序文件上。它与sudo不同，sudo可以配置某个用户可以执行某个命令或更改某个文件，而capability则是让程序拥有某种能力。</p>
<p><code>每个进程有三个和能力有关的位图：Inheritable(I)\Permitted(P)\Effective(E)，我们可以通过/proc/&lt;PID&gt;/status来查看进程的capability。</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>命令：cat /proc/$$/status | grep Cap
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span>结果：
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3">3</a></span><span><span style="color:#75715e"># 能够被当前进程执行的程序继承的capability。</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-4">4</a></span><span>CapInh: <span style="color:#ae81ff">0000000000000000</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-5">5</a></span><span><span style="color:#75715e"># 进程能够使用的能力，可以包含CapEff中没有的能力，这些能力是被进程自己临时放弃的，因此可以把CapEff看作是CapPrm的一个子集。</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-6">6</a></span><span>CapPrm: ffffffffffffffff
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-7">7</a></span><span><span style="color:#75715e"># 当一个进程要进行某个特权操作时，操作系统会检查CapEff的对应位是否有效，而不再是检查进程的有效UID是否为0。</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-8">8</a></span><span>CapEff: ffffffffffffffff
</span></span></code></pre></div><p>Docker启动容器的时候，会通过白名单的方式来设置传递给容器的capability，默认情况下，这个白名单只包含CAP_CHOWN等少数的能力。用户可以通过 -–cap-add 和 -–cap-drop 这两个参数来修改这个白名单。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1"> 1</a></span><span>$ docker run --rm -ti --cap-drop<span style="color:#f92672">=</span>chown ubuntu bash
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2"> 2</a></span><span><span style="color:#75715e"># chown 2.2/etc/hosts</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3"> 3</a></span><span><span style="color:#75715e"># 返回结果：chown:changing ownership of &#39;/etc/hosts&#39;: Operation not permitted</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4"> 4</a></span><span>发现禁掉CAP_CHOWN能力后，在容器里就无法改变容器的所有者了。如果不禁掉则正常。如下
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-5"> 5</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-6"> 6</a></span><span>$ docker run --rm -ti ubuntu bash
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-7"> 7</a></span><span><span style="color:#75715e"># chown 2.2/etc/hosts</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-8"> 8</a></span><span>容器应遵循最小权限原则，尽量不要用–privileged参数，不需要的能力全部去掉，甚至禁掉所有的能力。
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-9"> 9</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-10">10</a></span><span>$ docker run --rm -ti --cap-drop<span style="color:#f92672">=</span>all ubuntu bash
</span></span></code></pre></div><h3 id="10-selinux">10. SElinux</h3>
<p>SElinux 介绍链接 <a href="http://www.cnblogs.com/xiaoluo501395377/archive/2013/05/26/3100444.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.cnblogs.com/xiaoluo501395377/archive/2013/05/26/3100444.html</a></p>
<p>SELinux定义了系统中每个用户、进程、应用和文件访问及转变的权限，然后使用一个安全策略来控制这些实体(即用户、进程、应用和文件)之间的交互，安全策略指定了如何严格或宽松的进行检查。另外，SELinux比较复杂。</p>
<h3 id="11-apparmor">11. AppArmor</h3>
<p>文档配置链接 <a href="http://manpages.ubuntu.com/manpages/xenial/en/man5/apparmor.d.5.html" target="_blank" rel="noopener nofollow noreferrer" >http://manpages.ubuntu.com/manpages/xenial/en/man5/apparmor.d.5.html</a></p>
<p>AppArmor也是一种MAC控制机制，其主要作用是设置摸个可执行程序的访问控制权限，可以限制程序读/写某个目录/文件，打开/读/写网络端口等。AppArmor是一个高效和易于使用的Linux系统安全特性，它对操作系统和应用程序进行了从内到外的保护，即使是0day漏洞和未知的应用程序漏洞所导致的攻击也可被识破。AppArmor安全策略可以完全定义个别应用程序所能访问的系统资源与各自的特权，它包含了大量的默认策略，并将先进的静态分析和基于学习的工具结合了起来，可以在很短的时间内，为非常复杂的应用制定AppArmor规则。</p>
<h3 id="12-seccomp">12. Seccomp</h3>
<p>Seccomp(secure computing mode)是一种Linux内核提供的安全特性，它可以实现应用程序的沙盒机制，以白名单或黑名单的方式限制进程进行系统调用。</p>
<p>Seccomp首次于内核2.6.12版合入Linux主线。早期的Seccomp只支持过滤少数几个系统调用。较新版本的内核支持动态Seccomp策略，也就是seccomp-bpf，因为支持用BPF生成过滤规则，从而使Seccomp可以限制任意的系统调用，并且可以限制系统调用传入的参数。</p>
<p>Seccomp的使用</p>
<p>生成BPF形式的过滤规则；<br>
调用prctl系统调用将规则传入内核。<br>
在Docker容器启动的过程中，会对Seccomp设置一个默认的配置，但目前还不支持命令行参数做配置。</p>
<h3 id="13-grsecurity">13. grsecurity</h3>
<p>grsecurity提供了一个系统的内核patch，使Linux内核的安全性大大增强，并且它提供了一些工具让用户配置、使用这些安全特性。grsecurity可以用来控制资源访问权限。下面是一张关于grsecurity、SELinux和AppArmor的对比图。</p>
<h1 id="相关安全项目">相关安全项目</h1>
<p>与Docker安全相关的项目</p>
<h3 id="1-notary">1. Notary</h3>
<p>Docker对安全模块进行了重构，剥离出了名为Notary的独立项目。Notary的目标是保证server和client之间的交互使用可信任的连接，用于解决互联网的内容发布的安全性。该项目并未局限于容器应用，在容器场景下可以对镜像源认证、镜像完整性等安全需求提供更好的支持。</p>
<h3 id="2-docker-bench-security">2. docker-bench-security</h3>
<p>docker-bench-security提供一个脚本，它可以检测用户的生产环境是否符合Docker的安全实践。</p>
<h1 id="docker安全遗留问题">Docker安全遗留问题</h1>
<p>在Docker的安全问题上Docker社区做了很多的工作，但Docker依然有不少跟安全相关的问题尚未解决。</p>
<h3 id="1-user-namespace">1. User Namespace</h3>
<p>User Namespace可以将host中的一个普通用户映射成容器里的root用户，不过虽然允许进程在容器里执行特权操作，但这些特权只局限于该容器内。这对容器的安全是一个非常大的提升，恶意程序通过容器入侵host或者其他容器的风险大大降低，但这并不意味着容器就足够安全了。另外，由于内核层面隔离性不足，如果用户在容器的一个特权操作会影响到容器外，那么这个特权操作一般也是不被User Namespace所允许的。</p>
<h3 id="2-非root运行docker-daemon">2. 非root运行Docker daemon</h3>
<p>目前Docker daemon需要由root用户启动，而Docker daemon创建的容器以及容器里运行的应用实际上也是以root用户运行的。实现由普通用户启动Docker daemon和运行容器，有益于Docker的安全。但这个问题很难解决，因为创建容器需要执行很多特权，包括挂载文件系统、配置网络等。目前社区还没有一个好的方案。</p>
<h3 id="3-docker热升级">3. Docker热升级</h3>
<p>Docker管理容器的方式是中心式管理，容器由主机上的Docker daemon进程统一管理。中心式管理方式对于第三方的任务编排工具并不友好，因为什么功能都需要跟Docker关联起来。更大的问题是，如果Docker daemon挂掉了，重启daemon后，它无法接管容器，容器也不能运行了。在实际应用中，很多业务都是不能中断的，而停止容器就往往相当于停止业务，但如果因为安全漏洞的原因需要升级Docker，用于就将处于两难的境地。点击了解该问题的进展。</p>
<h3 id="3-磁盘限额">3. 磁盘限额</h3>
<p>默认情况下，Docker镜像、容器rootfs、数据卷都存放在/var/lib/docker目录里，也就是说跟host是共享同一个文件系统的。如果不对Docker容器做磁盘大小的配额限制，容器就可能用完磁盘的可用空间，导致host和其他容器无法正常工作。<br>
但是目前Docker几乎没有提供任何接口用于限制容器的磁盘大小。但graphdriver为devicemapper时，容器会被默认分配一个100GB的空间。这个空间大小可以在启动Docker daemon时设置为另一个默认值，但无法对每个容器单独设置一个不同的值。</p>
<blockquote>
<p>$ sudo docker daemon &ndash;storage-opt dm.basesize=5G</p>
</blockquote>
<p>除此之外，用户只能通过其他手段自行做一些隔离措施，例如为/var/lib/docker单独分配一个磁盘或分区。</p>
<h3 id="4-网络io">4. 网络I/O</h3>
<p>目前同一台机器上的Docker容器会共享宽带，但这可能出现某个容器占用大部分带宽资源，从而影响其他需要网络资源的容器正常工作的情况。Docker需要一个好的网络方案，除了要解决容器跨主机通信的问题，还要解决网络I/O限制的问题。</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[集群-ntp时间同步服务]]></title>
            <link href="https://blog.yunpiao.site/post/20220412103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20220412103946/</id>
            
            
            <published>2022-04-12T10:39:46+08:00</published>
            <updated>2022-04-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>本不想做运维的 奈何现在项目缺人手， 不仅需求多， 而且还需要教学弟， 这篇文章主要是记录在分布式集群中出现的hive或者hbase时间同步错误。</p>
</blockquote>
<h2 id="首先-为什么需要时间同步问题">首先， 为什么需要时间同步问题</h2>
<p>由于不同主机上的时区不同或者就是时间不同，导致各个集群上的时间不一致， 这就会导致&mdash; 查阅了许多资料， 暂时不知道为什么需要时间同步， 不过hive hbaase启动的时候， 都会检查各个节点的时间是否一致， 否则就会报错。</p>
<h2 id="其次-计算机是如何控制时间的">其次， 计算机是如何控制时间的</h2>
<p>计算机时间分为硬件时间和系统时间。 硬件时间是bios上的硬件电路里面的时钟，<br>
系统时间是指linux系统里维护的时间， 因为不能一直读取硬件时间，所以一般 开机后会进行一次硬件时间到软件时间的同步， 之后两个时间就各自运行，互不干扰， 除非手动同步。</p>
<h2 id="手动设置时间">手动设置时间</h2>
<p>相关命令：<br>
date    修改和显示日期和时间的命令。<br>
hwclock    将当前系统时间写入CMOS的命令，只有root用户才可以使用。<br>
ntpd     NTP服务的守护进程文件，需要先启动它才能提供NTP服务。<br>
ntpdate     客户端时间同步</p>
<blockquote>
<p>显示硬件时间 #hwclock<br>
设置硬件时钟的操作：#hwclock &ndash;set &ndash;date=&ldquo;09/17/2003 13:26:00&rdquo;<br>
硬件时钟与系统时钟同步：<br>
# hwclock &ndash;hctosys<br>
上面命令中，&ndash;hctosys表示Hardware Clock to SYStem clock。<br>
系统时钟和硬件时钟同步：<br>
# hwclock &ndash;systohc<br>
校准时区 # ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime</p>
</blockquote>
<h1 id="如何同步集群时间--ntp">如何同步集群时间- NTP</h1>
<p>NTP是Server Client运行方式<br>
安装NTP服务<br>
sudo apt-get install ntp</p>
<h2 id="配置ntpserver">配置NTPServer</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span><span style="color:#75715e"># vi /etc/ntp.conf</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>restrict 192.168.100.0 mask 255.255.255.0 nomodify     <span style="color:#75715e">#本地网段授权访问 </span>
</span></span></code></pre></div><p>ntpd启动后，客户机要等几分钟再与其进行时间同步，否则会提示“no server suitable for synchronization found”错误。</p>
<h2 id="客户端更新时间">客户端更新时间</h2>
<p><code>ntpdate 192.168.100.10</code></p>
<blockquote>
<p>敲完  收工</p>
</blockquote>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[大数据-HDP使用maven构建udf开发环境]]></title>
            <link href="https://blog.yunpiao.site/post/20220401000000/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20220401000000/</id>
            
            
            <published>2022-04-01T00:00:00+08:00</published>
            <updated>2022-04-01T00:00:00+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>环境：</p>
<ul>
<li>idea 2017</li>
<li>maven 4.0</li>
<li>hive 	1.2.1.2.6</li>
<li>win10</li>
</ul>
<h2 id="创建maven项目">创建maven项目</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span><span style="color:#f92672">&lt;project</span> <span style="color:#a6e22e">xmlns=</span><span style="color:#e6db74">&#34;http://maven.apache.org/POM/4.0.0&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>         <span style="color:#a6e22e">xmlns:xsi=</span><span style="color:#e6db74">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>         <span style="color:#a6e22e">xsi:schemaLocation=</span><span style="color:#e6db74">&#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>    <span style="color:#f92672">&lt;modelVersion&gt;</span>4.0.0<span style="color:#f92672">&lt;/modelVersion&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>    <span style="color:#f92672">&lt;groupId&gt;</span>hive<span style="color:#f92672">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>    <span style="color:#f92672">&lt;artifactId&gt;</span>udf<span style="color:#f92672">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>    <span style="color:#f92672">&lt;version&gt;</span>1.0-SNAPSHOT<span style="color:#f92672">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>    <span style="color:#f92672">&lt;packaging&gt;</span>jar<span style="color:#f92672">&lt;/packaging&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>    <span style="color:#f92672">&lt;name&gt;</span>hive<span style="color:#f92672">&lt;/name&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>    <span style="color:#f92672">&lt;url&gt;</span>http://maven.apache.org<span style="color:#f92672">&lt;/url&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>    <span style="color:#f92672">&lt;properties&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17">17</a></span><span>        <span style="color:#f92672">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span style="color:#f92672">&lt;/project.build.sourceEncoding&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18">18</a></span><span>    <span style="color:#f92672">&lt;/properties&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19">19</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20">20</a></span><span>    <span style="color:#f92672">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21">21</a></span><span>        <span style="color:#f92672">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22">22</a></span><span>            <span style="color:#f92672">&lt;groupId&gt;</span>org.apache.hive<span style="color:#f92672">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23">23</a></span><span>            <span style="color:#f92672">&lt;artifactId&gt;</span>hive-exec<span style="color:#f92672">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-24">24</a></span><span>            <span style="color:#f92672">&lt;version&gt;</span>0.13.0<span style="color:#f92672">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-25">25</a></span><span>        <span style="color:#f92672">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-26">26</a></span><span>    <span style="color:#f92672">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-27">27</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-28">28</a></span><span><span style="color:#f92672">&lt;/project&gt;</span>
</span></span></code></pre></div><p>等待安装好依赖</p>
<h2 id="编写udf函数">编写UDF函数</h2>
<p>编写一个生成MD5函数</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1"> 1</a></span><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">my_udf</span> <span style="color:#66d9ef">extends</span> UDF {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2"> 2</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3"> 3</a></span><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">my_udf</span>(String s) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4"> 4</a></span><span>        <span style="color:#66d9ef">return</span> getMD5(s);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5"> 5</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6"> 6</a></span><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> String <span style="color:#a6e22e">getMD5</span>(String str) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7"> 7</a></span><span>        <span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8"> 8</a></span><span>            <span style="color:#75715e">// 生成一个MD5加密计算摘要</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9"> 9</a></span><span>            MessageDigest md <span style="color:#f92672">=</span> MessageDigest.<span style="color:#a6e22e">getInstance</span>(<span style="color:#e6db74">&#34;MD5&#34;</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-10">10</a></span><span>            <span style="color:#75715e">// 计算md5函数</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-11">11</a></span><span>            md.<span style="color:#a6e22e">update</span>(str.<span style="color:#a6e22e">getBytes</span>());
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-12">12</a></span><span>            <span style="color:#75715e">// digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-13">13</a></span><span>            <span style="color:#75715e">// BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-14">14</a></span><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> BigInteger(1, md.<span style="color:#a6e22e">digest</span>()).<span style="color:#a6e22e">toString</span>(16);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-15">15</a></span><span>        } <span style="color:#66d9ef">catch</span> (Exception e) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-16">16</a></span><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">print</span>(<span style="color:#e6db74">&#34;MD5加密出现错误&#34;</span>);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-17">17</a></span><span>        }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-18">18</a></span><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;error&#34;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-19">19</a></span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-20">20</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-21">21</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-22">22</a></span><span>}
</span></span></code></pre></div><h2 id="生成jar包">生成jar包</h2>
<p>idea 里面直接点package</p>
<h2 id="上传jar包">上传jar包</h2>
<p>使用jar文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#66d9ef">add</span> jar hdfs:<span style="color:#f92672">///</span><span style="color:#66d9ef">user</span><span style="color:#f92672">/</span><span style="color:#66d9ef">admin</span><span style="color:#f92672">/</span>udf<span style="color:#f92672">-</span>md5.jar;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span><span style="color:#66d9ef">create</span> <span style="color:#66d9ef">temporary</span> <span style="color:#66d9ef">function</span> my_udf <span style="color:#66d9ef">as</span> <span style="color:#e6db74">&#39;my_udf&#39;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span><span style="color:#66d9ef">select</span> my_udf(word_count.column1) <span style="color:#66d9ef">from</span> word_count <span style="color:#66d9ef">limit</span> <span style="color:#ae81ff">5</span>
</span></span></code></pre></div>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[杂-吃灰待阅读链接整理]]></title>
            <link href="https://blog.yunpiao.site/post/20250827172241/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250827172241/</id>
            
            
            <published>2021-08-27T17:22:41+08:00</published>
            <updated>2021-08-27T17:22:41+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>🥲 虚假学习<br>
onetab 里面太多链接了, 都是待阅读的, 太多看不完, 看完了又很快忘记了,  所以我想先记一个流水账, 准备后期用 Gemini 全部整理一遍再阅读了</p>
</blockquote>
<h2 id="同时推荐下我的-chrome-小工具如果我的-blog-有人看的话">👋同时推荐下我的 chrome 小工具(如果我的 blog 有人看的话)</h2>
<p><a href="https://chromewebstore.google.com/detail/%E7%BD%91%E9%A1%B5-ai-%E8%BD%AC%E5%86%99%E5%99%A8/agfhhpkboppfaehehhicoojiaikholal" target="_blank" rel="noopener nofollow noreferrer" >网页 AI 转写器</a> 类似阅读模式, 生成一个清爽版的阅读版本,  比较好的是, 可以使用自己账号的 Gemini, DeepSeek 等 AI 工具</p>
<h3 id="一软件架构与系统设计">一、软件架构与系统设计</h3>
<h4 id="a-通用原则与高阶概念">A. 通用原则与高阶概念</h4>
<ul>
<li>
<p><strong>高可用与可扩展性</strong></p>
<ul>
<li><a href="https://mp.weixin.qq.com/s/KO793LjRtbDhrH-HGgVmyA" target="_blank" rel="noopener nofollow noreferrer" >亿级流量系统架构设计系列——1.核心设计原则</a></li>
<li><a href="https://www.thebyte.com.cn/architecture/history.html#_1-1-2-%E8%99%9A%E6%8B%9F%E5%8C%96%E6%8A%80%E6%9C%AF%E6%88%90%E7%86%9F" target="_blank" rel="noopener nofollow noreferrer" >1.1 云计算的演进变革 | 深入高可用系统原理与设计</a></li>
<li><a href="https://www.yuque.com/kaito-djycs/kb/gw7kbw" target="_blank" rel="noopener nofollow noreferrer" >「异地多活」参考资料</a></li>
<li><a href="http://kaito-kidd.com/2021/10/15/what-is-the-multi-site-high-availability-design/" target="_blank" rel="noopener nofollow noreferrer" >搞懂异地多活，看这篇就够了 | Kaito&rsquo;s Blog</a></li>
<li><a href="https://www.thebyte.com.cn/distributed-transaction/TCC.html" target="_blank" rel="noopener nofollow noreferrer" >5.3.2 TCC | 深入高可用系统原理与设计</a></li>
<li><a href="https://cloud.tencent.com/developer/article/2485144" target="_blank" rel="noopener nofollow noreferrer" >万字详解高可用架构设计-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://qnmlgb.tech/articles/6785a452003b07f9af672fa9/" target="_blank" rel="noopener nofollow noreferrer" >万字详解高可用架构设计 - 高可用架构 - 瓦斯阅读</a></li>
</ul>
</li>
<li>
<p><strong>设计原则与模式</strong></p>
<ul>
<li><a href="https://github.com/jarulraj/periodic-table" target="_blank" rel="noopener nofollow noreferrer" >系统设计原则周期表</a></li>
<li><a href="https://mp.weixin.qq.com/s/HKTPSUMiJOaeufLJ2ifJQg?clicktime=1750656720&amp;enterid=1750656720&amp;exptype=unsubscribed_card_recommend_article_u2i_mainprocess_coarse_sort_pcfeeds&amp;ranksessionid=1750656710_1&amp;scene=169&amp;subscene=200" target="_blank" rel="noopener nofollow noreferrer" >这次才真正懂了“组织架构决定技术架构”</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/138145081" target="_blank" rel="noopener nofollow noreferrer" >复杂度是不灭的，只会转移，难道一切都是徒劳的吗？ - 知乎</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/410049005" target="_blank" rel="noopener nofollow noreferrer" >架构设计-复杂度是不灭的 - 知乎</a></li>
<li><a href="https://mp.weixin.qq.com/s/ChNm8lgJkGg57pFCnmGp8w" target="_blank" rel="noopener nofollow noreferrer" >软件开发中的抽象泄露法则</a></li>
<li><a href="https://www.libaedu.com/info/541.html" target="_blank" rel="noopener nofollow noreferrer" >【图文】系统设计面经：7大Facebook系统设计面试问题及答案(2023) – 篱笆教育</a></li>
<li><a href="https://freedium.cfd/https://levelup.gitconnected.com/my-favourite-software-architecture-patterns-0e57073b4be1" target="_blank" rel="noopener nofollow noreferrer" >我最喜欢的软件架构模式 | 作者：Matt Bentley</a></li>
<li><a href="https://github.com/ByteByteGoHq/system-design-101?tab=readme-ov-file#communication-protocols" target="_blank" rel="noopener nofollow noreferrer" >system-design-101: 用视觉和简单的术语解释复杂的系统。帮助你准备系统设计面试。</a></li>
<li><a href="https://www.16elt.com/2024/09/25/first-book-of-byte-sized-tech/index.html" target="_blank" rel="noopener nofollow noreferrer" >《软件设计的哲学》中的思想</a></li>
<li><a href="https://github.com/summerjava/system-design-interview" target="_blank" rel="noopener nofollow noreferrer" >系统设计面试必读</a></li>
<li><a href="https://blog.thepete.net/blog/2019/12/09/delivering-on-an-architecture-strategy/" target="_blank" rel="noopener nofollow noreferrer" >实现架构策略</a></li>
<li><a href="https://medium.com/machine-words/writing-technical-design-docs-71f446e42f2e#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6ImRkMTI1ZDVmNDYyZmJjNjAxNGFlZGFiODFkZGYzYmNlZGFiNzA4NDciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIyMTYyOTYwMzU4MzQtazFrNnFlMDYwczJ0cDJhMmphbTRsamRjbXMwMHN0dGcuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIyMTYyOTYwMzU4MzQtazFrNnFlMDYwczJ0cDJhMmphbTRsamRjbXMwMHN0dGcuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTAzNDcwMTQ5ODM3NTY1MjA1OTQiLCJlbWFpbCI6Inl1bnBpYW8xMTFAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5iZiI6MTczNjkzMTQ5NSwibmFtZSI6IuS6kemjmCIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NKRGlGSVNPWHo1VUctYlZJMktteTlXWXVLd01jZzJ2c1h4Z2c0SHBNSTZmckxQcS1vPXM5Ni1jIiwiZ2l2ZW5_bmFtZSI6IumjmCIsImZhbWlseV9uYW1lIjoi5LqRIiwiaWF0IjoxNzM2OTMxNzk1LCJleHAiOjE3MzY5MzUzOTUsImp0aSI6IjExNDY5Y2Q3ZDk0ZjczZGE1OGQ2YTM3NDc1Nzc0OGIzMmJjMGM4N2MifQ.eT3NapOSAhG7CLgiSfJszIN8atBQ2v41q4KKztzk0ycPAMhsQFzvmvXvFyKrnsIUMTiybOqHuaQnBssDg15eFO-jO80pvpi60fTj_bXVRGNScH8bK53sSKbOYYwzoTSrW86qXTIx0ww_bzqVlhFSwhZH9eP7GKQ9G04srDKnwYoaKgk3ploFBe-Aaeemr_9JeCfFnAgutzBzRYmFZGKfBa5rRNZvZYtG8Az94Z3-t9CHMGos1daY0FLq6IEHy5P8LBHjTRsNTflKGbs7fF2VcO0N62nev4F44rxz6WhqeL6fMvQzVLsrrrGiDebt4mtHWQXQ1c3HxFhJnJvQT-59hA" target="_blank" rel="noopener nofollow noreferrer" >撰写技术设计文档。工程洞察</a></li>
<li><a href="https://unwiredcouch.com/2018/01/03/engineering-vision.html" target="_blank" rel="noopener nofollow noreferrer" >学习拥有工程愿景</a></li>
<li><a href="https://www.intercom.com/blog/run-less-software/" target="_blank" rel="noopener nofollow noreferrer" >运行更少的软件 - Intercom博客</a></li>
<li><a href="https://s3.amazonaws.com/systemsandpapers/papers/Frederick_Brooks_87-No_Silver_Bullet_Essence_and_Accidents_of_Software_Engineering.pdf" target="_blank" rel="noopener nofollow noreferrer" >没有银弹——软件工程的本质与偶然</a></li>
</ul>
</li>
<li>
<p><strong>单体 vs. 微服务</strong></p>
<ul>
<li><a href="https://tonybai.com/2025/06/17/rider-elephant-arch/" target="_blank" rel="noopener nofollow noreferrer" >“骑手与大象”架构：超越微服务与单体之争的务实之道？ - Tony Bai</a></li>
<li><a href="https://x.com/sandmagician/status/1593469165892820992?t=w1FZjG0Pyyq-EFQRpXtYGQ&amp;s=09" target="_blank" rel="noopener nofollow noreferrer" >Sand Magician 在 X 上的讨论：“也不知道过去几年这股奇怪的“微服务”风潮是怎么起来的&hellip;”</a></li>
<li><a href="https://github.com/GoogleCloudPlatform/microservices-demo" target="_blank" rel="noopener nofollow noreferrer" >一个包含10个微服务的示例云原生应用，展示了Kubernetes、Istio和gRPC。</a></li>
</ul>
</li>
<li>
<p><strong>领域驱动设计 (DDD)</strong></p>
<ul>
<li><a href="https://mp.weixin.qq.com/s?__biz=MzkxMjQzMjA0OQ==&amp;mid=2247485358&amp;idx=1&amp;sn=79e1be9140334d4487b337a839cbb9eb&amp;chksm=c07b43c736985e6de2df61d832b7cf3352c19ee1bd3fc5428316b83b644c68dda4c248a85189&amp;xtrack=1&amp;scene=90&amp;subscene=93&amp;sessionid=1738464877&amp;flutter_pos=0&amp;clicktime=1738464880&amp;enterid=1738464880&amp;finder_biz_enter_id=4&amp;ranksessionid=1738464872&amp;ascene=56&amp;fasttmpl_type=0&amp;fasttmpl_fullversion=7572126-zh_CN-zip&amp;fasttmpl_flag=0&amp;realreporttime=1738464880740#rd" target="_blank" rel="noopener nofollow noreferrer" >浅谈DDD领域驱动设计架构</a></li>
<li><a href="https://xyzghio.xyz/BasisOfDDD/" target="_blank" rel="noopener nofollow noreferrer" >关于 DDD 的认知与思考 | xyZGHio</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8701/" target="_blank" rel="noopener nofollow noreferrer" >DDD领域驱动设计入门与实践：从概念到代码示例 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9771/" target="_blank" rel="noopener nofollow noreferrer" >领域驱动设计40个核心概念精粹 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9190/" target="_blank" rel="noopener nofollow noreferrer" >两种常用代码范式：领域模型驱动与过程驱动 - 开发技术 - 冷月清谈</a></li>
</ul>
</li>
</ul>
<h4 id="b-特定系统案例">B. 特定系统案例</h4>
<ul>
<li><a href="https://mp.weixin.qq.com/s/yfsw11CQhwWORgNCpPDVQQ" target="_blank" rel="noopener nofollow noreferrer" >标签系统的架构设计与实现</a></li>
<li><a href="https://mp.weixin.qq.com/s/jpTezo6097QymO0GODidqw" target="_blank" rel="noopener nofollow noreferrer" >微信读书后台架构演进之路</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9528/" target="_blank" rel="noopener nofollow noreferrer" >系统架构设计全解：从思维到实践 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf" target="_blank" rel="noopener nofollow noreferrer" >Bigtable: 一个针对结构化数据的分布式存储系统</a></li>
</ul>
<h3 id="二编程语言与框架">二、编程语言与框架</h3>
<h4 id="a-go语言">A. Go语言</h4>
<ul>
<li><a href="https://tonybai.com/2025/06/16/go-avoid-critical-incident/" target="_blank" rel="noopener nofollow noreferrer" >GCP大面积故障，Go语言是“元凶”还是“背锅侠”？ - Tony Bai</a></li>
<li><a href="https://czyt.tech/post/use-semaphoregroup-in-go/" target="_blank" rel="noopener nofollow noreferrer" >在go中使用Semaphoregroup | 虫子樱桃</a></li>
<li><a href="https://lailin.xyz/categories/Go%E8%BF%9B%E9%98%B6%E8%AE%AD%E7%BB%83%E8%90%A5/#board" target="_blank" rel="noopener nofollow noreferrer" >分类 - Go进阶训练营 - Mohuishou</a></li>
<li><a href="https://goperf.dev/01-common-patterns/object-pooling/" target="_blank" rel="noopener nofollow noreferrer" >对象池 - Go优化指南</a></li>
<li><a href="http://open.qiniu.us/gop-ai-and-arch.pdf" target="_blank" rel="noopener nofollow noreferrer" >Go+, LLM+MCP与架构分享</a></li>
<li><a href="https://oilbeater.com/2024/01/09/alloc-slice-for-golang-2-md/#bytebufferpool" target="_blank" rel="noopener nofollow noreferrer" >Golang 中预分配 slice 内存对性能的影响（续） | Oilbeater 的自习室</a></li>
<li><a href="https://pub.huizhou92.com/error-handling-in-go-the-new-operator-da92a0207b1e" target="_blank" rel="noopener nofollow noreferrer" >Go中的错误处理：新的?操作符</a></li>
<li><a href="https://medium.com/@kartik11buttan/golang-error-handling-improvised-must-pattern-d867dc09c646" target="_blank" rel="noopener nofollow noreferrer" >GO LANG 错误处理 — 改进版 (必看模式)</a></li>
<li><a href="https://medium.com/@norbert.jakubczak/profiling-go-applications-cpu-memory-and-concurrency-insights-442a6e9c6979" target="_blank" rel="noopener nofollow noreferrer" >分析Go应用程序：CPU、内存和并发洞察</a></li>
<li><a href="https://levelup.gitconnected.com/lessons-from-running-go-services-in-production-for-2-years-b4741f7bce13" target="_blank" rel="noopener nofollow noreferrer" >在生产环境中运行Go服务的经验教训</a></li>
<li><a href="https://go.dev/blog/" target="_blank" rel="noopener nofollow noreferrer" >Go 编程语言博客</a></li>
<li><a href="https://mp.weixin.qq.com/s/E0mtD46vMjK16mpS3B8BWw" target="_blank" rel="noopener nofollow noreferrer" >Go 代码阅读不再难，goanalysis 工具帮你忙</a></li>
<li><a href="https://tonybai.com/2025/02/05/go-encoding-json-v2-proposal-json-processing-new-engine/" target="_blank" rel="noopener nofollow noreferrer" >Go encoding/json/v2提案：JSON处理新引擎 | Tony Bai</a></li>
<li><a href="https://github.com/pibigstar/go-demo/tree/master" target="_blank" rel="noopener nofollow noreferrer" >Go语言实例教程从入门到进阶</a></li>
<li><a href="https://mp.weixin.qq.com/s/57c2cWs8oM7FA80ZKThNHw" target="_blank" rel="noopener nofollow noreferrer" >Go1.24 新特性：sync.Map 性能提高、Go mod 增加 tool 指令、net/http 协议优化等</a></li>
<li><a href="https://github.com/xxjwxc/uber_go_guide_cn" target="_blank" rel="noopener nofollow noreferrer" >Uber Go 语言编码规范中文版</a></li>
<li><a href="https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-channel/" target="_blank" rel="noopener nofollow noreferrer" >Go 语言 Channel 实现原理精要 | Go 语言设计与实现</a></li>
<li><a href="https://draveness.me/golang/" target="_blank" rel="noopener nofollow noreferrer" >Go 语言设计与实现</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9372/" target="_blank" rel="noopener nofollow noreferrer" >Go应用监控：编译时插桩方案详解 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9753/" target="_blank" rel="noopener nofollow noreferrer" >Go应用崩溃案例分析：编译时插桩与竞态检测的冲突 - 开发技术 - 冷月清谈</a></li>
</ul>
<h4 id="b-python">B. Python</h4>
<ul>
<li><a href="https://github.com/codelion/adaptive-classifier" target="_blank" rel="noopener nofollow noreferrer" >一个灵活、自适应的动态文本分类系统</a></li>
<li><a href="https://huggingface.co/blog/codelion/adaptive-classifier" target="_blank" rel="noopener nofollow noreferrer" >自适应分类器：动态文本分类与持续学习</a></li>
<li><a href="https://xyzghio.xyz/WeakRefOfPy/" target="_blank" rel="noopener nofollow noreferrer" >Python 之 Weak Ref 弱引用 | xyZGHio</a></li>
</ul>
<h4 id="c-rust">C. Rust</h4>
<ul>
<li><a href="https://github.com/HelixDB/helix-db" target="_blank" rel="noopener nofollow noreferrer" >HelixDB: 一个强大的开源图向量数据库，使用Rust构建</a></li>
<li><a href="https://mp.weixin.qq.com/s/m-IBomxu88DlNcEyOgyOew?poc_token=HCmbCWijoK2KhnnZCS8X8c4TB6w37nkMPxaMDK8J" target="_blank" rel="noopener nofollow noreferrer" >Rust std fs 比 Python 慢！真的吗！？</a></li>
<li><a href="https://medium.com/@hrshh17softdev/rust-killed-my-go-microservice-and-it-deserved-it-c9fef79c0abb" target="_blank" rel="noopener nofollow noreferrer" >Rust 杀死了我的 Go 微服务（它罪有应得）</a></li>
</ul>
<h4 id="d-web与前端">D. Web与前端</h4>
<ul>
<li><a href="https://github.com/michaelshimeles/nextjs-starter-kit" target="_blank" rel="noopener nofollow noreferrer" >终极 Next.js 入门套件</a></li>
<li><a href="https://juejin.cn/post/7471300010601218057" target="_blank" rel="noopener nofollow noreferrer" >用第一性原理从零推导前端知识体系</a></li>
</ul>
<h3 id="三devops云与基础设施">三、DevOps、云与基础设施</h3>
<h4 id="a-kubernetesdocker与容器化">A. Kubernetes、Docker与容器化</h4>
<ul>
<li><a href="https://mp.weixin.qq.com/s/nMSIsS72fSXGqJO9Vy_Pfw" target="_blank" rel="noopener nofollow noreferrer" >滴滴弹性云基于 K8S 的调度实践</a></li>
<li><a href="https://medium.com/@devlink/the-end-of-docker-the-reasons-behind-developers-changing-their-runtimes-4b7697846f6f" target="_blank" rel="noopener nofollow noreferrer" >Docker 的终结？开发者改变运行时的原因</a></li>
<li><a href="https://medium.com/@kanishksinghpujari/kubernetes-without-docker-is-the-future-are-you-ready-f63a9a7746de" target="_blank" rel="noopener nofollow noreferrer" >没有 Docker 的 Kubernetes 是未来 — 你准备好了吗？</a></li>
<li><a href="https://blog.zeptonow.com/everything-was-fine-until-kubernetes-said-no-more-cpu-3768cda76326" target="_blank" rel="noopener nofollow noreferrer" >一切都很好，直到 Kubernetes 说‘没有更多的CPU了’</a></li>
<li><a href="https://blog.devgenius.io/5-secret-kubernetes-tricks-you-didnt-know-6d930dfdfffa" target="_blank" rel="noopener nofollow noreferrer" >5个你不知道的秘密Kubernetes技巧</a></li>
<li><a href="https://medium.com/@alexandru.lazarev/cpu-limits-in-kubernetes-why-your-pod-is-idle-but-still-throttled-a-deep-dive-into-what-really-136c0cdd62ff" target="_blank" rel="noopener nofollow noreferrer" >Kubernetes中的CPU限制：为什么你的Pod空闲但仍被节流</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1914388" target="_blank" rel="noopener nofollow noreferrer" >K8S 部署nfs服务器-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://www.cnblogs.com/joexu01/p/16836482.html" target="_blank" rel="noopener nofollow noreferrer" >【Kubernetes】K8s笔记（十四）：PersistentVolume 使用网络共享存储（NFS） - 博客园</a></li>
<li><a href="https://github.com/usualheart/install_k8s_official/blob/master/nfs-k8s/%E5%9C%A8k8s%E9%9B%86%E7%BE%A4%E4%BD%BF%E7%94%A8nfs%E5%8D%B7.md" target="_blank" rel="noopener nofollow noreferrer" >在k8s集群使用nfs卷.md</a></li>
<li><a href="https://github.com/madduci/kind-with-mesh" target="_blank" rel="noopener nofollow noreferrer" >使用 Terraform 构建的带有 Istio 的本地 kubernetes 集群</a></li>
<li><a href="https://icloudnative.io/posts/what-happens-when-k8s/" target="_blank" rel="noopener nofollow noreferrer" >kubectl 创建 Pod 背后到底发生了什么？ · 云原生实验室</a></li>
<li><a href="https://www.cloudnative101.net/posts/kubernetes-rbac-oidc-security-guide/" target="_blank" rel="noopener nofollow noreferrer" >Kubernetes RBAC 101: 如何通过 OIDC 强化集群安全</a></li>
<li><a href="https://www.cnblogs.com/larrydpk/p/15116902.html" target="_blank" rel="noopener nofollow noreferrer" >服务网格Istio入门-详细记录Kubernetes安装Istio并使用 - 博客园</a></li>
<li><a href="https://blog.csdn.net/heian_99/article/details/145473811" target="_blank" rel="noopener nofollow noreferrer" >Kubernetes Pod扩容预热陷阱：如何避免5xx错误和CPU飙升？-CSDN博客</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8735/" target="_blank" rel="noopener nofollow noreferrer" >Kubernetes 核心资源对象详解 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9615/" target="_blank" rel="noopener nofollow noreferrer" >OpenAI服务中断引发的思考：如何保障大规模K8s集群的稳定性？ - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8797/" target="_blank" rel="noopener nofollow noreferrer" >Docker 镜像与仓库管理实战指南 - 开发技术 - 冷月清谈</a></li>
</ul>
<h4 id="b-云原生与服务网格-istio">B. 云原生与服务网格 (Istio)</h4>
<ul>
<li><a href="https://istio.io/latest/zh/docs/concepts/traffic-management/" target="_blank" rel="noopener nofollow noreferrer" >Istio / 流量管理</a></li>
<li><a href="https://kiali.io/docs/tutorials/" target="_blank" rel="noopener nofollow noreferrer" >教程 | Kiali</a></li>
<li><a href="https://istio.io/latest/zh/docs/ops/deployment/architecture/" target="_blank" rel="noopener nofollow noreferrer" >Istio / 架构</a></li>
<li><a href="https://www.thebyte.com.cn/MicroService/Envoy.html#%E9%85%8D%E7%BD%AE%E9%9B%86%E7%BE%A4" target="_blank" rel="noopener nofollow noreferrer" >Envoy | 深入架构原理与实践</a></li>
<li><a href="https://istio.io/latest/zh/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports" target="_blank" rel="noopener nofollow noreferrer" >Istio / Ingress 网关</a></li>
<li><a href="https://www.thebyte.com.cn/architecture/target.html" target="_blank" rel="noopener nofollow noreferrer" >1.4 云原生的目标 | 深入高可用系统原理与设计</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9086/" target="_blank" rel="noopener nofollow noreferrer" >云原生技术与应用入门指南 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8713/" target="_blank" rel="noopener nofollow noreferrer" >云原生入门指南：核心概念及应用 - 开发技术 - 冷月清谈</a></li>
</ul>
<h4 id="c-数据库与存储">C. 数据库与存储</h4>
<ul>
<li><a href="https://www.interdb.jp/pg/index.html?s=09" target="_blank" rel="noopener nofollow noreferrer" >PostgreSQL Internals</a></li>
<li><a href="https://dirtysalt.github.io/html/42-things-I-learned-from-building-a-production-database.html" target="_blank" rel="noopener nofollow noreferrer" >我从构建生产数据库中学到的42件事</a></li>
<li><a href="https://www.codedump.info/post/20210701-memcached/" target="_blank" rel="noopener nofollow noreferrer" >Memcached的存储原理解析 | codedump notes</a></li>
<li><a href="https://github.com/VictoriaMetrics/VictoriaMetrics" target="_blank" rel="noopener nofollow noreferrer" >VictoriaMetrics: 快速、经济高效的监控解决方案和时间序列数据库</a></li>
<li><a href="https://mp.weixin.qq.com/s/ykrasJ2UeKZAMtHCmtG93Q" target="_blank" rel="noopener nofollow noreferrer" >OpenAI：将PostgreSQL伸缩至新阶段</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1831763" target="_blank" rel="noopener nofollow noreferrer" >万字长文：大规模 Elasticsearch 高可用集群环境调优实践-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://www.kuajingnet.com/pages/es/2b2926/" target="_blank" rel="noopener nofollow noreferrer" >4 Elasticsearch高可用分布式集群 | 跨境互联网</a></li>
<li><a href="https://www.modb.pro/db/115009" target="_blank" rel="noopener nofollow noreferrer" >ElasticSearch 集群高可用存储架构 - 墨天轮</a></li>
<li><a href="https://lvqiushi.github.io/2021/03/29/elastic%20search/" target="_blank" rel="noopener nofollow noreferrer" >Elastic Search 存储方式 | Lv&rsquo;s Blogs</a></li>
<li><a href="https://www.cnblogs.com/misakivv/p/18436873#tid-ixWChf" target="_blank" rel="noopener nofollow noreferrer" >下次再见啦 - k8s 分布式存储平台 &ndash; Longhorn</a></li>
<li><a href="https://hezhiqiang.gitbook.io/elkstack/elasticsearch/other/rrd" target="_blank" rel="noopener nofollow noreferrer" >时序数据库 | ELKstack 中文指南</a></li>
<li><a href="https://segmentfault.com/a/1190000002690600" target="_blank" rel="noopener nofollow noreferrer" >大数据 - 时间序列数据库的选择条件 - SegmentFault 思否</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9246/" target="_blank" rel="noopener nofollow noreferrer" >从自建到云端：数据库迁移实践指南 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8722/" target="_blank" rel="noopener nofollow noreferrer" >Redis 原理深度解析：从单机到集群，探索高性能缓存之道 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8721/" target="_blank" rel="noopener nofollow noreferrer" >Elasticsearch分布式搜索引擎解析 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9674/" target="_blank" rel="noopener nofollow noreferrer" >数据库查询优化实战：从71.7万/秒到1.4万/秒 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://github.com/Sahilb315/AtomixDB" target="_blank" rel="noopener nofollow noreferrer" >AtomixDB：一个用 Go 编写的持久化关系型数据库</a></li>
<li><a href="https://www.cnblogs.com/dnboy/p/18676548" target="_blank" rel="noopener nofollow noreferrer" >分布式系统架构7：本地缓存 - 卷福同学 - 博客园</a></li>
</ul>
<h4 id="d-监控可观测性与稳定性">D. 监控、可观测性与稳定性</h4>
<ul>
<li><a href="https://mp.weixin.qq.com/s/9rAhbG6lu-flNIGQEF5w0g" target="_blank" rel="noopener nofollow noreferrer" >稳定性，难的不是技术，而是</a></li>
<li><a href="https://mp.weixin.qq.com/s/KFZCQFP1oB5YOrT3tHBRCQ" target="_blank" rel="noopener nofollow noreferrer" >从滴滴的故障我们能学到什么</a></li>
<li><a href="https://www.xinfinite.net/t/topic/9519/" target="_blank" rel="noopener nofollow noreferrer" >1分钟定位应用“错慢”根因：链路诊断最佳实践 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8780/" target="_blank" rel="noopener nofollow noreferrer" >Prometheus 监控实战：架构解析与告警配置 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8692/" target="_blank" rel="noopener nofollow noreferrer" >Skywalking全链路跟踪：从架构到实践 - 开发技术 - 冷月清谈</a></li>
<li><a href="https://www.xinfinite.net/t/topic/8674/" target="_blank" rel="noopener nofollow noreferrer" >分布式系统监控应急的1-5-10实践 - 开发技术 - 冷月清谈</a></li>
</ul>
<h4 id="e-网络与协议">E. 网络与协议</h4>
<ul>
<li><a href="https://www.kawabangga.com/posts/5301" target="_blank" rel="noopener nofollow noreferrer" >四层负载均衡漫谈 | 卡瓦邦噶！</a></li>
<li><a href="https://blog.postman.com/api-protocols-in-2023/" target="_blank" rel="noopener nofollow noreferrer" >2023年API协议的演变格局 | Postman博客</a></li>
<li><a href="https://bytebytego.com/guides/api-web-development/" target="_blank" rel="noopener nofollow noreferrer" >ByteByteGo | API与Web开发</a></li>
<li><a href="https://bytebytego.com/guides/the-evolving-landscape-of-api-protocols-in-2023/" target="_blank" rel="noopener nofollow noreferrer" >ByteByteGo | 2023年API协议的演变格局</a></li>
<li><a href="https://juejin.cn/post/6844904133430870029" target="_blank" rel="noopener nofollow noreferrer" >VXLAN 基础教程：在 Linux 上配置 VXLAN 网络</a></li>
<li><a href="https://telegra.ph/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82NAT%E4%B8%80%E6%96%87%E5%90%83%E9%80%8F%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2%E7%9A%84%E6%89%80%E6%9C%89%E7%A7%98%E5%AF%86-02-09" target="_blank" rel="noopener nofollow noreferrer" >从零开始彻底搞懂NAT！一文吃透网络地址转换的所有秘密</a></li>
<li><a href="https://ntk148v.github.io/posts/linux-network-performance-ultimate-guide/#linux-network-packet-reception" target="_blank" rel="noopener nofollow noreferrer" >Linux网络性能终极指南</a></li>
<li><a href="https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&amp;mid=2247545415&amp;idx=1&amp;sn=bf661ba9be517a431858c8952aae6ef8&amp;chksm=e92a0f48de5d865e84f88c47b88e972d9fe22d59ec4fba30650c9904ad2a0cb79cf55d923670&amp;scene=58&amp;subscene=0#rd" target="_blank" rel="noopener nofollow noreferrer" >从铜线到云端：网络技术的跨越与未来趋势</a></li>
</ul>
<h4 id="f-虚拟化与linux内核">F. 虚拟化与Linux内核</h4>
<ul>
<li><a href="https://biriukov.dev/docs/page-cache/" target="_blank" rel="noopener nofollow noreferrer" >Linux页面缓存系列 | Viacheslav Biriukov</a></li>
<li><a href="https://www.ctfiot.com/46220.html" target="_blank" rel="noopener nofollow noreferrer" >QEMU虚拟化安全的攻击面探索与思考 | CTF导航</a></li>
<li><a href="https://lzcloudsecurity.gitbook.io/yun-an-quan-gong-fang-ru-men/di-wu-zhang-si-you-yun-yu-xu-ni-hua-gong-fang/kvm" target="_blank" rel="noopener nofollow noreferrer" >KVM | 云安全攻防入门</a></li>
<li><a href="https://blog.csdn.net/qq_21398167/article/details/48291065" target="_blank" rel="noopener nofollow noreferrer" >&lt;转&gt;virsh使用qemu+tcp访问远程libvirtd</a></li>
<li><a href="https://opengers.github.io/virtualization/kvm-libvirt-qemu-1/" target="_blank" rel="noopener nofollow noreferrer" >kvm libvirt qemu实践系列(一)-kvm介绍 | opengers</a></li>
<li><a href="https://segmentfault.com/a/1190000044794136" target="_blank" rel="noopener nofollow noreferrer" >linux - KVM虚拟化系统解决方案（三）&ndash;使用libvirt创建和管理虚拟机 - SegmentFault 思否</a></li>
<li><a href="https://github.com/0voice/kernel_awsome_feature/blob/main/KVM%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7libvirt.md" target="_blank" rel="noopener nofollow noreferrer" >KVM管理工具libvirt.md</a></li>
<li><a href="https://hackmd.io/@RinHizakura/r1uSVAWwp" target="_blank" rel="noopener nofollow noreferrer" >Linux 核心設計: Scheduler(7): sched_ext - HackMD</a></li>
<li><a href="https://operating-system-in-1000-lines.vercel.app/zh/" target="_blank" rel="noopener nofollow noreferrer" >简介 | 千行代码实现操作系统</a></li>
</ul>
<h3 id="四算法与数据结构">四、算法与数据结构</h3>
<ul>
<li><a href="https://leetcode.cn/discuss/post/3581838/fen-xiang-gun-ti-dan-dong-tai-gui-hua-ru-007o/" target="_blank" rel="noopener nofollow noreferrer" >分享丨【算法题单】动态规划- 力扣（LeetCode）</a></li>
<li><a href="https://oi-wiki.org/ds/monotonous-stack/" target="_blank" rel="noopener nofollow noreferrer" >单调栈 - OI Wiki</a></li>
<li><a href="https://leetcode.cn/problem-list/v4lGiYux/" target="_blank" rel="noopener nofollow noreferrer" >🔥 LeetCode 热题 HOT 100</a></li>
<li><a href="https://liweiwei1419.github.io/leetcode-solution-blog/leetcode-problemset/topological-sort/0207-course-schedule.html#%E6%96%B9%E6%B3%95%E4%B8%80%EF%BC%9A%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F%EF%BC%88kahn-%E7%AE%97%E6%B3%95%EF%BC%89" target="_blank" rel="noopener nofollow noreferrer" >LeetCode 第 207 题：“课程表”题解 | LeetCode 题解</a></li>
<li><a href="https://www.cnblogs.com/PaperHammer/p/16187703.html" target="_blank" rel="noopener nofollow noreferrer" >有关动态规划 - PaperHammer - 博客园</a></li>
<li><a href="https://programmercarl.com/%E8%83%8C%E5%8C%85%E6%80%BB%E7%BB%93%E7%AF%87.html#%E8%83%8C%E5%8C%85%E9%80%92%E6%8E%A8%E5%85%AC%E5%BC%8F" target="_blank" rel="noopener nofollow noreferrer" >代码随想录 - 背包问题</a></li>
<li><a href="https://labuladong.online/algo/practice-in-action/lowest-common-ancestor-summary/" target="_blank" rel="noopener nofollow noreferrer" >拓展：最近公共祖先系列解题框架 | labuladong 的算法笔记</a></li>
<li><a href="https://labuladong.online/algo/essential-technique/binary-tree-summary/#%E5%90%8E%E5%BA%8F%E4%BD%8D%E7%BD%AE%E7%9A%84%E7%89%B9%E6%AE%8A%E4%B9%8B%E5%A4%84" target="_blank" rel="noopener nofollow noreferrer" >二叉树系列算法核心纲领 | labuladong 的算法笔记</a></li>
<li><a href="https://labuladong.online/algo/intro/quick-learning-plan/#%E4%BA%8C%E5%8F%89%E6%A0%91-%E9%80%92%E5%BD%92%E6%80%9D%E6%83%B3" target="_blank" rel="noopener nofollow noreferrer" >速成读者学习规划 | labuladong 的算法笔记</a></li>
<li><a href="https://labuladong.online/algo/data-structure/binary-tree-part1/" target="_blank" rel="noopener nofollow noreferrer" >二叉树心法（思路篇） | labuladong 的算法笔记</a></li>
<li><a href="https://labuladong.online/algo/data-structure/binary-tree-part2/#%E9%80%9A%E8%BF%87%E5%90%8E%E5%BA%8F%E5%92%8C%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E7%BB%93%E6%9E%9C%E6%9E%84%E9%80%A0%E4%BA%8C%E5%8F%89%E6%A0%91" target="_blank" rel="noopener nofollow noreferrer" >二叉树心法（构造篇） | labuladong 的算法笔记</a></li>
</ul>
<h3 id="五人工智能与机器学习">五、人工智能与机器学习</h3>
<h4 id="a-大语言模型与生成式ai-gemini等">A. 大语言模型与生成式AI (Gemini等)</h4>
<ul>
<li><a href="https://mp.weixin.qq.com/s/_ZvyxRpgIA4L4pqfcQtPTQ" target="_blank" rel="noopener nofollow noreferrer" >陆奇最新演讲实录：我的大模型世界观</a></li>
<li><a href="https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf" target="_blank" rel="noopener nofollow noreferrer" >构建智能代理的实用指南</a></li>
<li><a href="https://sspai.com/post/94360" target="_blank" rel="noopener nofollow noreferrer" >Claude 的 MCP (模型上下文协议）有啥用？ - 少数派</a></li>
<li><a href="https://onevcat.com/2025/02/mcp/" target="_blank" rel="noopener nofollow noreferrer" >MCP 是什么，现状和未来 | OneV&rsquo;s Den</a></li>
</ul>
<h4 id="b-ai工具与应用">B. AI工具与应用</h4>
<ul>
<li><a href="https://xxchan.me/ai/2025/06/08/ai-coding.html" target="_blank" rel="noopener nofollow noreferrer" >我对各种 AI Coding Agent 工具的看法 - XX&rsquo;s Blog</a></li>
<li><a href="https://yachen.com/2025/06/06/2025-ai/" target="_blank" rel="noopener nofollow noreferrer" >2025 年的 AI 协助编程观察 – Yachen&rsquo;s Blog</a></li>
<li><a href="https://terriblesoftware.org/2025/04/23/the-hidden-cost-of-ai-coding/" target="_blank" rel="noopener nofollow noreferrer" >AI 编码的隐藏成本 – Terrible Software</a></li>
<li><a href="https://github.com/cyfyifanchen/one-person-company?tab=readme-ov-file#-%E7%BD%91%E7%AB%99%E7%B3%BB%E5%88%97---%E4%B8%80%E9%94%AE%E7%94%9F%E6%88%90%E7%BD%91%E7%AB%99" target="_blank" rel="noopener nofollow noreferrer" >一人公司 AI 工具系列</a></li>
<li><a href="https://www.xda-developers.com/notebooklm-tips-use-to-supercharge-productivity/" target="_blank" rel="noopener nofollow noreferrer" >我用来大幅提高生产力的5个NotebookLM技巧</a></li>
<li><a href="https://github.com/wangwei1237/LLM_in_Action" target="_blank" rel="noopener nofollow noreferrer" >大语言模型实战</a></li>
<li><a href="https://m.okjike.com/originalPosts/67db6c218a65f4381a051a0c" target="_blank" rel="noopener nofollow noreferrer" >𝕏 上有个大佬分享了 AI-Coding 的 9 个大坑 - 即刻App</a></li>
</ul>
<h4 id="c-概念与理论">C. 概念与理论</h4>
<ul>
<li><a href="https://jalammar.github.io/illustrated-gpt2/" target="_blank" rel="noopener nofollow noreferrer" >图解GPT-2 (可视化Transformer语言模型)</a></li>
<li><a href="https://jalammar.github.io/illustrated-word2vec/" target="_blank" rel="noopener nofollow noreferrer" >图解Word2vec</a></li>
</ul>
<h3 id="六安全">六、安全</h3>
<ul>
<li><a href="https://github.com/Safe3/openresty-manager" target="_blank" rel="noopener nofollow noreferrer" >Safe3/openresty-manager: 最简单、强大、美观的主机管理面板</a></li>
<li><a href="https://github.com/Safe3/uuWAF/tree/main" target="_blank" rel="noopener nofollow noreferrer" >Safe3/uuWAF: 行业领先的免费、高性能、AI和语义技术的Web应用防火墙</a></li>
<li><a href="http://www.luckysec.cn/posts/d4130481.html" target="_blank" rel="noopener nofollow noreferrer" >域环境密码凭证获取 | LuckySec</a></li>
<li><a href="https://www.bordergate.co.uk/dacl-attacks/" target="_blank" rel="noopener nofollow noreferrer" >Active Directory DACL 攻击 &lt; BorderGate</a></li>
<li><a href="https://medium.com/@Inching-Towards-Intelligence/htb-sauna-21-100-b31eb39d1309" target="_blank" rel="noopener nofollow noreferrer" >HTB — Sauna | 21/100</a></li>
<li><a href="https://www.anquanke.com/subject.html?id=193604" target="_blank" rel="noopener nofollow noreferrer" >安全KER - 安全资讯平台</a></li>
<li><a href="https://threezh1.com/2021/02/26/%E4%BA%91%E5%8E%9F%E7%94%9F%E5%AE%89%E5%85%A8Cheat_Sheet/" target="_blank" rel="noopener nofollow noreferrer" >云原生安全速查表 | Threezh1&rsquo;Blog</a></li>
<li><a href="https://socket.dev/blog/obfuscation-101-the-tricks-behind-malicious-code" target="_blank" rel="noopener nofollow noreferrer" >代码混淆101：揭开恶意代码背后的技巧</a></li>
<li><a href="https://docs.redhat.com/zh-cn/documentation/red_hat_enterprise_linux/7/html-single/virtualization_security_guide/index#sVirt-Labels-NFS" target="_blank" rel="noopener nofollow noreferrer" >虚拟化安全指南 | Red Hat 产品文档</a></li>
</ul>
<h3 id="七职业生产力与个人成长">七、职业、生产力与个人成长</h3>
<h4 id="a-职业建议与面试">A. 职业建议与面试</h4>
<ul>
<li><a href="https://github.com/0voice/Campus_recruitment_interview_questions" target="_blank" rel="noopener nofollow noreferrer" >2025 最新校招面试题合集</a></li>
<li><a href="https://x.com/CicidaMay/status/1957806596529979754?t=1xpegQ4Ony8dXU7glnKQAQ&amp;s=09" target="_blank" rel="noopener nofollow noreferrer" >Carolyn 在 X 上的分享：我的面试学习方式&hellip;</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/498762187" target="_blank" rel="noopener nofollow noreferrer" >软件技术人员的瓶颈，为35岁之后做准备 - 知乎</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/414009313" target="_blank" rel="noopener nofollow noreferrer" >用随机梯度下降来优化人生 - 知乎</a></li>
<li><a href="https://x.com/chrishlad/status/1502650707274608644" target="_blank" rel="noopener nofollow noreferrer" >Chris Hladczuk 在 X 上的提问：“你最好的职业建议是什么？”</a></li>
<li><a href="https://holyisland.blog/many-interview-in-2018/" target="_blank" rel="noopener nofollow noreferrer" >五家大型矽谷公司的軟體工程師面試經驗 ：Google Facebook LinkedIn AirBnb Quora</a></li>
<li><a href="https://medium.com/better-programming/the-software-engineering-study-guide-bac25b8b61eb" target="_blank" rel="noopener nofollow noreferrer" >142个资源助你精通编程面试</a></li>
<li><a href="https://readmedium.com/en/https:/medium.com/better-programming/the-software-engineering-study-guide-bac25b8b61eb" target="_blank" rel="noopener nofollow noreferrer" >142个资源助你精通编程面试 (备用链接)</a></li>
<li><a href="https://betterprogramming.pub/" target="_blank" rel="noopener nofollow noreferrer" >Better Programming 博客</a></li>
<li><a href="https://freedium.cfd/https://medium.com/better-programming/the-dos-and-donts-for-passing-your-technical-interview-1f2503c10733" target="_blank" rel="noopener nofollow noreferrer" >技术面试通过的注意事项 | Freedium</a></li>
<li><a href="https://gist.github.com/rondy/af1dee1d28c02e9a225ae55da2674a6f" target="_blank" rel="noopener nofollow noreferrer" >高效工程师.md</a></li>
<li><a href="https://justyy.com/archives/65890" target="_blank" rel="noopener nofollow noreferrer" >45 分钟模拟面试(编程、系统设计)+职业发展建议 | 小赖子的英国生活和资讯</a></li>
<li><a href="https://softwaredoug.com/blog/2024/09/07/your-team-needs-juniors" target="_blank" rel="noopener nofollow noreferrer" >你的公司需要初级开发者</a></li>
</ul>
<h4 id="b-生产力与知识管理">B. 生产力与知识管理</h4>
<ul>
<li><a href="https://linyun.craft.me/6nOmzcJzxGYW3l" target="_blank" rel="noopener nofollow noreferrer" >林云的马六甲XMA游记</a></li>
<li><a href="https://docs.divio.com/documentation-system/" target="_blank" rel="noopener nofollow noreferrer" >关于 | Divio 文档</a></li>
<li><a href="https://github.com/lyz-code/best-of-digital-gardens?tab=readme-ov-file" target="_blank" rel="noopener nofollow noreferrer" >精选数字花园/第二大脑的排名列表</a></li>
<li><a href="https://notes.yxy.ninja/" target="_blank" rel="noopener nofollow noreferrer" >计算机科学的第二大脑 | 工程洞见</a></li>
<li><a href="https://www.swyx.io/ideas" target="_blank" rel="noopener nofollow noreferrer" >Swyx 创意展示</a></li>
<li><a href="https://antfu.me/posts/about-yak-shaving-zh" target="_blank" rel="noopener nofollow noreferrer" >关于 Yak Shaving (剃牦牛毛)</a></li>
<li><a href="https://www.usmacd.com/cn/write_blog_afraid/" target="_blank" rel="noopener nofollow noreferrer" >如何破除写博客的焦虑 - 安全代码</a></li>
<li><a href="https://www.usmacd.com/cn/PKM_faults/" target="_blank" rel="noopener nofollow noreferrer" >知识管理的效率陷阱 - 安全代码</a></li>
</ul>
<h4 id="c-博客思想领袖与阅读清单">C. 博客、思想领袖与阅读清单</h4>
<ul>
<li><a href="https://github.com/qianguyihao/blog-list" target="_blank" rel="noopener nofollow noreferrer" >中文博客琅琊榜，只收录精品独立博客</a></li>
<li><a href="https://github.com/ljinkai/weekly?tab=readme-ov-file" target="_blank" rel="noopener nofollow noreferrer" >独立开发产品变现周刊，每周五发布</a></li>
<li><a href="https://skywind.me/blog/" target="_blank" rel="noopener nofollow noreferrer" >Skywind Inside - 写自己的代码，让别人猜去吧</a></li>
<li><a href="https://www.edony.ink/private/weekly-collections-16/" target="_blank" rel="noopener nofollow noreferrer" >每周收藏 16</a></li>
<li><a href="https://vale.rocks/posts" target="_blank" rel="noopener nofollow noreferrer" >帖子 | Vale.Rocks</a></li>
<li><a href="https://til.simonwillison.net/" target="_blank" rel="noopener nofollow noreferrer" >Simon Willison: TIL (今天我学到了)</a></li>
<li><a href="https://www.piglei.com/articles/programmer-reading-list-1/" target="_blank" rel="noopener nofollow noreferrer" >程序员阅读清单：我喜欢的 100 篇技术文章（1-20） | Piglei</a></li>
<li><a href="https://rkoutnik.com/2016/04/21/implementers-solvers-and-finders.html" target="_blank" rel="noopener nofollow noreferrer" >实现者、解决者和发现者</a></li>
<li><a href="https://ruanyf.aolifu.org/" target="_blank" rel="noopener nofollow noreferrer" >那些投递过阮一峰的作品</a></li>
<li><a href="https://javayhu.com/2018-nian-yi-ge-xiao-fu-ye-de-kai-fa-xin-de/" target="_blank" rel="noopener nofollow noreferrer" >一个副业应用的开发心得</a></li>
<li><a href="https://www.bestblogs.dev/newsletter" target="_blank" rel="noopener nofollow noreferrer" >每周精选文章 - BestBlogs.dev</a></li>
<li><a href="https://www.bestblogs.dev/sources" target="_blank" rel="noopener nofollow noreferrer" >订阅源 - BestBlogs.dev</a></li>
<li><a href="https://engineeringblogs.xyz/" target="_blank" rel="noopener nofollow noreferrer" >工程博客聚合</a></li>
</ul>
<h3 id="八工具项目与资源">八、工具、项目与资源</h3>
<h4 id="a-开源项目与工具">A. 开源项目与工具</h4>
<ul>
<li><a href="https://github.com/rutikwankhade/CoverView" target="_blank" rel="noopener nofollow noreferrer" >快速创建令人惊叹的博客封面图片</a></li>
<li><a href="https://coverview.vercel.app/editor" target="_blank" rel="noopener nofollow noreferrer" >Coverview - 为你的博客文章创建封面图片现在超级简单</a></li>
<li><a href="https://github.com/nicekate/qwen-tts" target="_blank" rel="noopener nofollow noreferrer" >nicekate/qwen-tts 文本转语音项目</a></li>
<li><a href="https://github.com/apache/fluss" target="_blank" rel="noopener nofollow noreferrer" >Apache Fluss: 为实时分析构建的流式存储</a></li>
<li><a href="https://github.com/maciekgajewski/LowLatencyProgrammingPresentation" target="_blank" rel="noopener nofollow noreferrer" >低延迟编程研讨会演示文稿</a></li>
<li><a href="https://github.com/facontidavide/cloudini" target="_blank" rel="noopener nofollow noreferrer" >点云压缩库</a></li>
<li><a href="https://github.com/antvis/mcp-server-chart" target="_blank" rel="noopener nofollow noreferrer" >一个使用 @antvis 生成可视化图表的模型上下文协议服务器</a></li>
<li><a href="https://excalidraw.com/" target="_blank" rel="noopener nofollow noreferrer" >Excalidraw 在线画图工具</a></li>
<li><a href="https://github.com/ShiFangJuMie/Telegram_Messages_Helper" target="_blank" rel="noopener nofollow noreferrer" >使用 LLM 平台总结和快速阅读 Telegram 消息</a></li>
<li><a href="https://tikzjax.com/" target="_blank" rel="noopener nofollow noreferrer" >TikZJax - 在网页上渲染LaTeX/TikZ图形</a></li>
<li><a href="https://github.com/hyperlight-dev/hyperlight-wasm" target="_blank" rel="noopener nofollow noreferrer" >一个使Wasm模块在轻量级虚拟机沙箱中运行的Rust库</a></li>
<li><a href="https://github.com/softmatcha/softmatcha" target="_blank" rel="noopener nofollow noreferrer" >一个用于十亿级语料库的软、快速模式匹配器</a></li>
<li><a href="https://softmatcha.github.io/" target="_blank" rel="noopener nofollow noreferrer" >SoftMatcha</a></li>
<li><a href="https://github.com/beilunyang/moepush" target="_blank" rel="noopener nofollow noreferrer" >一个基于 NextJS + Cloudflare 技术栈构建的可爱消息推送服务</a></li>
</ul>
<h4 id="b-学习平台与社区">B. 学习平台与社区</h4>
<ul>
<li><a href="https://www.linkedin.cn/" target="_blank" rel="noopener nofollow noreferrer" >领英企业服务</a></li>
<li><a href="https://linux.do/" target="_blank" rel="noopener nofollow noreferrer" >LINUX DO - 新的理想型社区</a></li>
<li><a href="https://randsinrepose.com/welcome-to-rands-leadership-slack/" target="_blank" rel="noopener nofollow noreferrer" >欢迎来到 Rands 领导力 Slack</a></li>
<li><a href="https://www.goodreads.com/" target="_blank" rel="noopener nofollow noreferrer" >Goodreads | 遇见你的下一本最爱书</a></li>
</ul>
<h4 id="c-新闻聚合器与社交媒体">C. 新闻、聚合器与社交媒体</h4>
<ul>
<li><a href="https://x.com/ZHO_ZHO_ZHO/status/1958539464994959715?t=h7x3PNmRN8uhhE9mtWLJBA&amp;s=09" target="_blank" rel="noopener nofollow noreferrer" >X 链接</a></li>
<li><a href="https://x.com/disksing/status/1555444153588543488?t=qYBjGXvR3v9o0yOY_lxYeQ&amp;s=09" target="_blank" rel="noopener nofollow noreferrer" >象牙山刘能 在 X 上的讨论</a></li>
<li><a href="https://me.deeptoai.com/bookmarks/twitter" target="_blank" rel="noopener nofollow noreferrer" >Twitter | 书签 — 熊布朗（Peng.G）</a></li>
<li><a href="https://x.com/feltanimalworld/status/1932809547409711462" target="_blank" rel="noopener nofollow noreferrer" >Susan STEM 在 X 上的讨论</a></li>
<li><a href="https://x.com/kiwiflysky/status/1928615865148170246?t=2zgR17qoPdPIPuMzLtKdYA&amp;s=09" target="_blank" rel="noopener nofollow noreferrer" >KIWI 在 X 上的分享</a></li>
<li><a href="https://x.com/PandaTalk8/status/1932373997796401322" target="_blank" rel="noopener nofollow noreferrer" >Mr Panda 在 X 上的讨论</a></li>
<li><a href="https://topsub.cc/" target="_blank" rel="noopener nofollow noreferrer" >Reddit 热门10篇文章</a></li>
<li><a href="https://www.reddit.com/r/SaaSMarketing/comments/1l4426n/get_perplexity_ai_pro_for_12_months_90_off_flash/" target="_blank" rel="noopener nofollow noreferrer" >获取 Perplexity AI PRO 12个月 – 90%折扣 [限时抢购]</a></li>
<li><a href="https://www.reddit.com/r/networking/" target="_blank" rel="noopener nofollow noreferrer" >企业网络设计、支持和讨论</a></li>
<li><a href="https://www.reddit.com/r/devops/" target="_blank" rel="noopener nofollow noreferrer" >《一切 DevOps》</a></li>
<li><a href="https://www.reddit.com/r/SideProject/" target="_blank" rel="noopener nofollow noreferrer" >r/SideProject - 分享副项目的社区</a></li>
<li><a href="https://news.ycombinator.com/show" target="_blank" rel="noopener nofollow noreferrer" >显示 | Hacker News</a></li>
<li><a href="https://app.daily.dev/?ua=true" target="_blank" rel="noopener nofollow noreferrer" >daily.dev | 开发者共同成长之地</a></li>
</ul>
<h3 id="九杂项文章与博客">九、杂项文章与博客</h3>
<h4 id="a-技术深度剖析与故事">A. 技术深度剖析与故事</h4>
<ul>
<li><a href="https://colin-scott.github.io/personal_website/research/interactive_latency.html" target="_blank" rel="noopener nofollow noreferrer" >每个程序员都应该知道的数字（按年份）</a></li>
<li><a href="https://www.codedump.info/post/20220703-weekly-21/" target="_blank" rel="noopener nofollow noreferrer" >周刊（第21期）：Lamport时钟介绍 | codedump notes</a></li>
<li><a href="https://mp.weixin.qq.com/s/Wv8VWEq7GFz5hJQ_iOtqsw" target="_blank" rel="noopener nofollow noreferrer" >周刊（第22期）：图解一致性模型</a></li>
<li><a href="https://mp.weixin.qq.com/s/Yb6OcCoM_Hhc4U8ESTYVbg" target="_blank" rel="noopener nofollow noreferrer" >周刊（第23期）：图解Blink-Tree：B+Tree的一种并发优化结构和算法</a></li>
<li><a href="https://mp.weixin.qq.com/s/9Y1EfzM5cups9oklByAW5Q" target="_blank" rel="noopener nofollow noreferrer" >周刊（第24期）：sqlite并发读写的演进之路</a></li>
<li><a href="https://docs.google.com/presentation/d/14KkpQamsTSxhvliYUUXOGAQ_C61v0BHsefZ344HUGB8/mobilepresent?slide=id.p" target="_blank" rel="noopener nofollow noreferrer" >给孩子的睡前故事：存储引擎</a></li>
<li><a href="https://www.codedump.info/post/20210803-choice-dimension/" target="_blank" rel="noopener nofollow noreferrer" >选择的维度 | codedump notes</a></li>
<li><a href="https://dustri.org/b/my-experience-with-canonicals-interview-process.html" target="_blank" rel="noopener nofollow noreferrer" >我与Canonical面试过程的经历</a></li>
<li><a href="https://drmingdrmer.github.io/tech/bla/2018/09/27/toaster.html" target="_blank" rel="noopener nofollow noreferrer" >软件工程是个面包机</a></li>
<li><a href="https://trashmoon.com/blog/2022/reflections-on-12-years-at-mapbox/" target="_blank" rel="noopener nofollow noreferrer" >离开Mapbox 12年后的反思 | trash moon</a></li>
<li><a href="https://antirez.com/news/144" target="_blank" rel="noopener nofollow noreferrer" >从我离开的地方</a></li>
<li><a href="https://cacm.acm.org/research/10-things-software-developers-should-learn-about-learning/" target="_blank" rel="noopener nofollow noreferrer" >软件开发者应该学习的10件事</a></li>
<li><a href="https://tante.cc/2025/03/03/who-is-free-software-for/" target="_blank" rel="noopener nofollow noreferrer" >自由软件为谁而生？</a></li>
</ul>
<h4 id="b-商业与副业项目">B. 商业与副业项目</h4>
<ul>
<li><a href="https://www.ahhhhfs.com/72138/" target="_blank" rel="noopener nofollow noreferrer" >2025全新影视解说流量变现课 - A姐分享</a></li>
<li><a href="https://www.ahhhhfs.com/72154/" target="_blank" rel="noopener nofollow noreferrer" >锦时 · 产品工具箱：20+实用工具，助力产品经理高效工作 - A姐分享</a></li>
<li><a href="https://sideproject.guide/idea" target="_blank" rel="noopener nofollow noreferrer" >关于你的点子</a></li>
<li><a href="https://nav.al/rich" target="_blank" rel="noopener nofollow noreferrer" >如何致富</a></li>
<li><a href="http://xiaolai.co/books/eba39b60cf1c0a10a097db8570d55b54/ch01.html" target="_blank" rel="noopener nofollow noreferrer" >《我也有话要说 - 普通人的讲演技能》 · 千万别装</a></li>
<li><a href="http://xiaolai.co/search" target="_blank" rel="noopener nofollow noreferrer" >笑来搜</a></li>
</ul>
<h4 id="c-其他有趣链接">C. 其他有趣链接</h4>
<ul>
<li><a href="https://www.xiaohongshu.com/explore/68416462000000000303d149" target="_blank" rel="noopener nofollow noreferrer" >从中医角度认识教师职业对人的消耗 - 小红书</a></li>
<li><a href="https://www.xiaohongshu.com/explore/680fbbea000000002002bfcd" target="_blank" rel="noopener nofollow noreferrer" >《Vibe Coding》教会你用AI写代码 必读神书 - 小红书</a></li>
<li><a href="https://mp.weixin.qq.com/s/3evxgF7amcXc7aTAqvxeuw?ref=edony.ink" target="_blank" rel="noopener nofollow noreferrer" >人生的意义是什么？这是我听过最好的答案</a></li>
<li><a href="https://m.okjike.com/originalPosts/67fdcdd94bfcd3978461ffbd?ref=edony.ink" target="_blank" rel="noopener nofollow noreferrer" >怎样避免成为【喂养一线城市】的饲料 - 即刻App</a></li>
<li><a href="https://space.bilibili.com/162183" target="_blank" rel="noopener nofollow noreferrer" >原子能的个人空间-哔哩哔哩视频</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/462990256" target="_blank" rel="noopener nofollow noreferrer" >【干货、万字长文】远不止B=MAP：《福格行为模型》到底讲了什么？ - 知乎</a></li>
<li><a href="https://mp.weixin.qq.com/s/VgY-Lx_VDwccpXgsruWoRg" target="_blank" rel="noopener nofollow noreferrer" >如何发现一个20岁时就值得投资的人</a></li>
<li><a href="https://yihui.org/cn/2020/07/silence/" target="_blank" rel="noopener nofollow noreferrer" >万言万当，不如一默 - 谢益辉</a></li>
<li><a href="https://www.usmacd.com/cn/elon_philosophy/" target="_blank" rel="noopener nofollow noreferrer" >马斯克的哲学 - 安全代码</a></li>
<li><a href="https://www.icebeer.top/%E5%BD%93%E6%89%80%E6%9C%89%E4%BA%BA%E9%83%BD%E5%9C%A8%E5%8D%B7%E5%A4%A7%E6%A8%A1%E5%9E%8B%EF%BC%8C%E4%BB%96%E4%BB%AC%E5%9C%A8%E6%8B%86%E8%A7%A3%E5%8E%9F%E5%AD%90%E7%BA%A7%E7%97%9B%E7%82%B9/" target="_blank" rel="noopener nofollow noreferrer" >当所有人都在卷大模型，他们在拆解原子级痛点 - 🍺 IceBeer</a></li>
</ul>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0" term="%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0" label="学习笔记" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[从零开始：如何用 Kubernetes 轻松管理云原生应用]]></title>
            <link href="https://blog.yunpiao.site/post/20250429101929/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20250429101929/</id>
            
            
            <published>2021-04-29T10:19:29+08:00</published>
            <updated>2021-04-29T10:19:29+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="kubernetes-不怕从零开始30-分钟搞定你的第一个云原生部署"><strong>“Kubernetes 不怕从零开始：30 分钟搞定你的第一个云原生部署”</strong></h2>
<blockquote>
<p>如果你从未接触过 Kubernetes，却想快速上手它管理容器化应用，这篇文章就是为你写的。本文将用最直观的步骤，教你如何在本地环境搭建一个 Kubernetes 集群，并部署一个简单的 Web 应用。无需复杂配置，只需一台电脑和 Docker 环境即可。</p>
</blockquote>
<hr>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/2025/04/e8b629391b08f091c5fcf36ab94bfec9.png" alt="image.png"  /></p>
<hr>
<h2 id="a-idprereqa前置条件-"><!-- raw HTML omitted --><!-- raw HTML omitted -->前置条件 🔧</h2>
<p>✅ 已安装 Docker Desktop（含 Kubernetes 插件）<br>
✅ 安装 <code>kubectl</code> 命令行工具<br>
👉 推荐：Windows 用户安装 WSL2，Mac 用户启用 Rosetta 兼容模式</p>
<hr>
<h2 id="a-idtutorialastep-by-step-教程-"><!-- raw HTML omitted --><!-- raw HTML omitted -->Step-by-Step 教程 🚀</h2>
<h3 id="step-1-启动本地集群">Step 1: 启动本地集群</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>minikube start --driver<span style="color:#f92672">=</span>docker  
</span></span></code></pre></div><p>📌 <strong>输出预期</strong>：</p>
<pre tabindex="0"><code>Kubernetes control-plane is running at https://...
</code></pre><h3 id="step-2-部署-nginx-示例应用-">Step 2: 部署 Nginx 示例应用 ✅</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1"> 1</a></span><span><span style="color:#75715e"># deployment.yaml  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2"> 2</a></span><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3"> 3</a></span><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4"> 4</a></span><span><span style="color:#f92672">metadata</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5"> 5</a></span><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-deployment  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6"> 6</a></span><span><span style="color:#f92672">spec</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-7"> 7</a></span><span>  <span style="color:#f92672">replicas</span>: <span style="color:#ae81ff">2</span>  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-8"> 8</a></span><span>  <span style="color:#f92672">selector</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-9"> 9</a></span><span>    <span style="color:#f92672">matchLabels</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-10">10</a></span><span>      <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-11">11</a></span><span>  <span style="color:#f92672">template</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-12">12</a></span><span>    <span style="color:#f92672">metadata</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-13">13</a></span><span>      <span style="color:#f92672">labels</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-14">14</a></span><span>        <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-15">15</a></span><span>    <span style="color:#f92672">spec</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-16">16</a></span><span>      <span style="color:#f92672">containers</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-17">17</a></span><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-18">18</a></span><span>        <span style="color:#f92672">image</span>: <span style="color:#ae81ff">nginx:latest  </span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-19">19</a></span><span>        <span style="color:#f92672">ports</span>:  
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-20">20</a></span><span>        - <span style="color:#f92672">containerPort</span>: <span style="color:#ae81ff">80</span>  
</span></span></code></pre></div><p>💡 <strong>解释</strong>：</p>
<ul>
<li><code>replicas: 2</code> = 自动创建 2 个相同的容器组（Container Group），防止单点故障。</li>
<li><code>containerPort: 80</code> = 开放 Web 访问端口，类似传统服务器的防火墙设置。</li>
</ul>
<h3 id="step-3-测试自动扩容-">Step 3: 测试自动扩容 📈</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>kubectl autoscale deployment nginx-deployment --cpu-percent<span style="color:#f92672">=</span><span style="color:#ae81ff">50</span> --min<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> --max<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>  
</span></span></code></pre></div><p>📊 <strong>结果</strong>：当 CPU 使用率超过 50%，Kubernetes 会自动增加容器数量，像外卖平台根据订单量动态调配骑手一样！</p>
<hr>
<h2 id="a-idtroubleshoota常见错误排查-"><!-- raw HTML omitted --><!-- raw HTML omitted -->常见错误排查 ❌</h2>
<table>
<thead>
<tr>
<th>错误信息</th>
<th>原因</th>
<th>解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CrashLoopBackOff</code></td>
<td>容器启动后立即崩溃</td>
<td>检查镜像名称是否拼写错误（如 <code>nginx:lates</code> → <code>nginx:latest</code>）</td>
</tr>
<tr>
<td><code>ImagePullBackOff</code></td>
<td>无法下载镜像</td>
<td>切换到国内镜像仓库（如阿里云 registry.aliyuncs.com/google_containers）</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="a-idnexta下一步学习建议-"><!-- raw HTML omitted --><!-- raw HTML omitted -->下一步学习建议 📚</h2>
<ul>
<li><strong>实战项目</strong>：尝试用 Helm Chart 部署 WordPress（附<a href="https://helm.sh/docs/" target="_blank" rel="noopener nofollow noreferrer" >官方文档链接</a>）</li>
<li><strong>深入原理</strong>：阅读《Kubernetes 设计模式》</li>
</ul>
<h2 id="docker-compose-vs-kubernetes-部署方式优缺点对比"><strong>Docker Compose vs Kubernetes 部署方式优缺点对比</strong></h2>
<table>
<thead>
<tr>
<th><strong>维度</strong></th>
<th><strong>Docker Compose</strong></th>
<th><strong>Kubernetes (K8s)</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>适用场景</strong></td>
<td>单机/本地开发、小型项目、快速原型验证</td>
<td>分布式系统、生产环境、大规模微服务集群</td>
</tr>
<tr>
<td><strong>学习曲线</strong></td>
<td>✅ 简单直观：YAML 文件定义服务依赖关系，无需掌握复杂概念</td>
<td>❌ 复杂陡峭：需理解 Pod、Service、Deployment、Ingress 等资源对象</td>
</tr>
<tr>
<td><strong>部署复杂度</strong></td>
<td>✅ 轻量级：单节点部署，<code>docker-compose up</code> 一键启动</td>
<td>❌ 重量级：需搭建集群（Minikube/kops）、配置网络存储（CNI/CRI）</td>
</tr>
<tr>
<td><strong>自动化能力</strong></td>
<td>❌ 基础功能：仅支持重启策略（restart: always），无自动扩容/负载均衡</td>
<td>✅ 强大自动化：内置 HPA（Horizontal Pod Autoscaler）、滚动更新、自我修复机制</td>
</tr>
<tr>
<td><strong>资源利用率</strong></td>
<td>❌ 固定分配：无法动态调度容器到不同主机，资源利用率低</td>
<td>✅ 智能调度：基于资源请求（CPU/Memory）动态分配 Pod 到最优节点</td>
</tr>
<tr>
<td><strong>网络与存储管理</strong></td>
<td>✅ 简单网络：默认桥接网络，手动配置端口映射</td>
<td>✅ 灵活网络：通过 CNI 插件实现跨节点通信；支持持久化卷（PV/PVC）动态供给</td>
</tr>
<tr>
<td><strong>服务发现与负载均衡</strong></td>
<td>❌ 有限支持：依赖 Docker 内部 DNS，需手动配置反向代理（如 Nginx）</td>
<td>✅ 内置服务网格：通过 Service 和 Ingress 实现自动服务发现与负载均衡</td>
</tr>
<tr>
<td><strong>安全性</strong></td>
<td>❌ 基础隔离：依赖 Docker 安全策略，缺乏细粒度权限控制（RBAC）</td>
<td>✅ 高级安全：支持命名空间隔离、RBAC、Network Policies、Secret 加密</td>
</tr>
<tr>
<td><strong>维护与调试</strong></td>
<td>✅ 快速迭代：修改 <code>docker-compose.yml</code> 后重启服务即可</td>
<td>❌ 复杂调试：需熟悉 <code>kubectl logs/describe/exec</code> 及监控工具（Prometheus/Grafana）</td>
</tr>
<tr>
<td><strong>社区与生态</strong></td>
<td>✅ 成熟稳定：Docker 生态广泛，文档丰富，插件众多（如 Portainer 管理界面）</td>
<td>✅ 活跃生态：CNCF 主导，Helm Charts、Operator Framework、Service Mesh（Istio/Linkerd）等</td>
</tr>
<tr>
<td><strong>典型命令对比</strong></td>
<td><code>bash&lt;br&gt;docker-compose up -d&lt;br&gt;docker-compose down&lt;br&gt;</code></td>
<td><code>bash&lt;br&gt;kubectl apply -f deployment.yaml&lt;br&gt;kubectl get pods,services,deployments&lt;br&gt;</code></td>
</tr>
<tr>
<td><strong>适用团队规模</strong></td>
<td>小型团队/个人开发者：敏捷开发、快速验证</td>
<td>中大型企业/云原生团队：高可用、弹性扩展需求</td>
</tr>
</tbody>
</table>
<hr>
<h3 id="总结建议"><strong>总结建议</strong></h3>
<ul>
<li>
<p><strong>选择 Docker Compose 的场景</strong>：</p>
<ul>
<li>开发/测试环境快速搭建</li>
<li>单服务器部署轻量级应用</li>
<li>不需要复杂编排和高可用性的项目</li>
</ul>
</li>
<li>
<p><strong>选择 Kubernetes 的场景</strong>：</p>
<ul>
<li>生产环境需保障 SLA（如 99.9% 可用性）</li>
<li>微服务架构下成百上千容器管理</li>
<li>动态扩缩容（如电商秒杀流量高峰）</li>
<li>需要多租户隔离、精细化权限控制</li>
</ul>
</li>
<li>
<p><strong>过渡路径</strong>：</p>
<ul>
<li>从 Docker Compose 迁移到 K8s：
<ul>
<li>使用 <code>kompose</code> 工具将 <code>docker-compose.yml</code> 转换为 Kubernetes 清单文件。</li>
<li>示例：  <code>kompose convert -f docker-compose.yml -o k8s-manifests/</code></li>
</ul>
</li>
</ul>
</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/kubernetes" term="kubernetes" label="Kubernetes" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/k8s" term="k8s" label="k8s" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Spark任务中的常见错误]]></title>
            <link href="https://blog.yunpiao.site/post/20201012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20201012103946/</id>
            
            
            <published>2020-10-12T10:39:46+08:00</published>
            <updated>2020-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>记录遇到过的Spark各种错误</p>
</blockquote>
<h3 id="1-too-many-open-files">1. Too many open files</h3>
<p>linux 中 一切皆文件， Too many open files 有可能是file， 也有可能是socket。 在这里一般是file， 在HDP集群上， 需要在ulimit里设置最大文件打开数量。 一般建议设置为10240 ， 默认是1024 。</p>
<h3 id="2-oom">2. OOM</h3>
<p>Spark需要有两个关于memory相关的设置项</p>
<ol>
<li>spark.drive.memory</li>
<li>spark.execute.memory<br>
一般如果经常需要collect() 等拉回数据到drive的话， spark.drive.memory 的内存需要设置的大点，不过受单机限制， 不能超过单机最大内存</li>
</ol>
<p>如果遇到execute oom的话， 一般是spark.execute.memory设置过小， 而分配到每个execute的数据量又太大， 或者数据倾斜， 造成某一个execute的数据量太大， 这里有集中解决方法。<br>
1） repartition或者coalesce  将分区降低<br>
2） 增大spark.execute.memory （可增加core数量）<br>
3） 控制reduce参数 set mapred.reduce.tasks=10</p>
<h3 id="3-filter后数据倾斜">3. filter后数据倾斜</h3>
<p>解决方法<br>
使用coalesce 降低分区， 并设置 shuffer=Ture</p>
<h3 id="4-join速度慢">4. join速度慢</h3>
<p>如果是大表join小表<br>
解决方法：将小表设置为广播变量, 使用udf进行join</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>tiny_table <span style="color:#f92672">=</span> spark<span style="color:#f92672">.</span>sc<span style="color:#f92672">.</span>boardcast(tiny_table)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">udf_join_table</span>(big_table_data):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span>        <span style="color:#66d9ef">return</span> tiny_table<span style="color:#f92672">.</span>value[big_table_data]
</span></span></code></pre></div><h3 id="5shuffle-fetchfailedexception">5.shuffle FetchFailedException</h3>
<p>org.apache.spark.shuffle.FetchFailedException:<br>
org.apache.spark.shuffle.MetadataFetchFailedException:<br>
Missing an output location for shuffle 0</p>
<p>解决方法：这种问题一般发生在有大量shuffle操作的时候,task不断的failed,然后又重执行，一直循环下去，直到application失败。<br>
一般遇到这种问题提高executor内存即可,同时增加每个executor的cpu,这样不会减少task并行度。</p>
<h3 id="6-drivermaxresultsize太小">6. driver.maxResultSize太小</h3>
<p>Caused by: org.apache.spark.SparkException:<br>
Job aborted due to stage failure: Total size of serialized<br>
results of 374 tasks (1026.0 MB) is bigger than<br>
spark.driver.maxResultSize (1024.0 MB)<br>
解决方法：spark.driver.maxResultSize默认大小为1G 每个Spark action(如collect)所有分区的序列化结果的总大小限制，简而言之就是executor给driver返回的结果过大，报这个错说明需要提高这个值或者避免使用类似的方法，比如countByValue，countByKey等。</p>
<p>将值调大即可</p>
<p>spark.driver.maxResultSize 2g</p>
<h3 id="找不到python">找不到python</h3>
<p>java.io.UIException: Cannot run program &ldquo;python2.7&rdquo;: error=2,没有那个文件或目录<br>
解决方法：execute环境变量问题 查看在环境变量中是否有python2.7</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Spark基础知识]]></title>
            <link href="https://blog.yunpiao.site/post/20200812103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20200812103946/</id>
            
            
            <published>2020-08-12T10:39:46+08:00</published>
            <updated>2020-08-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<h2 id="1-spark介绍">1. Spark介绍</h2>
<p><strong>Apache Spark</strong>是一个<a href="https://zh.wikipedia.org/wiki/%E5%BC%80%E6%BA%90" title="开源" target="_blank" rel="noopener nofollow noreferrer" >开源</a>集群运算框架，最初是由加州大学柏克莱分校AMPLab所开发。相对于<a href="https://zh.wikipedia.org/wiki/Apache_Hadoop" title="Apache Hadoop" target="_blank" rel="noopener nofollow noreferrer" >Hadoop</a>的<a href="https://zh.wikipedia.org/wiki/MapReduce" title="MapReduce" target="_blank" rel="noopener nofollow noreferrer" >MapReduce</a>会在运行完工作后将中介数据存放到磁盘中，Spark使用了存储器内运算技术，能在数据尚未写入硬盘时即在存储器内分析运算。Spark在存储器内运行程序的运算速度能做到比Hadoop MapReduce的运算速度快上100倍，即便是运行程序于硬盘时，Spark也能快上10倍速度。<!-- raw HTML omitted --><a href="https://zh.wikipedia.org/wiki/Apache_Spark#cite_note-1" target="_blank" rel="noopener nofollow noreferrer" >[1]</a><!-- raw HTML omitted -->Spark允许用户将数据加载至集群存储器，并多次对其进行查询，非常适合用于<a href="https://zh.wikipedia.org/wiki/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" title="机器学习" target="_blank" rel="noopener nofollow noreferrer" >机器学习</a>算法。<!-- raw HTML omitted --><a href="https://zh.wikipedia.org/wiki/Apache_Spark#cite_note-2" target="_blank" rel="noopener nofollow noreferrer" >[2]</a><!-- raw HTML omitted --></p>
<p>使用Spark需要搭配集群管理员和分布式存储系统。Spark支持独立模式（本地Spark集群）、<a href="https://zh.wikipedia.org/wiki/Apache_Hadoop" title="Apache Hadoop" target="_blank" rel="noopener nofollow noreferrer" >Hadoop YARN</a>或<a href="https://zh.wikipedia.org/wiki/Apache_Mesos" title="Apache Mesos" target="_blank" rel="noopener nofollow noreferrer" >Apache Mesos</a>的集群管理。<!-- raw HTML omitted --><a href="https://zh.wikipedia.org/wiki/Apache_Spark#cite_note-3" target="_blank" rel="noopener nofollow noreferrer" >[3]</a><!-- raw HTML omitted -->在分布式存储方面，Spark可以和<a href="https://zh.wikipedia.org/wiki/Apache_Hadoop" title="Apache Hadoop" target="_blank" rel="noopener nofollow noreferrer" >HDFS</a><!-- raw HTML omitted --><a href="https://zh.wikipedia.org/wiki/Apache_Spark#cite_note-4" target="_blank" rel="noopener nofollow noreferrer" >[4]</a><!-- raw HTML omitted -->、 <a href="https://zh.wikipedia.org/wiki/Apache_Cassandra" title="Apache Cassandra" target="_blank" rel="noopener nofollow noreferrer" >Cassandra</a><!-- raw HTML omitted --><a href="https://zh.wikipedia.org/wiki/Apache_Spark#cite_note-5" target="_blank" rel="noopener nofollow noreferrer" >[5]</a><!-- raw HTML omitted --> 、<a href="https://zh.wikipedia.org/wiki/OpenStack#Object_Storage_%28Swift%29" title="OpenStack" target="_blank" rel="noopener nofollow noreferrer" >OpenStack Swift</a>和<a href="https://zh.wikipedia.org/wiki/Amazon_S3" target="_blank" rel="noopener nofollow noreferrer" >Amazon S3</a>等接口搭载。 Spark也支持伪分布式（pseudo-distributed）本地模式，不过通常只用于开发或测试时以本机文件系统取代分布式存储系统。在这样的情况下，Spark仅在一台机器上使用每个CPU核心运行程序。</p>
<p>在2014年有超过465位贡献家投入Spark开发<!-- raw HTML omitted --><a href="https://zh.wikipedia.org/wiki/Apache_Spark#cite_note-6" target="_blank" rel="noopener nofollow noreferrer" >[6]</a><!-- raw HTML omitted -->，让其成为<a href="https://zh.wikipedia.org/wiki/Apache%E8%BB%9F%E4%BB%B6%E5%9F%BA%E9%87%91%E6%9C%83" title="Apache软件基金会" target="_blank" rel="noopener nofollow noreferrer" >Apache软件基金会</a>以及<a href="https://zh.wikipedia.org/wiki/%E5%B7%A8%E9%87%8F%E8%B3%87%E6%96%99" title="大数据" target="_blank" rel="noopener nofollow noreferrer" >大数据</a>众多开源项目中最为活跃的项目。</p>
<p>Spark特点：</p>
<p>运行速度快：采用DAG（Directed Acyclic Graph，有向无环图）执行引擎，以支持循环数据流与内存计算，基于内存的执行速度可比Hadoop MR快上百倍，基于磁盘的速度也能快十倍。<br>
容易使用：支持Scala、Java、Python和R语言进行编程。<br>
通用性：提供完整而强大的技术栈，包括SQL查询，流失计算、机器学习和图算法组件。<br>
运行模式多样：可运行于独立的集群模式中、Hadoop中、也可运行在Amazon EC2云环境中，可访问HDFS、Cassandra、HBase、Hive等数据源。</p>
<p>语言 scale 面向对象和函数式编程语言 运行在JVM上，兼容Java程序，能融合到Hadoop生态圈；具有强大的并发性，支持分布式系统。</p>
<h2 id="2spark与其他组件对比">2.Spark与其他组件对比</h2>
<h3 id="spark与hadoop相比">Spark与Hadoop相比：</h3>
<p>其计算模式也属于MR，但不局限与此，还提供多种数据集操作类型（RDD操作），编程模型更加灵活；<br>
Spark提供内存计算，中间结果放在内存中，IO开销，延迟低，拥有更高地迭代运算效率；<br>
基于DAG任务调度执行机制，由于MR迭代执行机制。<br>
使用Hadoop需要编写不少相对底层的代码，而Spark提供高层次、简洁的API。<br>
Spark主要替代Hadoop中的MR，而不能完全替代Hadoop，它很好地融入了Hadoop生态圈，可借助于YARN实现资源调度管理，借助HDFS实现分布式存储。<br>
Hadoop可使用廉价、异构的机器实现分布式存储和计算，而Spark对硬件（内存、CPU）要求稍高</p>
<h2 id="spark运行模式">Spark运行模式</h2>
<p>Spark的设计遵循“一个软件栈满足不同应用场景”的理念。其生态系统主要包含Spark Core、Spark SQL、Spark Streaming、MLLib和GraphX等组件。</p>
<p>Spark Core：包含了Spark的基本功能，如内存计算、任务调度、部署模式、故障恢复、存储管理等。Spark建立在统一的抽象RDD之上，使其可以基本一致的方式应对不同的大数据处理场景。通常所说的Apache Spark，就是指Spark Core；<br>
Spark SQL：它允许开发人员直接处理RDD，也可查询Hive、HBase等外部数据源。它的一个重要特点是能够统一处理关系表和RDD，使开发人员使用SQL命令进行查询，并进行复杂的数据分析；<br>
Spark Streaming：支持高吞吐量、可容错处理的实时流数据处理，其核心思路是将流式计算分解成一系列短小的批处理作业。支持多种数据输入源，如Kafka、Flume、和TCP套接字。<br>
MLlib（机器学习）：提供常用的机器学习算法，含聚类、分类、回归、协同过滤。<br>
GraphX（图计算）：是Spark中用于图计算的API，可认为是Pregel在Spark上的重写和优化。<br>
<img loading='lazy' decoding="async" src="https://upload-images.jianshu.io/upload_images/10970403-9392787513c4dd23.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="spark应用场景"  /></p>
<h3 id="spark基本组件">spark基本组件</h3>
<p>RDD：Resillient Distributed DataSet，弹性分布式数据集，是分布式内存的一个抽象概念，提供一种高度受限的共享内存模型；<br>
DAG：Directed Acyclic Graph，有向无环图，反映RDD之间的依赖关系；<br>
Executor：运行在工作节点（Worker Node）上的一个进程，负责运行Task；<br>
Application：用户编写的Spark应用程序；<br>
Job：一个Job含多个RDD及作用于RDD上的各种操作；<br>
Stage：是Job的基本调度单位，一个Job分为多组Task，每组Task被称为Stage，或者称为TaskSet，代表了一组关联的、相互之间没有Shuffle依赖关系的任务组成的任务集；<br>
Task：运行在Executor上的工作单元。</p>
<h3 id="spark运行架">Spark运行架</h3>
<p>集群资源管理器（Cluster Manager），可以是Spark自带的资源管理器，也可是YARN、Mesos等资源管理框架；<br>
工作节点（WorkNode）：运行作业任务；<br>
每个应用的任务控制节点（Driver）<br>
每个工作节点上负责具体任务的执行进程（Executor）</p>
<p>在Spark中，一个Application由一个Driver和若干个Job构成，一个Job由多个Stage构成构成，一个Stage由多个没有Shuffle关系的Task组成。当执行Application时，Driver会向集群资源管理器申请资源，启动Executor，并向Executor发送应用程序代码和文件，然后在Executor上执行Task，运行结束后，执行结果返回给Driver，或者写到HDFS或者其他数据库中。</p>
<h3 id="spark-运行流程">spark 运行流程</h3>
<p><img loading='lazy' decoding="async" src="https://upload-images.jianshu.io/upload_images/10970403-6e9c202626e9b817.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Spark运行框架"  /></p>
<ul>
<li>
<p>提交Application后, Drive创建 一个SparkContext, 简称sc, sc负责与集群管理组件通信,包括资源申请, 任务分配, 任务监控等</p>
</li>
<li>
<p>资源管理器为Executor分配资源 启动Executor进程 并发心跳包到资源管理器</p>
</li>
<li>
<p>SparkContext根据RDD的依赖关系构建DAG图，DAG图提交给DAGScheduler进行解析，将DAG图分解成Stage，并计算Stage间的依赖关系，然后将一个个TaskSet（即Stage）提交给底层调度器TaskScheduler进行处理；Executor向SparkContext申请Task，TaskScheduler将Task发放给Executor运行，同时，SparkContext将应用程序代码发送给Executor；</p>
</li>
<li>
<p>Task在Executor上运行，结果反馈给TaskScheduler，然后反馈给DAGScheduler-，运行完毕后写入数据，并释放资源。</p>
</li>
</ul>
<h2 id="spark设计与原理">Spark设计与原理</h2>
<p>Spark的核心是建立在统一的抽象RDD上，使Spark的各个组件无缝进行集成，在同一个应用程序中完成大数据计算任务。不同的RDD间的转换操作形成依赖关系，可实现管道化，避免中间结果的存储，降低数据复制、磁盘IO和序列化开销。</p>
<h3 id="rdd">RDD</h3>
<p>一个RDD是一个分布式对象集合，只读的分区记录集合。每个RDD可分成多个分区，每个分区是一个数据集片段，不同分区可保存到集群中不同的节点上，从而在集群的不同节点上并行计算。RDD是高度受限的共享内存模型，即RDD是只读的记录分区的集合，不能直接修改，只能基于稳定的物理存储中的数据集创建RDD，或者通过在其他RDD上执行确定的转换操作（如map、join、group by）而创建新的RDD。</p>
<p>RDD提供了“动作”（Action）和“转换”（Transformation），动作（count、collect等）用于执行计算并指定输出的形式，接受RDD，但是返回非RDD，而输出一个值或结果；转换（map、filter、groupBy、join）指定RDD间的相互依赖关系，接受RDD并返回RDD。RDD提供的API都是类似于map、filter、groupBy、join的粗粒度数据转换操作，不是针对某个数据项的细粒度修改。因此，RDD适合于对数据集中元素执行相同操作的批处理应用，而不适于需要异步、细粒度转换的应用。</p>
<p>RDD采用惰性调用，即在RDD执行过程中，真正的计算发生在RDD的动作操作，对于之前的所有转换操作，Spark只记录RDD生成的轨迹，即相互间的依赖关系，而不会触发真正的计算。进行动作操作的时候，Spark会根据RDD的依赖关系生成DAG，并从起点开始真正的计算。Spark根据RDD的依赖关系生成DAG，其中的逻辑处理成为一个Lineage（血缘关系），即DAG拓扑排序的结果。通过血缘关系连起来的RDD操作可实现管道化（pipeline），避免多次转换操作之间的数据同步等待，不会产生过多的中间数据，管道化后，一个操作的结果直接管道式地流入下一个操作。管道化也保证了每个操作在处理逻辑上的单一性，不像MR那样，为减少MR过程，在单个MR中写入复杂的逻辑。</p>
<h4 id="rdd-特点">rdd 特点</h4>
<p>高效的容错性。现有的分布式共享内存，KV存储，内存数据库等，为实现容错，须在集群节点之间进行数据复制或记录日志，这样节点间会有大量的数据传输。对于数据密集型应用而言会带来很大的开销。而在RDD的设计中，数据只读，不可修改。若需修改数据，须从父RDD转换到子RDD，在不同的RDD间建立血缘关系。所以，RDD天生具有容错机制，不需通过数据冗余的方式（如检查点）实现容错，只需通过RDD父子依赖（血缘）关系重新计算得到丢失的分区来实现容错，无需回归整个系统，避免数据复制的高开销，且重算可在不同节点间并行进行，实现高效容错。RDD依赖关系只需记录粗粒度转换操作，不需记录具体的数据和各种细粒度操作日志，降低了数据密集型应用的容错开销。<br>
中间结果持久化到内存。数据在内存中多个RDD操作之间进行传递，不需“落地”到磁盘，避免不必要的磁盘IO开销。<br>
存放的数据可是Java对象，避免不必要的对象序列化和反序列化。</p>
<p><img loading='lazy' decoding="async" src="https://upload-images.jianshu.io/upload_images/10970403-e050577e50da9dac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="宽依赖和窄依赖"  /><br>
宽窄依赖关系，使Spark具有天生的容错性，加快了Spark的执行速度。当RDD部分分区数据丢失时，可通过血缘关系获取足够的信息来重新运算和恢复丢失的数据分区，从而带来性能提升。窄依赖的失败更为高效，它只需根据父RDD分区重新计算丢失的分区即可，且可在不同的节点并行计算。</p>
<h3 id="stage划分">Stage划分</h3>
<p>Spark根据RDD的依赖关系生成DAG，在分析RDD中分区之间的依赖关系划分Stage。划分方法：在DAG中进行反向解析，遇到宽依赖就断开，遇到窄依赖就把当前的RDD加入到Stage中；将窄依赖尽量划分到同一个Stage中，可实现流水线计算。<br>
Stage的类型包括两种：ShuffleMapStage和ResultStage。</p>
<p>ShuffleMapStage：不是最终的Stage，它之后还有其他Stage。它的输出需要经过Shuffle过程，作为后续Stage的输入；此Stage以Shuffle为输出边界，起输入边界可从外部获取数据，也可是另一个ShuffleMapStage的输出，其输出可是另一个Stage的开始。一个Job里可能有，也可能没有该类型的Stage；<br>
ResultStage：最终的Stage，没有输出，而是直接产生结果或存储。其输入边界可是从外部获取数据，也可是另一个ShuffleMapStage的输出。一个Job中必定有该类型的Stage。</p>
<h3 id="rdd运行过程">RDD运行过程</h3>
<ul>
<li>
<p>创建RDD对象；</p>
</li>
<li>
<p>SparkContext负责计算RDD之间的依赖关系，构建DAG；</p>
</li>
<li>
<p>DAGScheduler负责将DAG图分解成多个Stage，每个Stage中含多个Task，每个Task会被TaskScheduler分发给各个WorkNode上的Executor去执行。</p>
<p><img loading='lazy' decoding="async" src="http://upload-images.jianshu.io/upload_images/10970403-69352791b0ddbb74?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="RDD在Spark中的运行过程"  /></p>
</li>
</ul>
<h3 id="dataframe">DataFrame</h3>
<h3 id="hive-on-spark">Hive On Spark</h3>
<p>Spark SQL增加SchemaRDD（即带有Schema信息的RDD），使得可执行SQL语句。Spark SQL的数据即可来自RDD，也可来自Hive、HDFS、Cassandra等外部数据源，还可以是Json格式的数据。Spark SQL支持Scala、Java、Python语言。</p>
<p><img loading='lazy' decoding="async" src="http://upload-images.jianshu.io/upload_images/10970403-990ca4bcf33f8673?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Spark SQL架构"  /></p>
<h2 id="spark-on-yarn">Spark On Yarn</h2>
<h2 id="spark-streaming">Spark Streaming</h2>
<h2 id="spark部署">Spark部署</h2>
<p><img loading='lazy' decoding="async" src="https://upload-images.jianshu.io/upload_images/10970403-57feec355db9e5b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"  /></p>
<h2 id="spark-调优">Spark 调优</h2>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Mysql基础知识]]></title>
            <link href="https://blog.yunpiao.site/post/20240416155743/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20240416155743/</id>
            
            
            <published>2020-07-16T15:57:42+08:00</published>
            <updated>2020-07-16T15:57:42+08:00</updated>
            
            
            <content type="html"><![CDATA[<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/202404172143494.png" alt=""  /></p>
<h1 id="存储引擎">存储引擎</h1>
<p><code>show engines;</code></p>
<p>5.7版本所有的存储引擎中只有 InnoDB 是事务性存储引擎，也就是说<strong>只有 InnoDB ⽀持事务</strong>。</p>
<p><code>show table status like &quot;table_name&quot; ;</code></p>
<p>mysql 数据库的 user 表是 MyISAM 存储结构</p>
<p>MyISAM 性能好</p>
<p><strong>MyISAM和InnoDB区别</strong></p>
<p>MyISAM采⽤表级锁(table-level locking)。</p>
<p>InnoDB⽀持⾏级锁(row-level locking)和表级锁,默认为⾏级锁</p>
<p>MyISAM是MySQL的默认数据库引擎（5.5版之前）。虽然性能极佳，⽽且提供了⼤量的特性，</p>
<p>包括全⽂索引、压缩、空间函数等，但MyISAM不⽀持事务和⾏级锁，⽽且最⼤的缺陷就是崩溃</p>
<p>后⽆法安全恢复。不过，5.5版本之后，MySQL引⼊了InnoDB（事务性数据库引擎），MySQL</p>
<p>5.5版本后默认的存储引擎为InnoDB。</p>
<p>InnoDB 优点</p>
<ul>
<li>
<p>支持行级别锁</p>
</li>
<li>
<p>支持事务和崩溃后恢复</p>
</li>
<li>
<p>支持外键</p>
</li>
<li>
<p>&hellip; 等</p>
</li>
</ul>
<h3 id="字符集">字符集</h3>
<blockquote>
<p>MySQL采⽤的是类似继承的⽅式指定字符集的默认值，每个数据库以及每张数据表都有⾃⼰的默认值，他们逐层继承。⽐如：某个库中所有表的默认字符集将是该数据库所指定的字符集（这些表在没有指定字符集的情况下，才会采⽤默认字符集）</p>
</blockquote>
<h3 id="索引"><strong>索引</strong></h3>
<blockquote>
<p>索引是存储引擎用于提高数据库表的访问速度的一种「数据结构」。</p>
</blockquote>
<p>有BTree索引 和 哈希索引, 单条优先的时候选择哈希索引， 否则选择 Btree 索引。</p>
<p>B树中的B+Tree 在两种方式下的实现方式不同</p>
<ul>
<li>
<p>MyISAM: B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候，⾸先按照B+Tree搜索算法搜索索引，如果指定的Key存在，则取出其 data 域的值，然后以 data 域的值为地址读取相应的数据记录。这被称为“⾮聚簇索引”。</p>
</li>
<li>
<p>InnoDB: 其数据⽂件本身就是索引⽂件。相⽐MyISAM，索引⽂件和数据⽂件是分离的，其表数据⽂件本身就是按B+Tree组织的⼀个索引结构，树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键，因此InnoDB表数据⽂件本身就是主索引。这被称为“聚簇索引（或聚集索引）”。⽽其余的索引都作辅助索引。辅助索引的data域存储相应记录主键的值⽽不是地址，这也是和MyISAM不同的地⽅。<strong>在根据主索引搜索时，直接找到key所在的节点即可取出数据；在根据辅助索引查找时，则需要先取出主键的值，再⾛⼀遍主索引。 因此，在设计表的时候，不建议使⽤过⻓的字段作为主键，也不建议使⽤⾮单调的字段作为主键，这样会造成主索引频繁分裂。 PS：整理⾃《Java⼯程师修炼之道》</strong></p>
</li>
</ul>
<h3 id="缓存">缓存</h3>
<p>mysql 8.0 版本前会有缓存的概念, 直接返回缓存中的结果</p>
<h3 id="事务">事务</h3>
<p><strong>事务四大特征</strong></p>
<ul>
<li>
<p>A：Atomic，原子性，将所有SQL作为原子工作单元执行，要么全部执行，要么全部不执行；</p>
</li>
<li>
<p>C：Consistent，一致性，事务完成后，所有数据的状态都是一致的，即A账户只要减去了100，B账户则必定加上了100；</p>
</li>
<li>
<p>I：Isolation，隔离性，如果有多个事务并发执行，每个事务作出的修改必须与其他事务隔离；</p>
</li>
<li>
<p>D：Duration，持久性，即事务完成后，对数据库数据的修改被持久化存储。</p>
</li>
</ul>
<p>单条 SQL 默认是事务的， 称为*<code>隐式事务</code>*。</p>
<p>多条之间使用 BEGIN; COMMIT; 包括</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-SQL" data-lang="SQL"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span><span style="color:#66d9ef">BEGIN</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span><span style="color:#66d9ef">UPDATE</span> accounts <span style="color:#66d9ef">SET</span> balance <span style="color:#f92672">=</span> balance <span style="color:#f92672">-</span> <span style="color:#ae81ff">100</span> <span style="color:#66d9ef">WHERE</span> id <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span><span style="color:#66d9ef">UPDATE</span> accounts <span style="color:#66d9ef">SET</span> balance <span style="color:#f92672">=</span> balance <span style="color:#f92672">+</span> <span style="color:#ae81ff">100</span> <span style="color:#66d9ef">WHERE</span> id <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span><span style="color:#66d9ef">COMMIT</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span><span style="color:#75715e">-- ROLLBACK回滚事务
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span><span style="color:#75715e"></span><span style="color:#66d9ef">BEGIN</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span><span style="color:#66d9ef">UPDATE</span> accounts <span style="color:#66d9ef">SET</span> balance <span style="color:#f92672">=</span> balance <span style="color:#f92672">-</span> <span style="color:#ae81ff">100</span> <span style="color:#66d9ef">WHERE</span> id <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span><span style="color:#66d9ef">UPDATE</span> accounts <span style="color:#66d9ef">SET</span> balance <span style="color:#f92672">=</span> balance <span style="color:#f92672">+</span> <span style="color:#ae81ff">100</span> <span style="color:#66d9ef">WHERE</span> id <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span><span style="color:#66d9ef">ROLLBACK</span>;
</span></span></code></pre></div><h3 id="事务控制语句">事务控制语句：</h3>
<ul>
<li>
<p>BEGIN 或 START TRANSACTION 显式地开启一个事务；</p>
</li>
<li>
<p>COMMIT 也可以使用 COMMIT WORK，不过二者是等价的。COMMIT 会提交事务，并使已对数据库进行的所有修改成为永久性的；</p>
</li>
<li>
<p>ROLLBACK 也可以使用 ROLLBACK WORK，不过二者是等价的。回滚会结束用户的事务，并撤销正在进行的所有未提交的修改；</p>
</li>
<li>
<p>SAVEPOINT identifier，SAVEPOINT 允许在事务中创建一个保存点，一个事务中可以有多个 SAVEPOINT；</p>
</li>
<li>
<p>RELEASE SAVEPOINT identifier 删除一个事务的保存点，当没有指定的保存点时，执行该语句会抛出一个异常；</p>
</li>
<li>
<p>ROLLBACK TO identifier 把事务回滚到标记点；</p>
</li>
<li>
<p>SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。</p>
</li>
</ul>
<h3 id="事务隔离">事务隔离</h3>
<p><strong>并发事务带来的问题</strong></p>
<ul>
<li>
<p>脏读 另一个事务<strong>读到了没提交</strong>的数据</p>
</li>
<li>
<p>丢失修改 两个事务<strong>同时修改</strong>数据造成某一个丢失</p>
</li>
<li>
<p>不可重复读 一个事务读取到了另一个事务<strong>修改</strong>前后的<strong>两个不同值</strong>或<strong>不同数量的数据</strong></p>
</li>
<li>
<p>幻读 select 某记录是否存在，不存在，准备插入此记录，但<strong>执行 insert 时发现此记录已存在，无法插入</strong></p>
</li>
</ul>
<p><strong>MySQL 不同级别对应的并发事务问题</strong></p>
<table>
<thead>
<tr>
<th>Isolation Level</th>
<th>脏读（Dirty Read）</th>
<th>不可重复读（Non Repeatable Read）</th>
<th>幻读（Phantom Read）</th>
</tr>
</thead>
<tbody>
<tr>
<td>Read Uncommitted 读取未提交</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Read Committed 读取已提交</td>
<td>-</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Repeatable Read 可重复读（MySQL 默认）</strong></td>
<td>-</td>
<td>-</td>
<td>Yes</td>
</tr>
<tr>
<td>Serializable 可串行化</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</tbody>
</table>
<p>默认情况下， MySQL 会出现幻读的问题， 但是进行了解决</p>
<h3 id="快照读和当前读"><strong>快照读和</strong>当前读</h3>
<p><strong>在读未提交隔离级别下，快照是什么时候生成的？</strong></p>
<p>没有快照，因为不需要，怎么读都读到最新的。不管是否提交</p>
<p><strong>在读已提交隔离级别下，快照是什么时候生成的？</strong></p>
<p>SQL语句开始执行的时候。</p>
<p><strong>在可重复读隔离级别下，快照是什么时候生成的？</strong></p>
<p>事务开始的时候</p>
<p><strong>1.在默认隔离级别下，select 语句默认是快照读</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Plain" data-lang="Plain"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>select a from t where id = 1
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>复制代码
</span></span></code></pre></div><p><strong>2.select 语句加锁是当前读</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Plain" data-lang="Plain"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span># 共享锁
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span>select a from t where id = 1 lock in share mode;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span>#排他锁
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5">5</a></span><span>select a from t where id = 1 for update;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6">6</a></span><span>复制代码
</span></span></code></pre></div><p><strong>3.update 语句是当前读</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Plain" data-lang="Plain"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>update t set a = a + 1;
</span></span></code></pre></div><h3 id="innodb-解决幻读问题">innodb 解决幻读问题</h3>
<p>MVCC 和 next-key Lock</p>
<ul>
<li>
<p><strong>RR隔离级别下间隙锁才有效，RC隔离级别下没有间隙锁；</strong></p>
</li>
<li>
<p><strong>RR隔离级别下为了解决“幻读”问题：“快照读”依靠MVCC控制</strong> <code>Multi-Version Concurrency Control</code><strong>，“当前读”通过间隙锁和行锁解决；</strong></p>
</li>
<li>
<p><strong>间隙锁和行锁合称next-key lock(存在 key 就行锁， 不存在就间隙锁)，每个next-key lock是前开后闭区间；</strong></p>
</li>
<li>
<p><strong>间隙锁的引入，可能会导致同样语句锁住更大的范围，影响并发度</strong></p>
</li>
</ul>
<p><strong>MVCC机制（<code>Multi-Version Concurrency Control</code>，即多版本并发控制）</strong></p>
<p>快照读就是每一行数据中额外保存两个隐藏的列，插入这个数据行时的版本号，删除这个数据行时的版本号（可能为空），滚动指针(指向 undo log 中用于事务回滚的日志记录)。</p>
<ul>
<li>
<p>插入</p>
<p>事务 ID  作为版本号</p>
</li>
<li>
<p>删除</p>
<p>原数据行的删除版本号设置为当前事务的 ID，然后根据原数据行生成一条 INSERT 语句，写入 undo log，用于事务执行失败时回滚。delete 操作实际上不会直接删除，而是将 delete 对象打上 delete flag，标记为删除，最终的删除操作是 purge 线程完成的。会将数据行的删除版本号设置为当前的事务的 ID，这样后面的事务 B 即便查到这行数据由于事务 B 的 ID&gt;删除版本号，也会忽略这条数据。</p>
</li>
<li>
<p>更新</p>
<p>所以执行更新操作时，其实是会将原数据行的删除版本号设置为当前事务的 ID，生成一条 INSERT 语句，写入 undo log，用于事务执行失败时回滚。插入一条新的数据，将事务的 ID 作为数据行的的创建版本号。</p>
</li>
<li>
<p>查询</p>
<ol>
<li>
<p>如果该行数据没有被加行锁中的 X 锁（也就是没有其他事务对这行数据进行修改），那么直接读取数据（前提是数据的版本号&lt;=当前事务版本号的数据,不然不会放到查询结果集里面）。</p>
</li>
<li>
<p>该行数据被加了行锁 X 锁（也就是现在有其他事务对这行数据进行修改），那么读数据的事务不会进行等待，而是回去 undo log 端里面读之前版本的数据（<strong>这里存储的数据本身是用于回滚的</strong>），在<strong>可重复读</strong>的隔离级别下，从 undo log 中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务 ID 的数据)，在<strong>提交读</strong>的隔离级别下，从 undo log 中读取的总是最新的快照数据。</p>
</li>
</ol>
<p>排它锁(X锁)和共享锁(S锁)：</p>
</li>
</ul>
<h3 id="加锁规则">加锁规则</h3>
<p>加锁规则里面，包含了两个“原则”、两个“优化”和一个“bug”。</p>
<ol>
<li>
<p>原则 1：加锁的基本单位是 next-key lock。希望你还记得，next-key lock 是前开后闭区间。</p>
</li>
<li>
<p>原则 2：查找过程中访问到的对象才会加锁。</p>
</li>
<li>
<p>优化 1：索引上的等值查询，给唯一索引加锁的时候，next-key lock 退化为行锁。</p>
</li>
<li>
<p>优化 2：索引上的等值查询，向右遍历时且最后一个值不满足等值条件的时候，next-key lock 退化为间隙锁。</p>
</li>
<li>
<p>一个 bug：唯一索引上的范围查询会访问到不满足条件的第一个值为止。</p>
</li>
</ol>
<p><strong>MySQL 锁机制</strong> 排它锁(X锁)和共享锁(S锁)：</p>
<ol>
<li>共享锁 lock in share mode S 锁</li>
</ol>
<pre><code>- 允许其它事务也增加共享锁读取

- 不允许其它事物增加排他锁(`for update`)

- 当事务同时增加共享锁时候，事务的更新必须等待先执行的事务commit后才行，如果同时并发太大可能很容易造成死锁
</code></pre>
<ol start="2">
<li>排他锁 for update X 锁</li>
</ol>
<pre><code>- 事务之间不允许其它排他锁或共享锁读取，修改更不可能

- 一次只能有一个排他锁执行commit之后，其它事务才可执行

**不允许其它事务增加共享或排他锁读取。修改是惟一的，必须等待前一个事务commit，才可**
</code></pre>
<p><code>SELECT ... FOR SHARE</code>和<code>SELECT ... FOR UPDATE</code>：</p>
<blockquote>
<p><code>SELECT ... FOR SHARE/LOCK IN SHARE MODE</code>只会锁定扫描过程中<em><strong>使用</strong></em>的索引里的记录行，即如果你的查询正好<em><strong>使用</strong></em>了覆盖索引，那么只有这个索引里的记录行会被锁定，主键索引的记录行是不会被锁定的。</p>
</blockquote>
<blockquote>
<p><code>SELECT ... FOR UPDATE</code>除了会锁定扫描过程中<em><strong>使用</strong></em>的索引里的记录行，相关的其他索引的记录行也会被锁定。换句话说就算你使用了覆盖索引，但是主键索引里的记录行也会被锁定。而又因为主键索引就已经包含了所有字段，那么就相当于锁定表的整行记录。</p>
</blockquote>
<p>MySQL在可重复读个里级别下（默认），才会启用间隙锁。你如果把隔离级别设置为读提交的话，就没有间隙锁了。但同时，你要解决可能出现的数据和日志不一致问题，需要把 binlog 格式设置为 row。</p>
<h3 id="undo-log">undo log</h3>
<p>undo_log 是一种逻辑日志，是旧数据的备份。有两个作用，用于事务回滚和为 MVCC 提供老版本的数据。</p>
<p><strong>可以认为当 delete 一条记录时，undo log 中会记录一条对应的 insert 记录，反之亦然，当 update 一条记录时，它记录一条对应相反的 update 记录。加锁后会备份数据， 用于回滚</strong></p>
<h3 id="数据库范式-数据库逻辑设计">数据库范式-数据库逻辑设计</h3>
<p>1NF是对属性的**<code>原子性</code>**，要求属性具有原子性，不可再分解；</p>
<p>2NF是对记录的**<code>唯一性</code><strong>，要求记录有唯一标识，即实体的唯一性，即</strong>不存在部分依赖**；某一列不能重复</p>
<p>表：学号、课程号、姓名、学分;</p>
<p><strong>正确做法:</strong></p>
<blockquote>
<p>学生：<code>Student</code>(学号, 姓名)；课程：<code>Course</code>(课程号, 学分)；选课关系：<code>StudentCourse</code>(学号, 课程号, 成绩)。</p>
</blockquote>
<p><strong>如果一个关系属于第二范式</strong>,并且在**<code>两个(或多个)非主键属性之间不存在函数依赖</code>**。(非主键属性之间的函数依赖也称为传递依赖),那么这个关系属于第三范式。</p>
<p>3NF是对字段的**<code>冗余性</code><strong>，要求任何字段不能由其他字段派生出来，它要求字段没有冗余，即</strong>不存在传递依赖**；</p>
<p>表: 学号, 姓名, 年龄, 学院名称, 学院电话</p>
<p><strong>正确做法：</strong></p>
<blockquote>
<p>学生：(学号, 姓名, 年龄, 所在学院)；学院：(学院，学院名称， 电话)。</p>
</blockquote>
<p>2NF和3NF的区别</p>
<ul>
<li>
<p>2NF依据是非主键列是否完全依赖于主键，还是依赖于主键的一部分。</p>
</li>
<li>
<p>3NF依据是非主键列是直接依赖于主键，还是直接依赖于非主键。</p>
</li>
</ul>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/linux" term="linux" label="linux" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%BF%90%E7%BB%B4" term="%E8%BF%90%E7%BB%B4" label="运维" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9D%82" term="%E6%9D%82" label="杂" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ELK-数据分析工具学习]]></title>
            <link href="https://blog.yunpiao.site/post/20200401103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20200401103946/</id>
            
            
            <published>2020-04-01T10:39:46+08:00</published>
            <updated>2020-04-01T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="elk数据分析工具学习">ELK数据分析工具学习</h1>
<hr>
<h2 id="学习参考资料">学习参考资料</h2>
<hr>
<p>ElasticSearch参考手册，学习<br>
<a href="http://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/index.html" target="_blank" rel="noopener nofollow noreferrer" >http://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/index.html</a><br>
DSL查询语法，包括查找（query）、过滤（filter）和聚合（aggs）等。<br>
Logstash参考手册，学习数据导入，包括输入（input）、过滤（filter）和输出（<br>
output）等，主要是filter中如何对复杂文本 进行拆分和类型 转化。<br>
<a href="http://udn.yyuap.com/doc/logstash-best-practice-cn/index.html" target="_blank" rel="noopener nofollow noreferrer" >http://udn.yyuap.com/doc/logstash-best-practice-cn/index.html</a></p>
<h2 id="httpskibanalogstashescontent">Kibana参考手册，使用Kibana提供的前端界面对数据进行快速展示，主要是对Visulize 模块的使<br>
<a href="https://kibana.logstash.es/content/" target="_blank" rel="noopener nofollow noreferrer" >https://kibana.logstash.es/content/</a></h2>
<h2 id="安装">安装</h2>
<h4 id="下载">下载</h4>
<blockquote>
<p>git clone <a href="https://github.com/elastic/stack-docker" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/elastic/stack-docker</a></p>
</blockquote>
<h4 id="安装-docker-compose">安装 docker-compose</h4>
<blockquote>
<p>pip install docker-compose</p>
</blockquote>
<h4 id="修改配置">修改配置</h4>
<blockquote>
<p>修改docker-compose.yml 中ip 127.0.0.1 为 0.0.0.0</p>
</blockquote>
<h4 id="启动">启动</h4>
<blockquote>
<p>docker-compose up</p>
</blockquote>
<h4 id="访问">访问</h4>
<blockquote>
<p>http://IP:5601</p>
</blockquote>
<h4 id="登陆">登陆</h4>
<blockquote>
<p>user=elastic password=changeme</p>
</blockquote>
<h2 id="参看文献">参看文献</h2>
<p><a href="https://github.com/elastic/stack-docker" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/elastic/stack-docker</a><br>
<a href="http://hao.jobbole.com/kibana/" target="_blank" rel="noopener nofollow noreferrer" >http://hao.jobbole.com/kibana/</a></p>
<hr>
<h2 id="基本操作">基本操作</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>curl -XGET <span style="color:#e6db74">&#39;http://localhost:9200/_count?pretty&#39;</span> -d <span style="color:#e6db74">&#39;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span><span style="color:#e6db74">{
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">3</a></span><span><span style="color:#e6db74">    &#34;query&#34;: {
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">4</a></span><span><span style="color:#e6db74">        &#34;match_all&#34;: {}
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">5</a></span><span><span style="color:#e6db74">    }
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6">6</a></span><span><span style="color:#e6db74">}
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7">7</a></span><span><span style="color:#e6db74">&#39;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>  <span style="color:#f92672">&#34;count&#34;</span>: <span style="color:#ae81ff">155300</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span>  <span style="color:#f92672">&#34;_shards&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span>    <span style="color:#f92672">&#34;total&#34;</span>: <span style="color:#ae81ff">79</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5">5</a></span><span>    <span style="color:#f92672">&#34;successful&#34;</span>: <span style="color:#ae81ff">44</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6">6</a></span><span>    <span style="color:#f92672">&#34;skipped&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-7">7</a></span><span>    <span style="color:#f92672">&#34;failed&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-8">8</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-9">9</a></span><span>}
</span></span></code></pre></div><h4 id="使用put-指定id">使用put, 指定id</h4>
<h4 id="使用post-自动生成id">使用post， 自动生成id</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span>PUT /website/blog/123
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span>  <span style="color:#e6db74">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span>  <span style="color:#e6db74">&#34;text&#34;</span>:  <span style="color:#e6db74">&#34;Just trying this out...&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-5">5</a></span><span>  <span style="color:#e6db74">&#34;date&#34;</span>:  <span style="color:#e6db74">&#34;2014/01/01&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-6">6</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2"> 2</a></span><span>  <span style="color:#f92672">&#34;_index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3"> 3</a></span><span>  <span style="color:#f92672">&#34;_type&#34;</span>: <span style="color:#e6db74">&#34;blog&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-4"> 4</a></span><span>  <span style="color:#f92672">&#34;_id&#34;</span>: <span style="color:#e6db74">&#34;R0DpVWEB9_9q-Fol0opS&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-5"> 5</a></span><span>  <span style="color:#f92672">&#34;_version&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-6"> 6</a></span><span>  <span style="color:#f92672">&#34;result&#34;</span>: <span style="color:#e6db74">&#34;created&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-7"> 7</a></span><span>  <span style="color:#f92672">&#34;_shards&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-8"> 8</a></span><span>    <span style="color:#f92672">&#34;total&#34;</span>: <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-9"> 9</a></span><span>    <span style="color:#f92672">&#34;successful&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-10">10</a></span><span>    <span style="color:#f92672">&#34;failed&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-11">11</a></span><span>  },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-12">12</a></span><span>  <span style="color:#f92672">&#34;_seq_no&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-13">13</a></span><span>  <span style="color:#f92672">&#34;_primary_term&#34;</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-14">14</a></span><span>}
</span></span></code></pre></div><p>自动生成的 ID 是 URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串。 这些 GUID 字符串由可修改的 FlakeID 模式生成，这种模式允许多个节点并行生成唯一 ID ，且互相之间的冲突概率几乎为零。</p>
<h4 id="取回文档-根据_index-_type-_id">取回文档 根据_index _type _id</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span>GET /website/blog/123
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2">2</a></span><span>GET /website/blog/123?_source<span style="color:#f92672">=</span>title,text
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3">3</a></span><span>GET /website/blog/123/_source
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2"> 2</a></span><span>  <span style="color:#f92672">&#34;_index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3"> 3</a></span><span>  <span style="color:#f92672">&#34;_type&#34;</span>: <span style="color:#e6db74">&#34;blog&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4"> 4</a></span><span>  <span style="color:#f92672">&#34;_id&#34;</span>: <span style="color:#e6db74">&#34;123&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-5"> 5</a></span><span>  <span style="color:#f92672">&#34;_version&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-6"> 6</a></span><span>  <span style="color:#f92672">&#34;found&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-7"> 7</a></span><span>  <span style="color:#f92672">&#34;_source&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-8"> 8</a></span><span>    <span style="color:#f92672">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-9"> 9</a></span><span>    <span style="color:#f92672">&#34;text&#34;</span>: <span style="color:#e6db74">&#34;Just trying this out...&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-10">10</a></span><span>    <span style="color:#f92672">&#34;date&#34;</span>: <span style="color:#e6db74">&#34;2014/01/01&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-11">11</a></span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-12">12</a></span><span>}
</span></span></code></pre></div><p>如果没有 会返回</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1"> 1</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-2"> 2</a></span><span>  <span style="color:#e6db74">&#34;_index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-3"> 3</a></span><span>  <span style="color:#e6db74">&#34;_type&#34;</span>: <span style="color:#e6db74">&#34;blog&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-4"> 4</a></span><span>  <span style="color:#e6db74">&#34;_id&#34;</span>: <span style="color:#e6db74">&#34;123&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-5"> 5</a></span><span>  <span style="color:#e6db74">&#34;_version&#34;</span>: 1,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-6"> 6</a></span><span>  <span style="color:#e6db74">&#34;found&#34;</span>: true,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-7"> 7</a></span><span>  <span style="color:#e6db74">&#34;_source&#34;</span>: <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-8"> 8</a></span><span>    <span style="color:#e6db74">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-9"> 9</a></span><span>    <span style="color:#e6db74">&#34;text&#34;</span>: <span style="color:#e6db74">&#34;Just trying this out...&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-10">10</a></span><span>    <span style="color:#e6db74">&#34;date&#34;</span>: <span style="color:#e6db74">&#34;2014/01/01&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-11">11</a></span><span>  <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-12">12</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><h2 id="检查文档是否存在">检查文档是否存在</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1">1</a></span><span>HEAD /website/blog/123?_source
</span></span></code></pre></div><p><code>200 - ok</code></p>
<h2 id="创建新文档">创建新文档</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span>POST /website/blog/
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-2">2</a></span><span><span style="color:#f92672">{</span> ... <span style="color:#f92672">}</span><span style="color:#75715e"># 自动生成id</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-3">3</a></span><span>PUT /website/blog/123?op_type<span style="color:#f92672">=</span>create
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-4">4</a></span><span><span style="color:#f92672">{</span> ... <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-5">5</a></span><span>PUT /website/blog/123/_create
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-6">6</a></span><span><span style="color:#f92672">{</span> ... <span style="color:#f92672">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-1"> 1</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-2"> 2</a></span><span>    <span style="color:#75715e">#如果可以创建</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-3"> 3</a></span><span>    <span style="color:#ae81ff">201</span> Created
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-4"> 4</a></span><span>    <span style="color:#75715e">#如果id重复</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-5"> 5</a></span><span>   <span style="color:#e6db74">&#34;error&#34;</span>: <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-6"> 6</a></span><span>      <span style="color:#e6db74">&#34;root_cause&#34;</span>: <span style="color:#f92672">[</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-7"> 7</a></span><span>         <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-8"> 8</a></span><span>            <span style="color:#e6db74">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;document_already_exists_exception&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-9"> 9</a></span><span>            <span style="color:#e6db74">&#34;reason&#34;</span>: <span style="color:#e6db74">&#34;[blog][123]: document already exists&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-10">10</a></span><span>            <span style="color:#e6db74">&#34;shard&#34;</span>: <span style="color:#e6db74">&#34;0&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-11">11</a></span><span>            <span style="color:#e6db74">&#34;index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-12">12</a></span><span>         <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-13">13</a></span><span>      <span style="color:#f92672">]</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-14">14</a></span><span>      <span style="color:#e6db74">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;document_already_exists_exception&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-15">15</a></span><span>      <span style="color:#e6db74">&#34;reason&#34;</span>: <span style="color:#e6db74">&#34;[blog][123]: document already exists&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-16">16</a></span><span>      <span style="color:#e6db74">&#34;shard&#34;</span>: <span style="color:#e6db74">&#34;0&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-17">17</a></span><span>      <span style="color:#e6db74">&#34;index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-18">18</a></span><span>   <span style="color:#f92672">}</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-19">19</a></span><span>   <span style="color:#e6db74">&#34;status&#34;</span>: <span style="color:#ae81ff">409</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-20">20</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><h4 id="更新文档">更新文档</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-1">1</a></span><span>PUT /website/blog/123
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-2">2</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-3">3</a></span><span>  <span style="color:#e6db74">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-4">4</a></span><span>  <span style="color:#e6db74">&#34;text&#34;</span>:  <span style="color:#e6db74">&#34;I am starting to get the hang of this...&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-5">5</a></span><span>  <span style="color:#e6db74">&#34;date&#34;</span>:  <span style="color:#e6db74">&#34;2014/01/02&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-6">6</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-2"> 2</a></span><span>  <span style="color:#f92672">&#34;_index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-3"> 3</a></span><span>  <span style="color:#f92672">&#34;_type&#34;</span>: <span style="color:#e6db74">&#34;blog&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-4"> 4</a></span><span>  <span style="color:#f92672">&#34;_id&#34;</span>: <span style="color:#e6db74">&#34;123&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-5"> 5</a></span><span>  <span style="color:#f92672">&#34;_version&#34;</span>: <span style="color:#ae81ff">2</span>,<span style="color:#960050;background-color:#1e0010">#版本会变成version2</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-6"> 6</a></span><span>  <span style="color:#f92672">&#34;result&#34;</span>: <span style="color:#e6db74">&#34;updated&#34;</span>,<span style="color:#960050;background-color:#1e0010">#更改为Update</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-7"> 7</a></span><span>  <span style="color:#f92672">&#34;_shards&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-8"> 8</a></span><span>    <span style="color:#f92672">&#34;total&#34;</span>: <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-9"> 9</a></span><span>    <span style="color:#f92672">&#34;successful&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-10">10</a></span><span>    <span style="color:#f92672">&#34;failed&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-11">11</a></span><span>  },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-12">12</a></span><span>  <span style="color:#f92672">&#34;_seq_no&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-13">13</a></span><span>  <span style="color:#f92672">&#34;_primary_term&#34;</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-14">14</a></span><span>}
</span></span></code></pre></div><h4 id="删除文档">删除文档</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-12-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-12-1">1</a></span><span>DELETE /website/blog/123
</span></span></code></pre></div><pre tabindex="0"><code>#存在
{
{
  &#34;_index&#34;: &#34;website&#34;,
  &#34;_type&#34;: &#34;blog&#34;,
  &#34;_id&#34;: &#34;123&#34;,
  &#34;_version&#34;: 3,
  &#34;result&#34;: &#34;deleted&#34;,
  &#34;_shards&#34;: {
    &#34;total&#34;: 2,
    &#34;successful&#34;: 1,
    &#34;failed&#34;: 0
  },
  &#34;_seq_no&#34;: 2,
  &#34;_primary_term&#34;: 1
}
}
#不存在
{
  &#34;found&#34; :    false,
  &#34;_index&#34; :   &#34;website&#34;,
  &#34;_type&#34; :    &#34;blog&#34;,
  &#34;_id&#34; :      &#34;123&#34;,
  &#34;_version&#34; : 4
}
</code></pre><h4 id="并发中遇到的问题">并发中遇到的问题</h4>
<blockquote>
<p>悲观并发控制</p>
</blockquote>
<p>这种方法被关系型数据库广泛使用，它假定有变更冲突可能发生，因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住，确保只有放置锁的线程能够对这行数据进行修改。</p>
<blockquote>
<p>乐观并发控制</p>
</blockquote>
<p>Elasticsearch 中使用的这种方法假定冲突是不可能发生的，并且不会阻塞正在尝试的操作。 然而，如果源数据在读写当中被修改，更新将会失败。应用程序接下来将决定该如何解决冲突。 例如，可以重试更新、使用新的数据、或者将相关情况报告给用户。</p>
<h4 id="乐观并发控制">乐观并发控制</h4>
<p>利用 _version 号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version 号来达到这个目的。</p>
<h4 id="实例流程">实例流程</h4>
<p>1.创建一个文档</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-1">1</a></span><span>PUT /website/blog/1/_create
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-2">2</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-3">3</a></span><span>  <span style="color:#e6db74">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-4">4</a></span><span>  <span style="color:#e6db74">&#34;text&#34;</span>:  <span style="color:#e6db74">&#34;Just trying this out...&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-14-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-14-5">5</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>version 版本号是 1<br>
2.重建索引保存修改 指定version</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-1">1</a></span><span>PUT /website/blog/1?version<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-2">2</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-3">3</a></span><span>  <span style="color:#e6db74">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-4">4</a></span><span>  <span style="color:#e6db74">&#34;text&#34;</span>:  <span style="color:#e6db74">&#34;Starting to get the hang of this...&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-15-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-15-5">5</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>version=2<br>
如果version 已经是2了，再对version1进行修改，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-2"> 2</a></span><span> <span style="color:#f92672">&#34;error&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-3"> 3</a></span><span>   <span style="color:#f92672">&#34;root_cause&#34;</span>: [
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-4"> 4</a></span><span>     {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-5"> 5</a></span><span>       <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;version_conflict_engine_exception&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-6"> 6</a></span><span>       <span style="color:#f92672">&#34;reason&#34;</span>: <span style="color:#e6db74">&#34;[blog][1]: version conflict, current version [2] is different than the one provided [1]&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-7"> 7</a></span><span>       <span style="color:#f92672">&#34;index_uuid&#34;</span>: <span style="color:#e6db74">&#34;_1drhEbXTB-rHBomeP7cJw&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-8"> 8</a></span><span>       <span style="color:#f92672">&#34;shard&#34;</span>: <span style="color:#e6db74">&#34;3&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-9"> 9</a></span><span>       <span style="color:#f92672">&#34;index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-10">10</a></span><span>     }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-11">11</a></span><span>   ],
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-12">12</a></span><span>   <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;version_conflict_engine_exception&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-13">13</a></span><span>   <span style="color:#f92672">&#34;reason&#34;</span>: <span style="color:#e6db74">&#34;[blog][1]: version conflict, current version [2] is different than the one provided [1]&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-14">14</a></span><span>   <span style="color:#f92672">&#34;index_uuid&#34;</span>: <span style="color:#e6db74">&#34;_1drhEbXTB-rHBomeP7cJw&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-15">15</a></span><span>   <span style="color:#f92672">&#34;shard&#34;</span>: <span style="color:#e6db74">&#34;3&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-16">16</a></span><span>   <span style="color:#f92672">&#34;index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-17">17</a></span><span> },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-18">18</a></span><span> <span style="color:#f92672">&#34;status&#34;</span>: <span style="color:#ae81ff">409</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-16-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-16-19">19</a></span><span>}
</span></span></code></pre></div><h2 id="使用外部版本号-更新版本号">使用外部版本号 更新版本号</h2>
<p>只有当前版本号小于更新版本号的时候，才会更新成功</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-1">1</a></span><span>PUT /website/blog/2?version<span style="color:#f92672">=</span>10&amp;version_type<span style="color:#f92672">=</span>external
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-2">2</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-3">3</a></span><span>  <span style="color:#e6db74">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;My first external blog entry&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-4">4</a></span><span>  <span style="color:#e6db74">&#34;text&#34;</span>:  <span style="color:#e6db74">&#34;This is a piece of cake...&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-17-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-17-5">5</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-1"> 1</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-2"> 2</a></span><span>  <span style="color:#f92672">&#34;_index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-3"> 3</a></span><span>  <span style="color:#f92672">&#34;_type&#34;</span>: <span style="color:#e6db74">&#34;blog&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-4"> 4</a></span><span>  <span style="color:#f92672">&#34;_id&#34;</span>: <span style="color:#e6db74">&#34;2&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-5"> 5</a></span><span>  <span style="color:#f92672">&#34;_version&#34;</span>: <span style="color:#ae81ff">10</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-6"> 6</a></span><span>  <span style="color:#f92672">&#34;result&#34;</span>: <span style="color:#e6db74">&#34;created&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-7"> 7</a></span><span>  <span style="color:#f92672">&#34;_shards&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-8"> 8</a></span><span>    <span style="color:#f92672">&#34;total&#34;</span>: <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-9"> 9</a></span><span>    <span style="color:#f92672">&#34;successful&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-10">10</a></span><span>    <span style="color:#f92672">&#34;failed&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-11">11</a></span><span>  },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-12">12</a></span><span>  <span style="color:#f92672">&#34;_seq_no&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-13">13</a></span><span>  <span style="color:#f92672">&#34;_primary_term&#34;</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-14">14</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-15">15</a></span><span><span style="color:#960050;background-color:#1e0010">#</span> <span style="color:#960050;background-color:#1e0010">如果不小于</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-16">16</a></span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-17">17</a></span><span>  <span style="color:#f92672">&#34;error&#34;</span>: {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-18">18</a></span><span>    <span style="color:#f92672">&#34;root_cause&#34;</span>: [
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-19">19</a></span><span>      {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-20">20</a></span><span>        <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;version_conflict_engine_exception&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-21">21</a></span><span>        <span style="color:#f92672">&#34;reason&#34;</span>: <span style="color:#e6db74">&#34;[blog][2]: version conflict, current version [10] is higher or equal to the one provided [10]&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-22">22</a></span><span>        <span style="color:#f92672">&#34;index_uuid&#34;</span>: <span style="color:#e6db74">&#34;_1drhEbXTB-rHBomeP7cJw&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-23">23</a></span><span>        <span style="color:#f92672">&#34;shard&#34;</span>: <span style="color:#e6db74">&#34;2&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-24">24</a></span><span>        <span style="color:#f92672">&#34;index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-25">25</a></span><span>      }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-26">26</a></span><span>    ],
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-27">27</a></span><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;version_conflict_engine_exception&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-28">28</a></span><span>    <span style="color:#f92672">&#34;reason&#34;</span>: <span style="color:#e6db74">&#34;[blog][2]: version conflict, current version [10] is higher or equal to the one provided [10]&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-29">29</a></span><span>    <span style="color:#f92672">&#34;index_uuid&#34;</span>: <span style="color:#e6db74">&#34;_1drhEbXTB-rHBomeP7cJw&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-30">30</a></span><span>    <span style="color:#f92672">&#34;shard&#34;</span>: <span style="color:#e6db74">&#34;2&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-31">31</a></span><span>    <span style="color:#f92672">&#34;index&#34;</span>: <span style="color:#e6db74">&#34;website&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-32">32</a></span><span>  },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-33">33</a></span><span>  <span style="color:#f92672">&#34;status&#34;</span>: <span style="color:#ae81ff">409</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-18-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-18-34">34</a></span><span>}
</span></span></code></pre></div><h4 id="更新文档-1">更新文档</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-1">1</a></span><span>POST /website/blog/1/_update
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-2">2</a></span><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-3">3</a></span><span>   <span style="color:#e6db74">&#34;doc&#34;</span> : <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-4">4</a></span><span>      <span style="color:#e6db74">&#34;tags&#34;</span> : <span style="color:#f92672">[</span> <span style="color:#e6db74">&#34;testing&#34;</span> <span style="color:#f92672">]</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-5">5</a></span><span>      <span style="color:#e6db74">&#34;views&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-6">6</a></span><span>   <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-19-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-19-7">7</a></span><span><span style="color:#f92672">}</span>
</span></span></code></pre></div>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[大数据-HDP拖拽式机器学习框架]]></title>
            <link href="https://blog.yunpiao.site/post/20200401103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20200401103946/</id>
            
            
            <published>2020-04-01T10:39:46+08:00</published>
            <updated>2020-04-01T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>拖拽式机器学习框架</p>
</blockquote>
<p>中科院开源图形化机器学习系统Easy ML</p>
<p>总结起来，Easy ML 的优势主要有三点：</p>
<ol>
<li>降低定义和执行机器学习任务的障碍 ;</li>
<li>共享和重用算法的实现，作业 DAG 和实验结果 ;</li>
<li>将独立算法和分布式算法无缝集成在一个任务中。</li>
<li></li>
</ol>
<p><a href="https://www.knime.com/" target="_blank" rel="noopener nofollow noreferrer" >https://www.knime.com/</a><br>
KNIME分析平台</p>
<p><a href="https://github.com/NVIDIA/DIGITS" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/NVIDIA/DIGITS</a><br>
DIGITS（D eep Learning G PU T raining S ystem）是用于训练深度学习模型的webapp。目前支持的框架有：Caffe，Torch和Tensorflow。</p>
<p><a href="https://rapidminer.com/pricing/" target="_blank" rel="noopener nofollow noreferrer" >https://rapidminer.com/pricing/</a><br>
一个平台 难道一切。<br>
RapidMiner的统一数据科学平台加速了 在单一环境中构建完整的分析工作流程，  从数据准备到机器学习，模型验证到部署，大大提高了效率，缩短了数据科学项目的价值。<br>
<a href="https://www.dominodatalab.com/product/" target="_blank" rel="noopener nofollow noreferrer" >https://www.dominodatalab.com/product/</a><br>
数据科学工作的一个地方<br>
使用现有的工具和语言开发，部署和协作。</p>
<p><a href="https://www.dataiku.com/" target="_blank" rel="noopener nofollow noreferrer" >https://www.dataiku.com/</a><br>
协同数据科学平台<br>
原型，部署和运行在规模</p>
<p><a href="http://www.cs.waikato.ac.nz/ml/weka/" target="_blank" rel="noopener nofollow noreferrer" >http://www.cs.waikato.ac.nz/ml/weka/</a><br>
机器学习组<br>
计算机科学的一个令人兴奋和潜在影响深远的发展是机器学习方法的发明和应用（ML）。这些使计算机程序能够自动分析大量数据，并确定哪些信息最相关。然后，这种结晶的信息可以用于自动进行预测或帮助人们更快更准确地做出决策。</p>
<p><a href="https://www.ibm.com/us-en/marketplace/spss-modeler" target="_blank" rel="noopener nofollow noreferrer" >https://www.ibm.com/us-en/marketplace/spss-modeler</a><br>
IBM SPSS Modeler<br>
SPSS Modeler provides predictive analytics to help you uncover data patterns, gain predictive accuracy and improve decision making.</p>
<p><a href="https://studio.azureml.net/" target="_blank" rel="noopener nofollow noreferrer" >https://studio.azureml.net/</a><br>
欢迎来到Azure机器学习</p>
<p><a href="https://www.sas.com/en_us/software/visual-data-mining-machine-learning.html" target="_blank" rel="noopener nofollow noreferrer" >https://www.sas.com/en_us/software/visual-data-mining-machine-learning.html</a><br>
SAS® Visual Data Mining and Machine Learning<br>
Huge performance gains. Innovative algorithms.<br>
One in-memory environment.</p>
<p><a href="https://www.h2o.ai/" target="_blank" rel="noopener nofollow noreferrer" >https://www.h2o.ai/</a><br>
H2O是世界领先的开源深度学习平台。H2O由世界100,000多个数据科学家和超过10,000个组织使用。</p>
<p><a href="https://www.alteryx.com/" target="_blank" rel="noopener nofollow noreferrer" >https://www.alteryx.com/</a><br>
自助数据分析的可重复工作流程</p>
<p>ELKI：开发由索引结构支持的KDD应用程序的 环境<br>
<a href="https://elki-project.github.io/" target="_blank" rel="noopener nofollow noreferrer" >https://elki-project.github.io/</a></p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[Spark-PySpark sql各种内置函数]]></title>
            <link href="https://blog.yunpiao.site/post/20200112103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20200112103946/</id>
            
            
            <published>2020-01-12T10:39:46+08:00</published>
            <updated>2020-01-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">  1</a></span><span>_functions <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">  2</a></span><span>    <span style="color:#e6db74">&#39;lit&#39;</span>: <span style="color:#e6db74">&#39;Creates a :class:`Column` of literal value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">  3</a></span><span>    <span style="color:#e6db74">&#39;col&#39;</span>: <span style="color:#e6db74">&#39;Returns a :class:`Column` based on the given column name.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">  4</a></span><span>    <span style="color:#e6db74">&#39;column&#39;</span>: <span style="color:#e6db74">&#39;Returns a :class:`Column` based on the given column name.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">  5</a></span><span>    <span style="color:#e6db74">&#39;asc&#39;</span>: <span style="color:#e6db74">&#39;Returns a sort expression based on the ascending order of the given column name.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6">  6</a></span><span>    <span style="color:#e6db74">&#39;desc&#39;</span>: <span style="color:#e6db74">&#39;Returns a sort expression based on the descending order of the given column name.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7">  7</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8">  8</a></span><span>    <span style="color:#e6db74">&#39;upper&#39;</span>: <span style="color:#e6db74">&#39;Converts a string expression to upper case.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9">  9</a></span><span>    <span style="color:#e6db74">&#39;lower&#39;</span>: <span style="color:#e6db74">&#39;Converts a string expression to upper case.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10"> 10</a></span><span>    <span style="color:#e6db74">&#39;sqrt&#39;</span>: <span style="color:#e6db74">&#39;Computes the square root of the specified float value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11"> 11</a></span><span>    <span style="color:#e6db74">&#39;abs&#39;</span>: <span style="color:#e6db74">&#39;Computes the absolute value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12"> 12</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13"> 13</a></span><span>    <span style="color:#e6db74">&#39;max&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the maximum value of the expression in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14"> 14</a></span><span>    <span style="color:#e6db74">&#39;min&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the minimum value of the expression in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15"> 15</a></span><span>    <span style="color:#e6db74">&#39;count&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the number of items in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16"> 16</a></span><span>    <span style="color:#e6db74">&#39;sum&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the sum of all values in the expression.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17"> 17</a></span><span>    <span style="color:#e6db74">&#39;avg&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the average of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18"> 18</a></span><span>    <span style="color:#e6db74">&#39;mean&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the average of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19"> 19</a></span><span>    <span style="color:#e6db74">&#39;sumDistinct&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the sum of distinct values in the expression.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20"> 20</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21"> 21</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22"> 22</a></span><span>_functions_1_4 <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23"> 23</a></span><span>    <span style="color:#75715e"># unary math functions</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-24"> 24</a></span><span>    <span style="color:#e6db74">&#39;acos&#39;</span>: <span style="color:#e6db74">&#39;Computes the cosine inverse of the given value; the returned angle is in the range&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-25"> 25</a></span><span>            <span style="color:#e6db74">&#39;0.0 through pi.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-26"> 26</a></span><span>    <span style="color:#e6db74">&#39;asin&#39;</span>: <span style="color:#e6db74">&#39;Computes the sine inverse of the given value; the returned angle is in the range&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-27"> 27</a></span><span>            <span style="color:#e6db74">&#39;-pi/2 through pi/2.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-28"> 28</a></span><span>    <span style="color:#e6db74">&#39;atan&#39;</span>: <span style="color:#e6db74">&#39;Computes the tangent inverse of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-29"> 29</a></span><span>    <span style="color:#e6db74">&#39;cbrt&#39;</span>: <span style="color:#e6db74">&#39;Computes the cube-root of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-30"> 30</a></span><span>    <span style="color:#e6db74">&#39;ceil&#39;</span>: <span style="color:#e6db74">&#39;Computes the ceiling of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-31"> 31</a></span><span>    <span style="color:#e6db74">&#39;cos&#39;</span>: <span style="color:#e6db74">&#39;Computes the cosine of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-32"> 32</a></span><span>    <span style="color:#e6db74">&#39;cosh&#39;</span>: <span style="color:#e6db74">&#39;Computes the hyperbolic cosine of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-33"> 33</a></span><span>    <span style="color:#e6db74">&#39;exp&#39;</span>: <span style="color:#e6db74">&#39;Computes the exponential of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-34"> 34</a></span><span>    <span style="color:#e6db74">&#39;expm1&#39;</span>: <span style="color:#e6db74">&#39;Computes the exponential of the given value minus one.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-35"> 35</a></span><span>    <span style="color:#e6db74">&#39;floor&#39;</span>: <span style="color:#e6db74">&#39;Computes the floor of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-36"> 36</a></span><span>    <span style="color:#e6db74">&#39;log&#39;</span>: <span style="color:#e6db74">&#39;Computes the natural logarithm of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-37"> 37</a></span><span>    <span style="color:#e6db74">&#39;log10&#39;</span>: <span style="color:#e6db74">&#39;Computes the logarithm of the given value in Base 10.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-38"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-38"> 38</a></span><span>    <span style="color:#e6db74">&#39;log1p&#39;</span>: <span style="color:#e6db74">&#39;Computes the natural logarithm of the given value plus one.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-39"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-39"> 39</a></span><span>    <span style="color:#e6db74">&#39;rint&#39;</span>: <span style="color:#e6db74">&#39;Returns the double value that is closest in value to the argument and&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-40"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-40"> 40</a></span><span>            <span style="color:#e6db74">&#39; is equal to a mathematical integer.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-41"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-41"> 41</a></span><span>    <span style="color:#e6db74">&#39;signum&#39;</span>: <span style="color:#e6db74">&#39;Computes the signum of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-42"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-42"> 42</a></span><span>    <span style="color:#e6db74">&#39;sin&#39;</span>: <span style="color:#e6db74">&#39;Computes the sine of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-43"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-43"> 43</a></span><span>    <span style="color:#e6db74">&#39;sinh&#39;</span>: <span style="color:#e6db74">&#39;Computes the hyperbolic sine of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-44"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-44"> 44</a></span><span>    <span style="color:#e6db74">&#39;tan&#39;</span>: <span style="color:#e6db74">&#39;Computes the tangent of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-45"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-45"> 45</a></span><span>    <span style="color:#e6db74">&#39;tanh&#39;</span>: <span style="color:#e6db74">&#39;Computes the hyperbolic tangent of the given value.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-46"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-46"> 46</a></span><span>    <span style="color:#e6db74">&#39;toDegrees&#39;</span>: <span style="color:#e6db74">&#39;.. note:: Deprecated in 2.1, use degrees instead.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-47"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-47"> 47</a></span><span>    <span style="color:#e6db74">&#39;toRadians&#39;</span>: <span style="color:#e6db74">&#39;.. note:: Deprecated in 2.1, use radians instead.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-48"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-48"> 48</a></span><span>    <span style="color:#e6db74">&#39;bitwiseNOT&#39;</span>: <span style="color:#e6db74">&#39;Computes bitwise not.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-49"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-49"> 49</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-50"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-50"> 50</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-51"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-51"> 51</a></span><span>_functions_1_6 <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-52"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-52"> 52</a></span><span>    <span style="color:#75715e"># unary math functions</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-53"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-53"> 53</a></span><span>    <span style="color:#e6db74">&#39;stddev&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the unbiased sample standard deviation of&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-54"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-54"> 54</a></span><span>              <span style="color:#e6db74">&#39; the expression in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-55"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-55"> 55</a></span><span>    <span style="color:#e6db74">&#39;stddev_samp&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the unbiased sample standard deviation of&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-56"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-56"> 56</a></span><span>                   <span style="color:#e6db74">&#39; the expression in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-57"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-57"> 57</a></span><span>    <span style="color:#e6db74">&#39;stddev_pop&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns population standard deviation of&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-58"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-58"> 58</a></span><span>                  <span style="color:#e6db74">&#39; the expression in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-59"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-59"> 59</a></span><span>    <span style="color:#e6db74">&#39;variance&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the population variance of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-60"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-60"> 60</a></span><span>    <span style="color:#e6db74">&#39;var_samp&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the unbiased variance of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-61"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-61"> 61</a></span><span>    <span style="color:#e6db74">&#39;var_pop&#39;</span>:  <span style="color:#e6db74">&#39;Aggregate function: returns the population variance of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-62"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-62"> 62</a></span><span>    <span style="color:#e6db74">&#39;skewness&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the skewness of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-63"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-63"> 63</a></span><span>    <span style="color:#e6db74">&#39;kurtosis&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns the kurtosis of the values in a group.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-64"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-64"> 64</a></span><span>    <span style="color:#e6db74">&#39;collect_list&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns a list of objects with duplicates.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-65"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-65"> 65</a></span><span>    <span style="color:#e6db74">&#39;collect_set&#39;</span>: <span style="color:#e6db74">&#39;Aggregate function: returns a set of objects with duplicate elements&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-66"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-66"> 66</a></span><span>                   <span style="color:#e6db74">&#39; eliminated.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-67"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-67"> 67</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-68"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-68"> 68</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-69"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-69"> 69</a></span><span>_functions_2_1 <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-70"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-70"> 70</a></span><span>    <span style="color:#75715e"># unary math functions</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-71"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-71"> 71</a></span><span>    <span style="color:#e6db74">&#39;degrees&#39;</span>: <span style="color:#e6db74">&#39;Converts an angle measured in radians to an approximately equivalent angle &#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-72"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-72"> 72</a></span><span>               <span style="color:#e6db74">&#39;measured in degrees.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-73"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-73"> 73</a></span><span>    <span style="color:#e6db74">&#39;radians&#39;</span>: <span style="color:#e6db74">&#39;Converts an angle measured in degrees to an approximately equivalent angle &#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-74"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-74"> 74</a></span><span>               <span style="color:#e6db74">&#39;measured in radians.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-75"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-75"> 75</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-76"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-76"> 76</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-77"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-77"> 77</a></span><span>_functions_2_2 <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-78"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-78"> 78</a></span><span>    <span style="color:#e6db74">&#39;to_date&#39;</span>: <span style="color:#e6db74">&#39;Converts a string date into a DateType using the (optionally) specified format.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-79"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-79"> 79</a></span><span>    <span style="color:#e6db74">&#39;to_timestamp&#39;</span>: <span style="color:#e6db74">&#39;Converts a string timestamp into a timestamp type using the &#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-80"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-80"> 80</a></span><span>                    <span style="color:#e6db74">&#39;(optionally) specified format.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-81"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-81"> 81</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-82"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-82"> 82</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-83"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-83"> 83</a></span><span><span style="color:#75715e"># math functions that take two arguments as input</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-84"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-84"> 84</a></span><span>_binary_mathfunctions <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-85"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-85"> 85</a></span><span>    <span style="color:#e6db74">&#39;atan2&#39;</span>: <span style="color:#e6db74">&#39;Returns the angle theta from the conversion of rectangular coordinates (x, y) to&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-86"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-86"> 86</a></span><span>             <span style="color:#e6db74">&#39;polar coordinates (r, theta).&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-87"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-87"> 87</a></span><span>    <span style="color:#e6db74">&#39;hypot&#39;</span>: <span style="color:#e6db74">&#39;Computes ``sqrt(a^2 + b^2)`` without intermediate overflow or underflow.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-88"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-88"> 88</a></span><span>    <span style="color:#e6db74">&#39;pow&#39;</span>: <span style="color:#e6db74">&#39;Returns the value of the first argument raised to the power of the second argument.&#39;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-89"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-89"> 89</a></span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-90"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-90"> 90</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-91"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-91"> 91</a></span><span>_window_functions <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-92"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-92"> 92</a></span><span>    <span style="color:#e6db74">&#39;row_number&#39;</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-93"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-93"> 93</a></span><span>        <span style="color:#e6db74">&#34;&#34;&#34;returns a sequential number starting at 1 within a window partition.&#34;&#34;&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-94"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-94"> 94</a></span><span>    <span style="color:#e6db74">&#39;dense_rank&#39;</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-95"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-95"> 95</a></span><span>        <span style="color:#e6db74">&#34;&#34;&#34;returns the rank of rows within a window partition, without any gaps.
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-96"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-96"> 96</a></span><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-97"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-97"> 97</a></span><span><span style="color:#e6db74">        The difference between rank and dense_rank is that dense_rank leaves no gaps in ranking
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-98"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-98"> 98</a></span><span><span style="color:#e6db74">        sequence when there are ties. That is, if you were ranking a competition using dense_rank
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-99"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-99"> 99</a></span><span><span style="color:#e6db74">        and had three people tie for second place, you would say that all three were in second
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-100"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-100">100</a></span><span><span style="color:#e6db74">        place and that the next person came in third. Rank would give me sequential numbers, making
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-101"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-101">101</a></span><span><span style="color:#e6db74">        the person that came in third place (after the ties) would register as coming in fifth.
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-102"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-102">102</a></span><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-103"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-103">103</a></span><span><span style="color:#e6db74">        This is equivalent to the DENSE_RANK function in SQL.&#34;&#34;&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-104"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-104">104</a></span><span>    <span style="color:#e6db74">&#39;rank&#39;</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-105"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-105">105</a></span><span>        <span style="color:#e6db74">&#34;&#34;&#34;returns the rank of rows within a window partition.
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-106"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-106">106</a></span><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-107"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-107">107</a></span><span><span style="color:#e6db74">        The difference between rank and dense_rank is that dense_rank leaves no gaps in ranking
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-108"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-108">108</a></span><span><span style="color:#e6db74">        sequence when there are ties. That is, if you were ranking a competition using dense_rank
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-109"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-109">109</a></span><span><span style="color:#e6db74">        and had three people tie for second place, you would say that all three were in second
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-110"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-110">110</a></span><span><span style="color:#e6db74">        place and that the next person came in third. Rank would give me sequential numbers, making
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-111"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-111">111</a></span><span><span style="color:#e6db74">        the person that came in third place (after the ties) would register as coming in fifth.
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-112"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-112">112</a></span><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-113"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-113">113</a></span><span><span style="color:#e6db74">        This is equivalent to the RANK function in SQL.&#34;&#34;&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-114"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-114">114</a></span><span>    <span style="color:#e6db74">&#39;cume_dist&#39;</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-115"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-115">115</a></span><span>        <span style="color:#e6db74">&#34;&#34;&#34;returns the cumulative distribution of values within a window partition,
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-116"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-116">116</a></span><span><span style="color:#e6db74">        i.e. the fraction of rows that are below the current row.&#34;&#34;&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-117"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-117">117</a></span><span>    <span style="color:#e6db74">&#39;percent_rank&#39;</span>:
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-118"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-118">118</a></span><span>        <span style="color:#e6db74">&#34;&#34;&#34;returns the relative rank (i.e. percentile) of rows within a window partition.&#34;&#34;&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-119"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-119">119</a></span><span>}
</span></span></code></pre></div>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[杂-记一次无脑的删除远程服务器路由表的修复行为]]></title>
            <link href="https://blog.yunpiao.site/post/20191212103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191212103946/</id>
            
            
            <published>2019-12-12T10:39:46+08:00</published>
            <updated>2019-12-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>上次写了学校登录VPN后访问不了内网, 这段时间一直没有想到解决方法, 昨晚突发奇想想修改默认路由表, 于是就在远程主机操作了一下 , 由于提示default 重复 , 于是很自然的想删除重建, 于是 无脑行为开始了.</p>
</blockquote>
<h2 id="1-当时我看到的路由表是这样的">1. 当时我看到的路由表是这样的</h2>
<p><code>sudo route</code></p>
<p>Kernel IP routing table<br>
Destination Gateway Genmask Flags Metric Ref Use Iface<br>
default 10.108.112.1 0.0.0.0 UG 100 0 0 eno1</p>
<h2 id="2-我无脑的删除了这条记录">2. 我无脑的删除了这条记录</h2>
<p><code>sudo route del default</code></p>
<h3 id="于是-我的ssh-断了">于是 我的ssh 断了&hellip;</h3>
<p>假设这有个瞠目结舌的表情包</p>
<h2 id="3-开始想解决方法我并不知道这个服务器在哪里">3. 开始想解决方法(我并不知道这个服务器在哪里)</h2>
<h3 id="３1--首先想到arp">３.1 . 首先想到arp</h3>
<p><code>sudo ARP</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">1</a></span><span>Address                  HWtype  HWaddress           Flags Mask            Iface
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">2</a></span><span>10.108.112.137           ether   6c:ae:8b:29:36:da   CM                    eno1
</span></span></code></pre></div><h3 id="此处已知mac地址">==此处已知mac地址==</h3>
<h3 id="32--想到ssh-能否用mac地址来连接">3.2 . 想到ssh 能否用mac地址来连接</h3>
<p>google 发现<br>
有人这样<br>
<code>sudo arp -s 192.168.1.200 00:35:cf:56:b2:2g temp &amp;&amp; ssh root@192.168.1.200 使用mac地址ssh</code></p>
<p>测试后ssh 无动于衷</p>
<h3 id="失败">==失败==</h3>
<h3 id="33--想到用-ipv6-扫描">3.3 . 想到用 ipv6 扫描</h3>
<h4 id="看了下nmap-支持ipv6-于是-想着扫描-结果ipv6-是2的64-个扫描域">看了下nmap 支持ipv6 于是 想着扫描, 结果ipv6 是2的64 个扫描域</h4>
<h3 id="失败-1">==失败==</h3>
<h3 id="34--google中发现有-邻居发现协议ndp救命稻草">3.4 . Google中发现有 邻居发现协议NDP(救命稻草)</h3>
<h4 id="linux-下">linux 下</h4>
<p><code>sudo ip neigh show</code><br>
<code>fe80::5639:dfff:fece:e9e7 dev eno1 lladdr 54:39:df:ce:e9:e7 router STALE</code></p>
<p>发现没有我要的 mac地址<br>
如果这个邻居发现不能广播询问周围的ipv6 伙伴 , 那这个就是无意义的</p>
<h4 id="查看ip-neigh-命令">查看ip neigh 命令</h4>
<p>发现有个发现 ip neigh show proxy<br>
执行后什么没什么动静<br>
想到可能会像ipv4 那样广播询问who is xxx</p>
<p>于是wireshark 抓包</p>
<p>过滤条件 ipv6</p>
<p><img loading='lazy' decoding="async" src="https://www.github.com/yunpiao/md/raw/img/1496731023594.jpg" alt="enter description here"  title="1496731023594" /></p>
<p>发现许多邻居返回ICMPVv6的arp包</p>
<p>再过滤<br>
eth.addr == 00:00:5e:00:53:00<br>
发现服务器返回了ipv6 地址</p>
<h3 id="胜利的曙光已经不远">==胜利的曙光已经不远==</h3>
<h2 id="4-最后的最后">4. 最后的最后</h2>
<p>ssh 链接上 ipv6 的服务器</p>
<p>添加默认路由表</p>
<p><code>sudo route add default 10.108.112.1</code></p>
<h3 id="完美解决">==完美解决==</h3>
<h2 id="5-参考">5. 参考</h2>
<ul>
<li><a href="http://www.2cto.com/net/201204/126259.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.2cto.com/net/201204/126259.html</a></li>
<li><a href="https://www.cellstream.com/intranet/faq/132-neighbor-discovery-nd-table-in-ipv6-windows-machines.html" target="_blank" rel="noopener nofollow noreferrer" >https://www.cellstream.com/intranet/faq/132-neighbor-discovery-nd-table-in-ipv6-windows-machines.html</a></li>
<li><a href="https://www.systutorials.com/docs/linux/man/8-ip-neighbour/" target="_blank" rel="noopener nofollow noreferrer" >https://www.systutorials.com/docs/linux/man/8-ip-neighbour/</a></li>
<li><a href="http://www.it165.net/network/html/201309/1183.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.it165.net/network/html/201309/1183.html</a></li>
<li><a href="http://qubaoquan.blog.51cto.com/1246748/292546" target="_blank" rel="noopener nofollow noreferrer" >http://qubaoquan.blog.51cto.com/1246748/292546</a></li>
<li><a href="http://www.packetlevel.ch/html/scapy/scapyipv6.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.packetlevel.ch/html/scapy/scapyipv6.html</a></li>
</ul>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[保险知识]]></title>
            <link href="https://blog.yunpiao.site/post/20191112103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191112103946/</id>
            
            
            <published>2019-11-12T10:39:46+08:00</published>
            <updated>2019-11-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>【金山文档】 保险分类知识思维导图 <a href="https://kdocs.cn/l/ciPNMcSpb98e" target="_blank" rel="noopener nofollow noreferrer" >https://kdocs.cn/l/ciPNMcSpb98e</a></p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/202404172136412.png" alt=""  /></p>
<h2 id="医疗保险">医疗保险</h2>
<p>凡是报销类的社保，要求治疗项目和药物必须在国家医保目录内，才可以报销。简称社保范围内用药。范围外的，不予报销。国家医保目录见链接：<a href="http://www.gov.cn/xinwen/2019-11/28/content_5456660.htm" target="_blank" rel="noopener nofollow noreferrer" >国家医保局、人力资源社会保障部印发 2019 年国家医保谈判准入药品名单</a></p>
<h2 id="团险">团险</h2>
<p>公司统一交的保险, 日常 6 险中多的那一险, 一般是意外险、医疗险, 具体根据公司选择的保险机构不同报销方案不同</p>
<h2 id="自由职业没工作交社保">自由职业/没工作交社保</h2>
<ol>
<li>
<p>在当地社保局以灵活就业人员身份，去申请缴纳职工医保和养老保险；</p>
</li>
<li>
<p>在户口所在地的社保局交居民医疗保险和居民养老保险。</p>
</li>
</ol>
<h2 id="寿险">寿险</h2>
<p>寿险按保障期限，可以分为定期寿险和终身寿险。定寿便宜，预算有限就先买来用着；终身寿险贵，适合预算足，想留笔钱给子女</p>
<p>赔付条件: 自己挂了，给家属钱</p>
<p>适合人群: 家庭支柱,</p>
<h2 id="重疾险">重疾险</h2>
<p>重疾险分类: 定期重疾险和终身重疾险，定期重疾险比终身重疾险便宜很多</p>
<p>重疾险・赔付条件 确诊即赔（部分疾病需达到合同约定条件），提前给付。</p>
<p>适合人群: 除了老人外的人。</p>
<p>重疾险对被保人的健康状况有要求，像高血压、有肺结节的人群很难投保</p>
<h2 id="医疗险">医疗险</h2>
<p>分类:医疗险常见的有百万医疗险、防癌医疗险和小额医疗险。</p>
<ol start="3">
<li>百万医疗险</li>
</ol>
<p>300+/ 年，是为治大病用的，不管是因为意外、癌症或其他疾病原因都可以报销（违法犯罪的除外！），通常有 1 万免赔额，所以基本也就住院才能用得到了，额度最高可以报销上百万。</p>
<ol start="4">
<li>防癌医疗险</li>
</ol>
<p>300～1000 / 年，是百万医疗险的精简版，只保因癌症导致的治疗，是专门给买不了百万医疗险提供的备胎。</p>
<ol start="5">
<li>小额医疗险</li>
</ol>
<p>500～600 / 年，是日常看小病用的，免赔额从零到几百不等，额度通常在两三万内，是百万医疗险的补充。</p>
<p>赔付条件: 医疗险都是报销型，花多少，报多少。仅限报销类花销</p>
<p>适合人群: 能买就买</p>
<h2 id="意外险">意外险</h2>
<p>分类: 综合意外险和交通工具意外险、猝死险、新冠隔离险等专项意外险。</p>
<p>理赔条件: 意外险的理赔有一个基础定义：所受到的伤害必须是外来的、突发的、非本意的、非疾病的，才会赔</p>
<p>适合人群: 便宜 尽量都买</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%94%9F%E6%B4%BB" term="%E7%94%9F%E6%B4%BB" label="生活" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E4%BF%9D%E9%99%A9" term="%E4%BF%9D%E9%99%A9" label="保险" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[囤货指南]]></title>
            <link href="https://blog.yunpiao.site/post/20191112103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191112103946/</id>
            
            
            <published>2019-11-12T10:39:46+08:00</published>
            <updated>2019-11-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>主要是提供思路，不是让大家全买了，结合自身情况按需选择，有条件提前<strong>买一个冰箱。</strong></p>
<h3 id="食物类">食物类</h3>
<p>**小贴示：**根据SH经验，经常发的物资是土豆、萝卜、洋葱、西葫芦，空间有限可少囤。</p>
<h4 id="基本生活类">基本生活类</h4>
<p><strong>主食</strong>：大米、杂粮米（小绿豆、黑米、糙米、藜麦）、面粉、挂面、意大利面、方便面、自嗨锅、米粉、即食麦片、谷物粉、各种烘培材料（黄油酵母、淡奶油、泡打粉等）<br>
<strong>蛋类</strong>：鸡蛋、咸鸭蛋、皮蛋<br>
<strong>油</strong>：食用油、橄榄油<br>
<strong>面包</strong>（法棍可以放到天荒地老，普通面包冷冻保存，方三个月没问题，吃的时候提前一晚上拿出来解冻）</p>
<h4 id="存放较久的果蔬类">存放较久的果蔬类</h4>
<p><strong>根茎菜</strong>：胡萝卜、洋葱、土豆、红薯、玉米、山药、地瓜、南瓜、冬瓜、萝卜、藕、青椒、茄子、豆角、西葫芦<br>
<strong>绿叶菜</strong>：大白菜、卷心菜、芹菜、茼蒿、娃娃菜（青菜等叶菜可以焯水后挤干分装冷冻保存）<br>
<strong>水果</strong>：苹果（与其他果蔬粉开放）、橙子、柚子、菠萝、石榴、桃子、龙岩、葡萄、香蕉</p>
<h4 id="冻货类占用冰箱">冻货类（占用冰箱）</h4>
<p><strong>冷冻蔬菜类</strong>：玉米粒、青豆、胡萝卜、西兰花<br>
<strong>肉类</strong>：五花肉、肉馅、鸡翅、牛腩、牛扒、肥牛卷、羊排、猪肋排、羊肉卷、糯米鸡、香肠、培根、小酥肉、牛肉丸<br>
<strong>海鲜类</strong>：带鱼、黄花鱼、龙利鱼、鳕鱼、虾仁、扇贝、<br>
<strong>方便成品</strong>：水饺、馄饨、烧卖、汤圆、乌冬面、冷冻披萨、西贝奶酪饼、火锅丸子</p>
<h4 id="保质期长首选">保质期长（首选）</h4>
<p><strong>罐头</strong>：午餐肉、水果、蕃茄丁、八宝饭、红烧肉罐头<br>
<strong>干货</strong>：木耳、菌菇、海带、裙带菜、黄花菜、紫菜、干豆角、笋干、脱水蔬菜、粉丝、海米、枸杞、芝麻、干贝<br>
<strong>豆制品</strong>：豆干、豆皮、豆筋、冻豆腐<br>
**王炸类：**火锅底料（保质期长，煮菜、猪肉、煮面红用）、压缩饼干、军用罐头</p>
<h4 id="酱料类">酱料类</h4>
<p>**葱姜蒜：**冷冻葱花、蒜蓉、冷冻姜片、冷冻香菜<br>
**基础调料：**盐、味精、鸡精、酱油、醋、糖、蚝油、料酒、豆瓣酱、黄豆酱、玉米淀粉、姜汁、姜粉<br>
**香料：**辣椒粉、干辣椒、野山椒、大料、花椒、花椒粉、胡椒粉、十三香<br>
<strong>酱菜</strong>：豆豉、榨菜、橄榄菜、梅干菜、辣白菜、酸黄瓜、酸菜</p>
<h4 id="零食饮品类">零食饮品类</h4>
<p>奶类：常温牛奶、酸奶、豆奶<br>
<strong>冲泡类</strong>：豆浆粉、黑芝麻糊、玉米糊；速溶燕麦、全脂奶粉、可可粉<br>
<strong>咖啡</strong>（容易被忽视，却又那么必需，家里有机器的就屯咖啡豆、咖啡粉）、茶叶、饮料（特别可乐）、纯净水、巧克力、冰激凌（占用冷冻空间，非必需不推荐）<br>
<strong>坚果</strong>：开心果、腰果、松子、核桃、杏仁、夏威夷果、每日坚果</p>
<h3 id="生活类">生活类</h3>
<h4 id="消耗品">消耗品</h4>
<p>卷纸、抽纸、安心裤、卫生巾、卫生棉条<br>
口罩、洗手液、消毒液、酒精喷雾<br>
湿纸巾、厨房纸、消毒湿巾、厨房湿巾</p>
<h4 id="日化用品">日化用品</h4>
<p>洗发水、沐浴露、护发素、香皂、肥皂、牙线、牙膏、面膜、身体乳<br>
洗碗布、洗洁精、洁厕灵、洗衣液、衣物柔顺剂<br>
皮搋子、一次性手套</p>
<h4 id="杂七杂八">杂七杂八</h4>
<p>电池（尤其燃气灶、热水器、遥控器电池，看好型号）<br>
垃圾袋、保鲜膜、一次性手套、密封袋<br>
检查菜刀状态，准备备用刀/磨刀石<br>
喇叭扩音器（紧急情况求助用）</p>
<h3 id="药品护理类">药品/护理类</h3>
<p>**小贴示：**使用前请咨询专业医生。</p>
<h4 id="药品类">药品类</h4>
<p>退烧止痛：美林/泰诺林/布洛芬（推荐精氨酸布洛芬颗粒）/对乙酰氨基酚<br>
消炎：罗红霉素/头孢/甲硝锉、红霉素软膏<br>
应急：云南白药喷雾、红花油、泻立停<br>
保健：维生素C、维生素D、综合维生素、钙片、补液盐、润喉糖、华素片<br>
老年保健：鱼油、Q10等<br>
孕期/备孕期保健：叶酸、DHA</p>
<h4 id="护理类">护理类</h4>
<p>体温计（推荐耳温枪）、纱布、创可贴、碘酒、棉签、百多邦、生理盐水、炉甘石、凡士林、开塞露、眼药水；<br>
有喘、喉炎史的：雾化机、雾化罐、相应药物<br>
血压仪、血糖仪</p>
<h3 id="孩子类">孩子类</h3>
<h4 id="宝宝生活">宝宝生活</h4>
<p>纸尿裤、拉拉裤、沐浴液、润肤霜、屁屁霜、理发器<br>
大宝宝：玩具、绘本、桌游、乐高、鸡娃资料</p>
<h4 id="宝宝饮食">宝宝饮食</h4>
<p>奶粉、维D、西梅泥（防便秘）<br>
宝宝做饭核桃油、宝宝细面、宝宝咖喱、宝宝酱油、宝宝肉松、零食、拌饭料</p>
<h4 id="宝宝药品与护理">宝宝药品与护理</h4>
<p>宝宝退烧药（美林/泰诺林）、咳嗽药、补液盐、退热贴、开塞露</p>
<h3 id="宠物类">宠物类</h3>
<p>猫粮、狗粮、宠物罐头、猫砂、鸟食鱼食、常用护理品及常用药、宠物指甲刀；</p>
<h3 id="运动健身">运动健身</h3>
<p>瑜伽垫、跑步机/划船机/椭圆机（择一即可）、跳绳、弹力带、筋膜枪</p>
<h3 id="特殊嗜好者">特殊嗜好者</h3>
<p>烟、酒、打火机、火柴</p>
<h3 id="有备无患">有备无患</h3>
<p>提前充好水、电、煤气、中水、热水（谁知道到时候会不会充不上）<br>
囤联系方式：如快递小哥、外卖跑腿小哥、货拉拉司机、附近菜场小卖部老板电话<br>
<strong>#万一被拉走系列#</strong> 一次性内裤、驱蚊喷雾、电蚊香、充电器、充电宝、耳塞、眼罩、消毒湿巾、牙线、指甲剪、Kindle、Ipad、电脑、扑克、书籍、洗漱用品</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E7%94%9F%E6%B4%BB" term="%E7%94%9F%E6%B4%BB" label="生活" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ML-k近邻最近邻]]></title>
            <link href="https://blog.yunpiao.site/post/20191012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191012103946/</id>
            
            
            <published>2019-10-12T10:39:46+08:00</published>
            <updated>2019-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>#1.KNN介绍<br>
#1.KNN介绍<br>
k临近算法 也叫Knn 是一种基于样本的模型,当K取值为1时<br>
算法原理是 对于新的实例, 在训练集中找到距离最相近的K个样本,根据这K个样本的所属的类别,来归类这个新样本.<br>
采取多数表决机制. 多数表决等价于经验风险最小化</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-3494d30474e703c0.png" alt="维基百科的图"  /><br>
#2.K近邻的距离度量方法<br>
由于KNN是基于实例的, 所以要进行距离度量,常见的距离度量方法有</p>
<ul>
<li>
<p>欧氏距离<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-79f24173b0389bac.png" alt="欧氏距离.png"  /></p>
</li>
<li>
<p>曼哈顿距离<br>
曼哈顿距离 指的是在坐标轴上的投影距离<br>
二维空间点的曼哈顿距离<br>
<code>|x1-x2|+|y1-y2|</code></p>
</li>
</ul>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-893e4d97a6e7fd5f.png" alt="曼哈顿距离"  /></p>
<ul>
<li>切比雪夫距离<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-0b654d5c17edb603.png" alt="切比雪夫距离"  /></li>
<li>余弦夹角<br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-3ad6e72cb1d3b98d.png" alt="余弦夹角"  /></li>
</ul>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-f6f5006ba8a70bac.png" alt="各种距离的算法实现.png"  /><br>
<img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-1b2a37afdc3c1fbc.png" alt="image.png"  /></p>
<p>还有许多距离算法 以后再慢慢写</p>
<h1 id="3-k值的选择">3 k值的选择</h1>
<h1 id="4-算法实现-kd树">4 算法实现 KD树</h1>
<h2 id="knn的实现方法">knn的实现方法</h2>
<h3 id="1-线性扫描">1. 线性扫描</h3>
<p>线性扫描是对整个数据集进行遍历,计算每个输入实例与数据集的向量距离,时间复杂度很高.</p>
<h3 id="--2-kd树">- 2. kd树</h3>
<p>对k维空间中的数据进行存储,并进行检索的树形数据结构</p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-045993663759a999.png" alt="k维生成kd树的例子"  /></p>
<p><img loading='lazy' decoding="async" src="https://img.yunpiao.site/blog/10970403-bb12a10cf3070ef9.png" alt="检索kd树的例子"  /></p>
<p>参考网址</p>
<ul>
<li><a href="http://blog.csdn.net/v_july_v/article/details/8203674" target="_blank" rel="noopener nofollow noreferrer" >http://blog.csdn.net/v_july_v/article/details/8203674</a></li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ML-spark 使用ml步骤]]></title>
            <link href="https://blog.yunpiao.site/post/20191012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191012103946/</id>
            
            
            <published>2019-10-12T10:39:46+08:00</published>
            <updated>2019-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>使用大数据工具进行数据预测</p>
</blockquote>
<pre tabindex="0"><code class="language-scale" data-lang="scale">import org.apache.spark.ml.Pipeline
import org.apache.spark.ml.classification._
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
import org.apache.spark.ml.feature.{IndexToString, StringIndexer, VectorAssembler}
import org.apache.spark.ml.param.ParamMap
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}
 
 
object ClassificationPipeline {
 def main(args: Array[String]) {
 if (args.length &lt; 1){
 println(&#34;Usage:ClassificationPipeline inputDataFile&#34;)
 sys.exit(1)
 }
 val conf = new SparkConf().setAppName(&#34;Classification with ML Pipeline&#34;)
 val sc = new SparkContext(conf)
 val sqlCtx = new SQLContext(sc)
</code></pre><h2 id="step-1">Step 1</h2>
<p>读取原始数据</p>
<ul>
<li>3.6216,8.6661,-2.8073,-0.44699,0</li>
<li>4.5459,8.1674,-2.4586,-1.4621,0</li>
<li>3.866,-2.6383,1.9242,0.10645,0</li>
<li>3.4566,9.5228,-4.0112,-3.5944,0</li>
<li>0.32924,-4.4552,4.5718,-0.9888,0</li>
<li>&hellip; &hellip;<br>
*/</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span> <span style="color:#66d9ef">val</span> parsedRDD <span style="color:#66d9ef">=</span> sc<span style="color:#f92672">.</span>textFile<span style="color:#f92672">(</span>args<span style="color:#f92672">(</span><span style="color:#ae81ff">0</span><span style="color:#f92672">)).</span>map<span style="color:#f92672">(</span><span style="color:#66d9ef">_</span><span style="color:#f92672">.</span>split<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;,&#34;</span><span style="color:#f92672">)).</span>map<span style="color:#f92672">(</span>eachRow <span style="color:#66d9ef">=&gt;</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span> <span style="color:#66d9ef">val</span> a <span style="color:#66d9ef">=</span> eachRow<span style="color:#f92672">.</span>map<span style="color:#f92672">(</span>x <span style="color:#66d9ef">=&gt;</span> x<span style="color:#f92672">.</span>toDouble<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-3">3</a></span><span> <span style="color:#f92672">(</span>a<span style="color:#f92672">(</span><span style="color:#ae81ff">0</span><span style="color:#f92672">),</span>a<span style="color:#f92672">(</span><span style="color:#ae81ff">1</span><span style="color:#f92672">),</span>a<span style="color:#f92672">(</span><span style="color:#ae81ff">2</span><span style="color:#f92672">),</span>a<span style="color:#f92672">(</span><span style="color:#ae81ff">3</span><span style="color:#f92672">),</span>a<span style="color:#f92672">(</span><span style="color:#ae81ff">4</span><span style="color:#f92672">))</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-4">4</a></span><span> <span style="color:#f92672">})</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-5">5</a></span><span> <span style="color:#66d9ef">val</span> df <span style="color:#66d9ef">=</span> sqlCtx<span style="color:#f92672">.</span>createDataFrame<span style="color:#f92672">(</span>parsedRDD<span style="color:#f92672">).</span>toDF<span style="color:#f92672">(</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-6">6</a></span><span> <span style="color:#e6db74">&#34;f0&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f1&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f2&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f3&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;label&#34;</span><span style="color:#f92672">).</span>cache<span style="color:#f92672">()</span>
</span></span></code></pre></div><h2 id="step-2">Step 2</h2>
<p>为了容易使用机器学习算法 设置lable index 从0开始</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-1">1</a></span><span><span style="color:#66d9ef">val</span> labelIndexer <span style="color:#66d9ef">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">StringIndexer</span><span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-2">2</a></span><span> <span style="color:#f92672">.</span>setInputCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;label&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-3">3</a></span><span> <span style="color:#f92672">.</span>setOutputCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;indexedLabel&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-2-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-2-4">4</a></span><span> <span style="color:#f92672">.</span>fit<span style="color:#f92672">(</span>df<span style="color:#f92672">)</span>
</span></span></code></pre></div><h2 id="step-3">Step 3</h2>
<p>定义特征列</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-1">1</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-2">2</a></span><span><span style="color:#66d9ef">val</span> vectorAssembler <span style="color:#66d9ef">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">VectorAssembler</span><span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-3">3</a></span><span><span style="color:#f92672">.</span>setInputCols<span style="color:#f92672">(</span><span style="color:#a6e22e">Array</span><span style="color:#f92672">(</span><span style="color:#e6db74">&#34;f0&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f1&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f2&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f3&#34;</span><span style="color:#f92672">))</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-3-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-3-4">4</a></span><span><span style="color:#f92672">.</span>setOutputCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;featureVector&#34;</span><span style="color:#f92672">)</span>
</span></span></code></pre></div><h2 id="step-4">Step 4</h2>
<p>创建随机森林分类器</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-1">1</a></span><span><span style="color:#66d9ef">val</span> rfClassifier <span style="color:#66d9ef">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">RandomForestClassifier</span><span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-2">2</a></span><span> <span style="color:#f92672">.</span>setLabelCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;indexedLabel&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-3">3</a></span><span> <span style="color:#f92672">.</span>setFeaturesCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;featureVector&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-4-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-4-4">4</a></span><span> <span style="color:#f92672">.</span>setNumTrees<span style="color:#f92672">(</span><span style="color:#ae81ff">5</span><span style="color:#f92672">)</span>
</span></span></code></pre></div><h2 id="step-5">Step 5</h2>
<p>转换lable列 到原始数据</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-1">1</a></span><span><span style="color:#66d9ef">val</span> labelConverter <span style="color:#66d9ef">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">IndexToString</span><span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-2">2</a></span><span> <span style="color:#f92672">.</span>setInputCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;prediction&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-3">3</a></span><span> <span style="color:#f92672">.</span>setOutputCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;predictedLabel&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-5-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-5-4">4</a></span><span> <span style="color:#f92672">.</span>setLabels<span style="color:#f92672">(</span>labelIndexer<span style="color:#f92672">.</span>labels<span style="color:#f92672">)</span>
</span></span></code></pre></div><h2 id="step-6">Step 6</h2>
<p>拆分数据</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-6-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-6-1">1</a></span><span><span style="color:#66d9ef">val</span> <span style="color:#a6e22e">Array</span><span style="color:#f92672">(</span>trainingData<span style="color:#f92672">,</span> testData<span style="color:#f92672">)</span> <span style="color:#66d9ef">=</span> df<span style="color:#f92672">.</span>randomSplit<span style="color:#f92672">(</span><span style="color:#a6e22e">Array</span><span style="color:#f92672">(</span><span style="color:#ae81ff">0.8</span><span style="color:#f92672">,</span> <span style="color:#ae81ff">0.2</span><span style="color:#f92672">))</span><span style="color:#960050;background-color:#1e0010">`</span>
</span></span></code></pre></div><h2 id="step-7">Step 7</h2>
<p>创建 ML pipeline .</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-1">1</a></span><span><span style="color:#66d9ef">val</span> pipeline <span style="color:#66d9ef">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Pipeline</span><span style="color:#f92672">().</span>setStages<span style="color:#f92672">(</span><span style="color:#a6e22e">Array</span><span style="color:#f92672">(</span>labelIndexer<span style="color:#f92672">,</span>vectorAssembler<span style="color:#f92672">,</span>rfClassifier<span style="color:#f92672">,</span>labelConverter<span style="color:#f92672">))</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-7-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-7-2">2</a></span><span> <span style="color:#66d9ef">val</span> model <span style="color:#66d9ef">=</span> pipeline<span style="color:#f92672">.</span>fit<span style="color:#f92672">(</span>trainingData<span style="color:#f92672">)</span>
</span></span></code></pre></div><h2 id="step-8">Step 8</h2>
<p>设置填充数据预测</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-8-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-8-1">1</a></span><span><span style="color:#66d9ef">val</span> predictionResultDF <span style="color:#66d9ef">=</span> model<span style="color:#f92672">.</span>transform<span style="color:#f92672">(</span>testData<span style="color:#f92672">)</span><span style="color:#960050;background-color:#1e0010">`</span>
</span></span></code></pre></div><h2 id="step-9">Step 9</h2>
<p>选择标签行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-9-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-9-1">1</a></span><span>predictionResultDF<span style="color:#f92672">.</span>select<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;f0&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f1&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f2&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;f3&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;label&#34;</span><span style="color:#f92672">,</span><span style="color:#e6db74">&#34;predictedLabel&#34;</span><span style="color:#f92672">).</span>show<span style="color:#f92672">(</span><span style="color:#ae81ff">20</span><span style="color:#f92672">)</span><span style="color:#960050;background-color:#1e0010">`</span>
</span></span></code></pre></div><h2 id="step-10">Step 10</h2>
<p>输出准确率</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-1">1</a></span><span><span style="color:#66d9ef">val</span> evaluator <span style="color:#66d9ef">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">MulticlassClassificationEvaluator</span><span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-2">2</a></span><span> <span style="color:#f92672">.</span>setLabelCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;label&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-3">3</a></span><span> <span style="color:#f92672">.</span>setPredictionCol<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;prediction&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-4">4</a></span><span> <span style="color:#f92672">.</span>setMetricName<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;precision&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-5">5</a></span><span> <span style="color:#66d9ef">val</span> predictionAccuracy <span style="color:#66d9ef">=</span> evaluator<span style="color:#f92672">.</span>evaluate<span style="color:#f92672">(</span>predictionResultDF<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-10-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-10-6">6</a></span><span> println<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;Testing Error = &#34;</span> <span style="color:#f92672">+</span> <span style="color:#f92672">(</span><span style="color:#ae81ff">1.0</span> <span style="color:#f92672">-</span> predictionAccuracy<span style="color:#f92672">))</span>
</span></span></code></pre></div><h2 id="step-11">Step 11</h2>
<p>保存模型</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scala" data-lang="scala"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-1">1</a></span><span><span style="color:#66d9ef">val</span> randomForestModel <span style="color:#66d9ef">=</span> model<span style="color:#f92672">.</span>stages<span style="color:#f92672">(</span><span style="color:#ae81ff">2</span><span style="color:#f92672">).</span>asInstanceOf<span style="color:#f92672">[</span><span style="color:#66d9ef">RandomForestClassificationModel</span><span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-2">2</a></span><span> println<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;Trained Random Forest Model is:\n&#34;</span> <span style="color:#f92672">+</span> randomForestModel<span style="color:#f92672">.</span>toDebugString<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-3">3</a></span><span> <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-11-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-11-4">4</a></span><span><span style="color:#f92672">}</span><span style="color:#960050;background-color:#1e0010">```</span>
</span></span></code></pre></div>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ML-决策树 随机森林学习]]></title>
            <link href="https://blog.yunpiao.site/post/20191012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191012103946/</id>
            
            
            <published>2019-10-12T10:39:46+08:00</published>
            <updated>2019-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<p><img loading='lazy' decoding="async" src="http://upload-images.jianshu.io/upload_images/10970403-b5e84597340b867b?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"  /></p>
<h2 id="连续值处理">连续值处理</h2>
<hr>
<p>西瓜书的例子</p>
<ul>
<li>Temperature: 40 48 60 72 80 90</li>
<li>PlayTennis: No No Yes Yes Yes No</li>
</ul>
<hr>
<p>决策树学习是一种逼近离散值目标函数的方法，在这种方法中学习到的函数被表示 为一棵决策树。学习得到的决策树也能再被表示为多个 if-then 的规则，以提高可读性。 这种学习算法是最流行的归纳推理算法之一，已经被成功地应用到从学习医疗诊断到学 习评估贷款申请的信用风险的广阔领域。</p>
<p>对属性 Temprature，首先按照连续属性 A 排序样例，然后确定目标分类不同的 相邻实例，于是我们可以产生一组候选阈值，它们的值是相应的 A 值之间的中间值。 可以证明产生最大信息增益的 c 值必定位于这样的边界中(Fayyad 1991)。然后可以通过计算与每个候选阈值关联的信息增益评估这些候选值。在当前的例子中，有两个候选 阈值，它们对应于目标属性 PlayTennis 变化时属性 Temperature 的值:(48+60)/2 和 (80+90)/2。然后计算每一个候选属性——Temperature&gt;54 和Temperature&gt;85的信息增 益，并选择最好的(Temperature&gt;54)。现在这个动态创建的布尔属性便可以和其他候选 的离散值属性一同“竞争”，以用于增长决策树。Fayyad &amp; Irani(1993)讨论了这种方 法的一个扩展，即把连续的属性分割成多个区间，而不是基于单一阈值的两个区间。 Utgoff &amp; Brodley(1991)和 Murthy et al.(1994)讨论了通过对几个连续值属性的线性 组合定义阈值参数的方法。</p>
<h2 id="c45缺失值处理">c4.5缺失值处理</h2>
<hr>
<p>1）如何选择划分属性。</p>
<p><img loading='lazy' decoding="async" src="http://upload-images.jianshu.io/upload_images/10970403-d1f7d45c8f3628e0?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"  /></p>
<p>2）给定划分属性，若某样本在该属性上缺失值，如何划分到具体的分支上。</p>
<p>按不同权重划分到每个节点，例如给定一个布尔属性 A，如果结点 n 包含 6 个已知 A=1 和 4个 A=0 的样例， 那么 A(x)=1 的概率是 0.6，A(x)=0 的概率是 0.4。于是，实例 x 的 60%被分配到 A=1 的 分支，40%被分配到另一个分支。</p>
<h2 id="其他缺失值处理">其他缺失值处理</h2>
<p>处理不完备数据集的方法主要有以下三大类：</p>
<p>####（一）删除元组</p>
<p>也就是将存在遗漏信息属性值的对象（元组，记录）删除，从而得到一个完备的信息表。这种方法简单易行，在对象有多个属性缺失值、被删除的含缺失值的对象与信息表中的数据量相比非常小的情况下是非常有效的，类标号（假设是分类任务）缺少时通常使用。然而，这种方法却有很大的局限性。它是以减少历史数据来换取信息的完备，会造成资源的大量浪费，丢弃了大量隐藏在这些对象中的信息。在信息表中本来包含的对象很少的情况下，删除少量对象就足以严重影响到信息表信息的客观性和结果的正确性；当每个属性空值的百分比变化很大时，它的性能非常差。因此，当遗漏数据所占比例较大，特别当遗漏数据非随机分布时，这种方法可能导致数据发生偏离，从而引出错误的结论。</p>
<p>####（二）数据补齐</p>
<p>这类方法是用一定的值去填充空值，从而使信息表完备化。通常基于统计学原理，根据决策表中其余对象取值的分布情况来对一个空值进行填充，譬如用其余属性的平均值来进行补充等。数据挖掘中常用的有以下几种补齐方法：</p>
<h5 id="1人工填写filling-manually">(1)人工填写（filling manually）</h5>
<p>由于最了解数据的还是用户自己，因此这个方法产生数据偏离最小，可能是填充效果最好的一种。然而一般来说，该方法很费时，当数据规模很大、空值很多的时候，该方法是不可行的。</p>
<h5 id="2特殊值填充treating-missing-attribute-values-as-special-values">(2)特殊值填充（Treating Missing Attribute values as Special values）</h5>
<p>将空值作为一种特殊的属性值来处理，它不同于其他的任何属性值。如所有的空值都用“unknown”填充。这样将形成另一个有趣的概念，可能导致严重的数据偏离，一般不推荐使用。</p>
<h5 id="3平均值填充meanmode-completer">(3)平均值填充（Mean/Mode Completer）</h5>
<p>将信息表中的属性分为数值属性和非数值属性来分别进行处理。如果空值是数值型的，就根据该属性在其他所有对象的取值的平均值来填充该缺失的属性值；如果空值是非数值型的，就根据统计学中的众数原理，用该属性在其他所有对象的取值次数最多的值(即出现频率最高的值)来补齐该缺失的属性值。另外有一种与其相似的方法叫条件平均值填充法（Conditional Mean Completer）。在该方法中，缺失属性值的补齐同样是靠该属性在其他对象中的取值求平均得到，但不同的是用于求平均的值并不是从信息表所有对象中取，而是从与该对象具有相同决策属性值的对象中取得。这两种数据的补齐方法，其基本的出发点都是一样的，以最大概率可能的取值来补充缺失的属性值，只是在具体方法上有一点不同。与其他方法相比，它是用现存数据的多数信息来推测缺失值。</p>
<h5 id="4热卡填充hot-deck-imputation或就近补齐">(4)热卡填充（Hot deck imputation，或就近补齐）</h5>
<p>对于一个包含空值的对象，热卡填充法在完整数据中找到一个与它最相似的对象，然后用这个相似对象的值来进行填充。不同的问题可能会选用不同的标准来对相似进行判定。该方法概念上很简单，且利用了数据间的关系来进行空值估计。这个方法的缺点在于难以定义相似标准，主观因素较多。</p>
<h5 id="5k最近距离邻法k-means-clustering">(5)K最近距离邻法（K-means clustering）</h5>
<p>先根据欧式距离或相关分析来确定距离具有缺失数据样本最近的K个样本，将这K个值加权平均来估计该样本的缺失数据。</p>
<p>同均值插补的方法都属于单值插补，不同的是，它用层次聚类模型预测缺失变量的类型，再以该类型的均值插补。假设X=(X1,X2…Xp)为信息完全的变量，Y为存在缺失值的变量，那么首先对X或其子集行聚类，然后按缺失个案所属类来插补不同类的均值。如果在以后统计分析中还需以引入的解释变量和Y做分析，那么这种插补方法将在模型中引入自相关，给分析造成障碍。</p>
<h5 id="6使用所有可能的值填充assigning-all-possible-values-of-the-attribute">(6)使用所有可能的值填充（Assigning All Possible values of the Attribute）</h5>
<p>这种方法是用空缺属性值的所有可能的属性取值来填充，能够得到较好的补齐效果。但是，当数据量很大或者遗漏的属性值较多时，其计算的代价很大，可能的测试方案很多。另有一种方法，填补遗漏属性值的原则是一样的，不同的只是从决策相同的对象中尝试所有的属性值的可能情况，而不是根据信息表中所有对象进行尝试，这样能够在一定程度上减小原方法的代价。</p>
<h5 id="7组合完整化方法combinatorial-completer">(7)组合完整化方法（Combinatorial Completer）</h5>
<p>这种方法是用空缺属性值的所有可能的属性取值来试，并从最终属性的约简结果中选择最好的一个作为填补的属性值。这是以约简为目的的数据补齐方法，能够得到好的约简结果；但是，当数据量很大或者遗漏的属性值较多时，其计算的代价很大。另一种称为条件组合完整化方法（Conditional Combinatorial Complete），填补遗漏属性值的原则是一样的，不同的只是从决策相同的对象中尝试所有的属性值的可能情况，而不是根据信息表中所有对象进行尝试。条件组合完整化方法能够在一定程度上减小组合完整化方法的代价。在信息表包含不完整数据较多的情况下，可能的测试方案将巨增。</p>
<h5 id="8回归regression">(8)回归（Regression）</h5>
<p>基于完整的数据集，建立回归方程（模型）。对于包含空值的对象，将已知属性值代入方程来估计未知属性值，以此估计值来进行填充。当变量不是线性相关或预测变量高度相关时会导致有偏差的估计。</p>
<h5 id="9期望值最大化方法expectation-maximizationem">(9)期望值最大化方法（Expectation maximization，EM）</h5>
<p>在缺失类型为随机缺失的条件下，假设模型对于完整的样本是正确的，那么通过观测数据的边际分布可以对未知参数进行极大似然估计（Little and Rubin）。这种方法也被称为忽略缺失值的极大似然估计，对于极大似然的参数估计实际中常采用的计算方法是期望值最大化(Expectation Maximization，EM）。该方法比删除个案和单值插补更有吸引力，它一个重要前提：适用于大样本。有效样本的数量足够以保证ML估计值是渐近无偏的并服从正态分布。但是这种方法可能会陷入局部极值，收敛速度也不是很快，并且计算很复杂。</p>
<p>EM算法是一种在不完全数据情况下计算极大似然估计或者后验分布的迭代算法。在每一迭代循环过程中交替执行两个步骤：E步（Excepctaion step,期望步），在给定完全数据和前一次迭代所得到的参数估计的情况下计算完全数据对应的对数似然函数的条件期望；M步（Maximzation step，极大化步），用极大化对数似然函数以确定参数的值，并用于下步的迭代。算法在E步和M步之间不断迭代直至收敛，即两次迭代之间的参数变化小于一个预先给定的阈值时结束。该方法可能会陷入局部极值，收敛速度也不是很快，并且计算很复杂。</p>
<h5 id="10多重填补multiple-imputationmi">(10)多重填补（Multiple Imputation，MI）</h5>
<p>多值插补的思想来源于贝叶斯估计，认为待插补的值是随机的，它的值来自于已观测到的值。具体实践上通常是估计出待插补的值，然后再加上不同的噪声，形成多组可选插补值。根据某种选择依据，选取最合适的插补值。</p>
<p>多重填补方法分为三个步骤：;为每个空值产生一套可能的填补值，这些值反映了无响应模型的不确定性；每个值都被用来填补数据集中的缺失值，产生若干个完整数据集合。;每个填补数据集合都用针对完整数据集的统计方法进行统计分析。;对来自各个填补数据集的结果进行综合，产生最终的统计推断，这一推断考虑到了由于数据填补而产生的不确定性。该方法将空缺值视为随机样本，这样计算出来的统计推断可能受到空缺值的不确定性的影响。该方法的计算也很复杂。</p>
<p>多重插补方法分为三个步骤：①为每个空值产生一套可能的插补值，这些值反映了无响应模型的不确定性；每个值都可以被用来插补数据集中的缺失值，产生若干个完整数据集合。②每个插补数据集合都用针对完整数据集的统计方法进行统计分析。③对来自各个插补数据集的结果，根据评分函数进行选择，产生最终的插补值。</p>
<p>假设一组数据，包括三个变量Y1，Y2，Y3，它们的联合分布为正态分布，将这组数据处理成三组，A组保持原始数据，B组仅缺失Y3，C组缺失Y1和Y2。在多值插补时，对A组将不进行任何处理，对B组产生Y3的一组估计值（作Y3关于Y1，Y2的回归），对C组作产生Y1和Y2的一组成对估计值（作Y1，Y2关于Y3的回归）。</p>
<p>当用多值插补时，对A组将不进行处理，对B、C组将完整的样本随机抽取形成为m组（m为可选择的m组插补值），每组个案数只要能够有效估计参数就可以了。对存在缺失值的属性的分布作出估计，然后基于这m组观测值，对于这m组样本分别产生关于参数的m组估计值，给出相应的预测即，这时采用的估计方法为极大似然法，在计算机中具体的实现算法为期望最大化法（EM）。对B组估计出一组Y3的值，对C将利用 Y1,Y2,Y3它们的联合分布为正态分布这一前提，估计出一组(Y1，Y2）。</p>
<p>上例中假定了Y1,Y2,Y3的联合分布为正态分布。这个假设是人为的，但是已经通过验证（Graham和Schafer于1999），非正态联合分布的变量，在这个假定下仍然可以估计到很接近真实值的结果。</p>
<p>多重插补和贝叶斯估计的思想是一致的，但是多重插补弥补了贝叶斯估计的几个不足。</p>
<p>(1)贝叶斯估计以极大似然的方法估计，极大似然的方法要求模型的形式必须准确，如果参数形式不正确，将得到错误得结论，即先验分布将影响后验分布的准确性。而多重插补所依据的是大样本渐近完整的数据的理论，在数据挖掘中的数据量都很大，先验分布将极小的影响结果，所以先验分布的对结果的影响不大。</p>
<p>(2)贝叶斯估计仅要求知道未知参数的先验分布，没有利用与参数的关系。而多重插补对参数的联合分布作出了估计，利用了参数间的相互关系。</p>
<h3 id="决策树总结">决策树总结</h3>
<p><img loading='lazy' decoding="async" src="https://upload-images.jianshu.io/upload_images/10970403-bc99a4047063f24d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="算法比较"  /></p>
<ul>
<li>决策树学习为概念学习和学习其他离散值的函数提供了一个实用的方法。ID3 系列算法使用从根向下增长法推断决策树，为每个要加入树的新决策分支贪婪 地选择最好的属性。</li>
<li>ID3算法搜索完整的假设空间(也就是说，决策树空间能够表示任何定义在离散 值实例上的任何离散值函数)。所以它避免了仅考虑有限的假设集合的方法的 主要问题:目标函数可能不在假设空间中。</li>
<li>隐含在ID3算法中的归纳偏置包括优先选择较小的树，也就是说，它通过对假 设空间的搜索增长树，使树的大小为正好能分类已有的训练样例。</li>
<li>过度拟合训练数据是决策树学习中的重要问题。因为训练样例仅仅是所有可能 实例的一个样本，向树增加分支可能提高在训练样例上的性能，但却降低在训 练实例外的其他实例上的性能。因此，后修剪决策树的方法对于避免决策树学 习中(和其他使用优选偏置的归纳推理方法)的过度拟合是很重要的。</li>
<li>对于基本ID3算法，研究者已经开发了大量的扩展。其中包括后修剪的方法; 处理实数值的属性;容纳缺少属性值的训练样例;当有了新的训练实例时递增 精化决策树;使用信息增益之外的其他属性选择度量;考虑与实例属性关联的 代价。</li>
</ul>
<p>&ndash;参考</p>
<ul>
<li><a href="http://blog.csdn.net/qq_30091945/article/details/71655660" target="_blank" rel="noopener nofollow noreferrer" >http://blog.csdn.net/qq_30091945/article/details/71655660</a></li>
<li>周本华机器学习</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ML-文本相似度]]></title>
            <link href="https://blog.yunpiao.site/post/20191012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191012103946/</id>
            
            
            <published>2019-10-12T10:39:46+08:00</published>
            <updated>2019-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>局部敏感哈希(LSH)</p>
</blockquote>
<h2 id="文本相识度">文本相识度</h2>
<p>计算文档文本相识度 主要方法</p>
<ul>
<li>欧氏距离</li>
<li>编辑距离</li>
<li>余弦距离</li>
<li>Jaccard 距离</li>
</ul>
<p>距离越近 相识度越高  负比</p>
<p>相识度公式</p>
<h2 id="文档的shingling">文档的Shingling</h2>
<p>为了计算  所以需要文档划分为小的短字符的集合 即子串</p>
<h4 id="k-shingling-就是k个集合为一起的子串">k-Shingling 就是k个集合为一起的子串</h4>
<p>{&ldquo;a, b&rdquo;, &ldquo;b,c&rdquo;}</p>
<p>k的选取视情况而定</p>
<h2 id="最小hash">最小hash</h2>
<p>假设我们有这样4篇文档（分词后）：</p>
<p>s1 = &ldquo;我 减肥&rdquo;<br>
s2= &ldquo;要&rdquo;<br>
s3 = &ldquo;他 减肥 成功&rdquo;<br>
s4 = &ldquo;我 要 减肥&rdquo;<br>
　　为方便叙述，我们取k=1，这时shingle全集为{我，他，要，减肥，成功}，将文档表示成特征矩阵，行代表shingle元素，列代表文档，只有文档j出现元素i时，矩阵M[i][j]=1，否则M[i][j] = 0.<br>
　　实际上，真正计算的过程中矩阵不是这样表示的，因为数据很稀疏。得到矩阵表示后，我们来看最小hash的定义。</p>
<p>最小hash定义为：特征矩阵按行进行一个随机的排列后，第一个列值为1的行的行号。举例说明如下，假设之前的特征矩阵按行进行的一个随机排列如下：</p>
<p>最小哈希值：h(S1)=3，h(S2)=5，h(S3)=1，h(S4)=2.</p>
<p>　　为什么定义最小hash？事实上，两列的最小hash值就是这两列的Jaccard相似度的一个估计，换句话说，两列最小hash值同等的概率与其相似度相等，即P(h(Si)=h(Sj)) = sim(Si,Sj)。为什么会相等？我们考虑Si和Sj这两列，它们所在的行的所有可能结果可以分成如下三类：</p>
<p>　　（1）A类：两列的值都为1；</p>
<p>　　（2）B类：其中一列的值为0，另一列的值为1；</p>
<p>　　（3）C类：两列的值都为0.</p>
<p>　　特征矩阵相当稀疏，导致大部分的行都属于C类，但只有A、B类行的决定sim(Si,Sj)，假定A类行有a个，B类行有b个，那么sim(si,sj)=a/(a+b)。现在我们只需要证明对矩阵行进行随机排列，两个的最小hash值相等的概率P(h(Si)=h(Sj))=a/(a+b)，如果我们把C类行都删掉，那么第一行不是A类行就是B类行，如果第一行是A类行那么h(Si)=h(Sj)，因此P(h(Si)=h(Sj))=P(删掉C类行后，第一行为A类)=A类行的数目/所有行的数目=a/(a+b)，这就是最小hash的神奇之处。</p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[ML-梯度下降代码-线性回归为例]]></title>
            <link href="https://blog.yunpiao.site/post/20191012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191012103946/</id>
            
            
            <published>2019-10-12T10:39:46+08:00</published>
            <updated>2019-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="梯度下降代码线性回归为例">梯度下降代码线性回归为例</h1>
<h2 id="bgd-批量梯度下降">bgd 批量梯度下降</h2>
<h2 id="sbd-随机梯度下降">sbd 随机梯度下降</h2>
<h2 id="mbfd-小批量随机梯度下降">mbfd 小批量随机梯度下降</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1">  1</a></span><span><span style="color:#f92672">import</span> numpy <span style="color:#66d9ef">as</span> np
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2">  2</a></span><span><span style="color:#f92672">import</span> random
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3">  3</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4">  4</a></span><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">gen_line_data</span>(sample_num<span style="color:#f92672">=</span><span style="color:#ae81ff">100</span>):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5">  5</a></span><span>    <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6">  6</a></span><span><span style="color:#e6db74">    y = 6*x1 + 40*x2
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7">  7</a></span><span><span style="color:#e6db74">    :return:
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8">  8</a></span><span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9">  9</a></span><span>    x1 <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>linspace(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">9</span>, sample_num)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10"> 10</a></span><span>    x2 <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>linspace(<span style="color:#ae81ff">4</span>, <span style="color:#ae81ff">13</span>, sample_num)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11"> 11</a></span><span>    x <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>concatenate(([x1], [x2]), axis<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>)<span style="color:#f92672">.</span>T
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12"> 12</a></span><span>    y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(x, np<span style="color:#f92672">.</span>array([, <span style="color:#ae81ff">4</span>])<span style="color:#f92672">.</span>T)  <span style="color:#75715e"># y 列向量</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13"> 13</a></span><span>    <span style="color:#66d9ef">return</span> x, y
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14"> 14</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15"> 15</a></span><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">my_bgd</span>(samples, y, step_size<span style="color:#f92672">=</span><span style="color:#ae81ff">0.01</span>, max_iter_count<span style="color:#f92672">=</span><span style="color:#ae81ff">10000</span>):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16"> 16</a></span><span>    len_x,dim <span style="color:#f92672">=</span> samples<span style="color:#f92672">.</span>shape
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17"> 17</a></span><span>    X <span style="color:#f92672">=</span> samples
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18"> 18</a></span><span>    y <span style="color:#f92672">=</span> y<span style="color:#f92672">.</span>flatten()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19"> 19</a></span><span>    w <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>zeros((dim,), dtype<span style="color:#f92672">=</span>np<span style="color:#f92672">.</span>float32)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20"> 20</a></span><span>    iter_count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21"> 21</a></span><span>    <span style="color:#66d9ef">while</span>(iter_count<span style="color:#f92672">!=</span>max_iter_count):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22"> 22</a></span><span>        <span style="color:#75715e"># 求出每一维损失</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23"> 23</a></span><span>        err <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span> 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-24"> 24</a></span><span>        error_w <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>zeros((dim,), dtype<span style="color:#f92672">=</span>np<span style="color:#f92672">.</span>float32)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-25"> 25</a></span><span>        <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len(X)):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-26"> 26</a></span><span>            pre_y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(w<span style="color:#f92672">.</span>T,X[i])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-27"> 27</a></span><span>            <span style="color:#66d9ef">for</span> j <span style="color:#f92672">in</span> range(dim):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-28"> 28</a></span><span>                error_w[j] <span style="color:#f92672">+=</span> (y[i]<span style="color:#f92672">-</span>pre_y) <span style="color:#f92672">*</span> X[i][j]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-29"> 29</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-30"> 30</a></span><span>        <span style="color:#75715e">#针对每一维更新w</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-31"> 31</a></span><span>        <span style="color:#66d9ef">for</span> j <span style="color:#f92672">in</span> range(dim):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-32"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-32"> 32</a></span><span>            w[j] <span style="color:#f92672">+=</span> error_w[j] <span style="color:#f92672">*</span> step_size <span style="color:#f92672">/</span>len_x
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-33"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-33"> 33</a></span><span>        
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-34"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-34"> 34</a></span><span>        <span style="color:#75715e">#算每次迭代的error function</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-35"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-35"> 35</a></span><span>        <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len(X)):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-36"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-36"> 36</a></span><span>            pre_y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(w<span style="color:#f92672">.</span>T,X[i])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-37"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-37"> 37</a></span><span>            loss <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">/</span> (len_x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)) <span style="color:#f92672">*</span> np<span style="color:#f92672">.</span>power((pre_y <span style="color:#f92672">-</span> y[i]), <span style="color:#ae81ff">2</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-38"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-38"> 38</a></span><span>            err <span style="color:#f92672">+=</span> loss
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-39"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-39"> 39</a></span><span>        iter_count <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-40"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-40"> 40</a></span><span>        
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-41"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-41"> 41</a></span><span>    <span style="color:#66d9ef">return</span> w
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-42"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-42"> 42</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-43"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-43"> 43</a></span><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">my_sgd</span>(samples, y, step_size<span style="color:#f92672">=</span><span style="color:#ae81ff">0.01</span>, max_iter_count<span style="color:#f92672">=</span><span style="color:#ae81ff">10000</span>):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-44"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-44"> 44</a></span><span>    len_x,dim <span style="color:#f92672">=</span> samples<span style="color:#f92672">.</span>shape
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-45"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-45"> 45</a></span><span>    X <span style="color:#f92672">=</span> samples
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-46"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-46"> 46</a></span><span>    y <span style="color:#f92672">=</span> y<span style="color:#f92672">.</span>flatten()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-47"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-47"> 47</a></span><span>    w <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>ones((dim,), dtype<span style="color:#f92672">=</span>np<span style="color:#f92672">.</span>float32)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-48"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-48"> 48</a></span><span>    iter_count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-49"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-49"> 49</a></span><span>    err <span style="color:#f92672">=</span> <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-50"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-50"> 50</a></span><span>    <span style="color:#66d9ef">while</span>(err <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0.001</span> <span style="color:#f92672">and</span> iter_count<span style="color:#f92672">!=</span>max_iter_count):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-51"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-51"> 51</a></span><span>        <span style="color:#75715e"># 求出每一维损失</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-52"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-52"> 52</a></span><span>        err <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span> 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-53"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-53"> 53</a></span><span>        error_w <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>zeros((dim,), dtype<span style="color:#f92672">=</span>np<span style="color:#f92672">.</span>float32)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-54"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-54"> 54</a></span><span>        <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len(X)):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-55"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-55"> 55</a></span><span>            pre_y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(w<span style="color:#f92672">.</span>T,X[i])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-56"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-56"> 56</a></span><span>            <span style="color:#66d9ef">for</span> j <span style="color:#f92672">in</span> range(dim):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-57"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-57"> 57</a></span><span>                error_w[j] <span style="color:#f92672">+=</span> (y[i]<span style="color:#f92672">-</span>pre_y) <span style="color:#f92672">*</span> X[i][j]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-58"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-58"> 58</a></span><span>                w[j] <span style="color:#f92672">+=</span> error_w[j] <span style="color:#f92672">*</span> step_size <span style="color:#f92672">/</span>len_x
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-59"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-59"> 59</a></span><span>        
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-60"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-60"> 60</a></span><span>        <span style="color:#75715e">#算每次迭代的error function</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-61"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-61"> 61</a></span><span>        <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len(X)):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-62"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-62"> 62</a></span><span>            pre_y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(w<span style="color:#f92672">.</span>T,X[i])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-63"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-63"> 63</a></span><span>            loss <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">/</span> (len_x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)) <span style="color:#f92672">*</span> np<span style="color:#f92672">.</span>power((pre_y <span style="color:#f92672">-</span> y[i]), <span style="color:#ae81ff">2</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-64"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-64"> 64</a></span><span>            err <span style="color:#f92672">+=</span> loss
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-65"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-65"> 65</a></span><span>        iter_count <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-66"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-66"> 66</a></span><span>    <span style="color:#66d9ef">return</span> w
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-67"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-67"> 67</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-68"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-68"> 68</a></span><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">my_mbgd</span>(samples, y, step_size<span style="color:#f92672">=</span><span style="color:#ae81ff">0.01</span>, max_iter_count<span style="color:#f92672">=</span><span style="color:#ae81ff">10000</span>,batch_size<span style="color:#f92672">=</span><span style="color:#ae81ff">0.2</span>):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-69"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-69"> 69</a></span><span>    len_x,dim <span style="color:#f92672">=</span> samples<span style="color:#f92672">.</span>shape
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-70"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-70"> 70</a></span><span>    X <span style="color:#f92672">=</span> samples
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-71"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-71"> 71</a></span><span>    y <span style="color:#f92672">=</span> y<span style="color:#f92672">.</span>flatten()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-72"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-72"> 72</a></span><span>    w <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>zeros((dim,), dtype<span style="color:#f92672">=</span>np<span style="color:#f92672">.</span>float32)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-73"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-73"> 73</a></span><span>    iter_count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-74"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-74"> 74</a></span><span>    <span style="color:#66d9ef">while</span>(iter_count<span style="color:#f92672">!=</span>max_iter_count):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-75"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-75"> 75</a></span><span>        <span style="color:#75715e"># 求出每一维损失</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-76"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-76"> 76</a></span><span>        err <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span> 
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-77"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-77"> 77</a></span><span>        error_w <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>zeros((dim,), dtype<span style="color:#f92672">=</span>np<span style="color:#f92672">.</span>float32)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-78"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-78"> 78</a></span><span>        index <span style="color:#f92672">=</span> random<span style="color:#f92672">.</span>sample(range(len_x),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-79"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-79"> 79</a></span><span>                              int(np<span style="color:#f92672">.</span>ceil(len_x <span style="color:#f92672">*</span> batch_size)))
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-80"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-80"> 80</a></span><span>        batch_samples <span style="color:#f92672">=</span> samples[index]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-81"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-81"> 81</a></span><span>        batch_y <span style="color:#f92672">=</span> y[index]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-82"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-82"> 82</a></span><span>        
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-83"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-83"> 83</a></span><span>        <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len(batch_samples)):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-84"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-84"> 84</a></span><span>            predict_y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(w<span style="color:#f92672">.</span>T, batch_samples[i])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-85"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-85"> 85</a></span><span>            <span style="color:#66d9ef">for</span> j <span style="color:#f92672">in</span> range(dim):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-86"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-86"> 86</a></span><span>                error_w[j] <span style="color:#f92672">+=</span> (batch_y[i] <span style="color:#f92672">-</span> predict_y) <span style="color:#f92672">*</span> batch_samples[i][j]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-87"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-87"> 87</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-88"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-88"> 88</a></span><span>        <span style="color:#66d9ef">for</span> j <span style="color:#f92672">in</span> range(dim):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-89"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-89"> 89</a></span><span>            w[j] <span style="color:#f92672">+=</span> step_size <span style="color:#f92672">*</span> error_w[j] <span style="color:#f92672">/</span> len_x
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-90"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-90"> 90</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-91"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-91"> 91</a></span><span>        <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len_x):
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-92"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-92"> 92</a></span><span>            predict_y <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>dot(w<span style="color:#f92672">.</span>T, samples[i])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-93"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-93"> 93</a></span><span>            loss <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">/</span> (len_x <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>)) <span style="color:#f92672">*</span> np<span style="color:#f92672">.</span>power((predict_y <span style="color:#f92672">-</span> y[i]), <span style="color:#ae81ff">2</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-94"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-94"> 94</a></span><span>            err <span style="color:#f92672">+=</span> loss
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-95"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-95"> 95</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-96"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-96"> 96</a></span><span>        iter_count <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-97"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-97"> 97</a></span><span>        
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-98"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-98"> 98</a></span><span>    <span style="color:#66d9ef">return</span> w
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-99"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-99"> 99</a></span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-100"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-100">100</a></span><span>samples, y <span style="color:#f92672">=</span> gen_line_data()
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-1">1</a></span><span><span style="color:#f92672">%%</span>time
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-1-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-1-2">2</a></span><span>my_sgd(samples, y)
</span></span></code></pre></div><pre><code>CPU times: user 108 ms, sys: 4 ms, total: 112 ms
Wall time: 106 ms
array([ 5.9729414, 40.014584 ], dtype=float32)
</code></pre>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" term="%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0" label="机器学习" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[文本相似度-simhash]]></title>
            <link href="https://blog.yunpiao.site/post/20191012103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20191012103946/</id>
            
            
            <published>2019-10-12T10:39:46+08:00</published>
            <updated>2019-10-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>之前流量检测项目针对恶意请求文本做过聚类 , 其中用到计算文本的相识度,文本相似度曾经尝试过用simhash,虽然现在看来在项目中效果并不理想, 但是在文本去重上最用还是很不错的, 以下是对simhash的梳理</p>
</blockquote>
<blockquote>
<p>首先google出品, 必属精品, 简单高效</p>
</blockquote>
<h2 id="1-simhash与传统hash的区别">1. simhash与传统hash的区别</h2>
<p>simhash名字就说明了simhash的用途, 相似的文本具有相识的hash值 .<br>
而普通的hash 只做微小改动hash就相差甚远</p>
<blockquote>
<p>###以下为百*的例子:(如有看到说侵权的, 百*负责)<br>
两个相差只有一个字符的文本串，“你妈妈喊你回家吃饭哦，回家罗回家罗” 和 “你妈妈叫你回家吃饭啦，回家罗回家罗”。<br>
　　通过simhash计算结果为：<br>
　　1000010010101101111111100000101011010001001111100001001011001011<br>
　　1000010010101101011111100000101011010001001111100001101010001011<br>
　　通过传统hash计算为：<br>
　　0001000001100110100111011011110<br>
　　1010010001111111110010110011101</p>
</blockquote>
<h2 id="2-simhash思想-简单易懂-">2. simhash思想 (简单,易懂 )</h2>
<p>a.分词 (分词算法)<br>
b.hash (每个词算hash值)<br>
c.加权 (hash*权重) 4.合并(doc中每个词hash累加) 5.降维(大于0的维度为1, 小于0的维度为0)</p>
<h2 id="结果比较">结果比较</h2>
<p>#TODO 之前效果 有时间会再实现一边</p>
<p>对比其他算法<br>
『百度的去重算法』</p>
<p>百度的去重算法最简单，就是直接找出此文章的最长的n句话，做一遍hash签名。n一般取3。 工程实现巨简单，据说准确率和召回率都能到达80%以上。<br>
『shingle算法』<br>
shingle原理略复杂，不细说。 shingle算法我认为过于学院派，对于工程实现不够友好，速度太慢，基本上无法处理海量数据。</p>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[安全-openvas搭建]]></title>
            <link href="https://blog.yunpiao.site/post/20190412103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20190412103946/</id>
            
            
            <published>2019-04-12T10:39:46+08:00</published>
            <updated>2019-04-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="1openvas-简介">1、openvas 简介</h1>
<p>Nessus是其中一个最流行的和有强力的漏洞扫描器,尤其是对UNIX系统。它最初是自由和开放源码,但他们在2005年关闭了源代码,在2008年取消了免费的“注册Feed”版本。现在每年花费1200美元。</p>
<p>在Nessus的基础上发展了一个免费开源的分支，OpenVAS 用于管理目标系统的漏洞的同时也可以进行攻击渗透。</p>
<h1 id="2-整体结构">2、 整体结构</h1>
<p><img loading='lazy' decoding="async" src="img/1489109830456.jpg" alt="整体架构"  title="1489109830456" /></p>
<h2 id="采用sc模式">采用S/C模式</h2>
<h2 id="server-层分为三部分">Server 层（分为三部分）</h2>
<blockquote>
<p>扫描器-openvas-scanner<br>
scanner监听端口为9391, 负责调用各种漏洞检测插件，完成实际的扫描操作。</p>
</blockquote>
<blockquote>
<p>管理器-openvas-manager<br>
监听端口为9390, 负责分配扫描任务，并根据扫描结果生产评估报告。</p>
</blockquote>
<blockquote>
<p>管理者-openvas-administrator<br>
administrator监听端 口为9393, 负责管理配置信息，用户授权等相关工作。</p>
</blockquote>
<h2 id="client-层任一都可以">Client 层(任一都可以)</h2>
<blockquote>
<p>openvas-cli(命令行接口):<br>
负责提供从命令行访问OpenVAS服务层程序。</p>
</blockquote>
<blockquote>
<p>greenbone-security-assistant(安装助手):<br>
负责提供访问OpenVAS服务层的web接口，便于通过浏览器来执行扫描任务，是使用最简便的客户层组件。</p>
</blockquote>
<blockquote>
<p>Greenbone-Desktop-Suite(桌面套件):<br>
负责提供访问OpenVAS服务层的图形程序界面，主要允许在Windows客户机中。</p>
</blockquote>
<h1 id="3-openvas各组件">3 、OpenVas各组件</h1>
<h3 id="安装辅助脚本">安装辅助脚本</h3>
<ul>
<li>openvas-setup:　　　　   执行安装</li>
<li>openvas-check-setup:　　可以在配置之前使用该工具进行检查，看哪里有问题，有问题的时候根据提示进行fix</li>
</ul>
<h3 id="server-组件">Server 组件</h3>
<ul>
<li>openvassd :　　扫描引擎</li>
<li>openvasmd:　　管理引擎    可以添加用户等操作，详细查看help，</li>
<li>openvasad　　  认证工具</li>
</ul>
<h3 id="漏洞库同步组件">漏洞库同步组件</h3>
<ul>
<li>openvas-nvt-sync　　　　同步nvt库（用于检测的脚本）</li>
<li>openvas-scapdata-sync    同步scap数据库（后台采用sqlite）</li>
<li>openvas-certdata-sync  同步 CERT 公告</li>
</ul>
<h1 id="4安装">4、安装</h1>
<h2 id="源码安装">源码安装</h2>
<h3 id="详见gitlib-安装">详见Gitlib 安装</h3>
<p><a href="http://gitlab.buptnsrc.com/16new/openvas/blob/master/README.md" target="_blank" rel="noopener nofollow noreferrer" >http://gitlab.buptnsrc.com/16new/openvas/blob/master/README.md</a></p>
<h1 id="5漏洞数据feed">5、漏洞数据（feed）</h1>
<p><img loading='lazy' decoding="async" src="img/1489062782315.jpg" alt="同步数据"  title="同步数据" /></p>
<h2 id="数据来源">数据来源</h2>
<h3 id="ntvs-数据">NTVs 数据</h3>
<p>OpenVas 包含了一个 NTVs（ Network Vulnerability Tests）的推送， 持续增长。所有检测方法都是通过NTVs 脚本进行检测的</p>
<p>NTVs 所有分类 详见 主机扫描下的可扫描类型</p>
<blockquote>
<p>NTVs 采用 nasl脚本语言编写   nasl 示例<br>
<a href="http://wald.intevation.org/scm/viewvco.php/scripts/2008/deb_016_1.nasl" target="_blank" rel="noopener nofollow noreferrer" >http://wald.intevation.org/scm/viewvco.php/scripts/2008/deb_016_1.nasl</a></p>
</blockquote>
<h3 id="scap-数据">SCAP 数据</h3>
<h4 id="主要包括">主要包括</h4>
<ul>
<li>CVE</li>
<li>CPE</li>
<li>OAVL</li>
</ul>
<h3 id="cert-公告数据">CERT 公告数据</h3>
<ul>
<li>CERT-Bund 公告</li>
<li>DFN-CERT 公告</li>
</ul>
<h2 id="数据数量截止到2017310">数据数量（截止到2017.3.10）</h2>
<ul>
<li>NTVs  截止到2017年 3月 10号 有50391个测试脚本</li>
<li>CVE数量 29346</li>
<li>cpe 数量 164099</li>
<li>OVAL定义数量 28175</li>
<li>CERT-Bund 公告数量 5629</li>
<li>DFN-CERT 公告数量 14226</li>
</ul>
<h2 id="更新方式">更新方式</h2>
<h3 id="可以手动或自己编写脚本-在启动服务前更新漏洞库">可以手动或自己编写脚本 在启动服务前更新漏洞库</h3>
<h3 id="openvas-做了一个更新限制-每天只能更新一次脚本">openvas 做了一个更新限制 每天只能更新一次脚本</h3>
<p>采用增量更新、 采用更新脚本进行更新</p>
<ul>
<li>openvas-nvt-sync　　　　同步nvt库（用于检测的脚本）</li>
<li>openvas-scapdata-sync    同步scap数据库（后台采用sqlite）</li>
<li>openvas-certdata-sync  同步 CERT 公告</li>
</ul>
<p>NTVs 可以离线更新  更新地址为<br>
<a href="http://www.openvas.org/openvas-nvt-feed-current.tar.bz2" target="_blank" rel="noopener nofollow noreferrer" >http://www.openvas.org/openvas-nvt-feed-current.tar.bz2</a></p>
<h2 id="更新频率">更新频率</h2>
<p>漏洞库openvas feed端 更新频率大约一周一次</p>
<h1 id="6主机扫描">6、主机扫描</h1>
<h2 id="ntv家族清单可扫描类型">NTV家族清单（可扫描类型）</h2>
<ol>
<li>AIX Local Security Checks</li>
<li>Amazon Linux Local Security Checks</li>
<li>Brute force attacks</li>
<li>Buffer overflow</li>
<li>CISCO</li>
<li>CentOS Local Security Checks</li>
<li>Citrix Xenserver Local Security Checks</li>
<li>Compliance</li>
<li>Databases</li>
<li>Debian Local Security Checks</li>
<li>Default Accounts</li>
<li>Denial of Service</li>
<li>F5 Local Security Checks</li>
<li>FTP</li>
<li>Fedora Local Security Checks</li>
<li>Finger abuses</li>
<li>Firewalls</li>
<li>FortiOS Local Security Checks</li>
<li>FreeBSD Local Security Checks</li>
<li>Gain a shell remotely</li>
<li>General</li>
<li>Gentoo Local Security Checks</li>
<li>HP-UX Local Security Checks</li>
<li>IT-Grundschutz</li>
<li>IT-Grundschutz-10</li>
<li>IT-Grundschutz-11</li>
<li>IT-Grundschutz-12</li>
<li>IT-Grundschutz-13</li>
<li>JunOS Local Security Checks</li>
<li>Mac OS X Local Security Checks</li>
<li>Mageia Linux Local Security Checks</li>
<li>Malware</li>
<li>Mandrake Local Security Checks</li>
<li>Netware</li>
<li>Nmap NSE</li>
<li>Nmap NSE net</li>
<li>Oracle Linux Local Security Checks</li>
<li>Peer-To-Peer File Sharing</li>
<li>Policy</li>
<li>Port scanners</li>
<li>Privilege escalation</li>
<li>Product detection</li>
<li>RPC</li>
<li>Red Hat Local Security Checks</li>
<li>Remote file access</li>
<li>SMTP problems</li>
<li>SNMP</li>
<li>Service detection</li>
<li>Settings</li>
<li>Slackware Local Security Checks</li>
<li>Solaris Local Security Checks</li>
<li>SuSE Local Security Checks</li>
<li>Ubuntu Local Security Checks</li>
<li>Useless services</li>
<li>VMware Local Security Checks</li>
<li>Web Servers</li>
<li>Web application abuses</li>
<li>Windows</li>
<li>Windows : Microsoft Bulletins</li>
</ol>
<h2 id="漏洞扫描结果示例apache-默认文件漏洞">漏洞扫描结果示例（Apache 默认文件漏洞）</h2>
<p><img loading='lazy' decoding="async" src="img/1489132187650.jpg" alt="检测结果"  title="结果示例" /></p>
<h1 id="7结果导出">7、结果导出</h1>
<h3 id="结果可以多种形式导出">结果可以多种形式导出</h3>
<ul>
<li>Anonymous XML</li>
<li>ARF</li>
<li>CPE</li>
<li>CSV Hosts</li>
<li>CSV Results</li>
<li>HTML</li>
<li>ITG</li>
<li>LaTeX</li>
<li>NBE</li>
<li>PDF</li>
<li>SVG</li>
<li>TXT</li>
<li>Verinice ISM</li>
<li>Verinice ITG</li>
<li>XML</li>
</ul>
<h1 id="8-参考网址">8、 参考网址</h1>
<p><a href="http://www.freebuf.com/articles/5474.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.freebuf.com/articles/5474.html</a><br>
<a href="http://www.freebuf.com/articles/system/110027.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.freebuf.com/articles/system/110027.html</a><br>
<a href="https://wizardforcel.gitbooks.io/daxueba-kali-linux-tutorial/content/28.html" target="_blank" rel="noopener nofollow noreferrer" >https://wizardforcel.gitbooks.io/daxueba-kali-linux-tutorial/content/28.html</a><br>
<a href="http://m.www.cnblogs.com/spacepirate/p/4113626.html" target="_blank" rel="noopener nofollow noreferrer" >http://m.www.cnblogs.com/spacepirate/p/4113626.html</a><br>
<a href="http://atic-tw.blogspot.com/2013/12/kali-openvas.html" target="_blank" rel="noopener nofollow noreferrer" >http://atic-tw.blogspot.com/2013/12/kali-openvas.html</a><br>
<a href="http://www.scap.org.cn/article_home_about-scap.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.scap.org.cn/article_home_about-scap.html</a><br>
<a href="http://wiki.scap.org.cn/scap/overview" target="_blank" rel="noopener nofollow noreferrer" >http://wiki.scap.org.cn/scap/overview</a></p>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[安全-pcap 包解析工具调研]]></title>
            <link href="https://blog.yunpiao.site/post/20190412103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20190412103946/</id>
            
            
            <published>2019-04-12T10:39:46+08:00</published>
            <updated>2019-04-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>调研了些可以大数据量的处理pcap包的工具</p>
</blockquote>
<p>opensoc<br>
<a href="https://blog.sectong.com/blog/opensoc_deep_analysis.html" target="_blank" rel="noopener nofollow noreferrer" >https://blog.sectong.com/blog/opensoc_deep_analysis.html</a></p>
<p>Security Onion<br>
<a href="http://www.freebuf.com/sectool/84043.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.freebuf.com/sectool/84043.html</a></p>
<p>packetpig<br>
<a href="https://zh.hortonworks.com/blog/big-data-security-part-two-introduction-to-packetpig/" target="_blank" rel="noopener nofollow noreferrer" >https://zh.hortonworks.com/blog/big-data-security-part-two-introduction-to-packetpig/</a></p>
<p><a href="https://github.com/packetloop/packetpig" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/packetloop/packetpig</a></p>
<p>ivre<br>
<a href="https://ivre.rocks/" target="_blank" rel="noopener nofollow noreferrer" >https://ivre.rocks/</a></p>
<p>hadoop-pcap<br>
<a href="https://github.com/RIPE-NCC/hadoop-pcap" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/RIPE-NCC/hadoop-pcap</a></p>
<p>aktaion<br>
<a href="https://github.com/jzadeh/aktaion" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/jzadeh/aktaion</a></p>
<p>afterglow<br>
<a href="http://afterglow.sourceforge.net/" target="_blank" rel="noopener nofollow noreferrer" >http://afterglow.sourceforge.net/</a></p>
<p>bokeh<br>
<a href="http://bokeh.pydata.org/en/latest/docs/user_guide/quickstart.html" target="_blank" rel="noopener nofollow noreferrer" >http://bokeh.pydata.org/en/latest/docs/user_guide/quickstart.html</a></p>
<p>open network insight<br>
<a href="https://github.com/Open-Network-Insight/open-network-insight" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/Open-Network-Insight/open-network-insight</a></p>
<p>bro<br>
<a href="https://github.com/bro/broctl" target="_blank" rel="noopener nofollow noreferrer" >https://github.com/bro/broctl</a></p>
<p><a href="https://www.bro.org/" target="_blank" rel="noopener nofollow noreferrer" >https://www.bro.org/</a></p>
<hr>
<p>参考<br>
<a href="http://raffy.ch/blog/2012/03/21/visualizing-packet-captures-for-fun-and-profit/" target="_blank" rel="noopener nofollow noreferrer" >http://raffy.ch/blog/2012/03/21/visualizing-packet-captures-for-fun-and-profit/</a></p>
<p><a href="http://ieeexplore.ieee.org/document/7502925/" target="_blank" rel="noopener nofollow noreferrer" >http://ieeexplore.ieee.org/document/7502925/</a></p>
<p><a href="http://www.sigcomm.org/ccr/papers/2013/January/2427036.2427038" target="_blank" rel="noopener nofollow noreferrer" >http://www.sigcomm.org/ccr/papers/2013/January/2427036.2427038</a></p>
<p><a href="https://media.blackhat.com/us-13/US-13-Hanif-Binarypig-Scalable-Malware-Analytics-in-Hadoop-Slides.pdf" target="_blank" rel="noopener nofollow noreferrer" >https://media.blackhat.com/us-13/US-13-Hanif-Binarypig-Scalable-Malware-Analytics-in-Hadoop-Slides.pdf</a></p>
<p><a href="https://bigsnarf.wordpress.com/2012/03/28/solutions-for-bigdata-ingest-of-network-traffic-analyzing-pcap-traffic-with-hadoop/" target="_blank" rel="noopener nofollow noreferrer" >https://bigsnarf.wordpress.com/2012/03/28/solutions-for-bigdata-ingest-of-network-traffic-analyzing-pcap-traffic-with-hadoop/</a></p>
<p><a href="https://sites.google.com/a/networks.cnu.ac.kr/yhlee/" target="_blank" rel="noopener nofollow noreferrer" >https://sites.google.com/a/networks.cnu.ac.kr/yhlee/</a></p>
<p><a href="https://zhuanlan.zhihu.com/p/23092014" target="_blank" rel="noopener nofollow noreferrer" >https://zhuanlan.zhihu.com/p/23092014</a></p>
<p><a href="https://www.ibm.com/developerworks/cn/opensource/os-cn-bigdata-ambari/index.html" target="_blank" rel="noopener nofollow noreferrer" >https://www.ibm.com/developerworks/cn/opensource/os-cn-bigdata-ambari/index.html</a></p>
<p><a href="https://www.elastic.co/products/kibana" target="_blank" rel="noopener nofollow noreferrer" >https://www.elastic.co/products/kibana</a></p>]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[安全-中间人攻击总结]]></title>
            <link href="https://blog.yunpiao.site/post/20190412103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20190412103946/</id>
            
            
            <published>2019-04-12T10:39:46+08:00</published>
            <updated>2019-04-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>
<p>中间人攻击总结</p>
</blockquote>
<h1 id="中间人攻击mitm">中间人攻击(MITM)</h1>
<h2 id="1-原理">1. 原理</h2>
<h3 id="apr-欺骗">APR 欺骗</h3>
<h4 id="三种方式">三种方式</h4>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><img loading='lazy' decoding="async" src="./images/1493092114715.jpg" alt="enter description here"  title="APR单向 " /></p>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><img loading='lazy' decoding="async" src="./images/1493092166013.jpg" alt="enter description here"  title="APR双向(进行NAT转发)" /></p>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><img loading='lazy' decoding="async" src="./images/1493092219754.jpg" alt="enter description here"  title="APR双向(数据包转发 充当路由)" /></p>
<h3 id="ssl证书伪造">SSL证书伪造</h3>
<h4 id="伪造过程">伪造过程</h4>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p><img loading='lazy' decoding="async" src="./images/1493092490534.jpg" alt="enter description here"  title="SSL伪造" /></p>
<h3 id="ssl卸载">SSL卸载</h3>
<h4 id="卸载过程">卸载过程</h4>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<ol start="8">
<li><img loading='lazy' decoding="async" src="./images/1493092755310.jpg" alt="enter description here"  title="APR欺骗 http to https" /></li>
</ol>
<h4 id="原因">原因</h4>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<h3 id="基于中间人攻击的dns劫持">基于中间人攻击的DNS劫持</h3>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<h1 id="中间人攻击实例">中间人攻击实例</h1>
<h1 id="前提">前提</h1>
<p>工具</p>
<ul>
<li>tcpdump</li>
<li>wireshark</li>
<li>driftnet</li>
<li>urlsnarf</li>
<li>arpspoof</li>
</ul>
<p>环境</p>
<ul>
<li>攻击者　10.108.113.147</li>
<li>被攻击者　10.108.112.243</li>
<li>网关  10.108.112.1<br>
　</li>
</ul>
<h1 id="实战步骤">实战步骤</h1>
<h2 id="1-开启arp欺骗">1. 开启ARP欺骗</h2>
<p><code>arpspoof -i eth0 -t  10.108.112.243  10.108.112.1</code><br>
欺骗目标主机 当前主机 是网关</p>
<p><code>arpspoof -i eth0 -t 10.108.112.1 10.108.112.243</code><br>
欺骗网关 当前主机是112.243<br>
10.108.115.122</p>
<h2 id="2-使用tcpdump-查看tcp信息">2. 使用tcpdump 查看tcp信息</h2>
<h3 id="查看80端口">查看80端口</h3>
<p><code>tcpdump -i eno0 dst host 10.108.112.243 and port 80</code></p>
<p><code>tcpdump -i enp2s0 '((tcp) and (port 80) and ((host 10.108.113.147)))' -A </code></p>
<p>查看 被攻击主机的80端口信息</p>
<h2 id="使用-wireshark">使用 wireshark</h2>
<p><code>ip.host == 10.108.115.122 and tcp.port ==80</code></p>
<h2 id="使用-driftnet-捕捉图片">使用 driftnet 捕捉图片</h2>
<p><code>driftnet -i etn0</code></p>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/security" term="security" label="Security" />
                            
                        
                    
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E5%AE%89%E5%85%A8" term="%E5%AE%89%E5%85%A8" label="安全" />
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E6%80%BB%E7%BB%93" term="%E6%80%BB%E7%BB%93" label="总结" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[基于链接的粒子群优化（LBPSO）的无监督文本聚类特征选择方法]]></title>
            <link href="https://blog.yunpiao.site/post/20180612103946/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/20180612103946/</id>
            
            
            <published>2018-06-12T10:39:46+08:00</published>
            <updated>2018-06-12T10:39:46+08:00</updated>
            
            
            <content type="html"><![CDATA[<p>论文阅读</p>
<p>论文目录结构</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-1"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"> 1</a></span><span>- 介绍
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-2"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"> 2</a></span><span>- 背景
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-3"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"> 3</a></span><span>  - 文本聚类
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-4"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"> 4</a></span><span>    token化
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-5"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"> 5</a></span><span>    去停用词
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-6"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"> 6</a></span><span>    提取词干
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-7"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"> 7</a></span><span>    加权
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-8"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"> 8</a></span><span>    向量化
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-9"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"> 9</a></span><span>  - 二进制粒子群优化算法
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-10"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10">10</a></span><span>    - 粒子群优化算法
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-11"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11">11</a></span><span>    - 全局最好
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-12"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12">12</a></span><span>    - local best
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-13"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13">13</a></span><span>    - 二元粒子群优化
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-14"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14">14</a></span><span>  - 无尺度网络
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-15"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15">15</a></span><span>  - kmeans
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-16"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16">16</a></span><span>  - 提出的方法
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-17"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17">17</a></span><span>    - 邻居选择策略
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-18"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18">18</a></span><span>    - 无尺度网络
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-19"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19">19</a></span><span>    - 链接矩阵计算
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-20"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20">20</a></span><span>    - 粒子重要性计算
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-21"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21">21</a></span><span>    - 解决方案
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-22"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22">22</a></span><span>    - 目标函数
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-23"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23">23</a></span><span>    - 交叉与变异
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-24"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-24">24</a></span><span>  - 提出的特征选择方法
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-25"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-25">25</a></span><span>    - 特征选择算法
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-26"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-26">26</a></span><span>    - LPSO算法与文本聚类算法的集成
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-27"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-27">27</a></span><span>  - 实验
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-28"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-28">28</a></span><span>  - 数据集
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-29"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-29">29</a></span><span>  - 评估指标
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-30"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-30">30</a></span><span>  - 评估结果
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="hl-0-31"><a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-31">31</a></span><span>  - 结论
</span></span></code></pre></div><p>论文 <a href="https://www.sciencedirect.com/science/article/pii/S0167739X17321854?via%3Dihub" target="_blank" rel="noopener nofollow noreferrer" >https://www.sciencedirect.com/science/article/pii/S0167739X17321854?via%3Dihub</a><br>
Link based BPSO for feature selection in big data text clustering<br>
文本特征选择<br>
特征选择搜索策略 分为三类 - 过滤器<br>
不使用学习算法 - 包装器<br>
使用学习算法 顺序算法和启发式算法 包装方法在分类准确性方面优于过滤方法<br>
顺序方法<br>
我们从空集开始，在每一步中添加一些特征，直到达到最大目标函数值。<br>
启发式方法<br>
评估是在不同的特征子集上执行的，以优化目标函数值 - 嵌入式<br>
将特征选择算法和学习模型结合 适中的计算成本实现高精度或良好性能（例如支持向量机和最小二乘回归</p>
<p>特征选择方法包括<br>
<a href="https://blog.csdn.net/kebu12345678/article/details/78485093" target="_blank" rel="noopener nofollow noreferrer" >https://blog.csdn.net/kebu12345678/article/details/78485093</a><br>
<a href="https://www.jianshu.com/p/ea48441d44a5" target="_blank" rel="noopener nofollow noreferrer" >https://www.jianshu.com/p/ea48441d44a5</a><br>
<a href="https://blog.csdn.net/oanqoanq/article/details/9238817" target="_blank" rel="noopener nofollow noreferrer" >https://blog.csdn.net/oanqoanq/article/details/9238817</a><br>
<a href="https://blog.csdn.net/shuzfan/article/details/52993427" target="_blank" rel="noopener nofollow noreferrer" >https://blog.csdn.net/shuzfan/article/details/52993427</a><br>
互信息<br>
顺序搜索 顺序前向和后向特征选择（SFS，SBS）以克服特征选择问题<br>
卡方检验<br>
pedersen<br>
术语强度 tewm strength<br>
信息增益<br>
主成分分析<br>
神经网络</p>
<p>特征选择是一种离散优化问题。文献中已经提出了各种类型的搜索技术，例如顺序前向和后向特征选择（SFS，SBS）以克服特征选择问题。但是，这些技术可能有早熟收敛问题或计算复杂度较高。为了缓解这些问题，基于群体解算器的演化计算（EC）技术为离散优化问题产生了较少计算成本的最优解。这些技术已被广泛用于寻找全球最佳并日益流行的人气。有许多元启发式算法，如粒子群算法[ 15,16 ]，人工蜂群（ABC）[ 17]，遗传算法（GA）[ 18,19 ]和蚁群优化[ 20 ]等[ 21,22 ]用于特征选择问题。</p>
<p>粒子群优化算法高效稳健的基于种群的优化算法</p>
<p>PSO算法是一种高效的文本聚类区域特征选择算法<br>
<a href="https://www.zhihu.com/question/23103725" target="_blank" rel="noopener nofollow noreferrer" >https://www.zhihu.com/question/23103725</a><br>
<a href="https://wenku.baidu.com/view/cd9bdbc7a1c7aa00b52acbe8.html" target="_blank" rel="noopener nofollow noreferrer" >https://wenku.baidu.com/view/cd9bdbc7a1c7aa00b52acbe8.html</a><br>
我们在BPSO中引入了一种新的邻居选择策略用于文本聚类中的特征选择。该算法充分利用了每个特征的区分能力，充分考虑了每个特征的相关性和冗余性。最后使用两个遗传算子来避免停滞问题并探索全局最优解。此外，平均绝对差值（MAD）用于评估粒子的适应性。</p>
<p>text clustering, basic binary PSO,<br>
introduction to scale free networks and its Barabasi &amp; Albert (BA)<br>
model and gives a brief introduction to k-means algorithm.<br>
Albert-László Barabási 和Réka Albert为了解释幂律的产生机制，提出了无标度网络模型（BA模型）。BA模型具有两个特性，其一是增长性，所谓增长性是指网络规模是在不断的增大的，在研究的网络当中，网络的节点是不断的增加的；其二就是优先连接机制，这个特性是指网络当中不断产生的新的节点更倾向于和那些连接度较大的节点相连接。BA模型对很多的现象都是可以解释的，例如研究生对导师的选择，在这个网络当中，研究生和导师都是不断增加的，而研究生总是倾向于选择已经带过很多研究生的导师。</p>
<p>二进制离散粒子群优化算法<br>
<a href="https://zh.wikipedia.org/wiki/%E6%97%A0%E5%B0%BA%E5%BA%A6%E7%BD%91%E7%BB%9C" target="_blank" rel="noopener nofollow noreferrer" >https://zh.wikipedia.org/wiki/%E6%97%A0%E5%B0%BA%E5%BA%A6%E7%BD%91%E7%BB%9C</a><br>
<a href="https://www.cnblogs.com/LonelyEnvoy/p/5981449.html" target="_blank" rel="noopener nofollow noreferrer" >https://www.cnblogs.com/LonelyEnvoy/p/5981449.html</a><br>
<a href="https://blog.csdn.net/google19890102/article/details/30044945" target="_blank" rel="noopener nofollow noreferrer" >https://blog.csdn.net/google19890102/article/details/30044945</a><br>
<a href="http://www.cnblogs.com/21207-iHome/p/6062535.html" target="_blank" rel="noopener nofollow noreferrer" >http://www.cnblogs.com/21207-iHome/p/6062535.html</a><br>
<a href="https://blog.csdn.net/qq_27755195/article/details/62216762" target="_blank" rel="noopener nofollow noreferrer" >https://blog.csdn.net/qq_27755195/article/details/62216762</a></p>
<p>聚类 聚类步骤</p>
<ol>
<li>
<p>token 分词</p>
</li>
<li>
<p>停用词</p>
</li>
<li>
<p>Stemming 词干提取<br>
<a href="https://segmentfault.com/a/1190000003839093" target="_blank" rel="noopener nofollow noreferrer" >https://segmentfault.com/a/1190000003839093</a></p>
</li>
<li>
<p>加权转化为向量</p>
</li>
</ol>
<p>二进制粒子群优化</p>
<p>无标度BA模型<br>
网络在自然界和社会中无处不在，描述了各种复杂的系统，即通过化学相互作用连接基因或蛋白质的遗传网络，由通过社会关系相互作用的个体组成的社会网络等。尽管它们多样性，但大多数出现在自然界中的网络遵循普遍的组织原则。特别是，已经发现许多科学兴趣的网络本质上是无标度的。本文展示了PSO群体中无标度网络的结构特征。<a href="https://www.sciencedirect.com/science/article/pii/S0167739X17321854?via%3Dihub#fig3" target="_blank" rel="noopener nofollow noreferrer" >图3</a>显示了无标度网络的一个例子。</p>
<p>k-means</p>
<p>提出方法</p>
<p>Neighbourhood selection strategy in PSO</p>
<p>无尺度拓扑结构<br>
Computation of link matrix<br>
Particle importance calculation</p>
<p>Solution representation</p>
<p>目标函数<br>
使用了遗传算法的交叉和变异操作 交叉运算符 突变因子</p>
<p>特征选择算法</p>
<p>LPSO与k-均值文本聚类算法的集成</p>
<p>经过预处理，提出的算法用于从原始特征空间中选择新的信息特征子集。这些特征不仅提高了聚类算法的效率，而且降低了计算复杂度。本文采用集成链路邻居策略的改进BPSO（LBPSO）算法进行特征选择。最后，k -means文本聚类算法在特征的信息子集上执行。一个完整的文本聚类过程和流程图<br>
<img loading='lazy' decoding="async" src="http://upload-images.jianshu.io/upload_images/10970403-13d529af0f4a9c36.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="特征子集选择过程"  /></p>
<p>实验及结果评估</p>
<p>标准</p>
<ul>
<li>准确性</li>
<li>归一化需信息</li>
<li>纯度</li>
<li>rand index指数</li>
</ul>]]></content>
            
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/categories/data/ml" term="data/ml" label="Data/ML" />
                            
                        
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/series/blog%E5%85%BB%E6%88%90%E8%AE%B0" term="blog%E5%85%BB%E6%88%90%E8%AE%B0" label="Blog养成记" />
                            
                        
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="https://blog.yunpiao.site/tags/%E8%AE%BA%E6%96%87" term="%E8%AE%BA%E6%96%87" label="论文" />
                            
                        
                    
                
            
        </entry>
    
        
        <entry>
            <title type="html"><![CDATA[]]></title>
            <link href="https://blog.yunpiao.site/post/docs/adr/2026-03-11-blog-frontmatter-taxonomy/" rel="alternate" type="text/html" />
            
                <id>https://blog.yunpiao.site/post/docs/adr/2026-03-11-blog-frontmatter-taxonomy/</id>
            
            
            <published>0001-01-01T00:00:00+00:00</published>
            <updated>0001-01-01T00:00:00+00:00</updated>
            
            
            <content type="html"><![CDATA[<h1 id="adr-2026-03-11-blog-front-matter-taxonomy-cleanup">ADR 2026-03-11: Blog Front Matter Taxonomy Cleanup</h1>
<h2 id="status">Status</h2>
<p>Accepted</p>
<h2 id="context">Context</h2>
<p>当前仓库是博客内容源，不是完整站点工程。历史文章来自多轮迁移和补写，front matter 口径已经出现明显漂移：</p>
<ul>
<li>一部分文章缺少 <code>slug</code></li>
<li>一部分文章缺少 <code>&lt;!--more--&gt;</code></li>
<li>一部分文章的 <code>categories</code> 字段存在，但没有实际分类值</li>
<li>历史文章里仍保留旧分类，如 <code>杂技浅尝</code>、<code>技术折腾</code>、<code>生活</code></li>
</ul>
<p>这次调整分两批完成：</p>
<ul>
<li>第一批：补 <code>slug</code>、补 <code>&lt;!--more--&gt;</code>、填充空的 <code>categories</code></li>
<li>第二批：统一历史旧 <code>categories</code>、清理占位 <code>summary</code>、清理重复 <code>&lt;!--more--&gt;</code>、修复明显脏标题</li>
</ul>
<h2 id="decision">Decision</h2>
<h3 id="1-slug-生成规则">1. <code>slug</code> 生成规则</h3>
<ul>
<li>只补缺失的 <code>slug</code></li>
<li>从现有 <code>date</code> 派生</li>
<li>格式固定为 <code>YYYYMMDDHHMMSS</code></li>
<li>不改已有 <code>slug</code>，即便其年份和 <code>date</code> 不一致，也留到后续单独治理</li>
</ul>
<h3 id="2---more---插入规则">2. <code>&lt;!--more--&gt;</code> 插入规则</h3>
<ul>
<li>只补缺失的文章</li>
<li>不插在 front matter 内</li>
<li>优先插在开头导语或第一个完整信息块之后</li>
<li>如果文章开头没有自然导语，则插在第一个可读信息块之后，保证首页摘要和 RSS 摘要至少可读</li>
</ul>
<h3 id="3-categories-规则">3. <code>categories</code> 规则</h3>
<ul>
<li><code>categories</code> 最终只允许以下 6 个专题：
<ul>
<li><code>Security</code></li>
<li><code>Backend</code></li>
<li><code>Kubernetes</code></li>
<li><code>Data/ML</code></li>
<li><code>Infra/Ops</code></li>
<li><code>Project Retrospective</code></li>
</ul>
</li>
<li>技术文章必须收口到上述 6 类之一</li>
<li>非技术文章不强行归类，直接移除 <code>categories</code> 字段</li>
</ul>
<h3 id="4-summary-规则">4. <code>summary</code> 规则</h3>
<ul>
<li>删除明显的占位摘要，例如 <code>这是文章的摘要部分</code></li>
<li>摘要优先由 <code>&lt;!--more--&gt;</code> 控制，不保留无信息量的占位字段</li>
</ul>
<h3 id="5-重复---more---规则">5. 重复 <code>&lt;!--more--&gt;</code> 规则</h3>
<ul>
<li>每篇文章最多保留一个 <code>&lt;!--more--&gt;</code></li>
<li>如果同一篇文章存在多个摘要截断点，保留位置更合理的那个，删除其余重复项</li>
</ul>
<h3 id="6-标题清理规则">6. 标题清理规则</h3>
<ul>
<li>只清理明显脏 token，不做风格化重写</li>
<li>本批次仅处理：
<ul>
<li>前缀 <code>blog-</code></li>
<li>中间或前缀 <code>todo-</code></li>
<li>误带上的 <code>.md</code></li>
</ul>
</li>
</ul>
<h3 id="7-本批次不处理的内容">7. 本批次不处理的内容</h3>
<ul>
<li>不统一历史 <code>tags</code></li>
<li>不修复已有 <code>slug</code> 与 <code>date</code> 的年份不一致问题</li>
<li>不做标题风格统一，只修脏数据</li>
<li>不调整文章正文论述内容，只处理 front matter 和摘要截断点</li>
</ul>
<h2 id="rationale">Rationale</h2>
<ul>
<li>先补齐缺失字段，收益最高，风险最低</li>
<li><code>slug</code> 从 <code>date</code> 派生，后续迁移到 Hugo 或其他静态站点时规则最稳定</li>
<li><code>categories</code> 收敛到 6 类，方便后续做专题页、导航和归档</li>
<li>占位 <code>summary</code> 和重复 <code>&lt;!--more--&gt;</code> 会直接污染首页摘要与 RSS，需要清掉</li>
<li>标题脏 token 会影响列表观感和 SEO 展示，但不值得为此重写所有标题</li>
</ul>
<h2 id="consequences">Consequences</h2>
<ul>
<li>新增或修复后的文章会更容易被首页、RSS、专题页消费</li>
<li>历史旧分类会被消除，但非技术文章可能没有 <code>categories</code></li>
<li>后续如果要进一步统一风格，还可以再做一轮标题和标签体系治理</li>
</ul>
]]></content>
            
                 
                    
                 
                    
                 
                    
                
            
        </entry>
    
</feed>
