Docker - Engine and Netowrking

Docker 引擎(Docker Engine), 顾名思意,是 Docker 的核心. 它为 Docker 提供动力, 并承担所有繁重的工作. 本文将深入探讨这一关键组件的内部运作, 以便了解 Docker 在内核下是如何工作的. The Evolution of the Docker Engine | Docker 引擎的演进 Docker 最初是一个巨大的单体(monolith), 所有代码都塞在同一个项目里. 对于 dotCloud来说, 这种方式一开始是可行的. 实际上, 这个方向运作得非常好, 以至于他们放弃了其他服务、把所有赌注都押在 Docker 上, 甚至把公司重命名为 Docker, Inc. 一开始, Docker 是一个又大又混乱的单体应用. 随着时间推移, Docker, Inc. 发现这种做法不可持续, 他们需要把系统拆分出来: 各个部分可以独立成长 更容易升级某些部分 - 可以替换旧组件而不影响整体 让社区更容易参与贡献 - 更小的组件意味着更多人能参与进来 更易跨平台 - 他们想要 Docker 在每个平台上运行, 而不只是 Linux 拆分的第一步是把客户端 client 剥离出来. 把客户端从大应用中抽出, 赋予它新职责: 把用户命令翻译成 Docker 引擎能理解的指令(也就是原来单体里"内核部分"的接口) 此时, Docker 引擎主要有两部分: ...

September 4, 2025 · 4 min · 653 words · Starslayerx

Docker - History

The Docker Story - Part1: Docker History docCloud - 也就是开发 Docker 的公司, 最初是一家 PaaS(平台即服务)公司, 他们在 PaaS 领域并没有太大的成功, 但他们构建了一个可以无缝管理客户系统与架构的工具: Docker. 2013 年, 他们决定放弃 PaaS 服务, 将全部精力投入到 Docker 这款产品上. Containers 容器 Docker 公司并没有发明容器这个概念. 实际上, 容器的概念已经演进了十多年, 很多参与者都做出了贡献, Linux 基金会和 Google 是推动整个生态走向成熟的重要力量. 假如你在运营一家公司, 希望将应用上线, 以前需要做的事大概是: 购买一台服务器 安装所有必要的应用和依赖 配置环境以匹配你的开发设置 部署应用 把服务器对外开放 看起来很简单, 但实际操作会很复杂: 要手动跟踪并更新每个依赖和配置 如果出问题, 需要手动去修复 基础设施团队需要估算服务器规格(内存、CPU 等) — 为了防止流量高峰崩溃, 通常会配置更高的规格(过度配置) 那台高配服务器大多数时间只是闲置着, 做最少量的工作 不能轻易扩展或在同一服务器上运行多个应用, 因为每个应用都需要独立的运行环境 总之, 非常混乱 后来出现了虚拟机(VM), 情况有了改善. 使用 VM 可以: 在同一台服务器上运行多个隔离的环境 为 VM 做快照并在不同服务器间复用 不再重复重复地搭建环境,这是一个很大的进步 但 VM 也有缺点: ...

September 3, 2025 · 2 min · 289 words · Starslayerx

How AI Assistants Make Precise Edits to Your Files

之前的文章介绍了如何制作一个基本的 AI 编程助手, 今天更近一步, 探讨 AI 助手如何对文件进行精确的修改. 实际的 AI Agent 不会读取所有的项目代码, 一般只会读取当前文件的代码, 当需要时才会去读取相关的代码文件. 然而, 输出也不会输出要修改的整个文件的代码, 因为这样输出不仅很慢, 同时成本也很高, 会有大量重复代码导致浪费(以 deepseek 为例, 输出 token 的价格是输入 token 价格的3倍, 是缓存命中输入 token 价格的24倍!), 因此一般是让模型输出要修改的代码和修改后的代码. 既然不能一次输出文件的所有代码, 这就引出了一个问题: 如何精确的修改代码文件? 首先要确定一种让模型准确地描述修改的格式, 并且提供健壮的格式匹配与错误重试机制(模型输出代码可能少个空格或者Tab), 这篇文章对这个问题做了探讨. 将 AI agent 生成的代码直接修改到文件中是一项核心能力, 然而实际上这常常出乎意料的困难, agent 可能会提出一个代码修改方案, 但实际修改却失败, 例如"找不到匹配的上下文"之类的错误, 需要手动干预. 许多 AI 编程助手的开发者都遇到过这种情况, 虽然 AI 理解代码的意图, 但将这种理解转化为精确的文件修改却带来了重大的技术挑战. Why Precise File Editing Matters 为什么精确的文件编辑至关重要 有效的文件编辑是编程助手的价值核心, 如果其不能可靠的修改文件, 需要人为手动修改, 就退化成了 AI 聊天引擎, 相比之下, 一个能够可靠自动化编辑的助手可以为开发者节省大量时间和认知负担. 根本的挑战在于, LLM 缺乏直接的文件系统访问权限, 他们必须通过专门的工具来描述预期的修改, 然后这些工具或 API 解释指令并尝试执行, LLM 的描述与文件系统状态之间的这种交接是常见的问题来源. 使用 GitHub Coplit、Aider、RooCode 或 Cursor 等工具的用户可能已经观察到这些问题: 编辑器无法找到正确的插入点、缩进不正确, 或者工具最终请求手动应用. ...

September 2, 2025 · 7 min · 1373 words · Starslayerx

Make an AI Coding Agent in python

这篇文件介绍如何使用 Python 制作一个基础的 AI 编程助手 Minimal AI Coding Agent 下面是一个 AI Coding Agent 至少需要的功能 Chat loop 对话循环 Call an LLM 调用大语言模型 Add tools to call 增加工具调用 Handle tool request 处理工具调用请求 Step 1: Chat Loop 首先, 聊天循环一直循环等待用户输入, Python 的 “input” 方法可以获取用户输入 print("Type q to quit") while True: user_message = input("You: ") if user_message == "q": break ai_message = f"You said {user_message}... so insightful" print(ai_message) 目前主流的 LLM 都是无状态的, 所以需要我们手动的去管理对话上下文, 这里使用一个 fake_ai 函数模拟真实的 LLM 调用, 并包含 role 和 content 内容 ...

September 1, 2025 · 11 min · 2265 words · Starslayerx

KMP Algorithm

KMP 算法 思想 KMP 算法, 全程 Knuth-Morris-Pratt 算法, 是一种高效的字符串匹配算法. 它的核心思想是: 在匹配过程中, 当发生文本串(text)与模式串(pattern)不匹配时, 能够利用已匹配过的部分信息, 智能地移动模式串, 从而避免从头开始匹配, 达到提高匹配效率的目的. KMP算法的巧妙之处在于: 它认为, 当发生不匹配时, 将模式串仅仅右移一位是"愚蠢"的. 当某个地方不匹配时, 表明前面实际上有部分内容已经匹配了. 如果直接从头开始匹配, 就浪费了这些信息. 有时前面已经匹配部分会有"重复"的性质, 可以利用这种性质, 让子串一次多移动几步, 从而加速匹配速度. 举例 主串S和模式串P S = BBC ABCDAB ABCDABCDABDE P = ABCDABD 当匹配到下面这种情况时: S = BBC ABCDAB ABCDABCDABDE P = ABCDABD 这里的 D 和上面空格不匹配, 此时保理匹配就直接向右移动一位, 但是通过观察已经匹配的部分 “ABCDAB” 可以发现, 该部分有相同的前后缀 “AB”, 因此可以直接将子串的 “A” 对准主串中后缀 “AB” 里面的 “A”: S = BBC ABCDAB ABCDABCDABDE P = ABCDABD 此时, S 的指针不用改变, 将 P 的指针回到 “C” 的位置就行了. 通过这种方法, 大大提升了效率. ...

August 31, 2025 · 6 min · 1133 words · Starslayerx

Redis Ordered Set

Redis 的有序集和(ordered set)同时具有"有序"和"集和"两种性质, 这种结构中每个元素都由一个成员和一个与成员相关联的分值组成, 其中成员与字符串方式存储, 而分值以64位双精度浮点数格式存储. 例如下面一个记录薪水的集和: 成员 分值 “perter” 3500 “bob” 3800 “jack” 4500 “tom” 5000 “mary” 5500 与集和一样, 有序集和中的元素都是唯一的, 同时, 成员将按照分值大小进行排序. 有序集和分值除了可以是数字外, 还可以是字符串 “+inf” 或者 “-inf”, 这两个特殊值分别表示无穷大和无穷小. 虽然有序集和的成员不可相同, 但是分值可以是相同的, 当两个或多个成员拥有相同的分值时,Redis 将按照这些成员在字典序中的大小对其进行排列. 有序集合是Redis提供的所有数据结构中最为灵活的一种, 它可以以多种不同的方式获取数据, 比如根据成员获取分值、根据分值获取成员、根据成员的排名获取成员、根据指定的分值范围获取多个成员等. ZADD: 添加或更新成员 ZADD sorted_set socre number [score number ...] 默认情况下, ZADD 命令将返回成功添加的新成员数量作为返回值, 对于更新操作会返回0(未添加新成员). 使用 XX | NX 选项来显示地指示命令 只更新 或 只添加操作 ZADD sorted [XX|NX] socre member [socre member ...] 若要返回所有被修改的成员数量(新添加 + 更新数量), 可使用 CH 选项 ZADD sorted_set [CH] socre number [score number ...] 复杂度: O(M * log(N)) 其中 M 为给定成员数量, N 为有序集和的成员数量 ...

August 30, 2025 · 4 min · 646 words · Starslayerx

Asyncio vs Gevents in Python

python 中 asyncio 和 gevent 是两种协程(在一个线程内实现并发)的实现, 这篇文章对比介绍这两者实现. 下面先介绍一下基础概念: Coroutines 协程 在 Python 中, 协程是可以暂停和继续运行的函数, 使得其是否适合并发编程. 定义使用 async def 语法, 协程运行编写非阻塞的操作. 在协程内, await 关键字用于暂停执行, 直到给定的任务完成, 从而运行其他协程在此其间并发运行. Event Loop 事件循环 事件循环是一种控制结构, 它不断地处理一系列事件, 处理任务并管理程序的执行流程. 等待事件发生, 处理后再等待下一个事件. 这种机制确保程序能够以高效有序的方式响应事件, 例如用户输入、计时器或者消息. 下面是事件循环如何管理协程: 任务提交: 当向事件循环提交一个协程时, 其被封装在一个 Task 对象中, 然后任务被安排在事件循环上运行. 内部队列: 事件循环使用几个内部数据结构来管理和调度这些任务 就绪队列 (Ready Queue): 包含可以立即运行的任务. I/O 选择器 (I/O Selector): 监控文件描述符, 并根据 I/O 准备情况调度任务 计划回调 (Scheduled Callbacks): 管理计划在一定延迟后运行的任务. 调度: 事件循环不断检查这些队列和数据结构, 以确定哪些任务已准备好执行. 然后它运行这些任务, 在遇到 await 语句时, 根据需要暂停和恢复它们. 并发管理: 通过交错执行多个协程, 事件循环无需多个线程即可实现并发. 在任何时候, 只有一个任务会运行, 但如果一个任务是 I/O 密集型的, 它会切换到另一个任务, 给人一种并行的错觉. ...

August 29, 2025 · 4 min · 694 words · Starslayerx

Prompt Organization

这篇文章旨在介绍 Python 中常用的提示词组织方式 f-string 使用 f 字符串填充变量得到提示词 def get_prompt(query: str) -> list[dict]: SYSTEM_PROMPT = f"""... ... 多行提示词, 也可以填充变量 """ USER_PROMPT = f"""INPUT: {query} .... """ return [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": USER_PROMPT}, ] 这种方法实现简单, 速度快, 但是: 多行字符串由于填充变量的需要, 需写在函数内, 导致代码格式混乱 # 实际上, 多行字符串还可以这样实现, 但也不太时候提示词太多的时候, 但这样代码格式会更加优雅 system_prompt = ( f"你是一名{role}负责...\n" f"具体规则:\n" f"1. ...." f"2. ...." ) 上面这种方法会将多行字符串合并, 注意不要加逗号, 不然就变成元组了 通过代码构造提示词, 任何修改都需要修改代码, 扩展性差 string.Template 使用 Python 元素字符串模板 SYSTEM_PROMPT = string.Template("""你是一名$role 多行提示词... """) USER_PROMPT = string.Template("""INPUT: $query """) def get_prompt(role: str, query: str) -> list[dict]: system_prompt = SYSTEM_PROMPT.subtitute(role="助手") user_prompt = USER_PROMPT.subtitute(query="问题...") return [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] 使用模板字符串, 模板则不必写在函数内, 且模板字符串可以选择替换部分变量, 使用 .safe_substitute()方法传入一个字典, 例如 {"query": "问题..."}, 对没有传入的变量解析为 $var 对比 f-string, 模板字符串更加灵活, 且可以只传入部分值 ...

August 28, 2025 · 2 min · 305 words · Starslayerx

From Python to Go

From Python to Go: Why We Rewrote Our Ingest Pipeline at Telemetry Harbor 我们将 Telemetry Harbor 的摄取管道从 Python FastAPI 重写为 Go,原因是遇到了严重的性能瓶颈。迁移后,效率提升了 10 倍,数据完整性因严格类型检查而得到加强,系统也拥有了稳定、可扩展的高并发时间序列数据摄取基础。 背景:打造一个时间序列数据平台 Telemetry Harbor 源自我们在汽车行业积累的经验。几乎每个项目都要重复搭建相同的基础设施:数据库、后端、数据摄取管道、可视化界面。每次都要花费数周时间,这让我们萌生了打造一个开箱即用平台的想法。 当时的市场方案并不理想。InfluxDB 的商业化策略让许多关键特性被锁在付费墙后,版本迁移成本高且在大数据负载下表现不佳。TimescaleDB 与 ClickHouse 技术上更强大,但依旧需要用户自行构建后端与摄取管道。我们看到了缺口——需要一个极简、可靠、可直接使用的平台。 Python FastAPI:原型开发的正确选择 MVP 阶段,我们在开发速度与运行性能之间权衡。最终选择了 Python FastAPI,因为它允许我们: 快速验证市场假设 迅速收集客户反馈并迭代 在低成本下尝试多种方案 尽快上线以抢占市场 早期架构非常直接:HTTP API(避免防火墙问题)、Redis + RQ 队列、TimescaleDB。测试效果良好,但很快暴露了性能隐患——RQ 的同步处理方式无法支撑高吞吐场景。 性能瓶颈:Python 无法跟上增长 随着数据量上升,性能问题逐渐浮现: 空闲 CPU 占用:10% 中等负载:约 40% CPU 高负载:120–300% CPU(峰值 800%),频繁崩溃 问题不仅在于 RQ 的同步限制,而是整个 Python 架构在常规负载下都难以维持稳定。这迫使我们考虑全面重写。 迁移决策:为什么选择 Go? 我们评估了 Rust 和 Go: ...

August 27, 2025 · 1 min · 148 words · Starslayerx

Redis Set

Redis 的集和 set 键允许用户将任意多个不同的元素存储到集和中, 既可以是文本数据, 也可以是二进制数据. 其与列表有以下两个明显的区别: 列表可以存储重复元素, 而集和只存储非重复元素 列表以有序方式存储元素, 而集和则以无序方式存储元素 下面介绍结合键的各个命令 Set 集和 SADD: 将元素添加到集和 SADD set element [element ...] 返回成功添加的新元素数量作为返回值, 由于集和不存储相同元素, 所以会自动忽略重复的元素 SREM: 从集和中移出元素 SREM set element [element ...] 返回被移除的元素数量, 同样的, 不存在的元素会被忽略 SMOVE: 将元素从一个集和移动到另一个集和 SMOVE source target element 移动操作成功时返回1, 若不存在于源集和, 返回0. 如果 source 的元素不存在, 则返回0表示失败. 如果 target 的元素已存在, 则会覆盖该元素. 从结果来看, 并不会导致 target 中元素变化, 但是会导致 source 中的该元素消失. SMEMBERS: 获取集和包含的所有元素 SMEMBERS set 由于集和是无序的, 且 SMEMBERS 命令不会进行任何排序操作, 所以根据元素添加的顺序不同, 含相同元素的集和执行该命令结果可能不同. SCARD: 获取集和包含的元素数量 SCARD set SISMEMBER: 检查给定元素是否存在于集和 ...

August 26, 2025 · 4 min · 780 words · Starslayerx