<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>乱世浮生</title><link>https://atbug.com/</link><description>Recent content on 乱世浮生</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 10 Mar 2026 22:15:52 +0800</lastBuildDate><atom:link href="https://atbug.com/index.xml" rel="self" type="application/rss+xml"/><item><title>我让龙虾替我工作了 38 天，它做了什么？</title><link>https://atbug.com/openclaw-38-days-what-did-it-do/</link><pubDate>Tue, 10 Mar 2026 22:15:52 +0800</pubDate><guid>https://atbug.com/openclaw-38-days-what-did-it-do/</guid><description>Photo by James Lee on Pexels
一个决定 一开年，OpenClaw 突然在技术圈出圈了。
这是一个自托管的 AI agent 网关，装在服务器上，能把 Telegram、WhatsApp、iMessage 这些聊天软件直接接到 AI agent——发一条消息，agent 帮你处理邮件、文件、代码，像个永远在线的私人助理。
出于好奇，1 月底我也装了一个。
最初装在 MacBook 上，但考虑到隐私安全，本地机器上有太多个人数据，不想让 agent 有过多访问权限，索性找了一台公有云的 Ubuntu 虚拟机，把它部署上去。模型用的是 GitHub Copilot，这得益于微软 MVP 赠送的 Copilot Pro 订阅，算是物尽其用。
就这样，第一个 agent 上线了。后来随着使用深入，发现有些事情需要专注写代码的，有些只需要轻量协助，agent 数量从 1 个慢慢加到了 3 个：Nova 管日常，Forge 专门写代码，assistant 跑腿打杂。</description></item><item><title>记忆不上云：mem9 + TiDB 打造 OpenClaw 私有记忆中枢</title><link>https://atbug.com/openclaw-private-memory-mem9-tidb-self-hosted/</link><pubDate>Mon, 09 Mar 2026 19:26:51 +0800</pubDate><guid>https://atbug.com/openclaw-private-memory-mem9-tidb-self-hosted/</guid><description>Photo by Tweesak C. on Pexels
记忆的代价 每次对话结束，AI agent 就会忘记一切。
这不是比喻。OpenClaw 的默认 memory-core 把记忆写进本地 .md 文件，当文件积累到上百个，agent 没有能力在每次对话前全部扫描——记忆变成了需要你主动提示才能检索的档案馆。
更根本的问题是：记忆无法跨 agent 共享。你告诉过主 agent 的偏好，coding agent 从零开始；每个 agent 都活在自己的信息孤岛里。
理想的 agent 记忆系统应该满足四点：自动注入、跨 agent 共享、新记忆实时可用、数据留在本地。
带着这四个要求，我开始寻找答案。这篇文章 介绍了 mem9 作为 AI agent 记忆方案的思路，给了我很大启发。但原文使用的是 mem9.ai 云服务——记忆数据存在远端。考虑到 agent 的记忆里沉淀着大量个人习惯、工作偏好和私人决策，这些数据不应该离开本机。因此本文在此基础上进一步，把整套系统搬到本地自托管。</description></item><item><title>Spring Boot 2 升 3：两条命令搞定 95%，AI 收尾</title><link>https://atbug.com/openrewrite-recipe-first-spring-boot-2-to-3-migration/</link><pubDate>Sun, 01 Mar 2026 08:33:41 +0800</pubDate><guid>https://atbug.com/openrewrite-recipe-first-spring-boot-2-to-3-migration/</guid><description>一年多前，我在另一个迁移项目里尝试过 OpenRewrite，做了可行性验证，最终评估下来方案不合适，那个项目转而采用了 AI 的方式。顺着那次探索写了个系列，然后就搁置了。最近真正的迁移计划提上日程，翻出来一看——当时踩过的坑、记下来的东西，全都用上了。
任何学习和痛苦都不会白费，只是兑现的时间不一定。
为什么迁移这么痛 Spring Boot 2 的 EOL 早已过去，但很多团队的升级计划还停在 Jira 的某个角落吃灰。不是不想升，是真的怕。
怕什么？怕的不是技术本身，而是规模。javax.* 全部变成 jakarta.*，Spring Security 配置 API 重写，WebMVC 和异步配置的适配器类被移除，JUnit 4 注解也换了一套。任何一个变化单独处理都不难，但加在一起，乘以整个代码库的文件数量，就成了一个让人望而却步的工程量——而且还是那种高度重复、极易出错、回归验证代价极高的工作。
这类变更有一个共同特征：规则清晰，但执行枯燥，且不能出错。每一处 javax.persistence.Entity 都应该改成 jakarta.persistence.Entity，没有例外。这种工作本质上不需要人来做判断，它需要的是一种可以精确、批量、可审计地修改代码的机制。
OpenRewrite 就是为此而生的。
OpenRewrite 是什么 OpenRewrite 是一个专为大规模代码变更设计的自动化重构引擎，最初由 Netflix 内部孵化，现在由 Moderne 维护。它的核心思想是：把代码解析成一棵无损语义树（Lossless Semantic Tree，LST），在树上做精确变换，再把结果写回代码——全程保留原有的格式、注释和空白。</description></item><item><title>【译】代理精神病：我们是否正在集体失智？</title><link>https://atbug.com/agent-psychosis-are-we-going-insane/</link><pubDate>Sun, 15 Feb 2026 12:22:29 +0800</pubDate><guid>https://atbug.com/agent-psychosis-are-we-going-insane/</guid><description>读到一篇发人深省的文章：《Agent Psychosis: Are We Going Insane?》。坦白说，最初吸引我的正是这个充满张力的标题——&amp;ldquo;psychosis&amp;rdquo;（精神病）一词带着刺眼的隐喻，而文中提出的 &amp;ldquo;agent coding addiction&amp;rdquo;（代理编程成瘾）更如一记警钟，直击当下 AI 编程热潮中被我们刻意忽略的集体焦虑：当人与机器的边界日渐模糊，我们是否正在一种多巴胺驱动的幻觉中，悄然滑向某种技术性失智？
译者注：本文作者 Armin Ronacher（Flask 框架创建者）以批判性视角探讨了当前 AI 编程代理的滥用现象。&amp;ldquo;Slop&amp;rdquo; 一词在文中指代缺乏人工监督、质量低下的 AI 生成代码；&amp;ldquo;vibeslop&amp;rdquo; 则特指仅凭 &amp;quot; 感觉 &amp;ldquo;（vibe）驱动、缺乏工程严谨性的代码产出。Gas Town 与 Beads 是 Steve Yegge 提出的实验性代理编程框架，其过度复杂的隐喻命名体系（如 Refinery/Mayor/Polecat）成为作者批评的焦点。
以下是正文。
你可以不使用 Refinery，甚至不使用 Witness 或 Deacon 来使用 Polecats。只需告诉 Mayor 关闭 rig，然后将工作分派给 polecats，并告知它们直接合并到 main 分支。或者 polecats 可以提交 MR，然后由 Mayor 手动合并。这完全取决于你。只有当你做了大量前期规范工作，并且有成堆的 Beads 需要通过长队列处理时，Refinery 才有用。</description></item><item><title>深入 GitHub Copilot SDK：架构设计与进阶应用</title><link>https://atbug.com/dive-into-github-copilot-sdk-architecture-and-advanced/</link><pubDate>Sat, 14 Feb 2026 20:06:16 +0800</pubDate><guid>https://atbug.com/dive-into-github-copilot-sdk-architecture-and-advanced/</guid><description>TL;DR 在完成了 GitHub Copilot SDK 入门：五分钟构建你的第一个 AI Agent 后，本文将带你深入理解 SDK 的底层设计：
核心架构：
Client vs Session：连接管理器与对话上下文的职责分离，支持多会话并发和资源复用 事件驱动模型：从“请求 - 响应”到“订阅 - 推送”，实现进度感知、提前中断、细粒度控制 工具调用机制：LLM 通过工具描述和参数 schema 自主决策，无需硬编码调用逻辑 进阶能力：
MCP 服务器集成：接入 GitHub、Slack、Notion 等预构建工具生态 自定义 Agent：构建专业化角色，携带长期记忆和特定工具集 性能优化：精简工具描述、控制返回值、选择合适模型，降低 token 成本 SDK 的价值不在于“简化 API 调用”，而是提供了一套经过生产验证的 Agent 运行时。理解其架构设计，才能在构建复杂应用时做出正确的技术选型。</description></item><item><title>GitHub Copilot SDK 入门：五分钟构建你的第一个 AI Agent</title><link>https://atbug.com/github-copilot-sdk-quickstart-build-first-ai-agent/</link><pubDate>Thu, 12 Feb 2026 22:13:47 +0800</pubDate><guid>https://atbug.com/github-copilot-sdk-quickstart-build-first-ai-agent/</guid><description>TL;DR GitHub Copilot SDK 的核心价值不是“调用 LLM”的便利性（这已经被 OpenAI SDK、LangChain 等解决），而是提供了一个经过生产验证的 Agent 运行时。
它解决的真正问题是：
编排复杂性：规划器、工具路由、状态管理已内置 稳定性：数百万开发者日常使用的可靠性保障 演进能力：新模型、新工具能力由 CLI 自动更新 当你开始构建下一个 AI 应用时，问自己两个问题：
我的核心价值在哪里？ 如果是业务逻辑和工具定义，使用 SDK；如果是底层编排创新，自建框架。 多久能到生产环境？ SDK 让你跳过 80% 的基础设施工作，专注于最后 20% 的差异化能力。 Agent 开发的门槛已经降低，但真正的挑战在于：定义有价值的工具，设计流畅的交互，解决真实的问题。技术不再是壁垒，想象力才是。
引言：为什么 Agent 开发不再是少数人的游戏 2026 年 1 月，GitHub 发布了 Copilot SDK，这标志着 AI Agent 开发从“专家领域”走向“大众工具”的关键转折。</description></item><item><title>深入容器运行时：从 stdout/stderr 到 kubectl logs 的完整日志流处理机制</title><link>https://atbug.com/container-runtime-stdout-stderr-logging-mechanism/</link><pubDate>Sat, 07 Feb 2026 02:44:12 +0800</pubDate><guid>https://atbug.com/container-runtime-stdout-stderr-logging-mechanism/</guid><description>TL;DR 理解容器运行时如何处理 stdout/stderr 流，不仅能满足技术好奇心，更有实际价值：
故障排查：日志丢失、截断、延迟等问题的根本原因 性能优化：高吞吐量场景下的 I/O 配置调优 架构决策：选择合适的容器运行时和日志收集方案 深入理解：Kubernetes、Containerd、Runc 各层的职责边界。 引言 作为一名长期使用 Kubernetes 的开发者，我一直对容器日志的底层机制充满好奇。每次执行 kubectl logs 命令，看着应用程序的输出实时流式显示在终端上，我都会忍不住思考：这些日志是如何从容器内部的 stdout 和 stderr 流，穿越多层抽象，最终到达屏幕？虽然日常工作中频繁使用这个功能，但对于这条路径上的技术细节，我却一直没有深入了解过。
这次，我决定彻底弄清楚这个问题：从应用程序的 printf() 或 console.log()，到终端看到的日志，这中间经历了哪些关键步骤？让我们从头开始，逐层揭开这个机制的面纱。
以下的内容基于目前最新的 Kubernetes v1.35.0 和 Containerd v2.2.1。
完整的日志流处理架构 核心概念：CRI、Shim 在深入技术细节之前，我们需要理解三个关键组件。它们构成了容器日志流处理的基础架构。
CRI：Container Runtime Interface CRI 是 Kubernetes 与容器运行时之间的标准接口。它的出现解决了一个关键问题：如何让 Kubernetes 支持多种容器运行时，而不需要为每个运行时编写专门的集成代码？ 关于 CRI 的详细介绍，可以浏览我之前的文章 Kubernetes 容器运行时接口 CRI。</description></item><item><title>从需求到发布：使用 OpenClaw + Ralph Loop 自动化开发 Nexus MCP Server</title><link>https://atbug.com/openclaw-ralph-loop-automated-nexus-mcp-server-development/</link><pubDate>Wed, 04 Feb 2026 01:35:36 +0800</pubDate><guid>https://atbug.com/openclaw-ralph-loop-automated-nexus-mcp-server-development/</guid><description>TL;DR 使用 OpenClaw + Ralph Loop + OpenCode 组合，完成了 Nexus MCP Server 的全自动化开发。
本文记录了从需求提出、Planning、Building，到遇到问题诊断、架构重构的完整过程，展示了 AI 辅助编码工具的实际效能。
源码以及实现的脚本、Planing、Building 等文档均提交到 GitHub 仓库 addozhang/nexus-mcp-server。
背景 今天在公司遇到一个问题：需要开发一个 Agent 来对比项目依赖细节，但远程仓库版本被删除导致用旧的 parent pom 构建失败。我想让 Agent 到 Nexus 仓库查找最接近的版本进行构建，以避免大跨度的版本升级引入新的问题。
向来不喜造轮子，内网外网找了一圈才找到一个基于 Node 的开源版 brianveltman/sonatype-mcp，马上着手验证，然后倒在了安装依赖包：内网仓库缺少一个间接依赖的包。
这就有了造轮子的借口了，装上 mcp-builder skill 马上 Vibe 一个。无奈模型有点弱鸡，稍微折腾一下，又没有太多时间草草收场。</description></item><item><title>Ruler：多 AI 编程助手统一配置管理方案</title><link>https://atbug.com/ruler-unified-ai-coding-assistant-configuration/</link><pubDate>Fri, 30 Jan 2026 00:14:42 +0800</pubDate><guid>https://atbug.com/ruler-unified-ai-coding-assistant-configuration/</guid><description>TL;DR 当你需要频繁在 Kilocode、OpenCode、Claude、Codex、GitHub Copilot 等多个 AI 编程助手之间切换时，Ruler 是你的最佳选择：
单一数据源：集中管理所有 AI 编程助手的 instructions 自动分发：一键将配置分发到各个编程助手的专属文件 MCP 配置管理：统一管理 Model Context Protocol 服务器配置 Skills 支持：集中管理并分发 Agent Skills 到各个工具 嵌套规则加载：支持复杂项目结构的分层配置管理 自动维护 .gitignore：保持项目目录整洁 背景：从混乱到标准化的演进 曾经的痛点 在我写《AGENTS.md：统一编码助手指令文件的新标准》那篇文章时，提到过 intellectronica/ruler 这个项目。当时实际上我并没有频繁使用这个工具，因为我主要使用的是单一的编程助手。
但现在情况不同了。
随着 AI 编程助手的爆发式增长，我需要频繁在多个工具之间切换：
Kilocode - 日常编码和快速原型 Claude - 深度思考和算法优化 Codex - API 开发和文档生成 OpenCode - 开源项目贡献 每个工具都有自己的：</description></item><item><title>Apple Container 0.8.0：从初生到成熟的七个月演进之路</title><link>https://atbug.com/apple-container-evolution-seven-months-journey/</link><pubDate>Sat, 24 Jan 2026 21:43:46 +0800</pubDate><guid>https://atbug.com/apple-container-evolution-seven-months-journey/</guid><description>TL;DR 2026 年 1 月 22 日，Apple Container 0.8.0 发布。距离 WWDC 2025 首次发布的 0.1.0，整整七个月，九个版本。
这七个月不仅是版本号的增长，更是一个架构理念的验证：每容器独立 VM 的隔离模式，能否在 macOS 上成为 Docker Desktop 的替代方案？
0.8.0 给出的答案是：在特定场景下，可以。但这个答案背后，是数据完整性危机（0.7.1）、网络限制的解除（macOS 26）、以及对生产就绪定义的重新思考。
本文不是功能列表，而是试图理解：Apple 为什么要做 Container？独立 VM 架构的代价是什么？七个月的快速迭代背后，哪些问题真正得到了解决，哪些仍在路上？
写在前面：为什么要关注 Apple Container？ 在讨论版本演进之前，先简单回顾一下 Apple Container 的独特价值：
原生 macOS 体验 苹果官方支持：基于 Apple Virtualization Framework 性能优越：每个容器独立虚拟机，无共享虚拟机开销 安全隔离：VM 级别隔离，比传统容器更安全 Apple Silicon 优化：为 ARM64 架构深度优化 与 Docker Desktop 的差异 特性 Docker Desktop Apple Container 架构模式 单一 VM + 多容器 每容器独立 VM 安全隔离 容器级 VM 级 macOS 集成 第三方工具 原生框架 价格 企业版收费 开源免费 但是，在 2025 年 6 月，0.</description></item><item><title>Obsidian Skills：让 AI 智能体精通 Obsidian 知识管理</title><link>https://atbug.com/obsidian-skills-mastering-knowledge-management-with-ai/</link><pubDate>Fri, 23 Jan 2026 21:12:54 +0800</pubDate><guid>https://atbug.com/obsidian-skills-mastering-knowledge-management-with-ai/</guid><description>TL;DR Obsidian Skills 不仅仅是一个技能包，它标志着 Agent Skills 生态正在从通用技能向垂直领域深度集成演进。过去，Agent Skills 主要侧重于任务类型的通用能力，比如代码审查、PDF 处理等广泛适用的场景。而 Obsidian Skills 的出现代表了一个重要转折点，工具官方开始主动拥抱 AI 代理，为自己的产品创建官方维护的专属技能包。这种垂直领域技能的价值在于深入理解工具的设计哲学，掌握最佳实践和惯用模式，与插件生态无缝对接，并能跟随工具版本同步更新，为用户提供权威且完整的解决方案。
本文通过详细介绍 Obsidian Skills 的三个核心技能（Obsidian Markdown、Obsidian Bases、JSON Canvas），展示了 AI 智能体如何真正理解 Obsidian 的独特之处。两个实战案例进一步证明了这些技能的实用价值，通过 Base 技能管理阅读清单，通过 Canvas 技能可视化知识网络，让 AI 成为知识管理的得力助手。随着更多工具厂商跟进，垂直领域技能将成为 AI 代理与专业工具深度融合的标准模式，最终实现用自然语言操作复杂工具的愿景。
引言：从 Agent Skills 到垂直领域应用 两周前，我写了一篇关于 Agent Skills 深度解析：为 AI 代理构建可复用的技能生态系统 的文章，探讨了 Agent Skills 规范如何让 AI 智能体获得模块化、可复用的能力。文章发布后，很多读者对这个生态的发展前景表示乐观。</description></item><item><title>通过 Azure 订阅 Github Copilot</title><link>https://atbug.com/github-copilot-azure-subscription-enterprise-guide/</link><pubDate>Sun, 18 Jan 2026 11:17:27 +0800</pubDate><guid>https://atbug.com/github-copilot-azure-subscription-enterprise-guide/</guid><description>Github Copilot 已从实验性工具成长为开发者日常工作流的核心组件。对于企业而言，通过 Azure 订阅来管理 Github Copilot 不仅是成本优化的选择，更是统一云资源管理、简化账单流程的必然路径。
本文不是简单的功能介绍，而是基于实际配置过程梳理出的完整落地方案。从 Azure 工作账户的创建，到 Github 组织与 Azure 订阅的绑定，再到 Copilot 席位的启用，每一步都直指核心操作。
Azure 工作账户：前置条件的准备 Github Copilot 的 Azure 计费集成要求使用 Azure 工作账户（Work Account），而非个人 Microsoft 账户。这一限制源于企业级订阅管理的权限体系：工作账户通过 Microsoft Entra ID（前身为 Azure Active Directory）进行身份管理，能够精细化控制订阅访问权限。
如果你的 Azure 主账户为个人账户，需要先创建一个有效的工作账户。
创建工作账户 登录 Azure Portal，进入 Microsoft Entra ID 服务。</description></item><item><title>Agent Skills 深度解析：为 AI 代理构建可复用的技能生态系统</title><link>https://atbug.com/agent-skills-reusable-ecosystem-for-ai-agents/</link><pubDate>Sat, 10 Jan 2026 19:10:36 +0800</pubDate><guid>https://atbug.com/agent-skills-reusable-ecosystem-for-ai-agents/</guid><description>背景 一个真实的故事 两周前，KiloCode 发布了 v4.141.0 版本，这个版本带来了一个重要特性：原生支持 Agent Skills。
在此之前，我在 KiloCode 中使用 skills 需要在一个 main rule 中手动 list 所有技能。每次添加新技能都需要：
&amp;lt;!-- 旧方式：在 main rule 中手动维护 --&amp;gt; # AVAILABLE SKILLS Skills are pre-packaged instructions. When a task matches a skill, use read_file to load the SKILL.</description></item><item><title>再见，褪去浮躁的 2025</title><link>https://atbug.com/2025-shedding-haste/</link><pubDate>Wed, 31 Dec 2025 08:15:58 +0800</pubDate><guid>https://atbug.com/2025-shedding-haste/</guid><description>昨晚队友问我今年还要写总结吗，说实话我也在犹豫。工作十几年几乎没正经写过总结，倒是这几年在这儿一直坚持。想了想，确实该好好交代一下——好像做完这件事，才能真正踏实地迈入新的一年。
今年，我四十了（突然想到《喜人 2》里那句“我六十了”的调子）。都说四十不惑，好像真有点那个意思了。不过队友笑我，说你前几年就已经这副“看开了”的样子了。也许吧，日子过着过着，人就慢慢通透了。但生活就是会冷不丁地梆梆给你两拳——所以翻回去看前几年的总结，变化才是永远的关键词。在我看来，所谓不惑，或许就是能坦然接受那些无法改变的，与那些一直在变的。
兜兜转转 10 年，去年底（2024）我回到了前东家，老板还是以前的老板，同事也大多熟悉，就连办公的楼层都没变。不过重新到岗，虽有一切要从头熟悉的压力，却也让人踏实——工作有挑战才有成长，在解决一个又一个问题的过程中，人也在默默向前走。
2025 年，工作日白天忙工作，晚上偶尔还要顾一下小朋友的学习，周末又得跑装修——大半年就这么过来了。虽然忙，年中时我还是重新开始跑步，通常是清晨五点半起床。从少量慢跑开始，逐渐堆跑量，慢慢跑出了以往没有过的距离和速度，每天坚持 30 到 60 分钟。
为了能早起，我有意减少了咖啡，晚上尽量不开电脑，让大脑彻底放松（以前随便弄点什么都得到凌晨）。没想到，连带着把困扰我多年的睡眠问题也给解决了。
公众号更新地更少了，借口找好了就在上面，没有大块完整的空余时间。今年只写了二十来篇，内容也更杂了，不再只围绕云原生展开。年底时，我把公众号改名为“乱世不浮生”。“乱世”指的是技术世界快速迭代、充满不确定的现实；“不浮生”是提醒自己沉下心来，持续学习、深入思考，用扎实的积累面对变化。
或许成长就是这样：一边与生活贴身过招，一边学着在变动中安放自己。四十不惑，未必是什么都明白了，而是懂得了在“变”与“不变”之间，找到自己下脚的节奏。
2025，就这样吧。不慌，不浮，慢慢来。
你好，2026。愿我们依然清醒、扎实、温和地前行。</description></item><item><title>不止于网络：从 Cilium 2025年度报告，洞察云原生基础设施的六大演进</title><link>https://atbug.com/cilium-2025-report-6-infrastructure-trends/</link><pubDate>Sun, 21 Dec 2025 06:20:26 +0800</pubDate><guid>https://atbug.com/cilium-2025-report-6-infrastructure-trends/</guid><description>Cilium 项目始于 2015 年 12 月 16 日的第一次提交，已从一个实验性的 IPv6 容器网络项目成长为云原生世界的 CNI（容器网络接口）的事实标准。也正值 Cilium 的十周年，社区发布了 《Cilium 2025 年度报告》（PDF 原文）。
CNCF 地位： Cilium 目前是 CNCF（云原生计算基金会）中贡献量第二大的项目，仅次于 Kubernetes。 市场占有率： 根据 Isovalent（Cilium 母公司）发布的 《2025 Kubernetes 网络现状报告》，Cilium 占据了超过 Cilium 目前是 CNCF（云原生计算基金会）中 的 CNI 部署份额，是第二名的两倍以上 。如果算上由 Cilium 驱动的托管服务（如 Azure CNI powered by Cilium 和 GKE Datapath V2），其覆盖率超过 60% 。 社区活跃度： 2025 年贡献了近 10,000 个 PR，年度开发活动较第一年增长了 55 倍 。 本文无意逐条复述报告内容。我们不妨将视线从具体数字上移开，转而透视这份行业风向标所揭示的云原生网络与安全核心趋势。希望能激发更多的思考与讨论。</description></item><item><title>Google ADK 深度探索（二）：不同语境下的专用上下文对象</title><link>https://atbug.com/google-adk-deep-dive-specialized-context-objects/</link><pubDate>Sat, 20 Dec 2025 16:43:07 +0800</pubDate><guid>https://atbug.com/google-adk-deep-dive-specialized-context-objects/</guid><description>在上一篇 《ADK 一等公民 Context 解析》 中，我们了解到上下文是智能体运行的核心。承载这些能力的核心容器是功能强大的 InvocationContext，但为提升安全性与易用性，ADK 对其进行了精细化的分类，为不同语境提供了粒度各异的专用上下文对象。
要理解上下文分类的粒度，让我们重温一下 ADK 的核心理念：发送给 LLM 的“工作上下文（Working Context）”是一个更丰富、有状态系统的编译视图（Compiled View）。
“上下文编译器” 在传统软件工程中，编译器将高级源代码转换为机器刻度的二级制文件，在编译过程中执行优化、类型检查和安全检查。类似地，ADK 运行时（Runtime）充当上下文编译器的角色。它摄取交互的“源代码“ &amp;ndash; 包括持久的会话状态（Session State）、临时的用户输入（User Instruction）、检索到的工件（Artifacts）、记忆库（Long-Term Knowledge）和系统指令（System Instruction）&amp;ndash; 并将他们”编译“成针对当前执行阶段量身定制的特定上下文对象。
这个编译过程需要针对智能体系统的不同组件提供不同的接口（参考 前文）。负责渲染系统提示词的指令提供者（Instruction Provider）所需的访问权限，与设计用于修改数据库的工具或用于验证用户授权的回调（Callback）截然不同。ADK 的四种主要上下文类型 &amp;ndash; InvocationContext、ReadonlyContext、CallbackContext 和 ToolContext &amp;ndash; 代表了这些不同的接口。每种类型都强制执行最小权限原则（Principle of Least Priviledge），确保组件在最小化潜在的错误或安全漏洞“爆炸半径”的范围内执行。
智能体状态的演变 从智能体的发展轨迹，我们也能窥探这种分离架构的必要性。早期的框架本质上将应用程序的整个状态转储到一个单一的对象中，并将这个“上帝对象（God Object）”传递给每个函数。这必然导致：</description></item><item><title>Google ADK 深度探索（一）：“一等公民”上下文 Context 解析</title><link>https://atbug.com/google-adk-deep-dive-first-class-context/</link><pubDate>Sun, 14 Dec 2025 22:05:52 +0800</pubDate><guid>https://atbug.com/google-adk-deep-dive-first-class-context/</guid><description>了解了 Google ADK 宏大的上下文架构设计（回顾上一篇文章），我们不禁要问：这些精妙的思想，最终是如何落地到一行行代码里的？
本文将聚焦 ADK 中作为“一等公民”的上下文（Context）机制，详解其如何通过会话状态、数据传递、服务访问等核心功能，解决智能体开发中的状态维护、跨步骤协作和资源调度难题。无论是管理用户偏好的 session.state，还是按需加载的工件存储，抑或是身份跟踪的 InvocationContext，ADK 的上下文设计无不体现着一种理念：智能体的能力边界，本质上取决于其上下文管理的精度与效率。
上下文（Context） 在智能体开发领域，一个日益凸显的挑战是上下文管理的复杂性。传统方法（如无限制地堆叠聊天历史或工具输出）会导致成本飙升、信号衰减甚至物理性性能瓶颈。而 ADK 的突破性在于——它将上下文从“被动拼接的文本”升级为系统化管理的架构核心，通过分层设计、动态编译和最小权限原则，实现了生产级智能体的高效运作。
在 ADK 中，上下文（Context）指的是智能体及其工具在特定操作期间所能获取的关键信息。它也是有效处理当前任务或者会话所需的必要背景知识和资源。
智能体有效运行需要的不只是最新的用户消息，上下文至关重要，通过上下文可以：
维护状态 存储对话过程中多个步骤的详细信息（例如，用户偏好、上一步的结果），这些都通过**会话状态（session.state）**来管理。
会话（Session）在 ADK 中是一个重要的概念，用于跟踪独立的对话。用户第一次与智能体交互时会创建一个 Session 对象，这个对象作为一个容器保存了与对话相关所有状态：
历史记录（session.events）：与该对话相关的所有交互，包括用户输入、智能体响应、工具调用请求/结果等。记录的事件序列提供了交互的完整、按时间顺序的历史记录，对于调试、审计和逐步了解代理行为非常有价值。这些信息是不可变的，是由框架自身维护的。 会话状态（session.state）：从数据结构上看是一个包含键值对的集合（字典或者 Map），用于存储智能体有效执行需要用到的信息，比如记录用户偏好、跟踪多轮流程中的步骤、收集信息等。session.state 是可变的。 会话可以保存在内存（InMemorySessionService）、数据库（DatabaseSessionService、SqliteSessionService、PerAgentDatabaseSessionService）中，具体要看使用是哪种 SessionService 的实现了。比如最常见的 InMemorySessionService，从下面这行代码就很容易看出其储存结构了。
#self.sessions: dict[str, dict[str, dict[str, Session]]] = {} session = self.</description></item><item><title>Google ADK - 构建生产级、高效的上下文感知多智能体框架</title><link>https://atbug.com/google-adk-efficient-context-aware-multi-agent-framework/</link><pubDate>Thu, 11 Dec 2025 22:02:04 +0800</pubDate><guid>https://atbug.com/google-adk-efficient-context-aware-multi-agent-framework/</guid><description>本文翻译自 Google Developer Blog 的 Architecting efficient context-aware multi-agent framework for production。
AI 智能体（Agent）的开发领域正经历着风起云涌的变化。我们早就跨过了还在做单轮聊天机器人原型的阶段。如今，各家机构正在部署的是那种复杂的、自主的智能体，它们能处理长链路任务（long-horizon tasks）：比如自动化工作流、开展深度研究，甚至维护庞大的代码库。
但这一愿景很快就撞上了一堵墙：上下文（Context）。
随着智能体运行时间的拉长，它们需要“记住”的信息量——聊天记录、工具返回的数据、外部文档、中间的推理过程——会呈爆炸式增长。目前的“通解”通常是依赖基础模型（Foundation Models）越来越大的上下文窗口（Context Window）。但是，单纯指望给智能体更大的空间来粘贴文本，绝不可能是长久的扩展之道。
为了构建可靠、高效且易于调试的生产级智能体，业界正在探索一门新的学科：
上下文工程（Context Engineering） —— 不再把上下文仅仅当作一段文本，而是将其视为系统中的“一等公民”，拥有独立的架构、生命周期和约束条件。
基于我们在扩展复杂的单智能体或多智能体系统方面的经验，我们在 Google Agent Development Kit (ADK) 中设计并迭代了上下文栈（Context Stack）来支持这一学科。ADK 是一个开源的、原生支持多智能体的框架，旨在让主动的上下文工程在实际系统中落地。
扩展性的瓶颈 更大的上下文窗口虽能缓解问题，却治标不治本。在实践中，那种幼稚的模式——把所有东西都追加到一个巨大的提示词（Prompt）里——会在以下三重压力下崩塌：
成本与延迟的恶性循环： 模型的推理成本和“首字延迟”（Time-to-first-token）会随着上下文长度迅速飙升。把原始的历史记录和冗长的工具返回结果“一股脑塞进”窗口，会让智能体变得既迟钝又昂贵。 信号衰减（“迷失在中间”）： 一个充斥着无关日志、过时工具输出或废弃状态的上下文窗口，会分散模型的注意力。这会导致模型死盯着过去的模式，而忽略了当前的指令。为了确保决策稳健，我们必须最大化相关信息的密度。 物理极限： 现实世界的工作负载——涉及完整的 RAG 检索结果、中间产物（Artifacts）和漫长的对话痕迹——最终甚至会撑爆最大的固定窗口。 单纯靠“砸 Token”只能争取时间，却改变不了问题的本质。要实现规模化，我们需要改变上下文的表示和管理方式，而不仅仅是纠结于一次调用能塞进多少内容。</description></item><item><title>清晨跑步 - 困扰多年的睡眠问题就这么解决了</title><link>https://atbug.com/morning-running-solved-sleep-problems/</link><pubDate>Wed, 10 Dec 2025 08:41:26 +0800</pubDate><guid>https://atbug.com/morning-running-solved-sleep-problems/</guid><description>我一直以为自己是那种睡眠需求少的人，即使睡得晚也能早起，所以从不需要闹钟。但现实是，长期以来我面临两个严重问题：睡眠时间短（通常只有 4-5 小时）和入睡困难。躺下后，脑子里总盘旋着白天未解决的工作问题。明知有问题，我却习惯在夜深人静时写代码或博客。如果能及时完成任务还好，但往往拖到凌晨，导致恶性循环。睡眠质量进一步下降，影响第二天效率。最近几个月，博客更新减少，一方面是家里装修占用周末，另一方面是我主动避免晚上碰电脑，以免影响早睡和晨跑。
我从 2018 年开始断断续续跑步，2020 年加大跑量，但因痛风问题停滞了一两年，转为居家原地慢跑。今年年中重新外出跑步，通常选择凌晨 5 点半起床。从小跑量开始，逐渐增加距离和速度（以往从未尝试过）。每天坚持 30-60 分钟，路线固定在附近公园。晚上 10 点后不再碰电脑或电子设备，确保大脑放松。
坚持晨跑后，体能提升明显，睡眠质量大幅改善：从 4-5 小时延长到 6-7 小时，甚至偶尔 8 小时。晚上 10 点开始犯困，能迅速入睡，白天精力充沛。建议大家从小量开始，结合规律作息，避免晚上高强度脑力活动。晨跑不仅解决睡眠问题，还带来更多生活乐趣。
基于 yihong 的 开源项目，我整理了几年的跑步记录，制作了个人跑步页面：https://atbug.com/running/。目前使用 Suunto 同步到 Strava，通过 GitHub Actions 每日自动更新数据。由于这几年换过多个运动设备，数据不够完整。
通过晨跑改善睡眠质量，这看似简单却有效。晨跑能提升体能、调节生物钟，帮助身体更快进入深度睡眠。相比药物或调整饮食，晨跑更可持续，且能同时改善整体健康。关键是坚持规律作息，避免晚上刺激大脑的活动。</description></item><item><title>Spring AI 与 Google ADK 集成实战：基于 Azure OpenAI 的智能体开发探索</title><link>https://atbug.com/spring-ai-google-adk-integration-azure-openai-agent-development/</link><pubDate>Tue, 11 Nov 2025 17:11:38 +0800</pubDate><guid>https://atbug.com/spring-ai-google-adk-integration-azure-openai-agent-development/</guid><description>TL;DR 本文用于验证 Spring AI 与 Google ADK 集成的可能性，为未来的正式版本提供实践经验和架构参考。核心收获在于验证了分层设计的可行性：Spring AI 处理 AI 服务抽象，ADK 管理智能体生命周期，两者通过标准接口实现实验性衔接。
需要注意的是，Google ADK 的 Spring AI 支持确实处于早期 SNAPSHOT 阶段，API 可能存在变更风险，但整体架构设计已经展现了良好的技术融合潜力。
背景 在企业级 AI 应用开发领域，如何将 Spring 生态的强大企业级特性与现代化智能体开发框架有机结合一直是技术团队面临的核心挑战。尤其是在探索 Google ADK（Agent Development Kit）与 Spring AI 的集成可能性时，开发者往往面临技术选型、架构设计和兼容性测试的多重考量。传统 AI 应用开发需要手动处理复杂的模型集成、工具调用和会话管理，导致代码耦合度高、维护成本大。本文通过一个简单但完整的时间查询智能体，展示了 Spring AI 的 Azure OpenAI 集成与 Google ADK 的智能体框架的融合可能性。特别需要注意的是，Google ADK 的 Java 版本中，Spring AI 支持目前仍处于 0.</description></item><item><title>Azure OpenAI 服务认证方式指南</title><link>https://atbug.com/azure-openai-service-authentication-guide/</link><pubDate>Tue, 23 Sep 2025 09:42:04 +0800</pubDate><guid>https://atbug.com/azure-openai-service-authentication-guide/</guid><description>引言 Azure OpenAI 服务提供了两种主要的认证方式：API 密钥认证和 Azure Active Directory (Azure AD) 认证。本教程将详细介绍如何配置和使用这两种认证方式，包括完整的代码示例和最佳实践。
为什么需要两种认证方式？ 特性 API 密钥认证 Azure AD 认证 安全性 中等 高 复杂度 简单 复杂 适用场景 开发测试 生产环境 权限管理 基于密钥 基于角色 审计能力 有限 完整 前置要求 Azure 订阅 Azure CLI 已安装并登录 Python 3.</description></item><item><title>告别代码修改：传统 VM 环境下的 OpenTelemetry 自动注入</title><link>https://atbug.com/opentelemetry-auto-injection-traditional-vm/</link><pubDate>Sat, 30 Aug 2025 18:23:19 +0800</pubDate><guid>https://atbug.com/opentelemetry-auto-injection-traditional-vm/</guid><description>在微服务架构中，可观测性就像是给应用装上 &amp;quot; 眼睛 &amp;quot; 和 &amp;quot; 耳朵 &amp;ldquo;。传统方式需要在每个服务中手动集成监控代码，而 OpenTelemetry Injector 提供了一种更优雅的解决方案。
TL;DR OpenTelemetry Injector 是一个专为传统 VM 环境设计的零代码观测工具。通过 Linux 的 LD_PRELOAD 机制，无需修改应用代码即可为 Java、Node.js、.NET 应用自动注入 OpenTelemetry 观测能力。核心优势：系统级自动化、多语言统一管理、生产环境就绪。不适合容器/K8s 环境，云原生场景建议使用 OpenTelemetry Operator、init-container、sidecar 等方案。
但是对于企业数据中心中的传统部署、混合技术栈的微服务架构，或者遗留系统的可观测升级，OpenTelemetry Injector 提供了一条优雅而高效的路径。
为什么需要零代码检测？ 想象一下，你负责维护一个包含数百个微服务的系统。每个服务都用不同的语言编写（Java、Node.js、.NET），现在需要为它们添加可观测性能力。
传统方式的问题：
需要修改每个服务的代码 不同语言的集成方式各不相同 版本升级时需要重新修改 不同语言中的功能不一致 增加了出错的风险 增加了维护的成本 这就是 OpenTelemetry Injector 诞生的背景。</description></item><item><title>预算有限，效率拉满：为什么 Kilo Code 成了我的首选 Coding Agent</title><link>https://atbug.com/budget-efficiency-kilo-code-choice/</link><pubDate>Fri, 29 Aug 2025 08:09:55 +0800</pubDate><guid>https://atbug.com/budget-efficiency-kilo-code-choice/</guid><description>TL;DR 打开 Kilo Code 的官网，首先入眼的就是这句话“适用于 VS Code 的最佳 AI 编程助手”，也确实我目前在 VS Code 上用过的几款插件中，Kilo Code 的表现是最好的。
写这篇文章的时候，我想到的是老板的那句话“有什么模型就用什么”。
在有限的条件下，寻找最优的解。
不乏有比 Kilo Code 更加优秀的工具，但往往需要更强的模型支持，或者更高的费用。投入和产出是成正比的，单次请求成本越高，慢慢地 Coding Agent 也有“氪金”的趋势。
照此以往，可能在不远的将来，优秀的工具和高级的模型会成为奢侈品，成为少数大企业的专属。
Kilo Code 作为站在巨人肩膀上（Cline + Roo Code）的存在，凭借着自身的优化和改进，成为了我目前的首选。
Kilo Code 可以支持 30+ 模型提供商，同时本身也提供了收费的 Kilo API。通过 Kilo API 可以使用几十种模型，包括 OpenAI、Claude、Gemini、MoonShot、Qwen、DeepSeek、xAI 各家的通用模型和 Coding 模型。并且计费与各家费用一样，Kilo Code 本身不加价不收手续费。</description></item><item><title>AGENTS.md：统一编码助手指令文件的新标准</title><link>https://atbug.com/agents-md-unifying-coding-agent-instructions/</link><pubDate>Sun, 24 Aug 2025 09:06:50 +0800</pubDate><guid>https://atbug.com/agents-md-unifying-coding-agent-instructions/</guid><description>TL;DR 从此刻起，你的项目中除了 README.md 文件外，可能还会包含另一个 markdown 文件：AGENTS.md
README.md：为人类设计，通常提供如项目介绍、安装指南、使用示例等信息。 AGENTS.md：为 Coding Agents 设计，提供额外的详细信息，如环境搭建、构建步骤、测试方法、代码规范、安全注意事项等。 背景 如果你经常在多种编程助手间来回切换，肯定会我将要说的问题深有体会。通常如果想要编程助手更好的工作、输出更高质量的代码，我们需要在项目中为其提供 instructions（指令 - 系统提示词）文件。比如 .cursor/rules、.clinerules、.github/copilot-instructions.md、claude.md、gemini.md。这些文件的内容通常类似，但各自的文件名和位置不同。即使类似 Cline、Roo Code、Kilo Code 这类同出一脉的编程助手，他们的目录名也各不相同。更不同说不同的编程助手了。
通常我的做法是使用软连接。首先创建一个统一的 instructions 文件，再通过软连接将 .clinerules 和 .cursor/rules、.github/copilot-instructions.md 都链接到同一个文件。
你也可以使用 intellectronica/ruler，ruler 可以将同一份 instructions 文件 .ruler/ 分发给多个编程助手，并更新 .gitignore 文件。
久而久之，你将会收获一个混乱的项目目录（再加上 MCP 服务器的管理文件），即使用上软链接或者 ruler（只是降低了维护成本）。</description></item><item><title>Obsidian 图片上传，这一款插件就够了</title><link>https://atbug.com/obsidian-image-upload-toolkit-stable/</link><pubDate>Sat, 23 Aug 2025 07:45:31 +0800</pubDate><guid>https://atbug.com/obsidian-image-upload-toolkit-stable/</guid><description>Obsidian 是一款强大的知识管理工具，但在处理本地图片上传时可能会遇到一些挑战。obsidian-image-upload-toolkit 插件通过持续的开发和优化，现已基本完成了对大部分主流云存储平台的支持，并实现了对用户图片路径的灵活处理，为 Obsidian 用户提供了专业的图片管理解决方案。
全面的云存储支持 该插件目前已经支持 8 种主流的云存储服务，几乎涵盖了市场上所有重要的图片存储平台：
Imgur：简单免费的图片托管服务，适合个人用户快速分享 阿里云 OSS：阿里云对象存储服务，提供高可靠、低成本的云端存储 Imagekit：专业的图片 CDN 和优化服务，提升图片加载速度 Amazon S3：业界标准的可扩展云存储服务，适合企业级应用 腾讯云 COS：腾讯云对象存储服务，国内用户访问速度快 七牛云 Kodo：国内知名的对象存储服务，提供丰富的数据处理功能 GitHub 仓库：基于 Git 的存储方案，适合开发者使用 Cloudflare R2：与 S3 兼容的存储服务，提供免费的存储和传输额度 这种全面的云存储支持使得用户可以根据自己的需求、预算和技术偏好选择最适合的存储方案，无论是免费服务还是付费的企业级解决方案。
灵活的图片路径处理 插件在图片路径处理方面表现出色，支持用户多种使用方式，适应不同的工作流程：
相对路径支持：插件能够正确处理 &amp;ldquo;./&amp;rdquo; 和 &amp;ldquo;../&amp;rdquo; 格式的相对路径，使用 path.normalize 来规范化路径，确保在复杂目录结构中也能正确解析图片路径。
多种附件位置选项：支持灵活的附件保存位置配置：</description></item><item><title>【译】AI 是否绑架了云原生创新？</title><link>https://atbug.com/the-future-of-ai-and-cloud-native/</link><pubDate>Sat, 23 Aug 2025 07:18:25 +0800</pubDate><guid>https://atbug.com/the-future-of-ai-and-cloud-native/</guid><description>译者注：近三年来，AI 热潮席卷科技行业，相关话题无处不在。与 AI 相关的讨论已经渗透到科技领域的每个角落，看似没有 AI 故事的产品变得更难推广。这里的云原生可以换成任何其他技术领域，AI 的兴起是否也在影响它们的发展和关注度？欢迎大家留言讨论。
在这个充满变革的时代，保持开放心态、持续学习和积极拥抱新技术至关重要。无论是 AI 还是云原生，唯有不断探索和实践，才能在技术浪潮中立于不败之地。让我们共同见证创新的力量，推动行业向前发展。
本文翻译自 Alan Shimel 的 Has AI Hijacked Cloud-Native Innovation?。
AI 似乎正在成为科技领域的绝对焦点，几乎占据了所有讨论空间。
从董事会会议室到行业活动的走廊，AI 话题无处不在。它令人兴奋、颠覆且充满变革力量。然而，AI 的迅猛发展也可能让其他重要技术创新被忽视。我不禁思考：AI 是否正在劫持云原生？
仅仅一两年前，云原生还是企业技术领域的明星。Kubernetes 话题热度不减，可观察性平台蓬勃发展，Backstage 推动着内部开发者门户，GitOps 流水线高效运转，CNAPs 日益智能，服务网格架构迅速普及。云原生不仅意味着迁移到公有云，更是应用现代化、混合与边缘部署，甚至裸机部署的核心战略。
云原生的普及有其深层原因——它为团队带来了创新所需的灵活性、可扩展性和模块化能力，帮助企业保持竞争力。
在 Techstrong，我们对云原生的发展充满信心，甚至将其作为核心虚拟活动的名称：Cloud Native Now – One for the Road。这充分体现了云原生理念在现代 IT 领域的深度影响力。</description></item><item><title>好久没更新 时间都去哪了</title><link>https://atbug.com/long-time-no-update-where-did-time-go/</link><pubDate>Sat, 16 Aug 2025 00:19:14 +0800</pubDate><guid>https://atbug.com/long-time-no-update-where-did-time-go/</guid><description>确实很久没更新了，如果不是 0 点要支持版本发布，今晚也不会这么晚还在电脑前。
这几个月工作确实很忙，回家后已经不想再坐回电脑前，甚至也不想打开自己的电脑。我的电脑上一直装着 Timing 记录日常状态，刚查了下 8 月至今只用了 6 小时，7 月 10 小时，5、6 月都不到 30 小时。
4 月在郊区买了套二手房，现在住的还是 2013 年买的那套。渐渐厌倦了市区的嘈杂，虽然方便是真方便。5 月开始装修，基本没度过完整的周末。一个字，累。
累也和自己的性格有关，事必躬亲，尤其在意细节。无论生活还是工作，有时太专注细节反而忽略了“大局”，容易迷失在复杂的细节里。
最近对技术内容关注也少了，这两年市场最火的还是 AI。从最初的大模型，到多智能体、各种工作流框架和编程辅助工具。应用落地方面，感觉编程辅助最突出。但不仅限于编程，虽然产品层出不穷，彼此差距不大，还没有杀手级功能。
好在装修基本完成，半个月后可能会有更多时间，也会有更多精力投入到工作和生活中。</description></item><item><title>macOS 原生容器 Apple Container 架构解析</title><link>https://atbug.com/macos-native-apple-container-architecture/</link><pubDate>Mon, 23 Jun 2025 20:55:51 +0800</pubDate><guid>https://atbug.com/macos-native-apple-container-architecture/</guid><description>TL;DR Apple Container 结合 macOS 框架与模块化插件体系，提供了强大的容器化环境。理解其架构与可扩展性，有助于开发者在云原生与虚拟化场景下充分发挥其能力。
引言 Container 是 Apple 提供的开源命令行工具，专为在 Mac 上运行 Linux 容器而构建，尤其针对 Apple Silicon 进行了优化。它由 Swift 编写，基于 Containerization Framework 构建。Apple Containerization 和 Container CLI 于 WWDC 2025 发布。
延伸阅读：
苹果发布 Containerization Framework：开启 macOS 容器化新纪元 Apple Container 开箱实践 Apple Container 是一种客户端 - 服务器架构系统，使 macOS 上能够运行容器化工作负载。本文将深入探讨其架构、启动流程、核心插件、可扩展性及安全性等内容。</description></item><item><title>Agentic Mesh：增强现代企业系统中的自主 AI 代理</title><link>https://atbug.com/agentic-mesh-enhancing-autonomous-ai-agents-in-modern-enterprise-systems/</link><pubDate>Sun, 22 Jun 2025 19:19:37 +0800</pubDate><guid>https://atbug.com/agentic-mesh-enhancing-autonomous-ai-agents-in-modern-enterprise-systems/</guid><description>译者注：本文介绍了 Agentic Mesh（代理网格）这一新型 AI 架构，强调其在现代企业自动化中的作用。文章指出传统 AI 代理系统存在单点故障、扩展性差、协作能力弱等问题。Agentic Mesh 通过自组织、动态协作、角色分工和安全治理，实现 AI 代理的高效协作、自适应优化和可扩展性。文中还介绍了反思、工具使用、规划、多代理协作等智能设计模式，并举例说明其在企业助手、网络安全和金融分析等场景的应用，展望了 AI 自动化的未来趋势。
阅读原文请跳转：https://medium.com/@vikram40441/agentic-mesh-enhancing-autonomous-ai-agents-in-modern-enterprise-systems-991fa6f36d6a
AI 驱动的智能代理正迅速成为企业自动化的核心。德勤预计，到2025年，采用生成式AI的企业中将有25%部署AI代理，2027年将增长至50%。虽然这种趋势令人振奋，但也带来了一个重要问题：
我们如何确保 AI Agent 具备可扩展性、适应性，并能无缝协作而不引发混乱？
答案在于Agentic Mesh——一个结构化、动态且自我进化的 AI Agent 网络，能够自主发现、协作并优化任务。但在深入了解Agentic Mesh 是什么之前，先探讨传统 AI 架构的局限性，以及为何需要更先进的框架。
为什么需要 Agentic Mesh？ 当前大多数企业仍依赖僵化的 AI 架构，Agent 各自为政或遵循预设流程。这些方式虽然可用，但随着企业 AI 自动化规模扩大，弊端日益显现。
传统 AI 架构的问题 1.</description></item><item><title>Apple Container 开箱实践</title><link>https://atbug.com/apple-container-hands-on/</link><pubDate>Sat, 21 Jun 2025 22:38:27 +0800</pubDate><guid>https://atbug.com/apple-container-hands-on/</guid><description>TL;DR Apple Container CLI 是一款强大的工具，可在 macOS 上运行 Linux 容器，特别针对 Apple Silicon 进行了优化。
该 CLI 设计为开发者友好，命令风格与 Docker CLI 类似。对于已经熟悉 Docker CLI 的用户，Container CLI 应该会非常熟悉，并且为 macOS 用户提供了更原生的体验。
本文将介绍 Container CLI、如何快速上手，以及如何在 macOS 上运行 Linux 容器。
关于 Container CLI Container 是 Apple 提供的开源命令行工具，专为在 Mac 上运行 Linux 容器而构建，尤其针对 Apple Silicon 进行了优化。它由 Swift 编写，基于 Containerization Framework 构建。Apple Containerization 和 Container CLI 于 WWDC 2025 发布。</description></item><item><title>苹果发布 Containerization Framework：开启 macOS 容器化新纪元</title><link>https://atbug.com/apple-containerization-framework-macos-release/</link><pubDate>Sun, 15 Jun 2025 12:12:00 +0800</pubDate><guid>https://atbug.com/apple-containerization-framework-macos-release/</guid><description>TL;DR 苹果 Containerization Framework 的发布，标志着 macOS 容器化进入全新阶段。
虽然苹果推出了原生容器化框架，但底层依然依赖虚拟机技术。为实现更高的安全性和隔离性，每个容器都运行在独立的轻量级虚拟机中，反而使虚拟机的使用频率和数量比以往更多。
对行业来说，会对如 OrbStack、Docker Desktop 等商业产品产生显著影响。实际上，类似 OrbStack 这样的本地容器解决方案早已存在（笔者本人也已使用一两年，并在博客中做过介绍），但苹果官方的原生方案无疑将加剧市场竞争，推动相关产品加速创新。
对开发者来说，虽然影响有限，但本地开发体验将变得更加安全高效（尤其是 M1 用户）。官方方案不仅提升了易用性和性能，也进一步促进了容器生态的融合与创新。
对于开发者和行业而言，这既是机遇，也是挑战。让我们拭目以待，苹果如何在容器领域持续发力，带来更多可能。
背景与动机 容器技术已成为现代软件开发和部署的基石。长期以来，macOS 用户在本地运行 Linux 容器时，主要依赖虚拟机方案（如 Docker Desktop、Colima、OrbStack 等），难以获得与 Linux 原生环境一致的体验。苹果在 WWDC2025 推出 Containerization Framework，旨在为 macOS 用户带来更安全、高效、原生的容器运行能力，推动开发生态进一步升级。
什么是容器 容器是一种轻量级的虚拟化技术，允许开发者在隔离的环境中运行应用程序。与传统虚拟机相比，容器共享宿主操作系统内核，但每个容器拥有独立的文件系统、网络和进程空间。这种方式使得容器启动速度更快、资源占用更低，非常适合微服务架构和持续集成/持续部署（CI/CD）流程。
与宿主机隔离：容器运行时与主机系统相互隔离，提升安全性。 工作负载之间隔离：不同容器之间互不影响，便于多应用并行部署。 便于开发环境一致性：开发、测试、生产环境可高度一致，减少“在我电脑上没问题”的情况。 运行时隔离：每个容器拥有独立的文件系统、网络和进程空间，保证应用运行互不干扰。 什么是 Containerization Framework Containerization Framework 是苹果开源的 Swift 框架，专为在 macOS 上运行 Linux 容器而设计。其核心目标包括：</description></item><item><title>我用 Vibe Coding 开发了一个量化交易程序</title><link>https://atbug.com/vibe-coding-quant-trading/</link><pubDate>Fri, 23 May 2025 22:04:03 +0800</pubDate><guid>https://atbug.com/vibe-coding-quant-trading/</guid><description>是的，你没看错，我确实用 Vibe Coding 写了一个量化交易程序。相信看到标题点进来的，再看到这么一句后，不免会冒出一个想法：“是谁给你的勇气写量化交易程序！”
先澄清一下，这并不是一个“正经”的量化交易程序，而是为了参加公司 AI 编程 Hackathon 开发的一个简单量化交易程序。
在比赛报名截止的前一天，我被拉进了编程小组。起初参加比赛纯属一时兴起，更没想到那几天工作和家里的事情都很忙，甚至连比赛的说明会和练习赛都没参加。到比赛的前一天，我还没能为小组贡献一行代码，甚至连比赛的细节都不清楚。
可能是巧合，淘汰赛当天凌晨 5 点我就醒了。既然醒了，不如起床看看比赛的说明。看完说明后，我发现用 Vibe Coding 是个不错的选择。因为时间有限，Vibe Coding 可以让我更专注于问题本身，而不是实现细节。
最终，我花了 2 个多小时搞定了这个量化交易程序，成绩自认为还不错。虽然因为某些原因小组赛没有晋级，但整个过程让我受益匪浅。
在这里，我想分享一下这个量化交易程序的实现思路和方法。
Vibe Coding 让我们先了解一下什么是 Vibe Coding。相信看完介绍后，你会明白是什么给了我写量化交易程序的勇气。
Vibe Coding 是什么？ Vibe Coding，有些中文翻译为“氛围编程”，听起来可能有些抽象。
这个概念最早由计算机科学家 Andrej Karpathy（OpenAI 联合创始人）于 2025 年 2 月提出。Vibe Coding 的核心思想是通过自然语言描述来生成代码，而不是通过传统的编程语言。它是一种基于自然语言的编程方法，允许用户通过自然语言描述，借助大模型工具生成代码。其目标是让编程变得更加简单和直观，尤其是对没有编程经验的人。</description></item><item><title>Docker BuildKit 实战：使用缓存优化依赖管理加速构建</title><link>https://atbug.com/docker-buildkit-cache-optimization-dependencies-acceleration/</link><pubDate>Sun, 18 May 2025 23:24:26 +0800</pubDate><guid>https://atbug.com/docker-buildkit-cache-optimization-dependencies-acceleration/</guid><description>什么是 Docker BuildKit Docker BuildKit 是 Docker 的下一代构建引擎，它提供了更高效、更灵活的容器镜像构建能力。BuildKit 于 2018 年引入，从 Docker 18.09 版本开始集成到 Docker 引擎中，并在 Docker 23.0 版本后成为默认的构建系统。
BuildKit 的主要特点 并行构建：能够并行执行独立的构建步骤，大幅提高构建效率 高级缓存机制：更智能的缓存系统，支持内容寻址存储 挂载功能：支持在构建过程中挂载文件系统，如缓存、密钥等 跨平台构建：可以在一个平台上构建用于其他平台的镜像 更安全的特权降级：更好的安全隔离 如何启用 BuildKit 从 Docker Engine v23.0 开始，BuildKit 已经开始作为 Docker 的默认构建引擎来使用。
可以通过设置环境变量 DOCKER_BUILDKIT= 0 来禁用。</description></item><item><title>几个常用的 MCP Server 收录平台</title><link>https://atbug.com/mcp-server-directory-platforms/</link><pubDate>Tue, 29 Apr 2025 06:39:54 +0800</pubDate><guid>https://atbug.com/mcp-server-directory-platforms/</guid><description>在人工智能与各类技术深度融合的当下，MCP（Model Context Protocol）发展迅猛热度不减。服务器的多样性和功能性不断拓展。对个人用户、AI 爱好者而言，找到合适的 MCP Server 收录平台，能更高效地获取工具和资源。以下是一些常用的 MCP Server 的收录平台，供大家参考：
mcp.so https://mcp.so/
目前已收录 11185 个 MCP 服务器。
该平台展示了众多特色 MCP 服务器，如与各类地图、数据库、软件集成的服务器，还有用于文本处理、图像生成等功能的服务器 。
awesome-mcp-servers https://github.com/punkpeye/awesome-mcp-servers/
目前已收录 4148 个 MCP 服务器。
基于 GitHub 仓库的索引，展示了 MCP 服务器的相关信息。该仓库提供了多种语言版本的文档，包括英文、中文（简体和繁体）、日语、韩语、葡萄牙语、泰语等。分类详细、全面，涵盖了多种类型的 MCP 服务器。
Glama MCP https://glama.ai/mcp/servers
一个 web 平台，提供了 MCP 服务器的索引和搜索功能，收录的内容来自上面的 awesome-mcp-servers 仓库。</description></item><item><title>从抓包看 MCP：AI 工具调用背后的通信机制</title><link>https://atbug.com/mcp-communication-protocol-packet-analysis/</link><pubDate>Sat, 26 Apr 2025 16:44:57 +0800</pubDate><guid>https://atbug.com/mcp-communication-protocol-packet-analysis/</guid><description>TL;DR 通过抓包分析，我们清晰地了解了 MCP 通信的全过程：从建立 SSE 连接、三步初始化、工具调用操作到最终的连接终止。可以看出，MCP 基于简单的 SSE 协议搭建了一个功能强大的工具调用框架，使 AI 代理能够便捷地调用外部工具完成复杂任务。
相比传统的接口调用方式，MCP 更加灵活，能够自动适应不同的工具集，让 AI 代理 &amp;quot; 即插即用 &amp;quot; 地使用各种服务能力，这也是其设计的精妙之处。
当然，MCP 也并不是完美的，作为一个新兴的协议，它仍然在不断发展中。未来可能会有更多的功能和特性被添加进来，以满足更复杂的需求。
背景 MCP 支持两种标准的传输实现：标准输入/输出（stdio）和 Server-Sent Event（下称 SSE）。stdio 基于命令行工具，多用于本地集成，通过进程通信来实现；SSE 基于客户端和服务器的网络通信，用于跨设备网络的通信场景。
既然是用抓包来分析，我们就要选择使用 SSE 传输 MCP server，然后通过工具进行网络抓包分析。在抓包分析之前，我们必要对 SSE 协议进行简单的了解。
SSE 协议 SSE 协议 是一种服务器推送技术，使客户端能够通过 HTTP 连接从服务器自动接受更新，通常用于服务器向客户端发送消息更新或者连续的数据流（流信息 streaming）。</description></item><item><title>一条命令搞定！存量 Spring REST 服务秒变 MCP 服务</title><link>https://atbug.com/one-command-to-convert-legacy-spring-rest-services-to-mcp-services/</link><pubDate>Sat, 05 Apr 2025 23:43:59 +0800</pubDate><guid>https://atbug.com/one-command-to-convert-legacy-spring-rest-services-to-mcp-services/</guid><description>TL;DR 在 AI 技术浪潮中，MCP 为服务集成带来了创新性思路，LLM 与 MCP 的组合更是为存量 API 服务注入新活力。
本文先阐述基于 Spring AI MCP 开发 MCP 服务的详细步骤，随后引入 OpenRewrite 框架及其 spring-rest-to-mcp 工具，实现 Spring REST 服务到 MCP 服务的自动化转换。
最后，借助示例项目，全方位展示从环境搭建、代码转换到任务编排执行的完整流程，助力开发者快速打通存量 Spring REST 服务对接 MCP 协议的通道，大幅提升服务集成的灵活性与智能化水平 。
背景 在上一篇 超越 API：MCP 如何成为 AI 时代的 “万能适配器”？ 文章发布后，我一直思考一件事。如果将以往的 API 集成看作系统的硬链接，那么 LLM + MCP 无疑是系统的软链接。MCP 的诞生，使我们能在运行时动态连接不同服务，摆脱设计阶段的束缚，实现更为灵活、智能的服务集成模式。</description></item><item><title>超越 API：MCP 如何成为 AI 时代的“万能适配器”？</title><link>https://atbug.com/beyond-apis-mcp-universal-adapter-ai-era/</link><pubDate>Fri, 28 Mar 2025 00:10:03 +0800</pubDate><guid>https://atbug.com/beyond-apis-mcp-universal-adapter-ai-era/</guid><description>TL;DR API 作为数字世界的连接纽带，虽推动了开放生态的繁荣，却因协议碎片化、开发高成本陷入“巴别塔困境”。MCP（模型上下文协议）的诞生，标志着 AI 交互范式从“人工编码适配”迈向“机器自主协作”。通过标准化服务描述与上下文感知机制，MCP 成为 AI 时代的“万能适配器”——既消除工具间的协议鸿沟，又支持运行时动态编排，使 AI 应用能像“热插拔硬件”一样自由调用跨领域服务。
本文将从 API 的历史演进、MCP 的设计理念、 以“帮我查询周末的天气，如果下雨就推荐附近的电影院”一个简单场景为例，展示 MCP 如何实现 AI 应用的智能编排，助力 AI 应用实现“所想即所得”的认知革命。
从代码接口到数字生态的基石 关键词：标准化、能力复用、开放。
API（Application Programming Interface，应用程序编程接口）是软件系统之间交互的标准化接口，它定义了数据交换的规则和协议。从手机 App 到云计算平台，API 支撑着现代数字世界的运行。
萌芽：本地化代码接口 上个世界 60 年代，UNIX 操作系统率先使用系统调用（System Call），如文件读写 open()、write() 等，为应用程序提供了访问操作系统资源的接口，成为 API 的雏形。随着结构化编程的兴起，API 逐渐演变为函数库（Library），如 C 语言的标准库 stdio.</description></item><item><title>Azure 云上 10 分钟全自动部署 K3s 集群实战</title><link>https://atbug.com/azure-k3s-cluster-automation-in-10-minutes/</link><pubDate>Sun, 16 Mar 2025 23:06:30 +0800</pubDate><guid>https://atbug.com/azure-k3s-cluster-automation-in-10-minutes/</guid><description>背景 在云计算与容器化技术飞速发展的今天，快速搭建可扩展的 Kubernetes 集群成为开发者的必备技能。尤其是在开发测试环境中，能够快速创建、销毁集群是提升工作效率的关键。
K3s 作为轻量级 Kubernetes 发行版，非常适合在云上快速搭建测试、开发或边缘计算集群。结合 IaC（基础设施即代码）工具 Terraform 和自动化脚本项目 k3s-cluster-automation，可以极大提升集群部署效率，降低人为操作风险。
本文将介绍如何通过 Terraform 与 k3s-cluster-automation 脚本，在 Azure 云上实现从虚拟机创建到 K3s 集群部署的全自动化流程，整个过程无需手动干预，大幅提升开发测试效率。
为什么选择 Azure + K3s + Terraform？ Azure：提供稳定的云计算资源，适合快速部署测试环境（还有另外一个原因，作为微软 MVP，可以免费使用 Azure 资源） K3s：轻量级 Kubernetes 发行版，资源占用少（仅需 512MB 内存），适合边缘计算与测试集群，是我喜欢的 Kubernetes 发行版之一 Terraform：基础设施即代码（IAC）工具，通过代码描述资源状态，支持版本控制与批量部署 前置条件 Azure 账户（需有资源创建权限） 本地环境：安装 Terraform CLI、Azure CLI K3s 部署工具：k3sup CLI（用于远程部署 K3s） 一、通过 Terraform 创建 Azure 虚拟机 1.</description></item><item><title>让效率飙升的 Alfred 秘籍：分享我的 Workflow 清单</title><link>https://atbug.com/alfred-workflow-efficiency-guide/</link><pubDate>Sun, 02 Mar 2025 17:10:07 +0800</pubDate><guid>https://atbug.com/alfred-workflow-efficiency-guide/</guid><description>说起 Alfred，我可能算是重度也是中毒用户了。从 15 年开始接触 Mac 的时候就开始用，到现在也差不多 10 年了，应该是我日常使用频率最高的一款软件，没有之一。
手上这台 MBP 是从 21 年底开始用的，日使用频次有所降低。公司那台也有日均 40+ 次的使用频次，庆幸 Alfred 是在公司软件白名单中（由于安全限制，有些功能无法使用）。
从 2.0 开始用起，用到了现在的 5.6。Alfred Powerpack 作为 Alfred 的灵魂，也是我购买的最值的软件之一。我从 3.0 开始购买的 Mege Supporter 版本（终生免费升级），一路升级到现在。在众多软件纷纷买断转订阅的当下，Alfred 依然坚持买断模式，真良心！
言归正传，购买 Powperpack 当然是为了使用 Workflow 功能，通过 Workflow 可以实现很多强大的功能，比如搜索、快捷操作、自动化等等。以前能找到一个趁手的 Workflow 时都是一件开心的事，这么多年积累了不少的 Workflow，自己也有尝试 写过一个。</description></item><item><title>Docker Hub 限速时代：Spegel 无状态缓存如何实现离线镜像共享</title><link>https://atbug.com/docker-hub-rate-limit-era-spegel-stateless-cache-offline-image-sharing/</link><pubDate>Sun, 02 Mar 2025 08:28:20 +0800</pubDate><guid>https://atbug.com/docker-hub-rate-limit-era-spegel-stateless-cache-offline-image-sharing/</guid><description>TL;DR Spegel 是一个非常有意思的项目，可以帮助我们在 Kubernetes 集群中实现镜像共享，提高镜像拉取的速度，减少对外部镜像仓库的依赖。对于一些离线或者内网环境、带宽优化和成本控制、容灾和高可用等场景，尤其是绕过 Docker Hub 镜像拉取限制方面，Spegel 都是一个不错的选择。
背景 Docker Hub 的限制越来越严，从今天的 4 月 1 日起，未经身份验证的用户每小时最多拉取 10 次镜像，并且是基于 IP 地址或 IPv6 子网限制，这意味着在一个局域网中多个用户共用一个公网 IP 的情况下，这个限制会更加严格。如果收到 429 Too Many Requests 响应，说明已经超过了限制，拉取请求被限流。
Docker Hub 多年来一直免费，但是随着用户数量的增加，成本也在增加，为了保证服务的稳定性，Docker Hub 也需要收费，这是一个必然的趋势。毕竟，运营如此全球规模的服务，维护成本是非常高的。天下没有免费的午餐。
但是对于一些个人开发者或者小团队来说，这样的限制可能会影响到他们的日常开发。
如何避免被限流 1. 登录 Docker Hub 最简单的方法，登录 Docker Hub 可以提高拉取镜像的次数，每小时 100 次。这对于个人开发者来说应该是足够的了。</description></item><item><title>从零开始：使用 OpenTelemetry Collector 构建强大的日志处理流水线</title><link>https://atbug.com/from-zero-building-a-powerful-log-processing-pipeline-with-opentelemetry-collector/</link><pubDate>Sat, 22 Feb 2025 17:30:48 +0800</pubDate><guid>https://atbug.com/from-zero-building-a-powerful-log-processing-pipeline-with-opentelemetry-collector/</guid><description>在 上篇文章 中，我们通过基于 Kubernetes 注解的 OpenTelemetry 动态发现为应用添加了日志采集的能力。从 OpenTelemetry Collector 日志我们可以看到为两个示例应用创建了 FileLog 接收器 来采集日志。
在本文中，我们将深入探索 OpenTelemetry Collector 的 FileLog 日志接收器的使用。
前文回顾 首先回看下上篇文章的结尾，Otel 的 Kubernetes Observer 发现了带有指定注解或者符合接收器创建规则的 Pod，自动为其创建了 FileLog 接收器。
我们将日志中 Reciver Creator 通过内置配置模板创建的接收器，转换为 OpenTelemetry Collector 配置文件：
config: receivers: filelog: include: - &amp;#34;/var/log/pods/default_java-sample-77b6d8f9c5-5zzh5_e6c5ca2a-6c3f-4ae4-89a6-34ca5cd9a3fa/java-sample/*.</description></item><item><title>基于 Kubernetes 注解的 OpenTelemetry 动态发现：打造自服务的可观测方案</title><link>https://atbug.com/self-service-observability-with-kubernetes-annotations-and-opentelemetry/</link><pubDate>Sat, 15 Feb 2025 22:23:15 +0800</pubDate><guid>https://atbug.com/self-service-observability-with-kubernetes-annotations-and-opentelemetry/</guid><description>背景 在云原生时代，Kubernetes 已经成为容器编排的事实标准，越来越多的企业将其作为核心基础设施来运行和管理现代化应用。然而，随着微服务架构的普及和容器化工作负载的动态性增强，传统的监控和可观测性工具逐渐暴露出局限性——静态配置难以应对快速变化的环境，手动维护监控规则的成本也变得越来越高。
传统的监控方式通常依赖于集中式的配置管理，即由管理员预先定义好需要监控的目标和服务。这种方式不仅缺乏灵活性，还容易导致配置更新滞后，无法及时反映集群中的实际状态，还伴随着出错的风险。这种挑战，即使是作为开放标准的可观测性框架 OpenTelemetry 也无法避免。
不过 OpenTelemetry 通过 Reciver Creator 和 Kubernetes Observer 特性可以解决这个问题，实现基于 Kubernetes 注解的 OpenTelemetry Collector 动态发现功能。
工作原理 Kubernetes Observer 负责检测 Pod 及其元数据（如暴露的端口、注解等）。Observer 除了支持 Kubernetes API，还支持 Docker API（1.24+）、ECS API、ECS Tasks 元数据端点、Host。 Receiver Creator 根据这些信息动态实例化适当的接收器（如文件日志接收器）。Receiver Creator 可以支持 log、metrics 和 trace 接收器的创建。 这一功能通过将监控配置的控制权下放至开发人员，显著简化了可观测性管理的复杂性。</description></item><item><title>OpenRewrite 学习笔记（四）：使用 JavaTemplate 创建复杂 LST</title><link>https://atbug.com/openrewrite-learning-notes-4-creating-complex-lst-with-javatemplate/</link><pubDate>Sun, 19 Jan 2025 11:24:20 +0800</pubDate><guid>https://atbug.com/openrewrite-learning-notes-4-creating-complex-lst-with-javatemplate/</guid><description>LST 是 OpenRewrite 的核心，是 OpenRewrite 实现精准、可控代码修改的关键支柱。本文将介绍如何使用 JavaTemplate 创建复杂的 LST。
背景 在操作代码的过程中，可能需要添加、修改、删除代码片段。比如添加一个变量声明、添加一个方法、修改一个方法体等。这些操作都需要创建 LST（Lossless Semantic Tree）。那如何创建一个变量声明的 LST？
我们需要创建变量声明 J.VariableDeclarations 以及变量 J.VariableDeclarations.NamedVariable，并将其添加到方法体中。看起来很简单，但是在使用构造器初始化对象时需要提供很多参数。何况很多参数自身的初始化也是一个复杂的过程，需要保证类型的属性匹配准确。
实际上不需要这么麻烦，也是 官方不推荐的方式。OpenRewrite 提供了 JavaTemplate 来简化这个过程。
JavaTemplate javaTemplate = JavaTemplate.builder(&amp;#34;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();&amp;#34;) .build(); 上面的代码片段中，通过 JavaTemplate 构建了一个变量声名赋值的 LST。使用 JavaTemplate 可以生成格式准确、类型属性完整的 LST，而不需要手动构造。</description></item><item><title>将博客从 GitHub Pages 迁移到 Azure Static Web Apps</title><link>https://atbug.com/migrate-blog-from-github-pages-to-azure-static-web-apps/</link><pubDate>Sun, 05 Jan 2025 19:53:57 +0800</pubDate><guid>https://atbug.com/migrate-blog-from-github-pages-to-azure-static-web-apps/</guid><description>背景 我的博客 atbug.com 一直采用 Hugo 作为静态网站生成器，内容和源码托管在 GitHub 仓库，通过 GitHub Actions 自动构建并发布到 addozhang.github.io 仓库，最终使用 GitHub Pages 进行托管。这种方式简单高效，适合个人博客的持续集成与部署。
迁移原因 作为微软 Azure MVP，我可以使用 Azure 的相关服务。Azure Static Web Apps 提供了更高的可用性、全球 CDN、自动化部署、集成认证等优势，因此决定将博客从 GitHub Pages 迁移到 Azure Static Web Apps。
迁移过程 准备工作
保持博客源码和内容依然托管在 GitHub 仓库，无需更改原有内容结构。 确保本地 Hugo 版本与生产环境一致，建议使用官方推荐的 Hugo Extended 版本。 创建 Azure Static Web App</description></item><item><title>再见，解惑的 2024</title><link>https://atbug.com/farewell-2024/</link><pubDate>Wed, 01 Jan 2025 09:04:38 +0800</pubDate><guid>https://atbug.com/farewell-2024/</guid><description>新年的钟声刚刚结束，站在 2025 年的第一天回望，2024 这一年，宛如一场盛大而深刻的旅程，为我解答了许多的人生谜题。如今，是时候一直挥手告别，迎接新的开始。
子曰：吾十有五而志于学，三十而立，四十而不惑，五十而知天命，六十而耳顺，七十而从心所欲，不逾矩。
孔子《论语·为政》
解惑的 2024 于时光长河中溯源，2024 成为我人生中具有独特意义的“解惑”之年。当岁月的指针指向不惑之岁的初端，过往的种种迷惑和迷茫，在这一年得以解开，完成了向“不惑”之境的转身。
书籍如明灯，照亮前行的路。
2024 于我是智识充盈的一年，阅读量超过过往任何一年。在这一年，我阅读了 30 本书，其中大部分是心理学、社会学、历史等人文类的书籍，正是这些书籍帮助我更好地理解人性、社会和历史。
诚如莫言在《千年悖论》的序言中所说：“在我们的短暂的一生中，不会有太多的大风大浪，不会有太多的悲欢离合，体验到的和精力过的事毕竟有限。”
幸而有书籍，我们得以跨越现实的局限，看到更多的人生百态，感受到更多的人生百味，不断地丰富内心的世界。
工作 今年社区的输出有所下降，精力更多的投入在产品和市场的支持上。我们推出了一款新的开源产品，该产品在市场上引发了一定程度的关注与回响，为公司带来了新的发展方向。
然而下半年，我结束了三年的远程办公，踏上了新的职业征程。虽然离开，我依然会参与开源社区的输出建设，因为都是很有趣的产品。
站在新的起点，充满了期待和决心，希望在新的一年有所突破。
学习 正如前文所述，2024 年是我智识充盈的一年，阅读了大量的书籍，找到了更多的兴趣点，真正的享受到了阅读的乐趣。
这个公众号“运营”四年了，四年来，我一直坚持着记录并分享自己的学习旅程。不出意外，这一年的产量延续了逐年下降的态势，全年更新了 23 篇，而且还夹杂了技术之外的内容。但每一篇都是我用心的记录，每一篇都是我对知识的理解和总结。
在新的一年，我将继续保持学习的热情，不断充实、提升自己。
生活 24 年是重度参与儿子学习的一年，陪伴他的时间更多。我为此也阅读了很多关于脑科学和儿童成长的书籍。在这一年，我看到了他的进步，也看到了他的成长。
相比学习成绩的提升，习惯的养成让我感到欣慰。这一年，他的阅读量有了明显的提升，也能够不间断地独立完成作业。虽然还有些小孩子的调皮，但我更希望还能够保持这种天性。
不出意外，经历了去年冬天的养膘季之后，体重又有所回升。希望在新的一年，恢复锻炼和健康饮食，降低体重，保持身体的健康。岁月不饶人，健康才是最重要的。</description></item><item><title>从 LB Ingress 到 ZTM：集群服务暴露新思路</title><link>https://atbug.com/from-lb-ingress-to-ztm-new-approach-to-cluster-service-exposure/</link><pubDate>Mon, 30 Dec 2024 11:33:10 +0800</pubDate><guid>https://atbug.com/from-lb-ingress-to-ztm-new-approach-to-cluster-service-exposure/</guid><description>上周六有幸参与了 KubeSphere 社区和 Higress 社区联合举办的「云原生 + AI Meetup 广州站」的活动。在会上，我分享了一篇关于「从 LB Ingress 到 ZTM：集群服务暴露新思路」的主题演讲。在这里，我将分享一下演讲的内容，同时将文章标题做了小调整。
集群服务暴露的需求 集群服务暴露的需求来自 Kubernetes 服务的虚拟化和网络隔离。众所周知，Kubernetes 的 Pod 是动态的，可能会频繁的删除、重建，重新调度到不同的节点，IP 地址也会随之变化。Kubernetes 使用 Service 来提供访问 Pod 的稳定接口，实现对服务的抽象。
Service 为 Pod 提供了一个稳定的 DNS 名称和虚拟 IP 地址，而不依赖于 Pod 的临时 IP。因此在集群内部的通信，通过 Service 的 ClusterIP 访问完全不存在问题。</description></item><item><title>AuthZen 工作组：打造统一的授权模型交互标准</title><link>https://atbug.com/authzen-future-standard-for-authorization-model-interactions/</link><pubDate>Sat, 21 Dec 2024 12:20:32 +0800</pubDate><guid>https://atbug.com/authzen-future-standard-for-authorization-model-interactions/</guid><description>背景 随着技术的快速发展并日益复杂化，安全和合规性的要求不断提升，传统的授权方案已经难以满足企业不断变化的需求。授权系统面临着如下挑战：
动态和细粒度的授权需求增加：现代应用和服务需要能够对用户权限进行实时的管理，以适应快速变化的业务需求和复杂的数据访问控制规则。传统的授权模式，如将权限直接嵌入到令牌中的 OAuth2，往往灵活度不足。 缺乏互操作性和标准化：随着业务和组织架构的复杂化，企业慢慢地会采用不同的授权解决方案，而且这些方案中往往因为缺乏同一的交互模型和协议，无法在不同系统、应用和服务之间进行无缝集成和安全交互，阻碍了技术创新以及现有架构的融合。 安全性和合规性要求提高，随着数据保护法规和行业合规标准的不断增强，组织需要更加精确和可控的授权机制来保护敏感数据。 这些促使授权系统需要持续演进以适应新的技术和业务需求，通过标准化机制、协议和格式来交换授权相关信息，促进向更动态、更细粒度的授权能力的范式转变。
如此就有了 OpenID 基金会的 AuthZen 工作组。
AuthZen 工作组 AuthZen 是 Authorization Exchange 的简称（起初看到这个名字我以为 Zen 就是那个 Zen），是 OpenID 基金会 比较新的工作组，官宣于 2023 年 10 月。
AuthZEN 工作组的目的是提供标准机制、协议和格式，以便在一个组织内部或跨组织之间的组件传递授权相关信息，这些组件可能由不同实体开发或来源。
正如起名字，这个工作组试图来解决在授权中遇到的 Exchange（交换） 问题，即如何在不同组件之间交换授权信息。
让我们来看一下 Exchange 发生在哪些地方：
XACML 授权模型 下面是源于 23 年前的 XACML 数据流模型。</description></item><item><title>OpenRewrite 学习笔记（三）：重构配方 Recipe 与访问者 Visitor</title><link>https://atbug.com/openrewrite-learning-notes-3-refactoring-recipe-and-visitor/</link><pubDate>Fri, 20 Dec 2024 07:11:14 +0800</pubDate><guid>https://atbug.com/openrewrite-learning-notes-3-refactoring-recipe-and-visitor/</guid><description>今天这篇我们来学习配方 Recipe 和访问者 Visitor，之所以一起介绍这两个是因为在 Recipe 的设计中使用了 访问者模式（Visitor Pattern）。
访问者模式是一种将算法与对象结构分开的软件设计模式。得益于这种分离，在将新操作添加到现有对象结构中时，无需修改对象结构。它是面向对象编程和软件工程中遵循开放/封闭原则的一种方法。
&amp;ndash; 维基百科
配方 Recipes 配方（Recipe）是一组可对 无损语义树（LST） 执行搜索和重构操作的逻辑集合。配方既可以代表一个独立的微小变更，也可以与其他配方组合，形成实现更复杂目标（如框架迁移）的大型改造方案。
OpenRewrite 提供了一个托管环境，用于发现、实例化和配置配方。在实现搜索或重构操作时，配方会委托给访问者（Visitor），由其负责抽象语法树（LST）的遍历和操作。
在第一篇的快速上手中，我们使用了配方 ChangeMethodName 将方法 hello 名改为 gretting。
扫描配方 Scanning Recipe 这是一个特别的 Recipe。如果一个 Recipe 需要生成新的源文件或者需要在进行更改前查看所有源文件，那它必须是 ScanningRecpie。
在实现层面 ScanningRecipe 扩展了 Recipe ，添加两个关键对象：accumulator 和 scanner。
accumulator 是由 Recipe 自身来定义的数据结构，用于存储 Recipe 运行时所需的任何信息。 scanner 是一个用数据来填充 accumulator 的 Visitor #visitor 除了两个对象外，ScanningRecipe 还提供了方法定义 getScanner() 这个方法需要子类来实现，返回值也是一个访问者 Visitor（在后面我们会介绍）。</description></item><item><title>OpenRewrite 学习笔记（二）：无损语义树 LST</title><link>https://atbug.com/openrewrite-lossless-semantic-tree-lst-overview/</link><pubDate>Sun, 08 Dec 2024 13:36:09 +0800</pubDate><guid>https://atbug.com/openrewrite-lossless-semantic-tree-lst-overview/</guid><description>在上一篇文章中我们提到，LST 是 OpenRewrite 实现精准、可控代码修改的关键支柱。本篇将更深入地探讨 OpenRewrite 在代码解析过程中，究竟是如何保留代码原有的精确语义结构的。
什么是 LST LST 是 Lossless Semantic Trees，无损语义树的缩写。
先上总结，说说我对这三个单词的理解：
Lossless 无损：代码解析和结构化过程中不丢失源代码中的任何信息（包括空格、注释、格式等），最终的属性结构中可以保留代码的原始细节。 Semantic（语义）：该树不仅仅是语法分析的产物，还能体现代码片段之间的语义关系。 Trees（树）：以树形结构组织代码元素，使得对代码片段的遍历、查询和变换更为直观且层次清晰。 说起语义树，就不得不提一下 AST。
抽象语法树 AST AST（Abstract Sytax Tree，抽象语法树），又常被称为语法树（Sytax Tree），是以树状的形式来表示编程语言的语法结构。AST 是编译器中广泛使用的的数据结构，用于表示程序代码的结构。
像下面的 AST 图是这段代码（欧几里得算法）的抽象语法树结构展示，树上的每个节点都表示源码中的一种结构。
while b ≠ 0: if a &amp;gt; b: a := a - b else: b := b - a return a 无损语义树 LST OpenRewrite 的 LST（无损语义树）具备一系列独特特性，使得在跨存储库的场景中依然能实现精确的代码搜索和转换：</description></item><item><title>OpenRewrite 学习笔记（一）：基本知识与原理解析</title><link>https://atbug.com/openrewrite-basics-and-principles-overview/</link><pubDate>Sun, 08 Dec 2024 13:31:20 +0800</pubDate><guid>https://atbug.com/openrewrite-basics-and-principles-overview/</guid><description>最近的工作多与系统改造相关，涉及框架与平台的迁移。偶然接触到 OpenRewrite 这款有趣的工具，于是打算整理一些学习笔记，也是对学习过程的记录。后续还会持续探索并更新文章，只是目前尚不确定会写多少篇。
随着软件项目日趋复杂、版本迭代和新技术引进的加快，工程师往往需要对成千上万行的代码进行统一风格化处理、批量重构或版本迁移。然而手动完成这些工作不仅费时费力，而且容易遗漏。大规模场景下，整个过程像是一个大的工程实施，因此就有了 Migration Engineering。
Migration Engineering 迁移工程是一种系统化的工程实践，广泛应用于技术栈更新、代码现代化、架构优化和大规模变更管理。通过自动化工具和流程的结合，它能够在确保质量的同时减少人工介入，从而提高效率，降低风险，并确保代码库在技术演进中保持一致性。
From ChatGPT 为了实现精确可控且可重复的代码自动化重构（Automated Refactoring），出现了多种工具和框架。OpenRewrite 正是其中备受关注的一个，它能帮助开发者在大规模代码重构中以一种结构化、可持续和可扩展的方式对代码进行批量改写和重构，从而提升开发团队的生产力和代码质量。
接下来，和我一起从从基础认识入手，了解什么是 OpenRewrite 以及它所依据的基本原理。
什么是 OpenRewrite OpenRewrite 是由 Moderne 公司开源的自动化重构框架。它的目标是通过一系列可组合的“配方”（官方术语 Recipes，可以理解为重构规则，为了更贴近官方文档的术语，将其翻译为配方。）在无需手动干预的情况下对代码进行有条理的结构重写。简言之，它不是简单的全局替换字符串工具，而是能基于 无损语义树（LST） 进行语义级别的代码修改。
应用场景 在深入原理之前，我们先来看几个常见的场景，这能帮助我们理解为什么要使用 OpenRewrite：
统一代码风格和格式化规则： 当团队决定采用统一的代码风格（如使用特定的命名规范、去除不必要的注释、多余的空格或换行），可以通过 OpenRewrite 的规则对全仓库进行自动化的清理。 批量升级依赖版本和框架迁移： 想象你维护着一个庞大的微服务体系结构，需要从 Spring Boot 2.x 升级到 3.x，这往往意味着在几十个甚至上百个仓库中同步修改数百个依赖项。OpenRewrite 可以通过一条 Recipe 来自动化完成这些变更。 统一日志框架或安全库： 如果团队决定弃用某个日志库或安全框架，需要全仓库替换相关调用。手动修改非常繁琐，而 OpenRewrite 能确保精确定位和替换代码。 遗留代码现代化： 对老旧项目的遗留代码进行现代化改造，例如将早期的 Java 语言特性（如匿名内部类）改为更现代的 Lambda 表达式，将旧的集合框架替换为流式处理 API，快速为老系统注入新特性和代码规范。 关键特性 语法级与语义级分析：基于 LST 进行代码解析，确保代码修改的精准性。 可扩展的配方（Recipes）：提供大量内置和可扩展的规则集，用于自动化处理常见的代码问题。配方可与其他配方组合成更复杂的配方。 语言与框架的广泛支持：编程语言如 Java、Kotlin、Groovy；数据格式：XML、YAML、Properties、JSON、ProtoBuf；构建工具 Maven、Gradle；框架：Quarkus、Spring、Micronaut、Jakarta。 如果说 Recipe 是 OpenRewrite 进行自动化重构的核心机制，那么 LST 就是是确保代码在重构时具备精确语义结构的基石。</description></item><item><title>狙击 K8s 用户的“流氓”专利：分布式软件定义网络 (dSDN)</title><link>https://atbug.com/patent-troll-targets-k8s-dsdn/</link><pubDate>Sun, 17 Nov 2024 12:22:24 +0800</pubDate><guid>https://atbug.com/patent-troll-targets-k8s-dsdn/</guid><description>背景 流氓专利（Patent Troll）通常是指一种专利滥用现象，其中专利的持有人或公司主要以专利诉讼和许可费为目的，而非通过生产或使用专利技术来进行创新。这类专利持有者有时被称为 专利流氓（Patent Trolls）。
流氓专利的主要特征：
没有实际的产品或服务 广泛的专利覆盖范围 诉讼作为主要手段 针对创新企业和开源社区 近年来，专利诉讼的阴云正逐渐笼罩技术创新领域。特别是在开源社区，专利“流氓”（Patent Trolls）的出现不仅威胁了开发者的创造力，还可能拖累整个行业的进步。以 CNCF（云原生计算基金会）生态为例，开发者们正在面临专利诉讼的压力，这些诉讼往往针对的是社区内广泛使用的基础性技术，而非真正创新的专利。
在这次的 KubeCon NA 2024 SALT LAKE CITY，CNCF 执行董事 Priyanka Sharma 在她的主题演讲中透露，专利流氓正在“追捕”使用 Kubernetes 的公司。这个专利就是 Edge Networking Systems LLC 公司的专利 Distributed software defined networking US-11695823-B1。
dSDN 专利 关于专利的完整内容，可以从 这里 获取。下面是针对这份长达 42 页内容的简单概括，如有解释不准的请指出。</description></item><item><title>探索 OpenTelemetry Profiling 进展：eBPF 代理快速上手</title><link>https://atbug.com/exploring-opentelemetry-profiling-progress-ebpf-agent-quickstart/</link><pubDate>Sun, 10 Nov 2024 22:34:07 +0800</pubDate><guid>https://atbug.com/exploring-opentelemetry-profiling-progress-ebpf-agent-quickstart/</guid><description>本文将介绍 OpenTelemetry Profiling 的进展，以及快速体验 Profiling 的 eBPF 代理。关于有 OTEL Profile 的概念和实现，将在下一篇介绍。
虽然 Profiling 进展迅速，Profile 功能仍然在实验阶段，不建议在生产环境使用。
背景 在软件工程中，可观测性（Observability）是指收集和分析程序执行情况、模块内部状态以及组件间通信数据的能力。可观测性的三大支柱——指标、跟踪和日志——为我们提供了深入了解应用程序行为的关键手段，尤其在分布式系统中尤为重要。
分布式系统的应用组件分布在多个节点上，导致事件链条和数据流的复杂化。通过有效运用这三大可观测性手段，可以帮助我们识别潜在问题、定位故障源，从而显著提高系统的稳定性和可维护性。
尽管可观测性的三大手段能够提供大量关于系统行为和性能的洞察，但无法探究程序内部代码函数层级的运行时表现。性能分析（Profiling）为深入了解应用程序的资源使用和执行效率提供了方法指导。持续性能分析（Continuous profiling）可以在应用程序运行期间连续进行性能剖析，帮助开发人员准确理解代码随着时间推移的运行情况。
今年 3 月，成立两年的 OpenTelemetry Profiling SIG 宣布支持 性能分析信号（Profiling Signal），半年多过去了，从规范到工具层面 Profiling 的支持取得了很大的进展。
进展 OTLP Profile 类型 继 traces、metrics、logs 和 baggage 之后，在 OTLP 1.</description></item><item><title>全面提升 Obsidian 体验：插件与日常技巧分享</title><link>https://atbug.com/enhance-obsidian-experience-plugins-tips/</link><pubDate>Sat, 09 Nov 2024 13:09:45 +0800</pubDate><guid>https://atbug.com/enhance-obsidian-experience-plugins-tips/</guid><description>上个月老崔发了 用 Obsidian 有效应对日常工作，介绍了日常使用的几个插件，其中有几个也是我常用的。正好最近在新的环境中配置 Obsidian，特此记录下常用的插件和技巧。
笔记这件事持续 N 多年了，从下面的数据来看算是重度笔记用户了吧。从起初的纯文本格式到 Markdown，从单纯的记录到知识库的建立，笔记应用也换了好几款。Obsidian 是我当前最喜欢的笔记应用，没有之一。Obsidian 是我去年从使用了 6 年的 Mweb 转过来的，甚至还为其写了一款 插件。
插件 是 Obsidian 最吸引我的点，除了官方的核心插件外，有社区插件（截止本文发出，共有 1986 个社区插件）。
轻核心 + 可扩展，也是很多应用的设计模式，真的是可盐可甜，丰俭由人。Obsidian 真是做到了极致，连目录导航都是通过插件来提供的。
下面是我使用的所有社区插件：
Advanced Tables Advanced URI Automatically reveal active file Banners Better Word Cound Calendar Clear Unused Images Dataview Dynamic Table Of Contents Excalidraw Iconize Image Upload Toolkit Kanban Linter Local Images Plus Media Extended Mind Map Obsidian Tabs Pandoc Plugin Periodic Notes Readwise Official Recent Files Reveal Active File Button Style Settings Tasks Templater Tracker Vault Full Statistics 下面介绍下在几个场景中我重度使用的几款插件（排名不分先后）。</description></item><item><title>在 Cilium CNI 集群上运行 vCluster 虚拟集群</title><link>https://atbug.com/run-vcluster-on-cilium-cni-cluster/</link><pubDate>Fri, 30 Aug 2024 16:52:12 +0800</pubDate><guid>https://atbug.com/run-vcluster-on-cilium-cni-cluster/</guid><description>上周在 KubeCon China 2024 大会上，我和社区伙伴们作为志愿者在 Cilium 项目展台与用户交流。有位用户询问 Cilium 是否能与 vCluster 集成，当时未能给出明确答复，特地回来后进行了测试。
答案是：在最新的 vCluster v0.20 中容器网络没有问题，但如果要在虚拟集群上使用 Cilium 的网络策略，则会受到限制。
背景 什么是虚拟集群 虚拟集群是一种在现有 Kubernetes 集群（Host 集群）之上创建的轻量级 Kubernetes 集群。它提供了一个独立的 Kubernetes 环境，但实际上是通过共享 Host 集群的资源来实现的。这种方式允许多个虚拟集群运行在同一个物理集群中，为不同的团队或项目提供隔离的开发、测试和生产环境，而不需要为每个环境创建单独的物理集群。
为什么要使用虚拟集群 多租户隔离：在一个物理集群中为不同团队或项目提供独立的 Kubernetes 环境，确保资源和安全性隔离。 开发与测试便利：开发人员可以快速创建和销毁与生产环境类似的虚拟集群，用于独立开发、测试和调试。 资源优化：通过共享基础设施提高资源利用率，减少硬件需求和管理开销。 灵活管理：虚拟集群允许独立配置、运行不同版本的 Kubernetes，有助于版本控制和灵活调整集群策略。 成本降低：减少维护多个物理集群的需求，优化资源分配，降低整体成本。 vCluster 简介 vCluster 是一种轻量级的虚拟集群解决方案，允许你在 Kubernetes 集群中创建和管理虚拟的 Kubernetes 集群。它是由 Loft Labs 开发的，用于多租户环境、开发测试环境以及简化复杂的 Kubernetes 集群管理。</description></item><item><title>【译】如何提升云原生应用的安全性</title><link>https://atbug.com/how-to-enhance-cloud-native-application-security/</link><pubDate>Mon, 26 Aug 2024 11:13:44 +0800</pubDate><guid>https://atbug.com/how-to-enhance-cloud-native-application-security/</guid><description>此文为原文翻译，仅供参考，不代表本人立场。翻译难免有疏漏，欢迎提出建议反馈。
本文翻译自 Hazel Raoult 的 How to Optimize Security in Cloud-Native Applications。
近年来，企业对云原生应用的依赖日益增加，这也使云原生应用成为攻击的首选目标。如今，企业的安全团队需要优先考虑这些资产的安全性，以防止大规模的服务中断和数据泄露。
根据 IBM 2024 年《Cost of a Data Breach（数据泄露成本报告）》显示，目前 45%的网络攻击 是基于云的。2024 年 7 月，微软的 Azure 遭遇了一次重大的 DDoS 攻击，导致关键服务 停运近10小时。对于依赖这些服务来为客户提供不间断服务的企业来说，这种停机时间无疑会带来巨大的损失。
基于这一背景，让我们探讨如何提升云原生应用的安全性，以防止类似情况的发生，并确保关键云服务的韧性和可靠性。
用户控制优化 人为因素始终在数字资产（包括云原生应用程序）的安全中发挥重要作用。除非我们在不久的将来完全被机器取代，否则人类仍将在代码编写、配置管理以及最终的应用安全性方面做出重要决策。这是一种巨大的压力，因为一个小小的错误就可能导致严重的漏洞，进而让我们的云资产暴露在攻击和操作失误的风险之下。
一个合理的解决方案是加强教育和培训，使开发人员和用户能够理解潜在风险，并学会实施强有力的安全协议和最佳实践。
在技术措施方面，应重点关注强大的身份和访问管理（IAM）。最小权限原则是实现强 IAM 的首选方法。它确保用户被授予完成任务所需的最低访问权限，从而显着减少攻击面和帐户受损的风险。
花时间为每个无服务器函数或容器创建最小角色或权限集。这样，即使云原生架构中的某个元素被攻破，造成的损害也将最小，且不会轻易扩散到其他组件。</description></item><item><title>极空间 Z4 Pro 两个月使用体验分享</title><link>https://atbug.com/z4-pro-two-months-experience/</link><pubDate>Fri, 16 Aug 2024 17:15:29 +0800</pubDate><guid>https://atbug.com/z4-pro-two-months-experience/</guid><description>极空间 Z4 Pro 入手已有两月余，现在来说说使用感受。
这之前我是坚定的网盘当，长期订阅 2T 的 iCloud，因为全家都是苹果的全家桶。去年底我自己换了小米 14，陆续换到了自己手边的苹果设备，唯独留下了生产力工具 MacBook。照片备份、文件存储成了最头疼的问题，遂将目光转向了个人 NAS 存储。经过一番筛选，最终选择了极空间的 Z4 Pro。
在开始之前，先整理下我所使用的功能：
数据存储 账号管理 照片管理 影视空间 文档同步 远程下载 Time Machine 远程访问 网盘备份 存储 Z4 Pro 有 5 个盘位：1 个 SSD 和 4 HDD，我只用了 1T 的 SSD 和 2 * 4T 的 HDD。SSD 用的是之前黑苹果时期留下来的，两块 4T 的希捷酷狼则是为了兼顾存储和噪音。当下对存储空间没有更高的要求，HDD 只用到了 15%。</description></item><item><title>Obsidian 图片上传插件：Image Upload Toolkit 0.6.0</title><link>https://atbug.com/obsidina-plugin-image-upload-toolkit-new-version/</link><pubDate>Wed, 31 Jul 2024 08:47:26 +0800</pubDate><guid>https://atbug.com/obsidina-plugin-image-upload-toolkit-new-version/</guid><description>Obsidian 插件 Image Upload Toolkit 刚刚发布了 0.6.0 版本。在最新版本中，新增了 AWS S3 支持，升级到最新版本后，您可以将文档中的图片上传到 AWS S3 存储。
Image Upload Toolkit 的第一个版本于去年六月发布。关于我为什么开发这个插件的更多信息，可以阅读我 去年的文章。
这个插件的主要功能是将本地图片上传到指定存储，将 Markdown 语法中的本地图片地址更新为图片的公共链接，并将结果复制到剪贴板。当然，您也可以选择更新原始内容。截至 0.6.0 版本，Image Upload Toolkit 支持 imgur、阿里云 OSS、Imagekit 和 AWS S3。
欢迎安装并试用这个插件，并提出 意见和建议。
在开发这个插件期间，我参考了 obsidian-imgur-plugin、obsidian-image-auto-upload-plugin、create-obsidian-plugin，并受到了 Mweb 的启发。</description></item><item><title>K8sGPT+Ollama：免费的 Kubernetes 自动化诊断方案</title><link>https://atbug.com/k8sgpt-ollama-free-k8s-automation-diagnosis/</link><pubDate>Mon, 17 Jun 2024 20:05:13 +0800</pubDate><guid>https://atbug.com/k8sgpt-ollama-free-k8s-automation-diagnosis/</guid><description>周末检查博客草稿，发现了这篇。记得当时是与 Kubernetes 自动化诊断工具：k8sgpt-operator 一起写的，算算过去了一年之久，这拖延症也算是病入膏肓了。原本想使用 K8sGPT + LocalAI 的方案，由于之前试过 Ollama，感觉使用起来也更加友好，而且 Ollama 同样提供了 对 OpenAI API 的支持，索性改成用 Ollama 吧。
介绍 k8sgpt-operator 的文章发布后，有小伙伴反馈 OpenAI 的使用门槛，这个问题确实比较棘手，但也不是不能解决。不过本文并不是介绍如何解决这种问题的，而是介绍 OpenAI 的替代方案： Ollama。
对 k8sgpt 和 k8sgpt-operator 就不做过多介绍了，有兴趣的可以看回 上一篇，去年底 k8sgpt 进入了 CNCF Sandbox。
1. 安装 Ollama Ollama 是一个开源的大模型工具，使用它可以在本地或云端轻松的安装和运行 多种流量的大模型。它的操作非常友好，只需简单的命令就能运行。在 macOS 上可以通过 homebrew 一键安装：</description></item><item><title>使用 ZTM 增强极空间 NAS 的远程访问能力</title><link>https://atbug.com/enhance-remote-access-for-zspace-nas-with-ztm/</link><pubDate>Thu, 13 Jun 2024 11:35:09 +0800</pubDate><guid>https://atbug.com/enhance-remote-access-for-zspace-nas-with-ztm/</guid><description>更新于 2024/10/23，试用于 ZTM 版本 v0.3.1。
今天这篇来说说极空间的远程访问功能，产品页面对远程访问的描述是：
让极空间真正成为你家庭中的网络设备控制中心：
无需公网 IP 就可以便捷访问家中路由器、Docker 和智能家居 不用学习网络知识也可以以轻松使用，快速添加 自定义名称及颜色，方便查找与收藏，配合容器使用，扩展丰富功能 比如借助该功能可以方便地管理家中的软路由，非常适合没有公网 IP 的用户。
然后就可以点击新添加的链接，在极空间的窗口内访问 OpenWrt 的 web 控制台了。
细心的你可能会注意到它只能支持 HTTP 协议（官方的功能介绍），只能通过极空间窗口访问，并且只能为管理员账号下使用该功能，灵活性大打折扣。比如远程访问家中的 Windows 设备（Windows 的远程桌面协议 RDP）；或者需要 ssh 远程访问 HomeLab 虚拟机。
这些极空间的远程访问就无能为力了，这里就要用到 ZTM 了。
关于 ZTM ZTM（Zero Trust Mesh）是一款开源的网络基础设施软件。它基于 HTTP/2 隧道构建，可以在任何类型的 IP 网络上运行，例如局域网、容器化网络和互联网等。</description></item><item><title>极空间无法 Docker 运行 OpenWrt 主路由拨号</title><link>https://atbug.com/openwrt-pppoe-not-work-on-zspace-via-docker/</link><pubDate>Tue, 11 Jun 2024 06:35:51 +0800</pubDate><guid>https://atbug.com/openwrt-pppoe-not-work-on-zspace-via-docker/</guid><description>写这篇文章的目的纯属留作记录，因为网上找不到相关的文章，枉我折腾半天，希望其他人不会重蹈覆辙。
月初，趁着 618 的活动下单了“心念已久”的 NAS - 极空间 Z4Pro。之所以在购物车中存在了这么久，因为我之前是妥妥的网盘党，长期使用 iCloud 的家庭存储。去年底换了安卓手机之后，照片备份、文档存储就成了一大问题：望着 2T 的 iCloud 也用不上，即使还有 80% 多的剩余空间。换到 NAS 一周后，经过一番探索后，目前使用感受不错，后期我会再开一篇写写我的使用方法。
Z4Pro 这台机器的 CPU 还算不错 - N97，相比 N100 来说主频还会更高一些。我也就动了信息，想让那台软路由小主机退休，将 Z4Pro 作为 All in One 来使用，双网口要好好用起来。不过虚拟机的是不考虑的，太重。还好，Z4Pro 支持 Docker，性能上的损耗也能接受。
网上这方面的文章太少，大多都是做旁路由来使用，官方有篇 文章 也是只是路由模式，没有做拨号。
整个操作不算麻烦，我也 folk 了 SuLingGG OpenWrt-Docker 准备定制一个 OpenWrt 镜像。容器部署完成后，也顺利进入了 OpenWrt。在配置 WAN 口做 PPPoE 拨号后，发现 WAN 口没有 MAC 地址信息也没有公网 IP 的分配。</description></item><item><title>Kubernetes Gateway API v1.1 解读</title><link>https://atbug.com/kubernetes-gateway-api-v1-1-overview/</link><pubDate>Wed, 15 May 2024 10:38:06 +0800</pubDate><guid>https://atbug.com/kubernetes-gateway-api-v1-1-overview/</guid><description>几天前，K8s Network SIG 发布了 Gateway API（简称 gwapi）的 v1.1 版本，这个版本的发布包含了多项重要功能的 GA（一般可用），以及一些实验功能的引入。这两部分分别通过标准渠道和实验渠道发布。
发布渠道用于指示网关 API 内的功能的稳定性。所有新功能和资源都是先从实验发布渠道开始，在后续迭代演进的过程中可能会升级到标准发布渠道，也有可能完全从 API 中废除。
通过下图可以对 gwapi 的发布渠道有个清晰的了解。 在这些更新中，在我看来当属服务网格的支持 GA 最为重要，这意味着服务网格标准 API 向着统一又迈进了一步。差不多两年前，我曾写过 SMI 与 Gateway API 的 GAMMA 倡议意味着什么， 在 gwapi 差不多 1.0 的时候，SMI 在停止更新的几个月后归档了。
标准发布渠道 GRPCRoute GA GRPCRoute API 的 v1 版本发布，标志着其可以在生产环境中稳定使用，并得到长期的支持的维护。同时 v1alpha2 版本被标记为废弃，在未来的版本中将会被移除。</description></item><item><title>使用 Cert Manager 自动管理 Kubernetes Gateway 证书</title><link>https://atbug.com/automated-k8s-gateway-certificates-management-with-cert-manager/</link><pubDate>Wed, 08 May 2024 23:11:35 +0800</pubDate><guid>https://atbug.com/automated-k8s-gateway-certificates-management-with-cert-manager/</guid><description>在本文中，我们将探讨如何利用 cert-manager 自动管理 Kubernetes Gateway 证书，以增强集群入口安全并提升管理效率。
背景 Kubernetes Gateway 或许，确切地应该称为 Kubernetes Gateway API （下称 GWAPI）。GWAPI 是由 SIG-NETWORK 社区管理的开源项目，是一种规范，不提供实现。Gateway API 被认为是 Kubernetes Ingress API 的继任者，在 2019 年的 Ingress 革命 中首次被提出，并在 2020 年 11 月发布了第一个版本。GWAPI 的发展非常快，截止本文发出已经演进到了 1.0.0 版本，并且 1.1.0 也即将发布（已经发布了 1.1.0-rc2）。
GWAPI 是一种管理 Kubernetes 集群内部和集群外部流量的方法。它提供了一个更精细、更模块化的方法来描述路由和网关配置，相比于传统的 Ingress API，GWAPI 设计上更为灵活和可扩展。</description></item><item><title>曲线救国：通过 OrbStack 在 Apple Silicon 平台搭建 K3s x86 集群</title><link>https://atbug.com/setup-k3s-x86-cluster-on-apple-silicon-with-orbstack/</link><pubDate>Fri, 26 Apr 2024 17:34:42 +0800</pubDate><guid>https://atbug.com/setup-k3s-x86-cluster-on-apple-silicon-with-orbstack/</guid><description>请原谅这个标题有点拗口。
如果你对 Apple Silicon、OrbStack、x86 架构或 K3s 集群中的任何一个概念不感兴趣，那么这篇文章可能不适合你。
背景 在我的工作流中，我依赖于 M1 MacBook Pro 和多个高效工具的结合。我使用 OrbStack 创建虚拟机，这个平台通过 Rosetta 进行二进制转换以实现虚拟化，不仅虚拟化速度快，还有其他众多优点（更多详情请见 OrbStack 优势）。同时，我偏爱使用 K3s 集群因为其轻量级特性，搭配先前介绍的 k3sup 工具，能够迅速搭建新的集群。
虽然这一系列工具的组合主打速度快和轻便，但当它们结合使用时，也会出现一些技术挑战。例如，在 OrbStack 创建的 x86 虚拟机上安装 K3s 时，我遇到了一个常见的问题，这个问题在社区中已经有了广泛的讨论并看似无解，具体可参见这个 GitHub 讨论。
Failed to create pod sandbox: rpc error: code = Unknown desc = failed to generate sanbdox container spec options: failed to generate seccomp spec opts: seccomp is not supported 在我的虚拟环境中，尽管使用 Docker 运行容器没有遇到问题，但在安装 K3s 时却遭遇挑战。值得注意的是，无论是 Docker 还是 K3s，它们都依赖于 Containerd 作为容器运行时。这一点让我怀疑问题可能出在 K3s 内置的 Containerd 配置上。</description></item><item><title>持续交付基金会发布最新的 CICD 趋势报告</title><link>https://atbug.com/cdf-release-latest-cicd-trends-report/</link><pubDate>Wed, 24 Apr 2024 10:44:01 +0800</pubDate><guid>https://atbug.com/cdf-release-latest-cicd-trends-report/</guid><description>持续交付基金会（Continuous Delivery Foundation，简称 CDF）前几天发布了最新的一期的 CICD 趋势报告。这份报告中的调查结果基于 SlashData 过去 8 次的调查数据，这些调查在 2020 年 Q3 度到 2024 年 Q1 的三年半时间里覆盖了全球超过 150,000 名受访者。
本文是针对部分结果的解读，结果数据来自官方的报告，解读的部分纯属个人观点。有兴趣的可以查看 完整的报告。
软件交付性能的演变 软件的交付性能主要通过代码发布周期、部署频率和服务恢复时间三个关键指标来衡量，这些指标能够直观地反映出一个组织在软件开发和运维方面的效率和效果。
代码发布周期 代码发布周期是指从代码提交（例如，合并到主分支）到成功部署到生产环境的时间。
这个指标衡量团队对新功能、修复或更新做出反应的速度。较短的发布周期意味着团队能够快速地将变更推送到生产环境，对业务需求和问题做出迅速响应。
从结果中可以看出代码变更的频率从 2020 年 Q3 开始逐年降低，尤其是变更频率低于每月一次的比例越来越高。
部署频率 部署频率指软件部署到生产环境的频率，可以是每日、每周、每月等。
高频率的部署通常表明高度自动化和成熟的持续交付能力。这种能力可以帮助团队减少单次部署的风险，因为每次部署的变更较小，更容易管理和修复。
这个结果于代码变更的周期较一致。
服务恢复时间 服务的恢复时间，这里的服务恢复是指在发生生产环境中断后，服务恢复到正常状态所需的时间。
这个指标反映了团队在面对生产环境问题时，快速恢复服务的能力。较短的恢复时间表明团队有有效的事故响应和问题解决流程。
从报告可以看到其向两个极端的发展：好的越来越好，差的越来越差。</description></item><item><title>抽丝剥茧：从 Linux 源码探索 eBPF 的实现</title><link>https://atbug.com/exploring-ebpf-implementation-through-linux-source-code/</link><pubDate>Sun, 31 Mar 2024 19:01:44 +0800</pubDate><guid>https://atbug.com/exploring-ebpf-implementation-through-linux-source-code/</guid><description>去年学习 eBPF，分享过 几篇 eBPF 方面的学习笔记，都是面向 eBPF 的应用。为了准备下一篇文章，这次决定从 Linux 源码入手，深入了解 eBPF 的工作原理。因此这篇又是一篇学习笔记，假如你对 eBPF 的工作原理也感兴趣，不如跟随我的脚步一起。文章中若有任何问题，请不吝赐教。
这里不会再对 eBPF 进行过多的介绍，可以参考我的另一篇 使用 eBPF 技术实现更快的网络数据包传输，结合 追踪 Kubernetes 中的数据包 可以了解 eBPF 的基本内容以及其在网络加速方面的应用。
接下来我们还是使用 eBPF sockops 中的程序 bpf_sockops 为例， 配合 Linux v6.8 源码探索 eBPF 的工作原理。
BPF 程序操作 在 load.</description></item><item><title>自定义 OpenTelemetry Collector 容器镜像</title><link>https://atbug.com/custom-opentelemetry-collector-container-image/</link><pubDate>Mon, 04 Mar 2024 22:17:56 +0800</pubDate><guid>https://atbug.com/custom-opentelemetry-collector-container-image/</guid><description>OpenTelemetry Collector 有两个官方发行版：Core 和 Contrib。
Core 发行版是 Collector 的基础发行版，供 OTel 开发人员进行开发和测试。它包含一组基本的扩展、连接器、接收器、处理器和导出器。 Contrib 发行版供非 OTel 开发人员进行实验和学习。它还扩展了 Core 发行版，并包含由第三方（包括供应商和个人社区成员）创建的组件，这些组件对整个 OpenTelemetry 社区非常有用。在之前的文章 《使用 OpenTelemetry 和 Loki 实现高效的应用日志采集和分析》 我用的就是这个发行版。 不管 Core 还是 Contrib 都不应该成为你生产工作负载的一部分。仅仅使用 Core 本身太过简单，无法满足组织的需求（尽管它提供的组件都是必须的）；虽然 Contrib 中提供的组件足够全面，然而并不是说每个组件都是你所需要的，太多冗余的组件显得过于臃肿，还增大的攻击面。
那如何选择你所需的发行版呢？答案就是构建自己的发行版。可以使用官方提供的名为 OpenTelemetry Collector Builder (OCB) 的工具来自定义附件。</description></item><item><title>初探 Backstage：快速上手指南</title><link>https://atbug.com/exploring-backstage-a-quick-start-guide/</link><pubDate>Sun, 28 Jan 2024 11:59:38 +0800</pubDate><guid>https://atbug.com/exploring-backstage-a-quick-start-guide/</guid><description>坦白说，虽然我之前阅读过相关文档，但实际上从未亲自尝试运行 Backstage。我一直有种感觉，Backstage 不过是一个开发者门户而非开发者平台。上周在 分享我对平台工程的理解 后，朋友圈中有人提议我写一篇关于 Backstage 入门的文章。这激起了我的好奇心，我决定深入探究一下 Backstage 究竟是什么。
Backstage 简介 Backstage 是一个用于构建开发人员门户的开放平台，统一了所有基础设施工具、服务和文档，以创建端到端的简化开发环境，由 Spotify 开源并捐赠给 CNCF。Backstage 提供了开箱即用的几个核心功能：
软件目录 软件目录（Software Catalog）是一个集中式系统，用于跟踪生态系统中所有软件（服务、网站、库、数据管道等）的所有权和元数据。开发人员提供软件的实体信息，Backstage 根据实体的信息与已有实体建立关联，并生成最终版本的软件实体保存在目录中。
从 Backstage 仓库的软件目录示例中可以找到 多种类型的实体定义。
软件模板 软件模板 （Software Template）是一个可以帮助开发在 Backstage 中创建组件的工具。默认情况下，它能够加载代码骨架、带有变量中的模板，然后将模板发布到某些位置，例如 GitHub 或 GitLab。
技术文档 技术文档（TechDocs） 是 Spotify 自行开发的直接内置于 Backstage 中的类文档代码解决方案。开发人员在与代码一起存在的 Markdown 文件中编写文档 - 只需很少的配置即可在 Backstage 中获得一个漂亮的文档站点。</description></item><item><title>聊聊我所理解的平台工程</title><link>https://atbug.com/my-understanding-of-platform-engineering/</link><pubDate>Sun, 21 Jan 2024 13:26:34 +0800</pubDate><guid>https://atbug.com/my-understanding-of-platform-engineering/</guid><description>Gartner 将平台工程列为 2024 顶级战略技术趋势之一。
说起平台工程（Platform Engineering） ，经常听到有人说是：新瓶装（平台工程）旧酒（DevOps）。
今天根据过去自服务平台的实践经验，聊聊我所理解的平台工程。
云原生平台 说到平台工程，不可不免地要聊聊云原生，不过这里不会针对是否转向云原生进行讨论。
云原生的三驾马车：微服务、Kubernetes、DevOps。根据过往的实践经验，我认为云原生技术平台的核心能力（包括但并不限于）可概括为：
容器平台：专注于容器化技术和 Kubernetes 编排，实现应用的弹性、高效存储和网络通信。这为微服务和 DevOps 的实现提供了基础架构支持。 微服务平台：集中管理微服务，包括统一的服务治理、配置管理、API 网关和支持多样的微服务框架，以适应复杂的服务交互和灵活的开发需求。 监控平台：提供全方位的监控系统，包括日志收集、性能指标监控、链路跟踪、实时告警以及监控数据的可视化展示，助力于系统的稳定运行和故障迅速定位。 DevOps 集成平台：集成持续集成和持续部署（CICD）流程，以及文档中心和代码质量管理，实现自动化、高效和标准化的软件开发和运维流程。 对于风靡多年的云原生（近来也有降温的趋势？），业界的褒贬不一：提升了研发效率和资源的利用率，；浪费资源、部署维护困难、可观测性变差等等。
云原生技术所面临的众多负面反馈，很大程度上源于其本身的复杂性。云原生平台向开发人员展现了过多的复杂性。
云原生的复杂性 云原生技术，尽管带来了许多优势，比如灵活性、可扩展性和高效的资源利用，但同时也引入了一定的复杂性：
技术栈的复杂性：云原生环境通常涉及到容器化、微服务架构、CI/CD、以及基于 Kubernetes 的容器编排等技术。这些技术各自有其学习曲线，并且技术之间需要集成并协同工作，增加了系统的整体复杂性。 管理和运维的复杂性：在云原生环境中，应用程序通常被分解为多个微服务，每个微服务部署在不同的容器中，这使得监控、日志记录、故障排查和性能优化变得更加复杂。 网络复杂性：微服务架构意味着服务之间有大量的网络通信，再叠加容器、混合多云的网络环境，管理这些服务间的网络流量、确保高可用性和网络安全，以及实现服务发现等都增加了网络管理的复杂性。 可观测性和监控的挑战：确保对在不断变化的环境中运行的众多微服务有足够的可见性，需要复杂的监控和日志系统。 安全挑战：云原生架构中的分布式和动态性质引入了新的安全挑战。例如，需要确保容器安全、服务间通信的安全，以及在动态环境中持续地管理和更新安全策略。 这些复杂性给开发人员带来了显著的摩擦和认知负担，从而降低了他们的开发体验。在专注于业务开发的开发人员与底层基础设施之间，形成了一个模糊的交界区域。平台工程正是专注于这个模糊地带，旨在缩小这一差距并简化开发流程。
平台工程是一个胶水层。
什么是平台工程 平台工程是一门在云原生时代为软件工程组织设计和构建工具链及工作流程的学科，旨在提供自助服务能力。平台工程师提供的综合产品通常被称为“内部开发者平台”，涵盖了应用程序整个生命周期的运营需求。
Platform engineering is the discipline of designing and building toolchains and workflows that enable self-service capabilities for software engineering organizations in the cloud-native era.</description></item><item><title>再见，逆行的 2023</title><link>https://atbug.com/fare-well-2023/</link><pubDate>Thu, 28 Dec 2023 08:11:05 +0800</pubDate><guid>https://atbug.com/fare-well-2023/</guid><description>接近年尾，又到了年终回顾的时刻。往年总结时总感叹时间的迅速流逝，似乎上一次总结才刚刚结束。今年，感到一丝庆幸——庆幸时间的飞逝，它告诉我没有什么是时间不能解决的。
去年，我用“超速”来形容 2022 年的节奏。而对于 2023 年，我选择了“逆行”这个词。
预期的逆行 在经历了过去三年的 xx 之后，我对 2023 年抱有较高的期待。但无论是在经济形势，还是在社区活动的参与度上，都没有看到所希望的反弹现象：报复性消费、报复性参会。这两者之间，原本看似无关，实则紧密相连。自 2021 年底经济开始下滑（作为一个股民的切身体会）以来，国内外许多企业纷纷裁员，但并未预料到这种低迷状态会持续如此之久，以至于对 2024 年的期待也变得谨慎。
奋斗的逆行 尽管遇到了困难、失望和挫折，但也看到了身边人的坚持不懈、务实奋斗。真正的成长往往伴随着挑战和逆境，正如上面所说的，时间的流逝证明了没有什么是时间不能解决的。
工作 今年工作上布道的工作投入的时间比较多，写了 28 篇文章，参与了 14 场技术分享，并参与组织了数场活动。尽管努力了，成效似乎微乎其微，这也成了今年长期困扰的问题。与老板的交谈后，意识到，对这些工作的期待过高了。针对我们“冷启动”的开源项目而言，这些努力似乎杯水车薪，尤其在市场低迷时期更是如此。
明年，打算降低预期，保持持续但低强度的社区输出，将更多时间投入到产品和市场支持上。
最近看了马道长的年终回顾，最后提到劳动合同快到期思考公司会不会续约。这倒点醒了我，之前从未考虑过这种事情，毕竟在 Flomesh 好像也有两年半了，哈哈。
学习 感觉乱七八糟的看了很多，不光是技术，还有时政、经济、历史，可以算是逮着什么看什么。这也意味着在技术学习上的时间有所减少。
这个公众号进入了第三个年头，依然是我的学习记录平台。今年写了 31 篇文章，虽然数量有所下降，但投入的时间和精力更多，收获也更丰富。
学习，依然坚持。
生活 明年是跟老婆的第 20 年，时间过得真快。今年吵架的次数少了，老婆笑骂脸皮越来越厚了。父母陪在身边，保持着舒适的距离。今年跟儿子一起打游戏的时间多了，准备再买台街机模拟器；对于他的学习，我和老婆都降低了预期不给他压力，反而他有了不小的进步。
年初收获了期待多年的 dream car，感谢老婆的支持。</description></item><item><title>使用 OpenTelemetry 和 Loki 实现高效的应用日志采集和分析</title><link>https://atbug.com/efficient-app-log-collection-analysis-opentelemetry-loki/</link><pubDate>Sun, 24 Dec 2023 23:30:45 +0800</pubDate><guid>https://atbug.com/efficient-app-log-collection-analysis-opentelemetry-loki/</guid><description>2025.2.15 更新：支持 Loki 3.3.2。
在之前的文章陆续介绍了 如何在 Kubernetes 中使用 Otel 的自动插桩 以及 Otel 与 服务网格协同实现分布式跟踪，这两篇的文章都将目标聚焦在分布式跟踪中，而作为可观测性三大支柱之一的日志也是我们经常使用的系统观测手段，今天这篇文章就来体验下应用日志的操作闭环。
背景 OpenTelemetry 简介 OpenTelemetry （以下简称 Otel）是一个开源项目，旨在为分布式追踪、度量和日志提供统一的标准，简化应用程序的观测性（Observability）。它提供了一系列工具和 API，用于收集和传输应用程序的性能数据和日志，帮助开发者和运维团队更好地理解系统的行为。功能包括自动和手动检测应用程序的追踪数据，收集关键度量指标，以及捕获和传输日志。Otel 支持多种编程语言和框架，可以与多个后端系统集成，如 Prometheus、Jaeger、Elasticsearch 等。
Log 是 OpenTelemetry 项目的一部分，旨在提供一种标准化的方式来收集、传输和存储日志数据。
Loki 简介 Loki 是 Grafana Labs 开发的一个水平可扩展、高可用性、多租户的日志聚合系统，专为效率和易用性而设计。与传统的日志聚合系统不同，Loki 主要索引日志内容的元数据而不是内容本身，这使得它既轻量又高效。Loki 采用了与 Prometheus 类似的标签系统，使得日志查询更加灵活和强大。常用于存储和查询大量日志数据，特别是与 Grafana 结合使用时，提供了强大的日志可视化和分析能力。</description></item><item><title>探索服务网格与 OpenTelemetry 的协同之分布式跟踪</title><link>https://atbug.com/integrate-service-mesh-with-opentelemetry-for-distributed-tracing/</link><pubDate>Fri, 08 Dec 2023 07:15:43 +0800</pubDate><guid>https://atbug.com/integrate-service-mesh-with-opentelemetry-for-distributed-tracing/</guid><description>在上一篇文章中，介绍了 如何在 k8s 中无侵入安装 Otel 探针 并实现了无侵入（某些语言还无法实现，比如 Go 的 eBPF 对内核的苛刻要求）的分布式跟踪。
这篇文章发出后有读者评论 javaagent 的“无侵入”一说，这里有必要解释下。“无侵入”主要指的是不需要修改应用程序的业务逻辑代码就能实现的功能，对应用程序透明无感知，让开发者专注于业务开发；同时由于无需修改应用程序代码，更易于集成；同时还维护简单，在多种语言、框架间保证功能的一致性。
而 Java Agent 在 JVM 启动时加载，它在运行时修改字节码来注入跟踪代码，而不是在应用程序的源代码层面上进行修改。
背景 分布式跟踪 分布式跟踪是监控和诊断微服务请求流程的关键技术，也是可观测性的关键组成部分，提供了对微服务架构中复杂交互和性能问题的深入洞察。它通过提供服务间请求链路的清晰视图来管理复杂性，并帮助识别性能瓶颈、优化资源分配、快速定位和解决故障，提高系统的整体可靠性。
服务网格的无侵入式分布式跟踪 又是无侵入性！服务网格中的代理自动处理所有入站和出站的网络通信，自动捕获、记录和分析服务间的请求和响应的详细细心，如请求时间、持续时间、状态代码和其他元数据。这种 实现方式 对应用程序本身透明，并且较 Java Agent 在运行时修改字节码更加彻底。
这里有个前提是应用程序能够在请求中传递上下文信息，这样 sidecar 代理生成和发送的跟踪信息最终可以串联在一起，不会发生断链。
网格的无侵入式分布式跟踪虽然为我们展示了请求的链路，但是如上图所示每个跨度（span）都是 sidecar 代理的信息。
紧跟上篇文章之后，我们今天将探索 服务网格 FSM 与 OpenTelemetry 的集成，实现应用、网格的全链路分布式跟踪。</description></item><item><title>在 Kubernetes 中无侵入安装 OpenTelemetry 探针</title><link>https://atbug.com/non-intrusive-inject-otel-auto-instrumentation-in-k8s/</link><pubDate>Thu, 07 Dec 2023 07:10:00 +0800</pubDate><guid>https://atbug.com/non-intrusive-inject-otel-auto-instrumentation-in-k8s/</guid><description>背景 OpenTelemetry 探针 OpenTelemetry（简称 Otel，最新的版本是 1.27） 是一个用于观察性的开源项目，提供了一套工具、APIs 和 SDKs，用于收集、处理和导出遥测数据（如指标、日志和追踪信息）。应用程序遥测数据（如追踪、指标和日志）的收集是通过探针来完成的，探针通常以库的形式集成到应用程序中，自动捕获重要信息协助监控和调试。OpenTelemetry 探针支持市面上大多数的编程语言，探针的安装（通常被称为插桩，Instrumentation）分为手动和自动两种方式。
手动插桩：指开发者直接在其应用程序代码中显式地添加遥测数据收集的代码，需要手动完成 SDK 初始化、插入追踪点、添加上下文信息等一系列操作。 自动插桩：利用 OpenTelemetry 提供的库自动捕获应用程序的遥测数据，无需或只需很少的代码更改。比如，Java 通过 javaagent 实现探针的自动安装。 二者各有优劣：手动插桩适用于需要高度定制和精确控制遥测数据收集的场景；自动插桩适合快速启动和简化集成，特别是在使用标准框架和库的应用程序中。
OpenTelemetry Operator 介绍 OpenTelemetry Operator 是一个为了简化 OpenTelemetry 组件在 Kubernetes 环境中的部署和管理而设计的 Kubernetes Operator。
OpenTelemetry Operator 通过 CRD（OpenTelemetryCollector、Instrumentation、OpAMPBridge） 实现在 Kubernetes 集群中自动部署和管理 OpenTelemetry Collector；在工作负载中自动安装 OpenTelemetry 探针。</description></item><item><title>快速探索 Tetragon：基于 eBPF 的安全可观察性和执行工具</title><link>https://atbug.com/explore-tetragon-security-observability-and-enforcement-tool-based-on-ebpf/</link><pubDate>Thu, 16 Nov 2023 22:10:37 +0800</pubDate><guid>https://atbug.com/explore-tetragon-security-observability-and-enforcement-tool-based-on-ebpf/</guid><description>Tetragon 是一种灵活的安全可观察性和运行时策略执行工具，可直接使用 eBPF 应用策略和过滤，从而减少了监控、进程跟踪以及实时执行策略的开销。
Tetragon 提供了如下功能：
监控进程执行 监控文件操作 监控网络活动 执行策略 最后一个侧重策略的执行，可以通过发送信号或覆盖系统调用的返回值对重要的安全事件做出反应；前三种侧重监控，并可以将监控数据与容器、Kubernetes 元数据进行关联。
演示 环境 操作系统：Ubuntu 20.04 内核：5.15.122 K8s 集群：k3s v1.27.1+k3s1 创建集群 export INSTALL_K3S_VERSION=v1.27.1+k3s1 curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable local-storage --disable metrics-server --disable servicelb --write-kubeconfig-mode 644 --write-kubeconfig ~/.</description></item><item><title>使用 k3sup 一分钟快速搭建 K3s 集群</title><link>https://atbug.com/setup-k3s-cluster-with-k3sup-in-one-minute/</link><pubDate>Fri, 27 Oct 2023 04:55:13 +0800</pubDate><guid>https://atbug.com/setup-k3s-cluster-with-k3sup-in-one-minute/</guid><description>更新于 2025 年 3 月 1 日：完整脚本已添加更多功能，并提交至 GitHub 仓库 k3s-cluster-automation。
​单命令集群部署：通过 SSH 一键部署含 1 个主节点 + N 个工作节点的 K3s 集群。 ​组件可定制化：可禁用非必要服务（如 Traefik 入口控制器、metrics-server 指标服务器等），实现轻量化部署。 ​多节点支持：通过环境变量 HOSTS 灵活配置节点列表。 ​版本控制：支持指定 K3s 版本（默认版本：v1.28.13+k3s1）。 ​Docker 集成：可选 Docker 运行时替代 containerd。 ​自动清理：一键移除集群所有组件，不留残留。 背景 在平时的工作中，为了方便在纯净的环境中进行测试，我经常需要在本地或者公有云环境中频繁地搭建和销毁集群。有时是在 我的 HomeLab 环境中，虽然 CPU 不强但胜在内存够大；后来有了微软 MVP 赠送的 Azure 额度之后，我也会经常在 Azure 虚拟机 中搭建，因为没有拉取镜像的网络问题。</description></item><item><title>单体 or 微服务？Service Weaver：我全都要！</title><link>https://atbug.com/service-weaver-framework-for-writing-distributed-applications/</link><pubDate>Wed, 11 Oct 2023 19:06:24 +0800</pubDate><guid>https://atbug.com/service-weaver-framework-for-writing-distributed-applications/</guid><description>TL;DR 怎么理解 Service Weaver，就是一个应用中有很多的接口，这些接口间会互相调用。如果将操作系统进程（应用）比做一块电路板，接口比做元器件。可以选择将哪些元器件放入该电路板中，哪些元器件放入其他的电路板中。
同一块电路板中的元器件间通过板上的导线连接（进程内的本地方法调用）；不同电路板中的元器件间通过排线来连接（远程方法调用）。
总结几个关键词：
一个编程框架 用于编写、部署、管理分布式应用 支持的语言 Go 在本地以单进程、多进程运行 在云端由框架拆分成微服务，并于云供应商集成 单体方式开发，微服务方式部署 体验了一圈下来，给我的感觉有点类似 Notion、Obsidian 这类笔记软件。传统的笔记软件只能引用其他的笔记，而这类笔记可以细粒度到 heading 内容。
放到微服务下就是管理的维度不在是服务本身，而且更小的接口，并且对某些接口进行扩展，即使所有接口都位于同一个二进制文件中。
背景 架构的演进，总是在解决问题的过程中引入新的问题，然后再解决问题，循环往复。
从单体到微服务 软件架构从单体演进到微服务架构已经十多年了，尤其是近几年云原生风生水起，微服务架构已有深入人心的架势。
单体架构由于在规模扩大时，单体面临性能瓶颈和硬件限制、无法支撑业务的快读迭代、开发效率下降协同难度增加等原因，颓势日渐明显。然后就有了微服务架构的提出，来解决单体架构的各种问题。
上云 由于云平台提供显著的成本效益，减少初始投资并实现按需付费、提供极大的灵活性和可扩展性、提供的稳定性和可靠性确保业务连续性、专业的安全保障和合规性支持减轻企业的运营负担，企业将其业务和数据迁移至云计算平台。
问题 拆分成微服务，由此带来了不少好处：更高效的应用扩展、更小的错误传播半径、独立的安全域以及完善的模块边界。
反过来，如何正确地找到边界进行拆分并非易事。拆分的依据是什么？two pizza team？依据资源使用、组织架构、数据结构？亦或是考虑未来的增长？
微服务的拆分执行下来毫无章法，最终的结果是微服务越来越多、更多的故障点、更长的链路、更大的延迟。这实际上增加了应用的开发、部署和管理成本。
原本单个二进制文件，拆分后有多个；原本一次部署完成，拆分后需要多个 CI/CD 流水线来部署；原本一个配置文件，拆分后需要维护多个。 微服务彼此独立部署，无法忽略多版本的情况。需要调整部署策略来降低风险，同样还有本地开发和测试的难度增加。 学习成本高，需要学习如何将应用二进制文件包装成容器，并了解云的各种工具和部署方式，即使对经验丰富的程序员来说也难以理解。 同时还要解决分布式带来的各种问题，如服务发现、安全、负载均衡，以及服务间的调用。 延迟增加，时间消耗在数据的序列化以及网络传输上。 为什么用 Service Weaver？ 今年 3 月 Google 开源了 Service Weaver，希望能解决微服务架构的各种问题。</description></item><item><title>Netflix 零配置服务网格与按需集群发现</title><link>https://atbug.com/translation-zero-configuration-service-mesh-with-on-demand-cluster-discovery/</link><pubDate>Mon, 25 Sep 2023 12:39:05 +0800</pubDate><guid>https://atbug.com/translation-zero-configuration-service-mesh-with-on-demand-cluster-discovery/</guid><description>本文翻译自由 David Vroom, James Mulcahy, Ling Yuan, Rob Gulewich 编写的 Netflix 博客 Zero Configuration Service Mesh with On-Demand Cluster Discovery。
Netflix 相信大家并不陌生，在 Spring Cloud 生态中就有 Netflix 全家桶。多年前，我也曾将基于 Netflix OSS 构建的微服务架构搬上了 Kubernetes 平台，并持续折腾好几年。
Spring Cloud Netflix 有着庞大的用户群以及用户场景，其提供了微服务治理的一整套解决方案：服务发现 Eureka、客户端负载均衡 Ribbon、断路器 Hystrix、微服务网格 Zuul。</description></item><item><title>探索 Gateway API 在 Service Mesh 中的工作机制</title><link>https://atbug.com/explore-how-gateway-api-works-in-service-mesh/</link><pubDate>Thu, 07 Sep 2023 12:57:26 +0800</pubDate><guid>https://atbug.com/explore-how-gateway-api-works-in-service-mesh/</guid><description>前几天 Gateway API 宣布在 0.8.0 中支持服务网格，这意味着 GAMMA（Gateway API for Mesh Management and Administration）有了新进展，虽然目前还是实验阶段。去年 6 月 Gateway API 发布 0.5.0 时，我还写了一篇 SMI 与 Gateway API 的 GAMMA 倡议意味着什么？。如今，SMI 作为 sandbox 项目的年度审查已经 过了几个月仍未提交，唏嘘。
废话不多说，我们来看下 0.8.0 下的 Gateway API 如何在 Service Mesh 中工作。</description></item><item><title>使用 Docker 运行 FRR BGP</title><link>https://atbug.com/run-frr-bgp-with-docker/</link><pubDate>Wed, 30 Aug 2023 13:13:48 +0800</pubDate><guid>https://atbug.com/run-frr-bgp-with-docker/</guid><description>之前写 在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer（下）- BGP 时，我曾在 OpenWRT 安装 FRR 来进行了测试。需要 OpenWRT 的环境不免会有些繁琐，假如只是做些更简单的测试，比如从 FRR 所在服务器上进行 VIP 的访问，则是不需要 OpenWRT 的，仅仅部署 FRR 就足够了。
今天我就尝试了直接使用 Docker 运行 FRR 来启动 BGP Router 进程，简单的几步即可实现，对环境要求非常低，只需要个 Docker 就行。如果你又更简单的方法，也请留言分享。
配置 创建一个本地目录在管理 FRR 配置，这个目录将会挂载到容器中。
mkdir frr 准备 FRR Daemon 的日志，开启 BGP daemon 进程。</description></item><item><title>Kubernetes 容器运行时接口 CRI</title><link>https://atbug.com/deep-dive-into-kubernetes-cri/</link><pubDate>Mon, 28 Aug 2023 12:25:57 +0800</pubDate><guid>https://atbug.com/deep-dive-into-kubernetes-cri/</guid><description>写这篇文章是来填 很久之前挖下的坑。
本文涉及组件的源码版本如下：
Kubernetes 1.24 CRI 0.25.0 Containerd 1.6 容器运行时（Container Runtime）是负责管理和执行容器的组件。它负责将容器镜像转化为在主机上运行的实际容器进程，提供镜像管理、容器的生命周期管理、资源隔离、文件系统、网络配置等功能。
常见容器运行时有下面这几种，这些容器运行时都提供了不同程度的功能和性能。但他们都遵循容器运行时接口（CRI），以便能够与 Kubernetes 或其他容器编排系统集成，实现容器的调度和管理。
containerd CRI-O Docker Engine Mirantis Container Runtime 有了 CRI，我们也可以“随意”地在几种容器运行时之间进行切换，而无需重新编译 Kubernetes。简单来讲，CRI 定义了所有对容器的操作，作为容器编排系统与容器运行时间的标准接口存在。
CRI 的前生今世 CRI 的首次引入是在 Kubernets 1.5，初始版本是 v1alpha1。在这之前，Kubernetes 需要在 kubelet 源码中维护对各个容器运行时的支持。
有了 CRI 之后，在 kubelet 中仅需支持 CRI 即可，然后通过一个中间层 CRI shim（grpc 服务器）与容器运行时进行交互。因为此时各家容器运行时实现还未支持 CRI。</description></item><item><title>使用 Fortio 做代理功能测试</title><link>https://atbug.com/use-fortio-for-proxy-test/</link><pubDate>Sat, 12 Aug 2023 01:11:46 +0800</pubDate><guid>https://atbug.com/use-fortio-for-proxy-test/</guid><description>介绍 Fortio 是一个用于微服务性能负载、性能、延迟测试和 Web UI 的工具。它通常与 Istio 和其他服务网格解决方案一起使用，但可以独立使用来测试网络延迟和 HTTP/gRPC 的负载特性。以下是关于 Fortio 的一些主要特点和信息：
用途：Fortio 可以创建一个定制的负载（查询/秒或 qps）并记录请求延迟的直方图，以及每秒查询的百分位数。 多协议支持：虽然最初是为 HTTP/1.1 设计的，但 Fortio 还支持 HTTP/2, gRPC, TCP 和 UDP。 Web UI：Fortio 提供了一个 Web 界面，使用户能够从浏览器中轻松配置测试并查看结果的直方图和百分位数。 结果存储：可以将结果存储为 JSON 文件，便于进一步的分析和比较。 灵活性：支持多种请求负载模式，包括固定 qps、固定并发连接数和最大自动调整 qps。 轻量级：Fortio 是用 Go 编写的，可以作为一个单一的二进制文件轻松部署。 集成：Fortio 可以与 Istio、Prometheus 等工具集成，以提供更深入的性能分析和观察。 Fortio 作为服务器 fortio server &amp;amp; 08:24:40.</description></item><item><title>CoreDNS 与多集群服务 MCS</title><link>https://atbug.com/multi-cluster-service-with-coredns/</link><pubDate>Mon, 17 Jul 2023 21:54:46 +0800</pubDate><guid>https://atbug.com/multi-cluster-service-with-coredns/</guid><description>Kubernetes 作为一项核心技术已成为现代应用程序架构的基础，越来越多的企业使用其作为容器编排系统。Kubernetes 集群经历了 从单 Kubernetes 集群到多 Kubernetes 集群、从多 Kubernetes 集群到 Kubernetes 多集群的演进，集群的展现形式不断发生着变化。
为此，Kubernetes 多集群 SIG 提出了 KEP-1645: Multi-Cluster Services API（以下简称 MCS API）应对 Kubernetes 多集群带来的挑战，详细内容可以看之前的介绍：认识一下 Kubernetes 多集群服务 API ，这篇要介绍的是多集群服务 DNS。
多集群服务 DNS 在官方建议的方案中使用了 多集群服务 DNS ，并提出了 Kubernetes 基于 DNS 的多集群服务发现规范 用于指导多集群服务 API 的实现。该规范可以认为是 Kubernetes 的基于 DNS 的服务发现规范 的扩展。</description></item><item><title>追踪 Kubernetes 中的 DNS 查询</title><link>https://atbug.com/tracing-dns-queries-in-kubernetes/</link><pubDate>Sat, 15 Jul 2023 16:29:56 +0800</pubDate><guid>https://atbug.com/tracing-dns-queries-in-kubernetes/</guid><description>在过去的文章中，我们曾 追踪过 Kubernetes 中的网络数据包，这篇文章将追踪 Kubernetes 中的 DNS 查询。
让我们以在 Pod 中解析 Service 完全限定域名（FQDN） foo.bar.svc.cluster.local 为例。
在开始之前，先回顾下 DNS 的解析流程。
DNS 的解析流程 简化版的 DNS 处理流程：
DNS 客户端（如浏览器、应用程序或者设备）发送域名 example.com 的查询请求。 DNS 解析器收到请求，查询本地缓存，如果本地有记录且未过期会返回本地的记录。 如果本地缓存未命中，DNS 解析器将从 DNS 根服务器开始向下查询，首先是顶级域名（Top Level Domain, TLD） DNS 服务器（这里是 .com），一直向下直到可以解析 example.com 的服务器。 能够解析 example.</description></item><item><title>浅析 CoreDNS 的工作机制</title><link>https://atbug.com/analysis-of-the-working-mechanism-of-coredns/</link><pubDate>Sat, 15 Jul 2023 13:05:47 +0800</pubDate><guid>https://atbug.com/analysis-of-the-working-mechanism-of-coredns/</guid><description>CoreDNS 是一个开源的域名系统（DNS）服务器，用于将域名解析为 IP 地址以实现网络通信。它是一个用 Go 语言编写的可扩展 DNS 服务器，旨在取代传统的 DNS 服务器并提供更灵活、可配置的解析方案。
CoreDNS 提供了模块化的插件系统，允许用户根据需求选择和组合插件，以定制 DNS 服务器的功能和行为。通过添加不同的插件，用户可以实现缓存、转发、重写、策略路由、服务发现等功能，从而满足各种复杂的域名解析需求。
插件由设置（Setup）、**注册（Registration）和处理程序（Handler）**部分组成。
Setup 程序解析配置和插件的指令。 Handler 是处理查询并实现所有逻辑的代码。 Registration 是在 CoreDNS 中注册插件 - 这在编译 CoreDNS 时完成。服务器可以使用所有注册的插件，每个服务器中配置哪些插件的决定在运行时进行，并在 CoreDNS 的配置文件 Corefile 中完成。 安装运行 在 macOS 上安装 CoreDNS：
brew install coredns 可以执行 coredns -plugins 来查看已经安装的插件：</description></item><item><title>Cilium 如何处理 L7 流量</title><link>https://atbug.com/deep-dive-into-cilium-l7-packet-processing/</link><pubDate>Sun, 11 Jun 2023 15:35:15 +0800</pubDate><guid>https://atbug.com/deep-dive-into-cilium-l7-packet-processing/</guid><description>还记得在 使用 Cilium 增强 Kubernetes 网络安全 示例中，我们通过设置网络策略限制钛战机 tiefighter 访问死星 deathstar 的 /v1/exhaust-port 端点，但放行着陆请求 /v1/request-landing。在提起 Cilium 时，都说其是使用 eBPF 技术推动的用于提供、保护和观察容器工作负载之间的网络连接的开源软件。eBPF 可以处理 L3/4 的数据包，但是对复杂的 L7 的协议处理的成本比较高，并且无法应对 L7 协议策略的灵活性。Cilium 引入 Envoy Proxy（Cilium 定制的发行版）作为 L7 代理，来处理该场景。
那 Cilium 是如何处理 L7 流量的呢？今天就让我们一探究竟。
注，这篇的内容是基于目前最新的 Cilium 1.13.3 和 proxy 1.</description></item><item><title>Obsidian 图片上传插件：Image Upload Toolkit</title><link>https://atbug.com/obsidian-plugin-image-upload-toolkit/</link><pubDate>Thu, 08 Jun 2023 20:03:19 +0800</pubDate><guid>https://atbug.com/obsidian-plugin-image-upload-toolkit/</guid><description>这篇文章主要来介绍下我开发的 Obsidian 图片上传插件 Image Upload Toolkit。
背景 为什么开发这个插件？这还要从去年说起。
去年我感觉到使用了 6 年的 Mweb Pro 已经无法满足我的需求了，这并不是说 Mweb 不是个好产品，反而过去几年中我经常向身边的朋友推荐这个产品。Mweb 是个非常好的产品，功能多、快捷键方便、界面也满足我个人的审美，而且买断制的付费也很有吸引力。
随着这两年写的内容越来越多，Mweb 即使再多的功能也无法满足一些个性化的需求。就比如说内容自动排版，个人习惯中英文、数字间加上空格、段落间的空行，等等。这些都是 Mweb 这种封闭的产品无法实现的，尤其是对个人开发者（Mweb 是独立开发者开发的）来说，满足个性化需求的成本是非常大的。何况，众口难调。
后面我陆续使用了 NotePlan、Notion 一段时间，Notion 我个人还算喜欢，但其仍是封闭的产品，很难对其进行扩展。
正当我在朋友圈感慨时，有人推荐了 Obsidian。试用之后眼前一亮，这插件系统太强大了。比如上面的自动排版问题，使用 Linter 插件完美得到解决。
于是乎，我通过安装多种插件、将快捷键改成与 Mweb 一致，几乎是平移到了 Obsidian，并收获了更多的功能。唯独令人遗憾的是无法复刻 Mweb 的图片上传功能（我一直用阿里云的 OSS 作为图床，使用 Mweb 可以自动上传并替换 markdown 语法中的图片地址），每次编辑完需要发布到博客前我都要复制到 Mweb 中上传。</description></item><item><title>GOTC 2023 参会流水账</title><link>https://atbug.com/gotc-2023-thoughts/</link><pubDate>Fri, 02 Jun 2023 18:15:30 +0800</pubDate><guid>https://atbug.com/gotc-2023-thoughts/</guid><description>有幸参加 2023 年 GOTC（The Global Opensource Technology Conference，全球开源技术峰会）两天的活动，可谓是是收获满满。
这次参加活动本是带着两个任务来的：一个是看（一声） LF APAC（Linux 基金会亚太区）开源布道者的展台；另一个是在云原生专场的分享：Kubernetes 跨集群的流量管理实践。
两天的活动原本计划第一天在展台，听各路大佬分享“八卦”；第二天除分享外就是听听其他几个感兴趣的分论坛。但临时被拉去主持云原生分会场，在分会场坐了一天，听了全部议题。也因为突然的变化，有了不一样的感悟。
前方预警，内容有些杂乱，定义为流水账一篇。或对或错，都是个人见解，其他的就当都市传说吧。
第一天 这次开源布道者团队在展区获得了一个展位，大会之前大家准备大展拳脚准备个酷点的背板。无奈被现实打趴下，由于几个人都太忙，最后背板是下面这个样子。是的，当你不做选择的时候系统会给你个默认的（当然，这都不重要）。
我是在 2021 年底在小马哥的怂（鼓）恿（励）下加入布道师团队的，正式开始是在 2022 年初。几乎一整年大家都是线上的交流，这次就变成了大型的网友见面会。这次虽然不是所有布道者都来参加，但是所有参加的人也没拍成集体照。
开源与开发者 一整天都在与人交流，更多的是听，听各个开源领域专家的分（吐）享（槽）。吐槽可以让人很愉悦，将原本稍显沉重的话题轻松讲出。
有开发团队吐槽开源团队（如 OSPO 等）：影响开发进度、规范流程不合理等等。有开源专家就反映团队设置流程检查节点看似合理，实际上却卡住了流程，最后谁都不愿担责。 有开源团队吐槽开发者：开源产品选型不合理、不遵守规范等。比如需要某个功能，直接从网上找来的开源项目没有许可、没有 READEME。 有开源团队吐槽企业的 KPI 设定，开源或做商业化，或要体现出价值。 这里面有企业、开发团队、开源团队，本应成为互相支持的正循环的，但企业需要业绩（活下去）、开发团队要支撑业务（有产出）、开源团队帮助企业更好地利用和贡献开源。彼此的关注点不同，却有一条无形的线联系着。开源团队如何向上提供开源战略和指导、降低成本和风险、促进共享和合作；向下传播开源文化、提供合规指导、技术支持。
说起来容易，做起来难。个人认为开源的最后一公里决定了开源在内部落地的成败。我们是否能够从当前企业的规模和实际需求出发，以一种务实的态度深入开发者群体，去了解他们所面对的情况。我不反对规范流程确实需要从上而下的执行，但是规范流程的制定也需要开发者的参与提供指导。我能想到的另一个点是，当一件事做起来很容易的话，应该可以让更多的人进入。 所以整个过程中，需要借助工具的力量去提升自动化减少人工的参与。
关于开源布道 其实加入开源布道师之后很长一段时间我都是很迷茫的。我本身参与开源的时间不长，文化理解也不够。平时看着大家对开源文化、开源合规等方面的专业，感觉甚是惭愧。自己只能写写公众号博客、在活动上分享下技术。后来被适兕老师“教育”两次，也理解到分享就是开源的本质之一，开源的理念是共享和开放。
可能很多人也像我一样不了解“开源布道”。经过两天几次的讨论，我们发现做个“开源布道者画像”可能会有帮助。能够帮助更多的人理解开源布道，能够加入进来。
饭局 当天的饭局是主办方安排的自助餐，大家坐的比较分散，一起的几个人快速地吃完离开准备下一场聊天。巧合的是，吃饭时隔壁桌的几个人在聊天，其中有人说“欢迎大家去烟台玩”。作为老家威海就在烟台隔壁的我来说就来了兴趣，攀谈之后发现是公司的客户，其中有人还在威海上的大学。</description></item><item><title>取代 Docker Desktop？Podman Desktop 发布 GA 版本 1.0</title><link>https://atbug.com/podman-desktop-announce-general-availability/</link><pubDate>Wed, 24 May 2023 23:46:39 +0800</pubDate><guid>https://atbug.com/podman-desktop-announce-general-availability/</guid><description>Podman（POD MANager）是一个跨平台的容器管理工具，可用于管理容器、镜像、卷以及以容器组形式存在的 Pod。Podman 可以在 Linux 上直接运行容器，但在像 macOS 和 Windows 这样的平台，是通过虚拟机间接运行容器。
Podman Desktop 提供的图形用户界面使开发人员可以方便快捷地在本地环境中创建和管理容器，简化了容器的使用，无需记忆和输入复杂的命令，降低容器的使用门槛。
主要特点 优秀的兼容性 多平台：支持 Linux、macOS、Windows 兼容 Docker API、Lima、Kind、Openshift Local、Podman Machine 容器和 Pod 管理 构建、运行容器的 Pod 无需 Kubernetes 直接运行 Pod 内置终端 ssh 到容器 与 Docker Compose 兼容 镜像和仓库管理 配置管理多个镜像仓库 构建、拉取、tag 和推送镜像 推送镜像到 Kind 集群 Kubernetes 兼容 Kubernetes YAML 创建 Pod 从容器或者 Pod 生成 Kubernetes YAML 兼容 Docker Extension 支持 Docker Desktop UI extensions，可以使用 OCI 镜像运行 Extension，如 flomesh/pipy-docker-ext 。</description></item><item><title>深入探索 Cilium 的工作机制</title><link>https://atbug.com/deep-dive-into-cilium/</link><pubDate>Sat, 20 May 2023 18:27:39 +0800</pubDate><guid>https://atbug.com/deep-dive-into-cilium/</guid><description>这篇之前写 Kubernetes 网络学习之 Cilium 与 eBPF 记录的内容，隔了几个月终于想起把笔记完成，作为探索 Cilium 工作原理的入门，也还是 Cilium 冰山一角，像是高级的网络策略、网络加密、BGP 网络、服务网格等方面并没有深入。如果阅读过程中有发现任何问题，也烦请纠正。
本文基于 Cilium v1.12 及 Kubernetes v1.25。
实验环境 我们使用 k8e 创建集群，因为 k8e 使用 Cilium 作为默认的 CNI 实现。在我的 homelab 上做个双节点（ubuntu-test1: 192.168.1.21、ubuntu-test2: 192.168.1.22）的集群。
Master 节点
curl -sfL https://getk8e.com/install.sh | API_SERVER_IP=192.168.1.21 K8E_TOKEN=ilovek8e INSTALL_K8E_EXEC=&amp;#34;server --cluster-init --write-kubeconfig-mode 644 --write-kubeconfig ~/.</description></item><item><title>Kubernetes 自动化诊断工具：k8sgpt-operator</title><link>https://atbug.com/automatic-diagnosis-of-kubernetes-clusters-using-k8sgpt-operator/</link><pubDate>Tue, 02 May 2023 17:33:28 +0800</pubDate><guid>https://atbug.com/automatic-diagnosis-of-kubernetes-clusters-using-k8sgpt-operator/</guid><description>背景 在 Kubernetes 上，从部署 Deployment 到正常提供服务，整个流程可能会出现各种各样问题，有兴趣的可以浏览 Kubernetes Deployment 的故障排查可视化指南（2021 中文版）。从可视化指南也可能看出这些问题实际上都是有迹可循，根据错误信息基本很容易找到解决方法。随着 ChatGPT 的流行，基于 LLM 的文本生成项目不断涌现，k8sgpt 便是其中之一。
k8sgpt 是一个扫描 Kubernetes 集群、诊断和分类问题的工具。它将 SRE 经验编入其分析器，并通过 AI 帮助提取并丰富相关的信息。
其内置了大量的分析器：
podAnalyzer pvcAnalyzer rsAnalyzer serviceAnalyzer eventAnalyzer ingressAnalyzer statefulSetAnalyzer deploymentAnalyzer cronJobAnalyzer nodeAnalyzer hpaAnalyzer（可选） pdbAnalyzer（可选） networkPolicyAnalyzer（可选） k8sgpt 的能力是通过 CLI 来提供的，通过 CLI 可以对集群中的错误进行快速的诊断。</description></item><item><title>从单集群到多集群：Kubernetes在多云混合云环境的演进</title><link>https://atbug.com/kubernetes-cluster-evolution-in-multi-hybrid-cloud/</link><pubDate>Thu, 06 Apr 2023 16:13:14 +0800</pubDate><guid>https://atbug.com/kubernetes-cluster-evolution-in-multi-hybrid-cloud/</guid><description>Kubernetes 作为一项核心技术已成为现代应用程序架构的基础，越来越多的企业使用 Kubernetes 作为容器编排系统。
下面的数据来自 2020 CNCF Survey 的原始数据，可以看到使用 Kubernete 的企业占比达到了 80%。
Kubernetes 的流行主要有以下几个原因：
自动化：Kubernetes 实现了容器的部署、扩展、负载均衡、故障恢复、滚动更新等操作的自动化，极大地简化了应用程序的管理和维护工作。这种自动化也提升了应用程序的弹性和可用性。 可移植性：Kubernetes 基于容器的架构模型，使得应用无需重新编码或更新配置就可以在任何云平台、物理机或者虚拟机中运行。 生态系统：Kubernetes 作为一个成功地开源项目，拥有强大的社区支持和生态系统，使其可以获得更好的创新、优化和安全性保障。从社区中，我们可以找到各种插件和工具，为开发者提供了丰富的选择和扩展性。 从单 Kubernetes 集群到多 Kubernetes 集群 初露端倪 企业中的应用程序通常比较复杂，需要不同的环境来进行开发、测试和生产部署。为了避免应用程序之间的干扰和交叉，通常需要在不同的 Kubernetes 集群中分别部署和管理应用程序。
在同一个数据中心不同的环境部署独立的 Kubernetes 集群之后不同环境下的集群规模、管理方式、可靠性和安全性各有不同，从开发、测试到生产，成本的投入也逐步地提升，来保证更好的性能、更高的可靠性和安全性。
这也是多 Kubernetes 集群的一种形式（注意这里说的是 多 Kubernetes 集群）。
迅猛发展 随着对云计算接受程度不断提高、企业规模的持续增长，越来越多的企业开始考虑采用或者已经采用多云和混合云的架构。多云和混合云的驱动因素很多，总结之后分成了两类：主动因素和被动因素。</description></item><item><title>软件栈的商品化：应用程序为先的云服务如何改变游戏规则</title><link>https://atbug.com/translation-application-first-cloud-services/</link><pubDate>Wed, 29 Mar 2023 07:55:33 +0800</pubDate><guid>https://atbug.com/translation-application-first-cloud-services/</guid><description>今天读到这篇文章，觉得不错就翻译一下。文章是翻译自 Steef-Jan Wiggers The Commoditization of the Software Stack:How Application-first Cloud Services are Changing the Game，内容来自 Bilgin Ibryam 在 QCon London 上的演讲。Ibryam 在分享中从应用开发和运维两个不同的维度来讨论架构的演进：内部架构和外部架构。二者从单体应用时期的明显的界限，到如今界限愈来愈模糊。
我认为随着架构的演进，应用程序发生着巨大的变化，能力从应用中分离出来，下沉到基础设施中，甚至变成新的基础设施。基础设施，也从狭义上的物理设施、计算资源演变为软件定义的能力，这些能力不断地被产品化、商品化。新生代的基础设施，以各种运行时的方式游离在应用程序之外，二者仍保持着一定的联系：API。
以下是原文的翻译。
云服务正在不断演进，这影响着开发者构建分布式应用程序的方式。在 QCon London 大会上，来自 Diagrid 的产品经理 Bilgin Ibryam 探讨了云原生技术（如 Dapr）与面向开发者的云服务的交集。
Ibryam 首先介绍了如何看待从 单体应用程序 到 微服务 的转变，以及接下来会出现什么。此外，他还谈到了云服务以及它正在以什么样的形式塑造架构的演变。</description></item><item><title>使用 eBPF 技术实现更快的网络数据包传输</title><link>https://atbug.com/accelerate-network-packets-transmission/</link><pubDate>Wed, 22 Mar 2023 07:03:28 +0800</pubDate><guid>https://atbug.com/accelerate-network-packets-transmission/</guid><description>在 上篇文章 用了整篇的内容来描述网络数据包在 Kubernetes 网络中的轨迹，文章末尾，我们提出了一种假设：同一个内核空间中的两个 socket 可以直接传输数据，是不是就可以省掉内核网络协议栈处理带来的延迟？
不论是同 pod 中的两个不同容器，或者同节点的两个 pod 间的网络通信，实际上都发生在同一个内核空间中，互为对端的两个 socket 也都位于同一个内存中。而在上篇文章的开头也总结了数据包的传输轨迹实际上是 socket 的寻址过程，可以进一步将问题展开：同一节点上的两个 socket 间的通信，如果可以 快速定位到对端的 socket &amp;ndash; 找到其在内存中的地址，我们就可以跳过内核协议栈的环节进而加速数据包的传输。
互为对端的两个 socket 也就是建立起连接的客户端 socket 和服务端 socket，他们可以通过 IP 地址和端口进行关联。客户端 socket 的本地地址和端口，是服务端 socket 的远端地址和端口；客户端 socket 的远端地址和端口，则是服务端 socket 的本地地址和端口。
当客户端和服务端在完成连接的建立之后，如果可以使用本地地址 + 端口和远端地址 + 端口端口的组合 指向 socket，仅需调换本地和远端的地址 + 端口，即可定位到对端的 socket，然后将数据直接写到对端 socket（实际是写入 socket 的接收队列 RXQ，这里不做展开），就可以避开内核网络栈（包括 netfilter/iptables）甚至是 NIC 的处理。</description></item><item><title>追踪 Kubernetes 中的数据包</title><link>https://atbug.com/tracing-network-packets-in-kubernetes/</link><pubDate>Sat, 18 Mar 2023 02:25:17 +0800</pubDate><guid>https://atbug.com/tracing-network-packets-in-kubernetes/</guid><description>网络和操作系统内核，对我来说是既陌生又满是吸引，希望能够拨开层层迷雾找到背后的真相。
在 上一篇文章 中我深入探讨了 Kubernetes 网络模型，这次我想更深入一点：了解数据包在 Kubernetes 中的传输，为学习 Kubernetes 的 eBPF 网络加速做准备，加深对网络和操作系统内核的理解。 文中可能有疏漏之处，还望大家赐教。
在开始之前，我可以用一句话来总结我的学习成果：数据包的流转其实就是一个网络套接字描述符（Socket File Descriptor，中文有点冗长，以下简称 socket fd）的寻址过程。 它不是简单的指 socket fd 的内存地址，还包括它的网络地址。
在 Unix 和类 Unix 系统中，一切皆文件，也可以通过文件描述符来操作 socket。
基础知识 数据包 既然要讨论数据包的流转，先看看什么是数据包。
网络数据包（network packet），也称为网络数据报（network datagram）或网络帧（Network frame），是通过计算机网络传输的数据单位。拿最常见的 TCP 数据包来看包含如下几个部分：
Ethernet header：链路层信息，主要包括目的 MAC 地址和源 MAC 地址，以及报文的格式，这里是 IP 包。 IP header：网络层信息，主要包括长度、源 IP 地址和目的 IP 地址以及报文的格式，当然这里必须是 TCP 包。 TCP header：传输层信息，包括源端口和目的端口。 数据：一般是第 7 层的数据，比如 HTTP 等。 这里没有介绍的 checksum 和 FCS 通常是用来检查数据包在传输过程中是否被篡改或者发生了错误。</description></item><item><title>Karmada：混合多云下的应用管理</title><link>https://atbug.com/deploy-application-on-multi-clusters-with-karmada/</link><pubDate>Sun, 12 Mar 2023 23:45:17 +0800</pubDate><guid>https://atbug.com/deploy-application-on-multi-clusters-with-karmada/</guid><description>背景 过去几年，公有云凭借着更高扩展性、灵活性、可靠性和安全性，吸引了大量的企业将应用程序部署到公有云上。随着业务规模的不断扩张，企业出于某些原因，如避免厂商锁定、追求更低的延迟、更高的可靠性等，选择将应用部署在更多的公有云上；也有些企业出于数据敏感性等原因，选择将部分应用部署有私有环境中。后者也更像是将上云的过程拉长。不管是多云还是混合云，基础设施都不可避免的存在着差异，企业不得不在适配底层设施上投入了大量的人力物力。
Kubernetes 的出现，完美地解决了这一问题。除了屏蔽基础设施层的差异解决了跨平台的问题，还提供自动化的容器编排、更高的扩展性、弹性和高可用性，其背后更是有着庞大的社区的支持。Kubernetes 的风靡，得到了大量企业的青睐。随着时间的推移，企业使用多个 Kubernetes 集群管理应用的情况越来越普遍。
如何在跨越多个集群、甚至是混合多云的环境下来管理应用成了新的难题。Karmada 的出现正是要解决这一问题。
Karmada Karmada 是 CNCF（Cloud Native Computing Foundation）下面的一个开源项目，旨在为 Kubernetes 集群提供一个平台来简化跨多个 Kubernetes 集群的应用程序部署和管理，并提高可用性和可扩展性。
借用一下官网的架构图。从图中可以看到 Karmada 提供了一个集中式控制平面，负责资源和策略的管理，以及资源的调度；数据平面则是其管理的集群，真正运行资源的集群。
控制平面的组件与 Kubernetes 的组件类似，也都是负责资源的调度。不同的是，Kubernetes 的控制平面负责将资源调度到计算节点，而 Karmada 的控制平面是将资源调度到某个集群。
拿 Deployment 的部署来说，在 Kubernetes 集群中，控制平面根据当前节点的资源情况选择某个或某些节点来运行 pod。在 Karmada 多集群下，创建了 Deployment 资源后，Karmada 控制平面根据策略将其调度到目标的集群：在目标集群中创建 Deployment 资源，所有集群中的副本数之和，就是期望的副本数。</description></item><item><title>使用 Terraform 创建 Azure 虚拟机</title><link>https://atbug.com/create-azure-virtual-machine-with-terraform/</link><pubDate>Thu, 02 Mar 2023 21:50:42 +0800</pubDate><guid>https://atbug.com/create-azure-virtual-machine-with-terraform/</guid><description>之前写过一篇 使用 Terraform 部署 Proxmox 虚拟机，那是一台 Core i7-8700 + 64G 的 Homelab 上搭建的虚拟机环境，这也是我一直以来的实验环境。直至去年加入微软 MVP 拿到了 Azure 的 credit，在需要资源较多或者拉取镜像频繁的情况下我也会选择使用 Azure 的虚拟机，尤其是最近经常在多集群的环境做测试。
在 Azure 上创建虚拟机，我也从一开始的 Web 页面专项 CLI，再到现在因为需要复杂配置时的 Terraform。这篇文章就分享下如何使用 Terraform 配置 Azure 虚拟机的创建。
Terraform 是一个基础设施即代码的软件工具。使用 Terraform 可以通过编写代码来描述基础设施（包括虚拟机、负载均衡器、数据库等云和本地资源）的期望状态，安全灵活高效地构建、更改和版本化云和本地资源。
前置条件 在开始之前，确保已经具备如下条件：
Azure 账户 Terraform CLI Azure CLI 认证 既然要做自动化，第一步就要解决认证的问题，毕竟每次都进行手工认证是不现实的，不符合懒人的特征。</description></item><item><title>使用 Ingress 访问 Dapr 应用</title><link>https://atbug.com/access-dapr-application-with-ingress-controller/</link><pubDate>Fri, 24 Feb 2023 06:32:19 +0800</pubDate><guid>https://atbug.com/access-dapr-application-with-ingress-controller/</guid><description>在 上一篇 文章中分享了分布式运行时 Dapr 的使用，在示例中将状态存储能力分离到 Dapr 运行时中，应用通过 Dapr API 来使用该能力。这篇文章将介绍如何通过 Ingress Controller（入口控制器）来访问 Dapr 应用。
方案 如何公开 Dapr 应用的访问，方案有两种：
像传统用法一样，配置应用的 Service 作为后端，由入口控制器直接将流量转发到应用容器，简单说就是支持自动配置的 L7 负载均衡器。 不直接访问应用容器，而是通过 Daprd 运行时来访问。这时，我们就需要将入口控制器也声明为 Dapr 应用，为其注入 Daprd 运行时容器。此时创建入口规则指向的后端，则是 Ingress 的 Dapr Service。 两种方案各有优劣，前者架构简单；后者虽然引入 Daprd 容器，架构复杂，消耗了更多的资源，但也因此可以使用 Dapr 的服务治理能力，如超时、重试、访问控制等等。
接下来我们将通过示例来分别验证两种方案。</description></item><item><title>【译】分拆：技术栈的自然演进</title><link>https://atbug.com/the-unbundling-of-tech-stack-translation/</link><pubDate>Sun, 12 Feb 2023 20:26:32 +0800</pubDate><guid>https://atbug.com/the-unbundling-of-tech-stack-translation/</guid><description>本文翻译自 Bilgin Lbryam 的 Unbundling: The Natural Evolution of Tech Stacks，翻译难免有所疏漏，有建议请反馈。
“unbundling” 如何翻译，有点纠结，我一度将其翻译成“解耦”，但解耦是 “decoupling” 的翻译。这里我将其翻译成分拆，如果你有更好的翻译请告知。
译者注 作者应该是去年 7 月离开红帽加入了基于 Dapr 的创业公司 Diagrid，曾写过 Multi-Runtime Microservices Architecture 介绍多运行时，多运行时实际上也是分拆的体现。
作者从多种技术和团队触发，介绍在演进中分拆的体现。除了文中提到，我认为可以分拆的是计算资源。将计算资源拆分：虚拟机、多租户、多集群、多云、混合云，以降低成本、避免供应商绑定、提升性能和可靠性。在计算资源拆分过程中，也衍生出了与之配套的技术来解决拆分后带来的不便。
随着 IT 领域的不断发展，新的软件架构、开发技术和工具层出不穷。包括微服务、微前端、零信任、服务网格和数据网格，并将其网格化。尽管这些技术和方法间存在着明显的不同，但它们都被一个共同趋势联系在一起：技术栈和团队的分拆。这种趋势包括将系统分解成更小的、独立的组件，并将工作组织成更小、更专注的团队，以实现更高的灵活性和模块化。
他们都是如何体现分拆的？
微服务 的出现是为了应对单体架构的局限性，随着应用程序的增长单体架构灵活性不足，并且扩展和维护困难。通过将单体应用程序分解为更小的、独立服务，就可以独立开发、部署和扩展应用程序的每一部分，从而缩短开发周期并提高灵活性。 六边形架构 的出现是为了通过将组件解耦并提供与它们交互的标准接口来提高 3 层应用程序的灵活性和可维护性。 领域驱动设计 (DDD) 是一种软件开发方法，可以帮助将整体应用程序分解成更小的、松耦合的、代表不同的业务领域或上下文的模块。 微前端 架构是一种设计方法，是将大型单体前端应用程序分解为较小的、独立的、可以单独开发和部署的模块。 JAMstack 通过将构成用户界面的 HTML、CSS 和 JavaScript 与为应用程序提供支持的服务器端代码和数据库分离，实现应用程序的前端和后端分离。由于系统的一部分的变更无需变更其他部分，从而可以更轻松地维护应用。 服务网格 将分布式应用程序的网络职责（例如路由、负载平衡和服务发现）与应用程序本身分离，使开发人员可以专注于构建业务逻辑和功能，而无需担心底层网络基础设施。 与微服务类似，数据网格 将大型复杂系统分解为更小的独立组件。它将数据治理和管理实践分解为更小的、独立组件，这些组件可以跨不同的数据源和系统一致地实现和执行。 2 个比萨团队 模型是一种在组织中组织团队和工作的策略，它提倡更小的团队能够更快地响应变化、沟通和协作，并可以更快地做出决策并更有效地解决问题。 每种技术趋势的最终结果都是分拆。将技术栈分解为独立的组件，将团队分解为更小、更专注的团队，这些团队可能会扩展到所有其他领域。在前端、数据、网络、安全之后，下一个拆分领域你认为会是什么？ 和我一起 致力于 Dapr 和分拆集成。 也可以在 @bibryam 上关注我，并大声说出关于 分拆 主题的任何想法和评论。</description></item><item><title>分布式应用运行时 Dapr：万物皆可 API</title><link>https://atbug.com/first-sight-of-dapr/</link><pubDate>Sat, 11 Feb 2023 11:18:45 +0800</pubDate><guid>https://atbug.com/first-sight-of-dapr/</guid><description>Dapr 分布式应用运行时 Distributed Application Runtime 的首字母缩写。有关多运行时，可以看下 Bilgin Ibryam 的 Multi-Runtime Microservices Architecture，不想看英文的可以看下我之前的翻译。
Dapr 是一个分布式系统工具包，通过提供 API 实现应用程序与外围组件的解耦合，让开发人员更加聚焦于业务逻辑的研发。解耦也是与传统 SDK 的很大区别，能力不再是通过应用程序中加入库的方式提供，而是通过应用附近的边车（sidecar）运行时提供（sidecar 不是广为人知的服务网格 sidecar - pod 中的容器，而是广泛使用在系统软件设计中的一种模式，比如操作系统的 initd、日志采集组件，甚至是 Java 中的多线程。）。因此这里说的 Dapr sidecar 可能是个独立的进程，也可能是 pod 中的一个容器。
在 Dapr 中我们可以看到很多常见 SDK 的能力：
如 SpringCloud、Netflix OSS 的 服务调用，以及超时、熔断、重试等 弹性策略 如 Spring Data KeyValue 一样提供 状态存储 的抽象，简化各种持久存储的访问 如 Kafka、NATS、MQTT 等消息代理，提供 发布/订阅 抽象供服务通过消息进行通信 如 Kafka、MQTT、RabbitMQ 提供以事件触发应用的抽象：绑定 如 Redis 一样的 分布式锁 如 Consul、Kubernetes 等的 名称解析 &amp;hellip; 以上能力都是通过 HTTP 和 gRPC API 暴露给应用，这些 API 在 Dapr 中被叫做 构建块（building blocks），并且也 仅提供抽象，也就是说你可以随意替换底层实现（Dapr 中也叫做 组件）而无需修改任何应用代码。</description></item><item><title>在 Kubernetes 上运行我的世界</title><link>https://atbug.com/run-minecraft-on-kubernetes/</link><pubDate>Thu, 26 Jan 2023 10:58:26 +0800</pubDate><guid>https://atbug.com/run-minecraft-on-kubernetes/</guid><description>假期给小朋友装上了叨叨许久的 Minecraft（我的世界），为了体验安装的是 开源启动器 HMCL。其实这游戏我也关注比较久了，不过感觉太耗时间。但被小朋友拉上一起玩，便研究了下自建服务器。GitHub 发现已经有人做好了 Minecraft 服务端容器镜像，先是在 HomeLab 上用 Docker 部署，通过多人连线就能玩起来了。
由于不会玩几下被小朋友给打死，后来才发现还有“和平模式”。无聊转而研究下如何在公有云上部署：
我的 HomeLab 常年运行，由于没有重要的数据，不管是对硬件稳定性和数据备份都没有投入，担心游戏数据丢失被埋怨。放在公有云上使用公有云的对象存储，避免数据丢失 偶尔外出时玩的话，还需要 VPN 连回家才能玩 他有朋友一起玩时还能方便联机 最主要的原因还是去年加入微软 MVP 时，有送 Azure 的 credit，不用实属浪费 基于上面的原因，决定将服务器部署在 Azure 上，开一个 8c16g 的虚拟机并安装 k3s。数据呢，通过 blog-csi-driver 持久化存储在 Azure 的 Blob Storage 上。
开始吧！
安装 k3s 运行下面的命令进行安装，1.</description></item><item><title>Kubernetes 网络学习之 Cilium 与 eBPF</title><link>https://atbug.com/learn-cilium-and-ebpf/</link><pubDate>Wed, 11 Jan 2023 18:12:58 +0800</pubDate><guid>https://atbug.com/learn-cilium-and-ebpf/</guid><description>这是 Kubernetes 网络学习的第五篇笔记，也是之前计划中的最后一篇。
深入探索 Kubernetes 网络模型和网络通信 认识一下容器网络接口 CNI 源码分析：从 kubelet、容器运行时看 CNI 的使用 从 Flannel 学习 Kubernetes VXLAN 网络 Kubernetes 网络学习之 Cilium 与 eBPF（本篇） &amp;hellip; 开始之前说点题外话，距离上一篇 Flannel CNI 的发布已经快一个月了。这篇本想趁着势头在去年底完成的，正好在一个月内完成计划的所有内容。但上篇发布后不久，我中招了花了一个多周的时间才恢复。然而，恢复后的状态让我有点懵，总感觉很难集中精力，很容易精神涣散。可能接近网上流传的“脑雾”吧，而且 Cilium 也有点类似一团迷雾。再叠加网络知识的不足，eBPF 也未从涉足，学习的过程中断断续续，我曾经一度怀疑这篇会不会流产。
文章中不免会有问题，如果有发现问题或者建议，望不吝赐教。
背景 去年曾经写过一篇文章 《使用 Cilium 增强 Kubernetes 网络安全》 接触过 Cilium，借助 Cilium 的网络策略从网络层面对 pod 间的通信进行限制。但当时我不曾深入其实现原理，对 Kubernetes 网络和 CNI 的了解也不够深入。这次我们通过实际的环境来探寻 Cilium 的网络。</description></item><item><title>再见，超速的 2022</title><link>https://atbug.com/fare-well-2002/</link><pubDate>Sat, 31 Dec 2022 07:40:32 +0800</pubDate><guid>https://atbug.com/fare-well-2002/</guid><description>快，是真的快，感觉 与 2021 再见 就在几天前，然而回头又想不到做了什么，该总结什么。
是的，这篇与技术无关。
工作 去年（我现在可以称其为去年）换了现在的工作，换了新的方向，对于未知当初的几个月充满了新鲜与忐忑。年初在老板的几次正向 “PUA” 下，对这个方向慢慢有了更新的认识，并坚定的走下去。
远程办公又一年，走出最初半年的“蜜月期”之后，尤其上半年的那几个月，心中充满了阴郁。短短的几个月，有点将居家做到了极致的异味，以至于年底两个月的居家变得无感了。也好，快速地度过了远程办公的“阵痛期”。
工作中仍然有挑战，但更多的是来自自己不曾接触的知识领域，无他唯有不断地探索学习。这一年，从工作从同事那边都学到了很多。
何其有幸，继续做着自己喜欢的工作。
学习 阅读方面，书看的是不够多，更多的是碎片化的阅读时间。“哪里不会学哪里”，遇到不会的不需要尴尬，也正式填充认知拼图的机会。学习之后，将其记录下来。写博客写公众号，实际上是我学习的一种方式。
公众号，这是我坚持的第二年，因为是记录学习，内容会显得杂乱。年初有朋友建议稍微运营一下，确实有考虑过，但最终放弃。不想在这上面浪费时间，不想改变写作的初衷，唯有持续学习才是写下去的动力。
统计了下，2020 年有 34 篇原创 5 篇翻译，虽然数量没有去年多，但是有在尝试进行思考和沉淀。内容虽然不会很多，但我希望继续保持下去，只发原创。
何其有幸，还能继续学习。
生活 明天与她进入第 19 个年头，明年我也 38 了，开始超过人生一半的时间；父母陪在身边，不住在一起但住得越来越近；儿子也一天天长大，可以一起开始打游戏了。
今年与医院打交道的次数有点多，所幸都一切都算顺利，坦然面对。
何其有幸，有你们相伴。
总结 过去一年，过得非常快，有得有失，有求而不得，有失而复得。
珍惜眼前，珍惜当下。
何其有幸，2023，它来了。
希望你愿意听 我心中的旧日辉煌
我的世界没有太阳 却依然明亮
理想的种子它随风飘荡不知去向何方</description></item><item><title>译：在 Kubernetes 上实施零信任</title><link>https://atbug.com/implementing-zero-trust-on-kubernetes/</link><pubDate>Thu, 29 Dec 2022 12:42:40 +0800</pubDate><guid>https://atbug.com/implementing-zero-trust-on-kubernetes/</guid><description>本文翻译自 ContainerJournal 的 2022 年度文章之一 《Implementing Zero-Trust on Kubernetes》，作者 Deepak Goel 在文中分享了 Kubernetes 上实施零信任的三个最佳实践。
作为云原生社区的基石，Kubernetes 帮助企业在生产环境中更高效地部署和管理容器。尽管 Kubernetes 最初设计时提供了基本的 安全功能，但广泛且迅速的采用以及日益复杂的威胁形势使 Kubernetes 更容易受到攻击。开发人员和安全专家当下的任务是扩展 Kubernetes 的内置安全性，以有效防范更复杂、更多样和更频繁的网络攻击。
以往“信任但要验证”的方式已被证明对云计算复杂的分布式特性无效，因此 Kubernetes 必须转向“从不信任，始终验证”的零信任模型思想，为业务提供更大的保护。
零信任模型的基本概念 基于“从不信任，始终验证”的原则，可以用三个基本概念来解释零信任模型：
安全网络：始终认为网络是敌对的和有威胁的。网络上的内部和外部数据和信息不断暴露在安全威胁之下。 安全资源：网络上存在的任何信息源，无论位于何处，对其都应持怀疑态度。 身份验证：默认情况下不应信任来自内部或外部网络的用户、设备和流量。零信任应该基于使用正确的身份验证和授权的访问控制。 零信任的三个最佳实践 Kubernetes 提供了灵活性，既是优势但也增加了复杂性，为了在不同的网络环境中运行，为服务和工作负载引入了许多配置选项。Kubernetes 部署考虑以下三个零信任模型的最佳实践，以提升安全保护和工作效率。
优化软件配置和访问权限 团队需要为服务和跨集群操作提供一致的配置。虽然 Kubernetes 提供了多种配置选项，但过多的选项会增加安全问题出现的几率。使用零信任框架，组织可以对服务进行持续验证并将其部署到多个集群，而不会危及任何安全性。通过在授予它们对应用程序和服务的任何安全权限之前仔细检查这些配置，组织可以加强分布式 Kubernetes 集群的安全性。</description></item><item><title>从 Flannel 学习 Kubernetes overlay 网络</title><link>https://atbug.com/cross-node-traffic-on-flannel-vxlan-network/</link><pubDate>Thu, 15 Dec 2022 07:00:13 +0800</pubDate><guid>https://atbug.com/cross-node-traffic-on-flannel-vxlan-network/</guid><description>这是 Kubernetes 网络学习的第四篇笔记。
深入探索 Kubernetes 网络模型和网络通信 认识一下容器网络接口 CNI 源码分析：从 kubelet、容器运行时看 CNI 的使用 从 Flannel 学习 Kubernetes VXLAN 网络（本篇） Kubernetes 网络学习之 Cilium 与 eBPF &amp;hellip; Flannel 介绍 Flannel 是一个非常简单的 overlay 网络（VXLAN），是 Kubernetes 网络 CNI 的解决方案之一。Flannel 在每台主机上运行一个简单的轻量级 agent flanneld 来监听集群中节点的变更，并对地址空间进行预配置。Flannel 还会在每台主机上安装 vtep flannel.</description></item><item><title>源码解析：从 kubelet、容器运行时看 CNI 的使用</title><link>https://atbug.com/how-kubelete-container-runtime-work-with-cni/</link><pubDate>Thu, 08 Dec 2022 22:32:25 +0800</pubDate><guid>https://atbug.com/how-kubelete-container-runtime-work-with-cni/</guid><description>这是 Kubernetes 网络学习的第三篇笔记。
深入探索 Kubernetes 网络模型和网络通信 认识一下容器网络接口 CNI 源码分析：从 kubelet、容器运行时看 CNI 的使用（本篇） 从 Flannel 学习 Kubernetes VXLAN 网络 Kubernetes 网络学习之 Cilium 与 eBPF &amp;hellip; 在上一篇中，通过对 CNI 规范的解读了解了网络配置的操作和相关的流程。在网络的几个操作中除了 CNI_COMMAND 外，有另外三个参数几乎每次都要提供 CNI_CONTAINERID、CNI_IFNAME 和 CNI_NETNS，这些参数无外乎都来自容器运行时。这篇将结合 Kubernetes 和 Containerd 源码，来分析一下 CNI 的使用。
Kubernetes 的源码来自分支 release-1.</description></item><item><title>认识一下容器网络接口 CNI</title><link>https://atbug.com/deep-dive-cni-spec/</link><pubDate>Tue, 06 Dec 2022 19:53:46 +0800</pubDate><guid>https://atbug.com/deep-dive-cni-spec/</guid><description>写在最前，周末写到这篇的时候我就发现可能是给自己挖了很大的坑，整个 Kubernetes 网关相关的内容会非常复杂且庞大。
深入探索 Kubernetes 网络模型和网络通信 认识一下容器网络接口 CNI（本篇） 源码分析：从 kubelet、容器运行时看 CNI 的使用 从 Flannel 学习 Kubernetes VXLAN 网络 Kubernetes 网络学习之 Cilium 与 eBPF &amp;hellip; 看自己能学到哪一步~
在 《深入探索 Kubernetes 网络模型和网络通信》 文章中，我们介绍了网络命名空间（network namespace） 如何在 Kubernetes 网络模型中工作，通过示例分析 pod 间的流量传输路径。整个传输过程需要各种不同组件的参与才完成，而这些组件与 pod 相同的生命周期，跟随 pod 的创建和销毁。容器的维护由 kubelet 委托给容器运行时（container runtime）来完成，而容器的网络命名空间则是由容器运行时委托网络插件共同完成。</description></item><item><title>深入探索 Kubernetes 网络模型和网络通信</title><link>https://atbug.com/deep-dive-k8s-network-mode-and-communication/</link><pubDate>Sun, 04 Dec 2022 08:48:11 +0800</pubDate><guid>https://atbug.com/deep-dive-k8s-network-mode-and-communication/</guid><description>这是 Kubernetes 网络学习的第一篇笔记。
深入探索 Kubernetes 网络模型和网络通信（本篇） 认识一下容器网络接口 CNI 源码分析：从 kubelet、容器运行时看 CNI 的使用 从 Flannel 学习 Kubernetes VXLAN 网络 Kubernetes 网络学习之 Cilium 与 eBPF Kubernetes 定义了一种简单、一致的网络模型，基于扁平网络结构的设计，无需将主机端口与网络端口进行映射便可以进行高效地通讯，也无需其他组件进行转发。该模型也使应用程序很容易从虚拟机或者主机物理机迁移到 Kubernetes 管理的 pod 中。
这篇文章主要深入探索 Kubernetes 网络模型，并了解容器、pod 间如何进行通讯。对于网络模型的实现将会在后面的文章介绍。
Kubernetes 网络模型 该模型定义了：
每个 pod 都有自己的 IP 地址，这个 IP 在集群范围内可达 Pod 中的所有容器共享 pod IP 地址（包括 MAC 地址），并且容器之前可以相互通信（使用 localhost） Pod 可以使用 pod IP 地址与集群中任一节点上的其他 pod 通信，无需 NAT Kubernetes 的组件之间可以相互通信，也可以与 pod 通信 网络隔离可以通过网络策略实现 上面的定义中提到了几个相关的组件：</description></item><item><title>kubectl foreach 在多个集群中执行 kubectl 命令</title><link>https://atbug.com/multi-clusters-operation-with-kubectl-foreach/</link><pubDate>Thu, 01 Dec 2022 08:04:02 +0800</pubDate><guid>https://atbug.com/multi-clusters-operation-with-kubectl-foreach/</guid><description>上周在写 K8s 多集群的流量调度 的 demo 部分时需要不停地在多个集群中安装组件、部署应用，或者执行各种命令。当时是通过 Linux shell 脚本并通过工具 kubectx 进行集群的切换，像这样：
或者这样：
操作繁琐，很是痛苦。
今天偶然间发现了一个 kubectl 插件 kubectl foreach ，可以在多个集群（contexts）上执行 kubectl 命令。比如 kubectl foreach cluster-1 cluster-2 -- get po -n kube-system 。
插件安装和使用很简单，通过 krew 进行安装：
kubectl krew install foreach 使用也很简单：
kubectl foreach -h Usage: kubectl foreach [OPTIONS] [PATTERN].</description></item><item><title>认识一下 Kubernetes 多集群服务 API</title><link>https://atbug.com/kubernetes-multi-cluster-api/</link><pubDate>Sun, 27 Nov 2022 22:37:11 +0800</pubDate><guid>https://atbug.com/kubernetes-multi-cluster-api/</guid><description>由于各种原因，采用 Kubernetes 的企业内部存在着几个、几十甚至上百个集群。比如处于研发流程上的考虑，不同环境下都存在独立的集群；监管层面的考虑，就地存储的用户数据需要搭配应用集群；单个集群的容量限制，无法满足业务体量；可用性要求的多云、多地、多中心；Kubernetes 原地升级成本大进而考虑新建集群，等等各种原因。然而，Kubernetes 设计之初并没有考虑多集群。
这些集群彼此之间看似独立，但又有着千丝万缕的关系。比如高可用性的多集群，实现了集群级的灾备，但集群中的服务如何实现跨集群的故障迁移？
我们先看下集群内的应用如何对集群外提供服务。由于 Kubernetes 网络隔离特性，存在着天然的网络边界，需要借助某些方案（如 Ingress、NodePort）来将服务暴露到集群外。虽然解决了连通性的问题，但是服务的注册和发现还无法解决。
通常我们将 Service 作为 Kubernetes 平台的服务注册和发现，今天要介绍的 Multi-Cluster Service（多集群服务 API，简称 MCS API） 则可以看成是 跨 Kubernetes 集群的服务注册发现。
MCS 介绍 MCS API 来自 Kubernetes Enhancement Proposal KEP-1645: Multi-Cluster Services API 目前还处于提案阶段。
MCS 的目标是既然多集群不可避免，那就定义一套 API 来实现服务的跨集群注册和发现，并且这套 API 足够轻量级，做到能够像访问本地服务一样访问其他集群的服务。</description></item><item><title>因为 null null 隔离结束的我差点不能回家</title><link>https://atbug.com/thoughts-after-encountering-null/</link><pubDate>Tue, 08 Nov 2022 14:10:25 +0800</pubDate><guid>https://atbug.com/thoughts-after-encountering-null/</guid><description>首先这篇文章不讲任何政策的东西，没有对政策和人员的吐槽。相反，整个过程中感受到的都是理解和支持。正如标题所写回家的过程有曲折，但影响不大，而结果让我有点想笑：就这？
作为一名程序员，有必要对问题做个复盘，摸清整个流程，了解下支撑咱们疫情防控的部分系统情况，进而思考下我们在数字化改造过程中可能有哪些不足。
背景 上个月底我因公去美国底特律参加了 KubeCon + CloudNativeCon 2022 NA ，从走之前的各种忐忑，到出去之后的坦然，以及最后顺利的满载而归。收获很多，这里暂且不表，后面我可能也抽时间写一下。
目前国内的入境防疫政策是 7 天的酒店集中隔离以及 3 天的居家健康监测。我在 11 月 2 日落地上海并顺利入住隔离酒店，开始了 7 天的隔离。家人帮忙从居委会咨询了可以回广州居家检测，往返机场两边都会提供封闭转运。因此我选择了在上海隔离 7 天，也早早预定了 7 天后就是 11 月 9 日返穗的机票。在 9 号解离前一天提供居住所在地的接收证明即可。
各地的政策不同，有的城市是可以提供盖章的证明，有些城市已经完成了无纸化的流程。这就像我们做系统集成的时候，要考虑针对不同系统的情况选择合适的集成方案。
过程 广州正是完成了数字化的城市之一（其他城市我也不了解），只要在微信小程序 粤省事 上找到 入粤健康申报，按照提示信息如实填写即可。提交之后，申报会派发到目的地所在的居委会，居委会的工作人员根据情况进行审批即可。
按照我和工作人员的理解，流程就是这样，很简单呐。然而不然，系统会有所差异。
上面是我经过两次提交申请，并跟工作人员多次沟通后“悟”出的流量。
起初在第 2 步就遇到了问题，提交健康申报后工作人员没有看到申请，而是过了几个小时候才看到。</description></item><item><title>【译】eBPF 和服务网格：还不能丢掉 Sidecar</title><link>https://atbug.com/translate-ebpf-service-mesh/</link><pubDate>Mon, 31 Oct 2022 20:51:17 -0400</pubDate><guid>https://atbug.com/translate-ebpf-service-mesh/</guid><description>服务网格以典型的 sidecar 模型为人熟知，将 sidecar 容器与应用容器部署在同一个 Pod 中。虽说 sidecar 并非很新的模型（操作系统的 systemd、initd、cron 进程；Java 的多线程），但是以这种与业务逻辑分离的方式来提供服务治理等基础能力的设计还是让人一亮。
随着 eBPF 等技术的引入，最近关于服务网格是否需要 sidecar （也就是 sidecarless）的讨论渐增。
笔者认为任何问题都有其起因，长久困扰服务网格的不外乎性能和资源占用。这篇文章翻译自 Buoyant 的 Flynn 文章 eBPF and the Service Mesh: Don&amp;rsquo;t Dismiss the Sidecar Yet。希望这篇文章能帮助大家穿透迷雾看透事物的本质。
本文要点 eBPF 是一个旨在通过（谨慎地）允许在内核中运行一些用户代码来提高性能的工具。 在可预见的未来，服务网格所需的第 7 层处理在 eBPF 中不太可能实现，这意味着网格仍然需要代理。 与 sidecar 代理相比，每个主机代理增加了操作复杂性并降低了安全性。 可以通过更小、更快的 Sidecar 代理来解决有关 Sidecar 代理的典型性能问题。 目前，sidecar 模型对服务网格仍是最有意义的。 关于 eBPF 的故事已经在云原生世界中泛滥了一段时间，有时将其描述为自切片面包以来最伟大的事物，有时则嘲笑它是对现实世界的无用干扰。当然，现实要微妙得多，因此仔细研究一下 eBPF 能做什么和不能做什么似乎是有必要的——技术毕竟只是工具，使用的工具应该适合手头的任务。</description></item><item><title>一文搞懂 Kubernetes Gateway API 的 Policy Attachment</title><link>https://atbug.com/explore-k8s-gateway-api-policy-attachment/</link><pubDate>Sun, 30 Oct 2022 18:01:30 -0400</pubDate><guid>https://atbug.com/explore-k8s-gateway-api-policy-attachment/</guid><description>背景 在《SMI 与 Gateway API 的 GAMMA 倡议意味着什么？》的一文中，介绍过 Kubernetes Gateway API 处理东西向（网格）流量的可能性。让我们重新看下 Gateway API 中对流量定义（路由）的定义，以 HTTP 路由为例：
apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: my-route namespace: gateway-api-example-ns2 spec: parentRefs: - kind: Gateway name: foo-gateway namespace: gateway-api-example-ns1 hostnames: - foo.example.com rules: - backendRefs: - name: foo-svc port: 8080 上面的 YAML 中，使用 parentRefs 字段将路由策略附加到了网关资源 foo-gateway 上。网关 foo-gateway 会根据请求头部的 HOST 来选择合适的 HTTPRoute，然后将流量代理到上游 Service foo-svc。简单来说，就是定义了“网关 -&amp;gt; 路由 -&amp;gt; 后端”的映射。</description></item><item><title>Kubernetes LoadBalancer Service 与负载均衡器</title><link>https://atbug.com/k8s-service-and-load-balancer/</link><pubDate>Fri, 30 Sep 2022 16:04:30 +0800</pubDate><guid>https://atbug.com/k8s-service-and-load-balancer/</guid><description>之前介绍过一些 Ingress 使用，比如 Ingress SSL 透传、Ingress 的多租户。从 Demo 看起来是创建 Ingress 之后，就能从集群外访问服务了。实际上除了 Ingress 的作用以外，还有 Kubernetes Service 和负载均衡器（Load Balancer）参与（当 Service 类型为 LoadBalancer 时）。
这篇文章就来介绍了 Kubernetes LoadBalancer Service 和两个比较典型的负载均衡器的工作原理。
LoadBalancer Service Service 是 Kubernetes 中的资源类型，用来将一组 Pod 的应用作为网络服务公开。每个 Pod 都有自己的 IP，但是这个 IP 的生命周期与 Pod 生命周期一致，也就是说 Pod 销毁后这个 IP 也就无效了（也可能被分配给其他的 Pod 使用）。而 Service 的 IP（ClusterIP） 则是在创建之后便不会改变，Service 与 Pod 之前通过 userspace 代理、iptables 和 ipvs 代理 等手段关联。</description></item><item><title>使用 Argo Rollouts 和服务网格实现自动可控的金丝雀发布</title><link>https://atbug.com/canary-release-via-argo-rollouts-and-service-mesh/</link><pubDate>Wed, 21 Sep 2022 23:37:43 +0800</pubDate><guid>https://atbug.com/canary-release-via-argo-rollouts-and-service-mesh/</guid><description>金丝雀发布是服务治理中的重要功能，在发布时可以可控地将部分流量导入新版本的服务中；其余的流量则由旧版本处理。发布过程中，可以逐步增加新版本服务的流量。通过测试，可以决定是回滚还是升级所有实例，停用旧版本。
有了金丝雀发布，使用真实流量对服务进行测试，通过对流量的控制可以有效的降低服务发布的风险。
本文将介绍如何将使用 Argo Rollouts 和服务网格 osm-edge 来进行应用的自动、可控的金丝雀发布，不涉及工作原理的分析。对工作原理有兴趣的同学可以留言，可以再做一篇原理的介绍。
Argo Rollouts Argo Rollouts 包括一个 Kubernetes控制器 和一组 CRD，提供如蓝绿色、金丝雀、金丝雀分析、体验等高级部署功能和 Kubernetes 的渐进交付功能。
Argo Rollouts 与 入口控制器 和服务网格集成，利用其流量管理能力，在发布期间逐步将流量转移到新版本。此外，Rollouts 可以查询和解析来自各种提供商的指标，以验证关键 KPI，并在更新期间推动自动推进或回滚。
Argo Rollouts 支持服务网格标准 SMI（Service Mesh Interface） 的 TrafficSplit API，通过 TrafficSplit API 的使用来控制金丝雀发布时的流量控制。
服务网格 osm-edge 服务网格是处理服务间网络通信的基础设施组件，旨在从平台层面提供可观性、安全以及可靠性特性，以进程外的方式提供原本由部分应用层逻辑承载的基础能力，真正实现与业务逻辑的分离。</description></item><item><title>零信任安全：SPIFFE 和 SPIRE 通用身份验证的标准和实现</title><link>https://atbug.com/what-is-spiffe-and-spire/</link><pubDate>Thu, 15 Sep 2022 12:00:09 +0800</pubDate><guid>https://atbug.com/what-is-spiffe-and-spire/</guid><description>最近正在读 《Solving the Bottom Turtle》 这本书，这篇是对部分内容的总结和思考，由于内容较多，会分几篇来发。
这本书的副标题是：
a SPIFFE Way to Establish Trust in Your Infrastructure via Universal Identity. 通过通用身份以 SPIFFE 的方式在基础设施中建立信任。
大家记住其中的有几个关键词：通用身份、SPIFFE 的方式、基础设施、建立信任。
背景 零信任是一种异常火热的安全模型，在之前翻译的文章中 《零信任对 Kubernetes 意味着什么》，对什么是零信任、为什么要实施零信任，做了初步的介绍。
过去几十年流行的边界安全模型，已经不在适合如今分布式的微服务架构、复杂多样的系统环境以及不可避免的人为失误。从零信任的原则来看，通过安全边界防护的人、系统和网络流量是不可信的，因为“你可能不是你”，身份不可信。
可能有人会问不是有用户名密码、证书么？这些都可能存在泄露，尤其是时间越长泄露的概率越高（在安全领域，只有 0 和 100，所有做不到 100% 的安全，都将其视作 0。这也是笔者对零信任的浅薄理解）。
零信任模型的核心是重塑身份的分配和验证方式，身份是构建零信任模型的基石。
身份 现实世界的身份 在说系统身份之前，先说下现实中的身份。大家都有身份证（Identity Card），生活中用到身份证的场景也越来越多，很多的服务都会查验身份证。为什么身份证就能验证持有人的身份，首先身份证是由政府机构颁发的，在颁发前会查验过申请的人信息（证明你就是你），这个充分可信的；身份证本身做了防伪处理，有特定的查验手段很难被伪造。这里有几个定义：</description></item><item><title>译：零信任对 Kubernetes 意味着什么</title><link>https://atbug.com/translate-zero-trust-for-k8s/</link><pubDate>Thu, 08 Sep 2022 21:45:44 +0800</pubDate><guid>https://atbug.com/translate-zero-trust-for-k8s/</guid><description>这篇是 Buoyant 的创始人 William Morgan 文章《What Does Zero Trust Mean for Kubernetes?》 的翻译，文章很好的解释了什么是零信任、为什么要实施零信任，以及服务网格如何以最小的代码实现零信任。
零信任是营销炒作，还是新的机会，各位看官你怎么看？
要点
零信任是一种被大量炒作的安全模型，但尽管存在营销噪音，但它对于具有安全意识的组织具有一些具体而直接的价值。 零信任的核心是，将授权从“在边界验证一次”转变为“随时随地验证”。 为此，零信任要求我们重新思考身份的概念，并摆脱基于位置的身份，例如 IP 地址。 Kubernetes 采用者在网络层实现零信任时具有明显的优势，这要归功于基于 Sidecar 的服务网格，它提供无需更改应用程序就可实现的最细粒度的身份验证和授权。 虽然服务网格可以提供帮助，但 Kubernetes 安全性仍然是一个复杂而微妙的话题，需要从多个层次进行了解。 零信任是一种位于现代安全实践前沿的强大的安全模型。这也是一个容易引起轰动和炒作的术语，因此很难消除噪音。那么，究竟什么是零信任，对于 Kubernetes，它究竟意味着什么？在本文中，我们将从工程的角度探讨什么是零信任，并构建一个基本框架来理解它对 Kubernetes 运维和安全团队等的影响。
介绍 如果你正在构建现代云软件，无论是采用 Kubernetes 还是其他软件，可能都听说过“零信任”一词。零信任的安全模式变得如此重要，以至于美国联邦政府已经注意到：白宫最近发布了一份联邦零信任战略的备忘录，要求所有美国联邦机构在年底前满足特定的零信任安全标准。2024财年；国防部创建了零信任参考架构；美国国家安全局发布了一份Kubernetes 强化指南，专门描述了 Kubernetes 中零信任安全的最佳实践。
随着这种噪音，零信任无疑吸引了很多营销关注。但尽管有噪音，零信任不仅仅是一个空洞的术语——它代表了对未来安全的一些深刻和变革性的想法。那么具体来说，什么是零信任，为什么它突然变得如此重要？零信任对 Kubernetes 用户意味着什么？</description></item><item><title>Docker 向全面集成 containerd 又迈进一步</title><link>https://atbug.com/docker-manipulate-image-via-containerd/</link><pubDate>Mon, 05 Sep 2022 07:08:15 +0800</pubDate><guid>https://atbug.com/docker-manipulate-image-via-containerd/</guid><description>Docker 在刚刚发布的 Docker Desktop 4.12.0 中，加入了实验特性：进一步集成 containerd，使用 containerd 来管理和存储镜像。
为什么说是“进一步集成”？这就要翻翻 Docker 和 containerd 的历史了。
containerd 的诞生 containerd 最早出现在 Docker Engine 中，后来为了将 Docker Engine 做得更加轻量、快速和健壮，在 2016 年 Docker 将 containerd 从 daemon（dockerd） 中独立出来，并完成了与 daemon 的集成。独立出来的 containerd 全面支持 OCI（Open Container Initiative）资源的启动和生命周期的管理，也因此 containerd 可以支持 runc（前身是 Docker 中的 libcontainer，后来捐赠给 LF）以外的其他 OCI 实现。2017 年 Docker 将 containerd 捐献给 CNCF；2019 年 2 月，containerd 毕业。</description></item><item><title>k3s 上的 kube-ovn 轻度体验</title><link>https://atbug.com/run-kube-ovn-on-k3s/</link><pubDate>Sat, 03 Sep 2022 19:10:39 +0800</pubDate><guid>https://atbug.com/run-kube-ovn-on-k3s/</guid><description>kube-ovn 从名字不难看出其是一款云原生的网络产品，将 SDN 等能力带入云原生领域。让 ovn/ovs 的使用更容易，屏蔽了复杂度，降低了使用的难度，并与云原生进行了结合。
借助 OVS/OVN 在 SDN 领域成熟的能力，Kube-OVN 将网络虚拟化的丰富功能带入云原生领域。目前已支持子网管理， 静态 IP 分配，分布式/集中式网关，Underlay/Overlay 混合网络， VPC 多租户网络，跨集群互联网络，QoS 管理， 多网卡管理，ACL 网络控制，流量镜像，ARM 支持， Windows 支持等诸多功能。
安装 k3s 参考官方的准备工作文档，操作系统使用 Ubuntu 20.04 以及 k3s v1.23.8+k3s2。
在安装之前确保 /etc/cni/net.d/ 目录内容为空，不为空则清空其下的所有文件。kube-ovn 本身通过实现 cni 来管理网络。
在安装 k3s 需要禁用 k3s 默认的网络策略控制器和flannel 的后端（默认是 VXLAN）：</description></item><item><title>另眼旁观 Linkerd 2.12 的发布：服务网格标准的曙光？</title><link>https://atbug.com/thoughts-on-linkerd-release/</link><pubDate>Sun, 28 Aug 2022 10:03:12 +0800</pubDate><guid>https://atbug.com/thoughts-on-linkerd-release/</guid><description>8 月 24 日，发明服务网格的公司 Buoyant 发布了 Linkerd 2.12，这是时隔近一年的版本发布。不知道大家的对新版本期待如何，在我来看新版本中的功能对于一年的时间来说确实不算多，但是我想说的是 Linkerd 还是那个 Linkerd，还是秉持着一贯的 “Keep it simple” 的设计理念。
新版本的内容不一一介绍，其中最主要的功能：per-route 策略，相比上个版本基于端口的策略，扩展到了 per-route。
而我想说的特别之处也就是在 per-route 和策略上。
per-route 和策略有何特别之处？ 在《SMI 与 Gateway API 的 GAMMA 倡议意味着什么？》 层有过猜想：未来服务网格的标准有可能全部或者部分以 Kubernetes 原生 API 的方式存在。
“网关 API，之于集群是入口/出口网关，之于 Pod 是 inbound/outbound sidecar。”</description></item><item><title>源码解析 kubectl port-forward 工作原理</title><link>https://atbug.com/how-kubectl-port-forward-works/</link><pubDate>Tue, 09 Aug 2022 07:10:28 +0800</pubDate><guid>https://atbug.com/how-kubectl-port-forward-works/</guid><description>本文的源码基于 Kubernetes v1.24.0，容器运行时使用 Containerd 1.5，从源码来分析 kubectl port-forward 的工作原理。
通过 port-forward 流程的分析，梳理出 kubectl -&amp;gt; api-server -&amp;gt; kubelet -&amp;gt; 容器运行时 的交互，了解 cri 的工作方式。
kubectl 简单创建个 pod：
kubectl run pipy --image flomesh/pipy:latest -n default 在执行 kubectl forward 时添加参数 -v 9 打印日志。
kubectl port-forward pipy 8080 -v 9 .</description></item><item><title>SMI 与 Gateway API 的 GAMMA 倡议意味着什么？</title><link>https://atbug.com/why-smi-collaborating-in-gateway-api-gamma/</link><pubDate>Fri, 22 Jul 2022 22:34:43 +0800</pubDate><guid>https://atbug.com/why-smi-collaborating-in-gateway-api-gamma/</guid><description>就在上周 Gateway API 发布版本 0.5.0，其中几个最重要的 CRD Gateway、GatewayClass 以及 HTTPRoute 第一次升级到了 beta 版本。升级的详细内容这里不做详谈，我也想说说的是与版本一同发布的，也是很容易被忽略的社区合作倡议 GAMMA。
GAMMA 是 Gateway API Mesh Management and Administration 的简写，这个计划是 Gateway API 子项目中的一个专用工作流。这个小组的目标是调研、设计和跟踪 Gateway API 资源、语义和其他与服务网格技术和用例相关的组件。此外，小组还努力倡导服务网格项目的 Gateway API 实现之间的一致性，无论使用何种技术栈或者代理。
可能你会有疑问如此简单一个倡议，有什么特别？有两点：
规范的参与：这个倡议由 SIG Network（Gateway API 所在的 Kubernetes SIG）与服务网格社区共同发起，服务网格社区有来自 Cilium Service Mesh、Consul、Istio、Kuma、Linkerd、SMI、NGINX Service Mesh 和 Open Service Mesh 的代表。SMI 与其他几个不同，它是服务网格的规范 API，并非是实现，Gateway API 也一样。 面向东西向流量：小组的首个工作探索使用 Gateway API 处理东西向流量已经开始。东西向流量，也就是服务网格中服务到服务的流量。 可见，服务网格部分 API 的统一将迈进一步，且成为 Kubernetes 原生 API 的一员。当然现在还言之过早，还需看各个厂商的跟进程度。</description></item><item><title>在 Kubernetes 上执行 GitHub Actions 流水线作业</title><link>https://atbug.com/run-github-actions-runners-on-kubernetes/</link><pubDate>Sun, 10 Jul 2022 16:56:09 +0800</pubDate><guid>https://atbug.com/run-github-actions-runners-on-kubernetes/</guid><description>GitHub Actions 是一个功能强大、“免费” 的 CI（持续集成）工具。
与之前介绍的 Tekton 类似，GitHub Actions 的核心也是 Pipeline as Code 也就是所谓的流水线即代码。二者不同的是，GitHub Actions 本身就是一个 CI 平台，用户可以使用代码来定义流水线并在平台上运行；而 Tekton 本身是一个用于构建 CI/CD 平台的开源框架。
Pipeline as Code，既然与代码扯上了关系。那流水线的定义就可繁可简了，完全看需求。小到一个 GitHub Pages，大到流程复杂的项目都可以使用 GitHub Actions 来构建。
本篇文章不会介绍如何使用 GitHub Actions 的，如果还未用过的同学可以浏览下官方的文档。今天主要来分享下如何在 Kubernetes 上的自托管资源来执行流水线作业。
0x01 背景 在介绍 GitHub Actions 的时候，免费带上了引号，为何？其作为一个 CI 工具，允许用户定义流水线并在平台上运行，需要消耗计算、存储、网络等资源，这些运行流水线的机器称为 Runner。GitHub 为不同类（等）型（级）的用户每月提供了不同的免费额度（额度用完后，每分钟 0.</description></item><item><title>译：Kubernetes 最佳实践</title><link>https://atbug.com/translate-kubernetes-best-practices/</link><pubDate>Tue, 05 Jul 2022 07:53:30 +0800</pubDate><guid>https://atbug.com/translate-kubernetes-best-practices/</guid><description>本文翻译自 Jack Roper 的文章 Kubernetes Best Practice。
译者：文章中作者从应用程序开发、治理和集群配置三个方面给出了一些 Kubernetes 的最佳实践，同时翻译过程中也加入了我过往的一些使用经验。有误的地方，也欢迎大家指正。
在这篇文章中，我将介绍一些使用 Kubernetes (K8s) 时的最佳实践。
作为最流行的容器编排系统，K8s 是现代云工程师掌握的事实标准。众所周知，不管使用还是维护 K8s 复杂的系统，因此很好地掌握它应该做什么和不应该做什么，并知道什么是可能的，将是一个好的开局。
这些建议包含 3 大类中的常见问题，即应用程序开发、治理和集群配置。
最佳实践目录 使用命名空间 使用就绪和存活探针（译者注：还有启动探针） 使用自动缩放 使用资源请求和约束 使用 Deployment、DaemonSet、ReplicaSet 或者 StatefulSet 跨节点部署 Pod 使用多节点 使用基于角色的访问控制（RBAC） 在外部托管Kubernetes集群（使用云服务） 升级Kubernetes版本 监控集群资源和审计策略日志 使用版本控制系统 使用基于Git的工作流程（GitOps） 缩小容器的大小 用标签整理对象（译者注：或理解为资源） 使用网络策略 使用防火墙 使用命名空间 K8s 中的命名空间对于组织对象、在集群中创建逻辑分区以及安全方面的考虑至关重要。 默认情况下，K8s 集群中有 3 个命名空间，default、kube-public 和 kube-system。</description></item><item><title>译：边缘计算的 4 种类型（大致分类）</title><link>https://atbug.com/translate-4-types-edge-computing-by-latency/</link><pubDate>Tue, 31 May 2022 07:12:31 +0800</pubDate><guid>https://atbug.com/translate-4-types-edge-computing-by-latency/</guid><description>本篇文章译自 SUNKU RANGANATH 的 4 Types of Edge Computing - Broadly Categorized。文章通过 往返终端设备和数据中心的延迟 来对边缘计算的类型进行大致的分类，通俗易懂，方便大家对边缘计算有个大概的了解。
边缘计算被认为是分布式计算的下一个前沿。 然而，并不是每个人都知道什么是边缘计算以及存在多种类型的边缘计算。
简单地说，边缘计算通过将计算靠近最终用户，实现了过多设备（通常在 5G 中称为用户设备 UE）和电信数据中心核心之间的连接。
促成边缘计算的关键因素是 UE 和需要高度分布式架构的计算服务器之间的往返通信所带来的延迟。
了解各种类型边缘计算的一种简单方法是基于其靠近终端设备的程度以及与数据中心的往返延迟。 将延迟作为主要因素，边缘计算可以大致分为以下几类：
IoT 边缘 本地边缘 接入边缘 网络边缘 IoT 边缘 这种边缘的延迟预期通常小于 1 毫秒。
这几乎涵盖了任何连接到私有或公共网络（如互联网）的设备。该设备可以是能够处理数据的智能设备，例如移动电话或中继周围环境信息的简单传感器。
包括但不限于零售亭、摄像头、工厂传感器、联网汽车、无人机、联网路灯、智能停车咪表、远程手术设备等。
本地边缘 这种边缘的延迟预期通常小于 5 毫秒。</description></item><item><title>ttl.sh：临时 Docker 镜像的匿名仓库</title><link>https://atbug.com/store-ephemeral-image-in-ttl-registry/</link><pubDate>Mon, 30 May 2022 07:25:42 +0800</pubDate><guid>https://atbug.com/store-ephemeral-image-in-ttl-registry/</guid><description>在平时的工作中，不知道你有没有经常需要构建容器镜像进行测试，并且不一定是在构建环境中使用镜像。这时候就需要将镜像推送到镜像仓库做中转，然后在别处拉取并运行容器。久而久之，因为忘记清理镜像仓库中的“垃圾”镜像越来越多。
当然，也可以使用类似 Harbor 这种带有自动清理功能镜像仓库。但只是作为临时镜像的中转，Harbor 这种未免太重了。
今天要介绍的 ttl.sh 正适合处理这种场景。
ttl.sh ttl.sh 是一个匿名的临时镜像仓库，免费使用无需登录，并且已经开源。无需登录，镜像名称本身就提供了保密性，比如你可以使用 UUID 来作为镜像名称，使用同一个 UUID 来推送和拉取镜像。
使用 ttl.sh 的使用格外简单，跟平时使用 Docker Hub 或者 Docker Registry 没差别，只是 tag 的需要注意一下。
docker build 构建镜像时通过 tag 为镜像指定有效期，比如 ttl.sh/b0a2c1c3-5751-4474-9dfe-6a9e17dfb927:1h。有效期默认是 1 小时，最长是 24 小时。有效的 tag 可以是 5m、300s、4h、1d，如果超过 24 小时有效期会被设置为 24 小时；如果时间格式无效，有效期设置为默认的 1 小时； 使用 docker push 推送镜像； 使用 docker pull 拉取镜像。 比如：</description></item><item><title>CICD 的供应链安全工具 Tekton Chains</title><link>https://atbug.com/tekton-chains-secure-supply-chain/</link><pubDate>Sat, 14 May 2022 13:46:16 +0800</pubDate><guid>https://atbug.com/tekton-chains-secure-supply-chain/</guid><description>软件供应链是指进入软件中的所有内容及其来源，简单地可以理解成软件的依赖项。依赖项是软件运行时所需的重要内容，可以是代码、二进制文件或其他组件，也可以是这些组件的来源，比如存储库或者包管理器之类的。包括代码的已知漏洞、受支持的版本、许可证信息、作者、贡献时间，以及在整个过程中的行为和任何时候接触到它的任何内容，比如用于编译、分发软件的基础架构组件。
CICD 流水线作为基础架构组件，承担着软件的构建、测试、分发和部署。作为供应链的一环，其也成为恶意攻击的目标。CICD 的行为信息可以作为安全审查的重要依据，比如构建打包的环境、操作流程、处理结果等等。
今天介绍的 Tekton Chains，便是这样的行为收集工具。
Tekton Chains Chains 是一个 Kubernetes CRD 控制器，用于管理 Tekton 中的供应链安全。Chains 使 Tekton 能够在持续交付中，捕获 PipelineRun 和 TaskRun 运行的元数据用于安全审计，实现二进制溯源和可验证的构建，成为构成保证供应链安全的基础设施的一部分。
当前 Chains（v0.9.0）提供如下功能：
使用用户提供的加密密钥，将 TaskRun 的结果（包含 TaskRun 本身和 OCI 镜像）镜像进行签名 支持类似 in-toto 的证明格式 使用多种加密密钥类型和服务（x509、KMS）进行签名 签名的存储支持多种实现 接下来的 Demo，我们还是继续使用之前在 Tekton Pipeline 实战 用的 Java 项目 tekton-test，在代码中同样还包含了 Pipeline 和 Task 的定义。</description></item><item><title>撸一个 Alfred Workflow</title><link>https://atbug.com/create-an-alfred-workflow/</link><pubDate>Tue, 03 May 2022 21:38:22 +0800</pubDate><guid>https://atbug.com/create-an-alfred-workflow/</guid><description>本人算是个 Alfred 的重度依赖者了（上图是去年换了新电脑后的使用数据），Alfred 也算 Mac 上的第一款付费软件，买的 mega 版。安装的 workflow 估计有几十个，不过常用的估计有十几个吧。
用了几年，一直都秉承着能不造轮子就不造的原则，基本也都能找到想要的 workflow。为什么突然要写个 workflow，这要从 macOS 12.3 移除了 Python2 说起。自从升级了 12.3，好多 workflow 都无法正常工作了，其中就有常用的 Safari 历史搜索。苦等了一段时间，也不见作者更新，不得已自己下场来写。正好五一假期有时间，顺便远离工作放空一下。
搜索后看到了如何使用 Go 编写 workflow 这篇文章，就准备用 Go 来写。然后就有了我的第一个 workflow：Safari Toolkit。
现在是实现了原 workflow 的所有功能，对 Safari 的访问历史进行搜索：基于 URL 和 Title，然后选择搜索结果在 Safari 中打开。</description></item><item><title>使用 Rclone 同步文件到 Google Drive</title><link>https://atbug.com/sync-local-file-to-google-drive-with-rclone/</link><pubDate>Sun, 01 May 2022 21:10:18 +0800</pubDate><guid>https://atbug.com/sync-local-file-to-google-drive-with-rclone/</guid><description>目前我使用 Calibre 软件来管理电子书，电子书备份在 iCloud 中。但是每次推送电子书到 Kindle 都要打开电脑，感觉不够方便。如果能在手机端就可以操作岂不是更香。
遂将目标瞄向了 calibre-web，这个类似 calibre 的 web 端。可以浏览、下载 Calibre 数据库中保存的书籍，也有 Docker 镜像提供。我有一个 x86 的虚拟机用来补全 m1 的短板，可以用来运行 calibre-web 容器。
接下来就是数据的持久化了。众所周知 iCloud 一如 Apple 的系统一样封闭，无法在 Linux 上使用。想起 Google Drive 有 15GB 的免（bai）费（piao）空间，可以用来备份电子书。记得 Rclone 可以支持 Google Drive 的同步（网络问题不在讨论范围），虽然只有单向同步但正好满足我的需求。</description></item><item><title>Git代码仓库无损整理：目录拆分和重命名</title><link>https://atbug.com/split-git-repository-without-commit-lost/</link><pubDate>Sat, 30 Apr 2022 20:02:48 +0800</pubDate><guid>https://atbug.com/split-git-repository-without-commit-lost/</guid><description>这篇文章主要使用&amp;quot;核弹级&amp;quot;命令git-filter-branch, 对代码仓库进行整理. 这里的整理包括子目录重命名, 子目录独立为新项目(保留或者不保留原来的结构)等. 整理之后, 原来的提交信息都会完好的保存下来, 方便追溯.
重要的事情说三遍：
强烈建议第一次使用的时候做好备份, 切记!!! 强烈建议第一次使用的时候做好备份, 切记!!! 强烈建议第一次使用的时候做好备份, 切记!!!
每处理完一个分支, 都需要执行如下操作, 并重新克隆项目并处理下一个分支
git remote remove origin git remote add origin xxxxxx.git git push -u origin BRANCH-NAME git push --tags 将子文件夹独立为新的仓库 git filter-branch --prune-empty --tag-name-filter cat --subdirectory-filter FOLDER-NAME BRANCH-NAME 演示:</description></item><item><title>开放服务网格 Open Service Mesh 如何开放？</title><link>https://atbug.com/how-open-presented-in-open-service-mesh/</link><pubDate>Wed, 13 Apr 2022 06:50:56 +0800</pubDate><guid>https://atbug.com/how-open-presented-in-open-service-mesh/</guid><description>TL;DR 本文从服务网格发展现状、到 Open Service Mesh 源码，分析开放服务网格中的开放是什么以及如何开放。笔者总结其开放体现在以下几点：
资源提供者（Provider）接口和资源的重新封装：通过资源提供者接口抽象计算平台的资源，并封装成平台、代理无关的结构化类型，并由统一的接口 MeshCataloger 对外提供访问。虽然目前只有 Kubernetes 相关资源的 Provider 实现，但是通过抽象出的接口，可以兼容其他平台，比如虚拟机、物理机。 服务网格能力接口：对服务网格能力的抽象，定义服务网格的基础功能集。 代理控制面接口：这一层与反向代理，也就是 sidecar 的实现相关。实际上是反向代理所提供的接口，通过对接口的实现，加上 MeshCataloger 对资源的访问，生成并下发代理所需的配置。 背景 服务网格是什么 服务网格是在 2017年由 Buoyant 的 Willian Morgan 在 What’s a service mesh? And why do I need one? 给出了解释。
服务网格是处理服务间网络通信的基础设施组件，旨在从平台层面提供可观性、安全以及可靠性特性，以进程外的方式提供原本由部分应用层逻辑承载的基础能力，真正实现与业务逻辑的分离。典型的实现是与应用程序一起部署的可扩展网络代理（通常称为 sidecar 模型），来代理服务间的网络通信，也是服务网格特性的接入点。这些代理就组成了服务网格的数据平面，并由控制平面进行统一的管理。</description></item><item><title>如何在 Kubernetes Pod 内进行网络抓包</title><link>https://atbug.com/how-to-sniff-packet-in-kubernetes-pod/</link><pubDate>Sun, 03 Apr 2022 21:22:30 +0800</pubDate><guid>https://atbug.com/how-to-sniff-packet-in-kubernetes-pod/</guid><description>使用 Kubernetes 时，经常会遇到一些棘手的网络问题需要对 Pod 内的流量进行抓包分析。然而所使用的镜像一般不会带有 tcpdump 命令，过去常用的做法简单直接暴力：登录到节点所在节点，使用 root 账号进入容器，然后安装 tcpdump。抓到的包有时还需要拉回本地，使用 Wireshark 进行分析。而且整个过程非常繁琐，跨越几个环境。
正好前几天也做了一次抓包问题排查，这次就介绍一下快速进行网络抓包的几种方法。
TL;DR 几种方法各有优缺点，且都不建议在生产环境使用。假如必须使用，个人倾向于 kubectl debug 临时容器的方案，但这个方案也有不足。
使用额外容器：这种方案为了 Pod 添加一个额外的容器，使用了静态编译的 tcpdump 进行抓取，借助了多容器共享网络空间的特性，适合 distroless 容器。缺点是需要修改原来的 Pod，调式容器重启会引起 Pod 重启。 kubectl plugin ksniff：一个 kubectl 插件。支持特权和非特权容器，可以将捕获内容重定向到 wireshark 或者 tshark。非特权容器的实现会稍微复杂。 kubectl debug 临时容器：该方案对于 distroless 容器有很好的支持，临时容器退出后也不会导致 Pod 重启。缺点是 1.</description></item><item><title>《BeyondCorp Part III: The Access Proxy》解读</title><link>https://atbug.com/the-access-proxy-notes/</link><pubDate>Sun, 20 Mar 2022 21:58:52 +0800</pubDate><guid>https://atbug.com/the-access-proxy-notes/</guid><description>这是一篇发表于 2016 年的论文，是 BeyondCorp 系列的第三篇。虽然过去多年，但是在流量的精细化控制方面仍然值得学习。
BeyondCorp 是 Google 对零信任模型的实现。它建立在 Google 十年经验的基础上，并结合了社区的想法和最佳实践。通过将访问控制从网络边界转移到个人用户，BeyondCorp 几乎可以在任何位置进行安全工作，而无需传统的 VPN。
The Access Proxy 详细描述了 BeyondCorp 前端基础设施的实现，介绍了在实现 Access Proxy（以下简称 AP）时的挑战与实践教训，并给出了最佳实践。
AP 是在负载均衡反向代理的基础上增加了安全的处理，增加了验证、授权、集中日志记录，以及自服务配置。整个设计上，充分利用了对流量的精细化控制。
认证 在传统代理的 TLS 外，引入认证功能。该功能的核心之一是请求方识别，验证用户身份。并支持一系列的身份验证选项。
此外，认证还需要对后端服务“隐藏”身份验证凭证，也不会对后端服务自身的验证流程。
多平台的认证 使用了一致的、不可复制的、唯一设备标识符和用于跟踪设备最新状态的存储，实现对设备的信任取代对网络的信任。
对于不同的平台，充分利用各平台标识。比如 PC 和笔记本操作系统的设备证书，移动设备系统的设备标识。
授权 授权的引入与认证功能一样，将基础功能与后端服务分离，解放了后端服务。但是由于复杂度限制，只能执行粗粒度的策略，还需与后端服务的细粒度策略结合使用。
此外，还需解决代理与后端的双向认证问题，毕竟从ZTN（Zero Trust Network 零信任网络）的角度看内部网络同样不可信。通过双向认证确保数据（元数据，通常是请求头）不可欺骗的同时，还有额外的收益：通过增加元数据轻松地扩展 AP 的能力，新增的元数据也被后端接受并使用。</description></item><item><title>在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer（下）- BGP</title><link>https://atbug.com/load-balancer-service-with-metallb-bgp-mode/</link><pubDate>Mon, 07 Mar 2022 07:37:16 +0800</pubDate><guid>https://atbug.com/load-balancer-service-with-metallb-bgp-mode/</guid><description>在上一篇《在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer（上）- Layer2》中，我们使用 MetalLB 的 Layer2 模式作为 LoadBalancer 的实现，将 Kubernetes 集群中的服务暴露到集群外。
还记得我们在 Configmap 中为 MetalLB 分配的 IP 地址池么？
apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: default protocol: layer2 addresses: - 192.</description></item><item><title>在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer（上）- Layer2</title><link>https://atbug.com/load-balancer-service-with-metallb/</link><pubDate>Thu, 03 Mar 2022 01:12:17 +0800</pubDate><guid>https://atbug.com/load-balancer-service-with-metallb/</guid><description>这是系列文章的上篇，下篇《在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer（下）- BGP》。
TL;DR 网络方面的知识又多又杂，很多又是系统内核的部分。原本自己不是做网络方面的，系统内核知识也薄弱。但恰恰是这些陌生的内容满满的诱惑，加上现在的工作跟网络关联更多了，逮住机会就学习下。
这篇以 Kubernetes LoadBalancer 为起点，使用 MetalLB 去实现集群的负载均衡器，在探究其工作原理的同时了解一些网络的知识。
由于 MetalLB 的内容有点多，一步步来，今天这篇仅介绍其中简单又容易理解的部分，不出意外还会有下篇（太复杂，等我搞明白先 :D）。
LoadBalancer 类型 Service 由于 Kubernets 中 Pod 的 IP 地址不固定，重启后 IP 会发生变化，无法作为通信的地址。Kubernets 提供了 Service 来解决这个问题，对外暴露。
Kubernetes 为一组 Pod 提供相同的 DNS 名和虚拟 IP，同时还提供了负载均衡的能力。这里 Pod 的分组通过给 Pod 打标签（Label ）来完成，定义 Service 时会声明标签选择器（selector）将 Service 与 这组 Pod 关联起来。</description></item><item><title>使用 Cilium 增强 Kubernetes 网络安全</title><link>https://atbug.com/enhance-kubernetes-network-security-with-cilium/</link><pubDate>Sun, 13 Feb 2022 05:03:48 +0800</pubDate><guid>https://atbug.com/enhance-kubernetes-network-security-with-cilium/</guid><description>TL;DR 在本篇，我们分别使用了 Kubernetes 原生的网络策略和 Cilium 的网络策略实现了 Pod 网络层面的隔离。不同的是，前者只提供了基于 L3/4 的网络策略；后者支持 L3/4、L7 的网络策略。
通过网络策略来提升网络安全，可以极大降低了实现和维护的成本，同时对系统几乎没有影响。
尤其是基于 eBPF 技术的 Cilium，解决了内核扩展性不足的问题，从内核层面为工作负载提供安全可靠、可观测的网络连接。
目录 TL;DR 目录 背景 示例应用 Kubernetes 网络策略 测试 思考 Cilium 网络策略 Cilium 简介 测试 背景 为什么说 Kubernetes 网络存在安全隐患？集群中的 Pod 默认是未隔离的，也就是 Pod 之间的网络是互通的，可以互相通信的。
这里就会有问题，比如由于数据敏感服务 B 只允许特定的服务 A 才能访问，而服务 C 无法访问 B。要禁止服务 C 对服务 B 的访问，可以有几种方案：</description></item><item><title>探秘 k8e：极简 Kubernetes 发行版</title><link>https://atbug.com/explore-simple-kubernetes-distribution/</link><pubDate>Mon, 07 Feb 2022 01:00:29 +0800</pubDate><guid>https://atbug.com/explore-simple-kubernetes-distribution/</guid><description>TL;DR 本文介绍并安装体验了极简 Kubernetes 发行版，也顺便分析学习下编译的流程。
背景 k8e 本意为 kuber easy，是一个 Kubernetes 的极简发行版，意图让云原生落地部署 Kubernetes 更轻松。k8e 是基于另一个发行版 k3s ，经过裁剪（去掉了 Edge/IoT 相关功能、traefik等）、扩展（加入 ingress、sidecar 实现、cilium等）而来。
k8e 具有以下特性：
单二进制文件，集成了 k8s 的各种组件、containerd、runc、kubectl、nerdctl 等 使用 cilium 作为 cni 的实现，方便 eBPF 的快速落地 支持基于 Pipy 的 ingress、sidecar proxy，实现应用流量一站式管理 只维护一个 k8s 版本，目前是 1.</description></item><item><title>使用 sdkman 在 M1 Mac 上 安装 graalvm jdk</title><link>https://atbug.com/install-graalvm-on-m1-mac-with-sdkman/</link><pubDate>Thu, 27 Jan 2022 11:07:34 +0800</pubDate><guid>https://atbug.com/install-graalvm-on-m1-mac-with-sdkman/</guid><description>&lt;p>SDKMAN 是一款管理多版本 SDK 的工具，可以实现在多个版本间的快速切换。安装和使用非常简单：&lt;/p></description></item><item><title>追踪 Kubernetes 中的网络流量</title><link>https://atbug.com/tracing-path-of-kubernetes-network-packets/</link><pubDate>Sat, 22 Jan 2022 10:15:31 +0800</pubDate><guid>https://atbug.com/tracing-path-of-kubernetes-network-packets/</guid><description>译者注：
这篇文章很全面的罗列出了 Kubernetes 中涉及的网络知识，从 Linux 内核的网络内容，到容器、Kubernetes，一一进行了详细的说明。
​文章篇幅有点长，不得不说，网络是很复杂很麻烦的一层，但恰恰这层多年来变化不大。希望翻译的内容对大家能有所帮助，有误的地方，也欢迎大家指正。
本文翻译获得 Learnk8s 的授权，原文 Tracing the path of network traffic in Kubernetes 作者 Kristijan Mitevski。
TL;DR： 本文将代理了解 Kubernetes 集群内外的数据流转。从最初的 Web 请求开始，一直到托管应用程序的容器。
目录 目录 Kubernetes 网络要求 Linux 网络命名空间如何在 pod 中工作 Pause 容器创建 Pod 中的网络命名空间 为 Pod 分配了 IP 地址 检查集群中 pod 到 pod 的流量 Pod 命名空间连接到以太网桥接器 跟踪同一节点上 pod 间的流量 跟踪不同节点上 pod 间的通信 位运算的工作原理 容器网络接口 - CNI 检查 pod 到服务的流量 使用 Netfilter 和 Iptables 拦截和重写流量 检查服务的响应 回顾 Kubernetes 网络要求 在深入了解 Kubernetes 中的数据流转之前，让我们先澄清下 Kubernetes 网络的要求。</description></item><item><title>Kubernetes HPA 基于 Prometheus 自定义指标的可控弹性伸缩</title><link>https://atbug.com/kubernetes-pod-autoscale-on-prometheus-metrics/</link><pubDate>Tue, 18 Jan 2022 12:03:59 +0800</pubDate><guid>https://atbug.com/kubernetes-pod-autoscale-on-prometheus-metrics/</guid><description>在《Kubernetes 的自动伸缩你用对了吗？》 一文中详细说明了如何使用 Kubernetes 的自动伸缩。在 Kubernetes 中弹性伸缩主要有三种：HPA、VPA、CA。本文不再详细说明，有兴趣的可以看那篇文章。这里主要来说下 Pod 水平缩放 HPA。
随着 Kubernetes v1.23 的发布，HPA 的 API 来到了稳定版 autoscaling/v2：
基于自定义指标的伸缩 基于多项指标的伸缩 可配置的伸缩行为 从最初的 v1 版本 HPA 只支持 CPU、内存利用率的伸缩，到后来的自定义指标、聚合层 API 的支持，到了 v1.18 版本又加入了配置伸缩行为的支持，HPA 也越来越好用、可靠。
依靠 CPU 或者内存指标的扩容并非使用所有系统，看起来也没那么可靠。对大部分的 web 后端系统来说，基于 RPS（每秒请求数）的弹性伸缩来处理突发的流量则会更加靠谱。
Prometheus 也是当下流行开源监控系统，通过 Prometheus 可以获取到系统的实时流量负载指标，今天我们就来尝试下基于 Prometheus 的自定义指标进行弹性伸缩。</description></item><item><title>eBPF 和 Wasm：探索服务网格数据平面的未来</title><link>https://atbug.com/ebpf-wasm-service-mesh/</link><pubDate>Tue, 11 Jan 2022 10:40:56 +0800</pubDate><guid>https://atbug.com/ebpf-wasm-service-mesh/</guid><description>本文翻译自 Vivian Hu 的 《eBPF and Wasm: Exploring the Future of the Service Mesh Data Plane》。
在 2021 年 12 月 2 日，Cilium 项目宣布了 Cilium Service Mesh 项目的测试版。在 2020 年 8 月 Google Cloud 宣布基于 eBPF 的 Google Kubernetes 服务（GKS）的数据平面 V2 的一年后，Cilium Service Mesh 带来了 “无边车服务网格”（sidecarless service mesh）的想法。它扩展了 Cilium eBPF 产品来处理服务网格中的大部分边车代理功能，包括 7 层路由和负载均衡、TLS 终止、访问策略、健康检查、日志和跟踪，以及内置的 Kubernetes Ingress。</description></item><item><title>快速搭建实验环境：使用 Terraform 部署 Proxmox 虚拟机</title><link>https://atbug.com/deploy-vm-on-proxmox-with-terraform/</link><pubDate>Mon, 03 Jan 2022 12:07:35 +0800</pubDate><guid>https://atbug.com/deploy-vm-on-proxmox-with-terraform/</guid><description>自从用上 m1 的电脑，本地开发环境偶尔会遇到兼容性的问题。比如之前尝试用 Colima 在虚拟机中运行容器运行时和 Kubernetes，其实际使用的还是 aarch64 虚拟机，实际使用还是会有些差异。
手上有台之前用的黑苹果小主机，吃灰几个月了，实属浪费。正好元旦假期，有时间折腾一下。
CPU: Intel 8700 6C12T MEM: 64G DDR4 DISK: 1T SSD 折腾的目的：
将平台虚拟化 提供多套实验环境 快速创建销毁实验环境 体验基础设施即代码 IaaS 主要用到的工具：
虚拟化工具 Proxmox VE Terraform：开源的基础设施即代码工具 terraform-provider-proxmox：Terraform Proxmox Provider，通过 Proxmox VE 的 REST API 在创建虚拟机。 安装 Proxmox 虚拟化工具 从官网 下载 ISO 镜像，写入到 U 盘中。macOS上推荐使用 balenaEtcher 写盘。</description></item><item><title>再见，2021</title><link>https://atbug.com/farewell-2021/</link><pubDate>Thu, 30 Dec 2021 23:48:32 +0800</pubDate><guid>https://atbug.com/farewell-2021/</guid><description>没错，这是一篇 2021 年的盘点。
2021 年马上结束，翻看幕布，看到了年初给自己定下的目标。记性不好，好像把这个事情给忘记了。
工作 工作这么多年，已然跨过的 35 岁这个坎。没有想象中的困难，唯有一路的坚持。
职业生涯 自己一直坚持走技术路线，不愿走上管理这条路。就像有些事情，不是不会也不是不能，而是不愿。可能是自己性格使然，也唯有技术一条路可走，所以只有坚持，尤其是自己也喜欢搞搞技术。
过去的自己埋头写代码、学习，鲜有与外界的沟通。正好借着去年工作的事情走出去，参与社区。见识到了技术人的简单直接，也结交到了志同道合的朋友。
正职 年初太太跟我说，本命年少折腾，工作稳定点就行，不求其他。但还是在年中的时候换了工作，感谢上家公司的各种经历让自己成长很多，与志同道合的人一起做些有趣的事情。
有幸加入到现在的公司，认识到一群更简单的人：简单的上司、简单的同事，简单地做着不简单的事情。短短几个月，自己学习了很多。这也正是我想要的：有机会不断地学习和挑战。
现在的公司是远程办公的模式，自己掌控工作的时间，与家人相处的时间也多了（虽然很多时间也是在工作）。刚开始还会有些新鲜，慢慢地工作和生活的界限越来越模糊。不过既然意识到问题，可以慢慢的调整。
学习 书看的不多，仅仅几本。但收益良多，比如《微习惯》、《深度工作》、《飞轮效应》还是比较推荐。准备有时间重读《深度工作》，配合现在的远程工作感觉会更适合实施。
技术方面则是开拓知识面，关注一些技术博客、网站。尤其是 3 月底开始决定好好更新公众号。其实之前断断续续有在写博客，平时工作也都记笔记的习惯。本来想的好，将笔记好好整理一下发在公众号上。
但实际上手才会发现，有时写一篇文章不管是翻译，还是技术探索类的都会用上几个小时的时间。比如画个复杂点的图，也好耗费几十分钟一个小时。几个小时的成块时间，对于工作的人来说太过宝贵。
到今天应该有发过 50 多篇（其中 8、9 月份刚接触新工作，写的确实少），也算是没有打脸，如今也有了1000 多的关注。虽然写了这么多，但是回头看下，有点繁杂，不够系统。
还有顺手完成了 CKA 的考试。
生活 我将生活放到了最后，不是说不重视生活，而是希望生活平平淡淡就好。
家人在工作和生活上总是给予我莫大的支持，感谢他们的付出。
电子产品虽然也有在玩，但是玩的少了。基本就是买买买、卖卖卖的节奏，没有太多的时间投入在上面。
也因为换工作的间隙，才有了北疆之行，有幸领略了北疆的风光。希望有机会再走伊昭和独库。
临近年底，也下定决心了结一件心事，半年后再看。
总结 想想过去一年发生的种种，有事与愿违、有意料之外、还有柳暗花明。
这让我想起阿甘正传里那一段：人生就像一盒各式各样的巧克力，你永远不知道下一块将会是哪种。</description></item><item><title>Colima：MacOS 上的极简容器运行时和 Kubernetes（支持 m1）</title><link>https://atbug.com/containers-runtime-on-macos-with-colima/</link><pubDate>Sun, 26 Dec 2021 12:31:16 +0800</pubDate><guid>https://atbug.com/containers-runtime-on-macos-with-colima/</guid><description>Colima 是一个以最小化设置来在MacOS上运行容器运行时和 Kubernetes 的工具。支持 m1（文末讨论），同样也支持 Linux。
Colima 的名字取自 Container on Lima。Lima 是一个虚拟机工具，可以实现自动的文件共享、端口转发以及 containerd。
Colima 实际上是通过 Lima 启动了名为 colima 的虚拟机，使用虚拟机中的 containerd 作为容器运行时。
使用 Colima 的使用很简单，执行下面的命令就可以创建虚拟机，默认是 Docker 的运行时。
初次运行需要下载虚拟机镜像创建虚拟机，耗时因网络情况有所差异。之后，启动虚拟机就只需要 30s 左右的时间。
colima start INFO[0000] starting colima INFO[0000] creating and starting ... context=vm INFO[0119] provisioning .</description></item><item><title>OpenFaaS - 以自己的方式运行容器化函数</title><link>https://atbug.com/openfaas-case-study-zh/</link><pubDate>Fri, 17 Dec 2021 09:13:59 +0800</pubDate><guid>https://atbug.com/openfaas-case-study-zh/</guid><description>译者注： 本文篇幅较长，有助于了解 FaaS 和 OpenFaaS。作者分别从开发人员和运维人员的视角来了解 OpenFaaS，对了解新的技术是个很好的方式。
本文翻译自 Ivan Velichko 的 OpenFaaS - Run Containerized Functions On Your Own Terms。
长期以来，无服务器（serverless） 对我来说无非就是 AWS Lambda 的代名词。Lambda 提供了一种方便的途径，可以将任意代码附加到平台事件（云实例的状态变更、DynamoDB 记录的更新或新的 SNS 消息）中。但是，我时不时会想到某个逻辑，但其又没大到足以有自己的服务，同时有不适合任何现有服务的范围。因此，我经常将其放入函数中，以便日后使用 CLI 命令或者 HTTP 调用来调用它。
几年前，我来开了 AWS，自那以后，我一直怀念部署无服务器功能的便利性。因此，当我得知 OpenFaaS 项目时惊喜万分。它将在 Kubernetes 集群上部署函数变得简单，甚至仅需要 Containerd 就可以部署到虚拟机上。</description></item><item><title>策略即代码：为了 OpenPolicyAgent 学 Rego？试试 Javascript</title><link>https://atbug.com/policy-as-code-with-pipy/</link><pubDate>Wed, 08 Dec 2021 07:49:37 +0800</pubDate><guid>https://atbug.com/policy-as-code-with-pipy/</guid><description>距离上个版本 用 Pipy 实现 OPA，已经过去快半年了。当初使用Pipy 实现了可信镜像仓库的检查，那时的版本实现起来会稍微复杂，从策略仓库到证书创建到Admission Webhook 的创建都需要大量的人工操作，配置和逻辑也还是耦合在一起。
这个版本安装和使用起来会更加简单。
当初我用“不务正业”来形容 Pipy 实现准入控制，等看完这篇文章，欢迎留言说说你的看法。
架构 还是继续上次的场景，在 Pod 创建时对 Pod 使用的镜像所在仓库进行检查，以及检查镜像的 tag 是否合法。
这里借助 Pipy Repo 的能力，将代表策略的脚本和配置交由 Repo 进行管理；Pipy 实例实时从 Pipy Repo 同步策略，并进行动态加载。
同时 Pipy Repo 对外提供 REST API 来管理策略，对策略的修改更容易。也方便与企业现有管理后台进行对接。
下面就开始部署验证，这里所使用的所有代码都已提交到 GitHub 仓库：https://github.com/flomesh-io/demo-policy-as-code。</description></item><item><title>沙盒化容器：是容器还是虚拟机</title><link>https://atbug.com/sandboxed-container/</link><pubDate>Tue, 07 Dec 2021 07:55:16 +0800</pubDate><guid>https://atbug.com/sandboxed-container/</guid><description>随着 IT 技术的发展，AI、区块链和大数据等技术提升了对应用毫秒级扩展的需求，开发人员也面临着的功能快速推出的压力。混合云是新常态，数字化转型是保持竞争力的必要条件，虚拟化成为这些挑战的基本技术。
在虚拟化的世界，有两个词耳熟能详：虚拟机和容器。前者是对硬件的虚拟化，后者则更像是操作系统的虚拟化。两者都提供了沙箱的能力：虚拟机通过硬件级抽象提供，而容器则使用公共内核提供进程级的隔离。有很多人将容器看成是“轻量化的虚拟机”，通常情况下我们认为容器是安全的，那到底是不是跟我们想象的一样？
容器：轻量化的虚拟机？ 容器是打包、共享和部署应用的现代化方式，帮助企业实现快速、标准、灵活地完成服务交互。容器化是建立在 Linux 的命名空间（namespace）和控制组（cgroup） 的设计之上。
命名空间创建一个几乎隔离的用户空间，并为应用提供专用的系统资源，如文件系统、网络堆栈、进程ID和用户ID。随着用户命名空间的引入，内核版本 3.8 提供了对容器功能的支持：Mount（mnt）、进程 ID（pid）、Network（net）、进程间通信（ipc）、UTS、用户 ID（user）6 个命名空间（如今已达 8 个，后续加入了 cgroup 和 time 命名空间）。
cgroup 则实施对应用的资源限制、优先级、记账和控制。cgroup可以控制 CPU、内存、设备和网络等资源。
同时使用 namespace 和 cgroup 使得我们可以在一台主机上安全地运行多个应用，并且每个应用都位于隔离的环境中。
虚拟机提供更强大的隔离 虽然容器很棒，足够轻量级。但通过上面的描述，同一个主机上的多个容器其实是共享同一个操作系统内核，只是做到了操作系统级的虚拟化。虽然命名空间提供了高度的隔离，但仍然有容器可以访问的资源，这些资源并没有提供命名空间。这些资源是主机上所有容器共有的，比如内核 Keyring、/proc、系统时间、内核模块、硬件。
我们都知道没有 100% 安全的软件，容器化的应用也一样，从应用源码到依赖库到容器 base 镜像，甚至容器引擎本身都可能存在安全漏洞。发生容器逃逸的风险远高于虚拟机，黑客可以利用这些逃逸漏洞，操作容器的外部资源也就是宿主机上的资源。除了漏洞，有时使用的不当也会带来安全风险，比如为容器分配了过高的权限（CAP_SYS_ADMIN 功能、特权权限），都可能导致容器逃逸。
而虚拟机依靠硬件级的虚拟化，实现的硬件隔离比命名空间隔离提供了更强大的安全边界。与容器相比，虚拟机提供了更高程度的隔离，只因其有自己的内核。
由此可见，容器并不是真正的“沙盒”，也并不是轻量化的虚拟机。有没有可能为容器增加一个更安全的边界，尽可能的与主机操作系统隔离，做到类似虚拟机的强隔离，使其成为真正的“沙盒”？</description></item><item><title>从 Docker 的信号机制看容器的优雅停止</title><link>https://atbug.com/gracefully-stopping-docker-containers-with-correct-command/</link><pubDate>Mon, 29 Nov 2021 07:30:43 +0800</pubDate><guid>https://atbug.com/gracefully-stopping-docker-containers-with-correct-command/</guid><description>此文是前段时间笔记的整理，之前自己对这方面的关注不够，因此做下记录。
有太多的文章介绍如何运行容器，然而如何停止容器的文章相对少很多。
根据运行的应用类型，应用的停止过程非常重要。如果应用要写文件，停止前要保证正确刷新数据并关闭文件；如果是 HTTP 服务，要确保停止前处理所有未完成的请求。
信号 信号是 Linux 内核与进程以及进程间通信的一种方式。针对每个信号进程都有个默认的动作，不过进程可以通过定义信号处理程序来覆盖默认的动作，除了 SIGSTOP 和 SIGKILL。二者都不能被捕获或重写，前者用来将进程暂停在当前状态，而后者则是从内核层面立即杀掉进程。
有两个比较重要的进程 SIGTERM 和 SIGKILL。SIGTERM 是优雅地关闭命令，SIGKILL 则是暴力的关闭命令。比如 Docker，容器会先收到 SIGTERM 信号，10s 后会收到 SIGKILL 信号。
还有很多其他的信号，只是限定于特定的上下文。
中断 硬件的中断就像操作系统的信号。通常发生在硬件想要向操作系统注册事件时。操作系统必须立即停止运行，并处理中断。
比较常见的中断例子就是键盘中断，比如按下 ctrl+z 或者 ctrl+c。Linux 将其分别转换成 SIGTSTP 和 SIGINT。硬件中断过去通常用来处理键盘和鼠标输入，但如今被用作操作系统软件驱动层面的信号轮训。
Docker 前面说了这么多终于来到 Docker，容器的独特之处在于通常只运行一个进程。即使是单进程，容器内 PID 为 1 的进程也具有 init 系统的特殊规则和职责。</description></item><item><title>Kubernetes Deployment 的故障排查可视化指南（2021 中文版）</title><link>https://atbug.com/troubleshooting-kubernetes-deployment-zh-v2/</link><pubDate>Sat, 20 Nov 2021 18:29:19 +0800</pubDate><guid>https://atbug.com/troubleshooting-kubernetes-deployment-zh-v2/</guid><description>将应用部署到 Kubernetes 时通常会使用 Deployment、Service、Ingress，整个应用从部署到正常运行，经历的流程很长。从 kubectl apply YAML 文件，经过 apiserver、controller manager、scheduler、kubelet、以及 CRI、CNI 等众多组件的协同工作。
漫长的“行程”，Pod 也经历各种正常和不正常的状态变化，即使正常运行也会出现服务无法访问的问题。对于刚开始在 Kubernetes 平台开展工作的同学来说，故障的排查确实棘手。之前工作的时候，经常要协助排查各种问题。去年在 Learnk8s 上看到了关于 Deployment 故障排查的视图，我还参考做了当时整个平台的故障排查视图，包括了从项目源码、CICD 流水线、部署整个流程的故障排查参考。
现在 Learnk8s 的 Deployment 排查指南更新了，也有了中文版本。
年中翻译 Learnk8s 的文章《Kubernetes 的自动伸缩你用对了吗？》 时，与 Daniele Polencic 沟通时被问及是否能翻译故障排查的可视化指南。
年中的时候就翻译完了，今天电报上被告知文章 A visual guide on troubleshooting Kubernetes deployments已更新，排查视图较上一版有了部分的调整。</description></item><item><title>Monterey 12.0.1 上的 bug</title><link>https://atbug.com/bug-with-m1-pro-and-monterey/</link><pubDate>Thu, 18 Nov 2021 08:54:44 +0800</pubDate><guid>https://atbug.com/bug-with-m1-pro-and-monterey/</guid><description>最近换上了 MacBook Pro 2021，也慢慢将工作转到新的电脑上。结束了一年多的黑白配，之前工作主力机是我的黑苹果，配置以及 OpenCore 的引导放在这里了。
为了稳定性，系统一直停留在了 10.15.4。新的电脑拿到手就是 12.0.1，之前也就在我另一台 2016 款的 macbook pro 上用过几个周的 12.0.0。
新的系统加上新的架构，不免有有些 bug，今天就说下我所遇到的两个比较棘手的。
1. 安全相关 EXC_BAD_ACCESS (SIGKILL (Code Signature Invalid))
公司的核心产品是用 c++ 开发的，编译之后我都习惯性的放到 /usr/local/bin 目录中。在新系统中，就出现了上面的错误（从控制台获取），运行的时候进程直接被 kill。
网上查了下，说与 kernel cache 有关，重启可解决。
This was caused by kernel caching of previously signed binaries and my replacing those binaries with newly compiled binaries which weren&amp;rsquo;t part of a signed package.</description></item><item><title>Kubernetes 上调试 distroless 容器</title><link>https://atbug.com/debug-distroless-container-on-kubernetes/</link><pubDate>Wed, 03 Nov 2021 07:40:40 +0800</pubDate><guid>https://atbug.com/debug-distroless-container-on-kubernetes/</guid><description>TL;DR 本文内容：
介绍 distroless 镜像、作用以及简单的使用 如何针对 distroless 容器的进行调试 临时容器(v.1.18+)的使用 Distroless 镜像 Distroless 容器，顾名思义使用 Distroless 镜像作为基础镜像运行的容器。
&amp;ldquo;Distroless&amp;rdquo; 镜像只包含了你的应用程序以及其运行时所需要的依赖。不包含你能在标准 Linxu 发行版里的可以找到的包管理器、shells 或者其他程序。
GoogleContainerTools/distroless 针对不同语言提供了 distroless 镜像：
gcr.io/distroless/static-debian11 gcr.io/distroless/base-debian11 gcr.io/distroless/java-debian11 gcr.io/distroless/cc-debian11 gcr.io/distroless/nodejs-debian11 gcr.io/distroless/python3-debian11 Distroless 镜像有什么用？ 那些可能是构建镜像时需要的，但大部分并不是运行时需要的。这也是为什么上篇文章介绍 Buildpacks 时说的一个 builder 的 stack 镜像包含构建时基础镜像和运行时基础镜像，这样可以做到镜像的最小化。
其实控制体积并不是 distroless 镜像的主要作用。将运行时容器中的内容限制为应用程序所需的依赖，此外不应该安装任何东西。这种方式可能极大的提升容器的安全性，也是 distroless 镜像的最重要作用。</description></item><item><title>无需 Dockerfile 的镜像构建：BuildPack vs Dockerfile</title><link>https://atbug.com/build-docker-image-without-dockerfile/</link><pubDate>Fri, 29 Oct 2021 07:36:43 +0800</pubDate><guid>https://atbug.com/build-docker-image-without-dockerfile/</guid><description>过去的工作中，我们使用微服务、容器化以及服务编排构建了技术平台。为了提升开发团队的研发效率，我们同时还提供了 CICD 平台，用来将代码快速的部署到 Openshift（企业级的 Kubernetes） 集群。
部署的第一步就是应用程序的容器化，持续集成的交付物从以往的 jar 包、webpack 等变成了容器镜像。容器化将软件代码和所需的所有组件（库、框架、运行环境）打包到一起，进而可以在任何环境任何基础架构上一致地运行，并与其他应用“隔离”。
我们的代码需要从源码到编译到最终可运行的镜像，甚至部署，这一切在 CICD 的流水线中完成。最初，我们在每个代码仓库中都加入了三个文件，也通过项目生成器（类似 Spring Initializer）在新项目中注入：
Jenkinsfile.groovy：用来定义 Jenkins 的 Pipeline，针对不同的语言还会有多种版本 Manifest YAML：用于定义 Kubernetes 资源，也就是工作负载及其运行的相关描述 Dockerfile：用于构建对象 这个三个文件也需要在工作中不断的演进，起初项目较少（十几个）的时候我们基础团队还可以去各个代码仓库去维护升级。随着项目爆发式的增长，维护的成本越来越高。我们对 CICD 平台进行了迭代，将“Jenkinsfile.groovy”和 “manifest YAML”从项目中移出，变更较少的 Dockerfile 就保留了下来。
随着平台的演进，我们需要考虑将这唯一的“钉子户” Dockerfile 与代码解耦，必要的时候也需要对 Dockerfile 进行升级。因此调研了一下 buildpacks，就有了今天的这篇文章。
什么是 Dockerfile Docker 通过读取 Dockerfile 中的说明自动构建镜像。Dockerfile 是一个文本文件，包含了由 Docker 可以执行用于构建镜像的指令。我们拿之前用于测试 Tekton 的 Java 项目的 Dockerfile 为例：</description></item><item><title>低复杂度 - 服务网格的下一站</title><link>https://atbug.com/service-mesh-unnecessary-complexity/</link><pubDate>Fri, 15 Oct 2021 07:58:25 +0800</pubDate><guid>https://atbug.com/service-mesh-unnecessary-complexity/</guid><description>译者：
作为一个曾经在制造业企业的基础架构团队任职，为支持公司的“互联网基因”和“数字化转型”落地了云原生基础设施平台，并在尝试采用服务网格未成的我来说，看到这篇文章深有感触。尤其是文中所说的“人少，问题多，需要快速输出价值”，直戳到了痛处。有限的人手有限的时间，我们需要将大部分精力集中在解决成熟度曲线较低的基本问题上，要想很好的运行复杂的系统是非常困难的。
服务网格是一个新的基础设施层，可以承载很多的功能，未来还会有更大的想象空间和光明的未来。
以上的种种原因，也促使我后来选择进入一家提供服务网格的产品企业，也希望服务网格可以被更简单的使用。
“道阻且长，行则将至！”
本文翻译自 Chris Campbell 的 How Unnecessary Complexity Gave the Service Mesh a Bad Name
关键要点 采用服务网格有巨大的价值，但必须以轻量级的方式进行，以避免不必要的复杂性。 在实施服务网时，要采取务实的方法，与技术的核心功能保持一致，并小心干扰（译者：注意力的分散）。 服务网格的一些核心特性包括标准化监控、自动加密和身份识别、智能路由、可靠的重试和网络可扩展性。 服务网格可以提供强大的功能，但这些功能会分散本应对核心优势的关注，并且这些功能也不是实施服务网格的主要原因。 在初始实施服务网格时没有必要去关注那些明显会分散注意力的功能，比如复杂的控制平面、多集群支持、Envoy、WASM 和 A/B 测试。 服务网格是 Kubernetes 世界中的一个热门话题，但许多潜在的采用者已经有些失望了。服务网格的落地受到压倒性的复杂性和看似无穷无尽的供应商解决方案的限制。在我亲自浏览了这个领域之后，我发现采用服务网格具有巨大的价值，但它必须以轻量级的方式完成，以避免不必要的复杂性。尽管普遍存在幻灭感，但服务网格的未来依然光明。
在工作中学习 我进入服务网格的世界始于我在一家老牌的财富 500 强技术公司担任云计算架构师的角色。在开始我们的服务网格之旅时，我身边有许多强大的工程师，但大多数人几乎没有云计算开发经验。我们的组织诞生于云计算之前，完全实现云计算的价值需要时间。我们的传统业务线主要集中在技术栈的硬件元素上，云计算的决策最初是由为运送硬件或为该硬件提供固件和驱动程序而开发的流程驱动的。
随着该组织经历其“数字化转型”，它越来越依赖于提供高质量的软件服务，并逐渐开发出更好的方法。但作为云计算架构师，我仍在为优先考虑硬件的业务流程，以及具有不同技能、流程和信念的工程团队导航。随着时间的推移，我和我的团队在将 .NET 应用程序迁移到 Linux、采用 Docker、迁移到 AWS 以及与之相关的最佳实践（如持续集成、自动化部署、不可变基础设施、基础设施即代码、监控等）方面变得熟练并成功。但挑战依然存在。</description></item><item><title>自动替换 Kubernetes 镜像</title><link>https://atbug.com/kubernetes-images-swapper/</link><pubDate>Wed, 06 Oct 2021 08:01:41 +0800</pubDate><guid>https://atbug.com/kubernetes-images-swapper/</guid><description>前几天有朋友在问如何在某云上拉取 Tekton 的镜像，这种情况其实比较普遍不只是某云。工作中经常要用到过某些靠运气才能拉取到的镜像，这对工作来说真是极度的不友好。
因此也萌生了个想法，维护一个后网络友好的仓库镜像，在 Pod 创建时将镜像仓库切换到自维护的仓库，从自维护的仓库拉取镜像。
前几天体验了极狐Gitlab 的容器镜像库，便是为这个想法做的准备。当然其他的云厂商也有提供针对个人版的免费镜像仓库和企业版仓库。
正好 Pipy 作为策略引擎，非常适合实现这种策略的执行。
实现思路 Admission Webhook Kubernetes 动态准备控制 的 MutatingWebhookConfiguration 可以 hook Pod 的创建或者更新，然后调用目标服务对 Pod 资源对象进行 patch 操作。
策略引擎 Pipy 作为应用的核心，也就是 MutatingWebhookConfiguration 的目标服务，以策略引擎的角色完成策略的执行。
Pipy 支持从文件或者 HTTP 地址加载脚本，这里为了便于策略的更新，使用了后者。
对于从 HTTP 地址加载脚本，HTTP 地址返回内容的第一行会作为 Pipy 的主脚本，Pipy 启动时会加载主脚本，其他的文件也会被缓存到内存中。</description></item><item><title>极狐GitLab SaaS 内测轻度体验</title><link>https://atbug.com/jihu-gitlab-experience/</link><pubDate>Fri, 01 Oct 2021 08:18:09 +0800</pubDate><guid>https://atbug.com/jihu-gitlab-experience/</guid><description>感谢极狐团队为 GitLab（SaaS）本地化的努力，同时也感谢小马哥提供的内测资格。
最近突然想到了个点子，需要使用一个私有的镜像仓库。极狐GitLab 有提供容器镜像库，正好和 CICD 一起做个轻度体验。
容器镜像库 Container Registry 文档介绍在这里，目前还是英文。（应该本地化的工作量很大，文档还没翻译。）
容器镜像库可以作为独立镜像仓库使用（为什么要这么用，卖个关子下篇文章见），就是使用 docker 命令将构建好的镜像推送到 容器镜像库。
当然也可以同 CICD 流水线结合使用，后文也会介绍。
独立使用 本地登录 Container Registry 有两种验证方式：
使用用户名和密码 开启了双重身份验证，可以使用访问个人访问令牌 其实，不管是否开始双重验证，都建议使用访问令牌。
docker login registry.gitlab.cn #根据提示输入用户名和密码或者令牌 image 的名字最多有三层，即 registry.example.com/[namespace] 之后的内容最多有 3 层。比如下面的 image 名字 myproject/my/image
registry.example.com/mynamespace/myproject/my/image:rc1 其次 image 名字的第一层必须是镜像名，如上面的 myproject。</description></item><item><title>容器神话 Docker 是如何一分为二的</title><link>https://atbug.com/how-docker-broke-in-half/</link><pubDate>Mon, 20 Sep 2021 08:01:30 +0800</pubDate><guid>https://atbug.com/how-docker-broke-in-half/</guid><description>译者点评：
最近听了很多资深的人士关于开源，以及商业化的分析。开源与商业化，听起来就是一对矛盾的所在，似乎大家都在尝试做其二者的平衡。是先有开源，还是先有商业化？俗话说“谈钱不伤感情”，近几年背靠开源的创业公司如雨后春笋般涌现，即使是开发人员也是需要生活的。
容器神话 Docker 曾经无比风光，盛极一时。即使这样一个备受瞩目，大获风投的热捧的独角兽也未能免俗，并付出了不小的代价。
今天这篇文章讲述了 Docker 这家公司从诞生到巅峰到没落，这一路上所做的抉择，并最终做了开源与商业的分离，再一次从开源踏上找寻商业化之路。这些都是值得我们参考和思考的，不管是已经开源或者准备从事开源的。
这篇文章翻译自How Docker broke in half 这家改变游戏规则的容器公司是其昔日的外衣。作为云时代最热门的企业技术业务之一的它到底发生了什么？
Docker 并没有发明容器——将计算机代码打包成紧凑单元的方法，可以轻松地从笔记本电脑移植到服务器——但它确实通过创建一套通用的开源工具和可重用的镜像使其成为主流，这使所有开发人员只需构建一次软件即可在任何地方运行。
Docker 使开发人员能够轻松地将他们的代码“容器化”并将其从一个系统移动到另一个系统，迅速将其确立为行业标准，颠覆了在虚拟机 (VM) 上部署应用程序的主要方式，并使 Docker 成为新一代最快被采用的企业技术之一。
今天，Docker 仍然活着，但它只是它可能成为的公司的一小部分，从未成功地将这种技术创新转化为可持续的商业模式，最终导致其企业业务于 2019 年 11 月出售给 Mirantis。InfoWorld 采访了十几位前任和现任 Docker 员工、开源贡献者、客户和行业分析师，了解 Docker 如何分崩离析的故事。
Docker 诞生了 2008 年由 Solomon Hykes 在巴黎创立的 DotCloud，这个后来成为 Docker 的公司最初被设计为供开发人员轻松构建和发布他们的应用程序的平台即服务 (PaaS)。</description></item><item><title>ARM64 平台基于 openEuler + iSula 环境部署 Kubernetes</title><link>https://atbug.com/setup-kubernetes-running-with-isulad-on-openeuler/</link><pubDate>Thu, 02 Sep 2021 20:41:06 +0800</pubDate><guid>https://atbug.com/setup-kubernetes-running-with-isulad-on-openeuler/</guid><description>为什么要在 arm64 平台上部署 Kubernetes，而且还是鲲鹏 920 的架构。说来话长 。。。 此处省略5000 字。
介绍下系统信息；
架构：鲲鹏 920(Kunpeng920) OS：openEuler 20.03 (LTS-SP1) CPU：4c 内存：16G 硬盘：若干 整个过程虽然参考了鲲鹏论坛的帖子，不过还是颇费周折。
TL;DR 整个过程中要注意 arm64 平台上安装 Kubernetes 及网络组件，需要使用 arm64 版本的镜像。
环境配置 1.关闭 selinux #临时关闭 setenforce 0 #永久关闭 SELINUX=disabled vim /etc/sysconfig/selinux 2. 关闭swap分区 #临时关闭 swapoff -a #永久关闭 注释 swap 行 vim /etc/fstab 3.</description></item><item><title>使用 Flomesh 进行 Dubbo 服务治理</title><link>https://atbug.com/enhance-dubbo-service-governance-with-flomesh/</link><pubDate>Wed, 18 Aug 2021 09:50:28 +0800</pubDate><guid>https://atbug.com/enhance-dubbo-service-governance-with-flomesh/</guid><description>写在最前 和上一篇《使用 Flomesh 强化 Spring Cloud 服务治理》一样，这次同样是在无代码侵入的情况下对 Dubbo 服务治理的提升。
更多治理场景陆续添加中，有兴趣的可关注 https://github.com/flomesh-io/service-mesh-dubbo-demo。
开源的 Pipy 作为 Flomesh 的核心，得益于其轻量及灵活性可以通过编程的方式轻松快速的支持多中平台的服务发现机制，比如 Eureka、Consul、Nacos 等。
概览 细节 环境搭建 搭建 Kubernetes 环境，可以选择 kubeadm 进行集群搭建。也可以选择 minikube、k3s、Kind 等，本文使用 k3s。
使用 k3d 安装 k3s。k3d 将在 Docker 容器中运行 k3s，因此需要保证已经安装了 Docker。
$ k3d cluster create dubbo-demo -p &amp;#34;80:80@loadbalancer&amp;#34; --k3s-server-arg &amp;#39;--no-deploy=traefik&amp;#39; 安装 Flomesh 从仓库 https://github.</description></item><item><title>使用 Flomesh 强化 Spring Cloud 服务治理</title><link>https://atbug.com/enhance-springcloud-service-governance-with-flomesh/</link><pubDate>Tue, 17 Aug 2021 18:47:33 +0800</pubDate><guid>https://atbug.com/enhance-springcloud-service-governance-with-flomesh/</guid><description>写在最前 这篇是关于如何使用 Flomesh 服务网格来强化 Spring Cloud 的服务治理能力，降低 Spring Cloud 微服务架构落地服务网格的门槛，实现“自主可控”。
文档在 github 上持续更新，欢迎大家一起讨论：https://github.com/flomesh-io/flomesh-bookinfo-demo。
架构 环境搭建 搭建 Kubernetes 环境，可以选择 kubeadm 进行集群搭建。也可以选择 minikube、k3s、Kind 等，本文使用 k3s。
使用 k3d 安装 k3s。k3d 将在 Docker 容器中运行 k3s，因此需要保证已经安装了 Docker。
$ k3d cluster create spring-demo -p &amp;#34;81:80@loadbalancer&amp;#34; --k3s-server-arg &amp;#39;--no-deploy=traefik&amp;#39; 安装 Flomesh 从仓库 https://github.</description></item><item><title>开源评估框架</title><link>https://atbug.com/a-framework-for-open-source-evaluation/</link><pubDate>Mon, 09 Aug 2021 08:36:21 +0800</pubDate><guid>https://atbug.com/a-framework-for-open-source-evaluation/</guid><description>本文由本人翻译自 Bilgin Ibryam 的 A Framework for Open Source Evaluation，首发在云原生社区博客。
如今，真假开源无处不在。最近开源项目转为闭源的案例越来越多，同时也有不少闭源项目（按照 OSI 定义）像开源一样构建社区的例子。这怎么可能，开源项目不应该始终如此吗？
开源不是非黑即白，它具有开放性、透明、协作性和信任性的多个维度。有些开源是 Github 上的任何项目，有些必须通过 OSI 定义，有些是必须遵守不成文但普遍接受的开源规范。这里通过看一些商业和技术方面，再讨论社区管理习惯，来同大家分享一下我对评估开源项目的看法。
免责声明 这些是我的个人观点，与我的雇主或我所属的软件基金会和项目无关。 这不是法律或专业意见（我不是律师，也不是专门从事 OSS 评估的），而是外行的意见。 更新：我收到了多位开源律师的反馈并更新了文章！ 这篇博文由订阅和分享按钮赞助，点击这些按钮表示支持。 知识产权 关于“开源”项目的第一个问题是关于知识产权的所有权。好消息是，即使不了解这些法律含义，你可以应用一个简单的 Litmus 测试。该项目是否属于你信任的信誉良好的开源基金会？例如，FSF 拥有其托管项目的版权，更多情况下拥有基金会（如 ASF、LF) 通过贡献者许可协议，聚合对其项目的贡献许可权。在任何一种情况下，你都可以相信他们将充当良好的去中心化管家，并且不会在一夜之间改变项目的未来方向。如果一个项目不属于信誉良好的软件基金会，而是由一家公司提供支持，那么问题是你是否信任该公司作为供应链合作伙伴。如果这些问题的答案是肯定的，请转到下一部分。如果答案是否定的，那么你最好调查一下版权所有者是谁，以及他们对你的长期前景和潜在风险是什么。今天的单一供应商开源项目，明天可能会变成闭源。
许可 商标出现在许可之前的原因是软件的权利人（通常是作者）通过许可授予最终用户使用一个或多个软件副本的许可。自由软件许可证是一种说明，它授予源代码或其二进制形式的使用者修改和重新分发该软件的权利。如果没有许可，这些行为将受到版权法的禁止。这里的重点是权利人可以改变主意并更改许可。权利持有人可以决定在多个许可证下分发软件或随时将许可证更改为非开源许可证。该软件也可能在公共领域，在这种情况下，它不受版权法的限制。公共领域并不等同于开源许可证，这是一种不太流行的方法，我们可以在这里忽略。
同样，如果不是律师，这是一个外行对许可的 Litmus 测试：该项目是否根据 OSI 批准的许可清单获得的许可？如果答案是肯定的，那么你可以依靠这些基金会的尽职调查来审查、分类许可并指出任何限制。如果答案是否定的，请让你公司的律师来查看和解释许可上的每个字以及可能的许可兼容性影响。
治理 在余下的检查中，我们正在从更多的商业和法律方面转向涉及开源项目领域的技术和社区。</description></item><item><title>游记 - 2021 北疆之行</title><link>https://atbug.com/2021-xinjiang-trip/</link><pubDate>Sat, 07 Aug 2021 09:51:22 +0800</pubDate><guid>https://atbug.com/2021-xinjiang-trip/</guid><description> 趁着换工作的间隙，来一场“蓄谋已久”、“说走就走”的旅行。
新疆，是一个美丽而遥远的地方。前段时间看了李娟的《冬牧场》，更是提起了我对新疆对游牧哈萨克族的兴趣。以至后来梦中见到一望无际的戈壁滩，让自己下定决心做出改变。
背景 这次北疆之行准备仓促，从路线方案到寻找同伴，都是出发前一周搞定的。
同伴找的是云原生社区的 宋净超 Jimmy，同样也是老乡（两人在外漂多年，沟通全是普通话。。。），他从北京出发。由于他的工作性质特别&amp;ndash;远程工作，所以当初临时找同伴的时候想到了他，而且还也在有驾照在手。也由于他需要远程工作，这次我们选择了房车自驾，正好本人也想体验一下房车。
路书发在了这里，全程 2800km+。
建议 先说总结，假如要你跟我走同样的路线而且喜欢驾驶，不建议房车（三围太大）。理由：
山路多，各种的弯路，而且路的一侧就是悬崖（我好像还拍过一段 25 分钟的视频） 昭苏到琼库什台一段的山路，很窄！ 车太重，爬坡非常慢，坡太多！ 车高重心高，戈壁上高速公路横风有时比较强，车子晃动厉害（尤其是吐鲁番至乌鲁木齐的风电区域）；即使没有横风，在超大货车的时候也会有同样的感觉 房车的居住体验还是不错的（人多不一定），随停随住。
我的理想方案（下次考虑这么来）：
非房车 这次的路线，商业化迹象很明显，尤其是独库公路那条线。没有房车，吃住也不需要担心。
吃：首选牧民家，体验最好；再就是各种餐馆，只要是本地人开的，都不会差；自己做？（这次带了灶具，一次没做过） 住：酒店 &amp;gt; 牧民蒙古包（商业性的） &amp;gt; 自带帐篷（应急+体验） 不开房车，伊昭、独库公路和其他的山路都会轻松，体验更好。假如是 SUV，底盘高也方便深入草原。注意，为了保护草原牧民的操场车辆不能随便进。可以多问下牧民，付费吃住的话应该没问题。
房车 房车适合宿在草原。假如，真想体验房车，我的建议就是：乌鲁木齐飞伊宁租车，直奔琼库什台村（这段山路实在避不开）。看过琼库什台的原生态草原，其他的草原也就没了兴趣。在那里安静住几天，骑骑马。
行程 本来想写写的，有点多无从下手，看路书凑合下吧。先放上列表，有时间我再完善。
Day01 广州 - 乌鲁木齐 Day02 乌鲁木齐 - 赛里木湖 Day03 赛里木湖 - 昭苏草原 Day04 昭苏草原 - 琼库什台村 Day05 琼库什台村 - 那拉提景区 Day06 那拉提 - 独库公路南段 - 库车市 Day07 库车市 - 和硕县 Day08 和硕县 - 吐鲁番市 - 乌鲁木齐 视频 Jimmy 剪辑的视频 独库公路（有点枯燥，想看的单独找我要吧）</description></item><item><title>Kubernetes 必备工具：2021</title><link>https://atbug.com/translation-kuberletes-essential-tools-2021/</link><pubDate>Thu, 15 Jul 2021 08:10:22 +0800</pubDate><guid>https://atbug.com/translation-kuberletes-essential-tools-2021/</guid><description>有别于前些天的文章 - 常用的几款工具让 Kubernetes 集群上的工作更容易 偏重于工具类来提升工作效率，今天这篇文章更加适合用来做选型时的参考。
文档翻译自 Kubernetes Essential Tools: 2021，篇幅较长，做了部分增删。
介绍 在本文中，我将尝试总结我最喜欢的 Kubernetes 工具，并特别强调最新的和鲜为人知但我认为会非常流行的工具。
这只是我根据我的经验得出的个人清单，但为了避免偏见，我还将尝试提及每种工具的替代方案，以便你可以根据自己的需要进行比较和决定。我将尽可能缩短这篇文章并提供链接，以便你可以自行探索更多内容。我的目标是回答这个问题：“我如何在 Kubernetes 中做 X？” 通过描述不同软件开发任务的工具。
K3D K3D 是我最喜欢的在笔记本电脑上运行 Kubernetes (K8s) 集群的方式。它非常轻巧且速度非常快。它是使用 Docker 围绕 K3S 的包装器。所以，你只需要 Docker 来运行它并且资源使用率非常低。唯一的问题是它不完全符合 K8s 标准，但这不应该是本地开发的问题。对于测试环境，你可以使用其他解决方案。K3D 比 Kind 快，但 Kind 完全兼容。
备选 K3S 物联网或者边缘计算 Kind 完全兼容 Kubernetes 的备选 MicroK8s MiniKube Krew Krew 是管理的必备工具 Kubectl 插件，这是一个必须有任何 K8S 用户。我不会详细介绍超过 145 个可用插件，但至少安装 kubens 和 kubectx。</description></item><item><title>Rego 不好用？用 Pipy 实现 OPA</title><link>https://atbug.com/pipy-implement-kubernetes-admission-control/</link><pubDate>Tue, 13 Jul 2021 08:44:56 +0800</pubDate><guid>https://atbug.com/pipy-implement-kubernetes-admission-control/</guid><description>还不知道 Pipy 是什么的同学可以看下 GitHub 。
Pipy 是一个轻量级、高性能、高稳定、可编程的网络代理。Pipy 核心框架使用 C++ 开发，网络 IO 采用 ASIO 库。 Pipy 的可执行文件仅有 5M 左右，运行期的内存占用 10M 左右，因此 Pipy 非常适合做 Sidecar proxy。
Pipy 内置了自研的 pjs 作为脚本扩展，使得Pipy 可以用 JS 脚本根据特定需求快速定制逻辑与功能。
Pipy 采用了模块化、链式的处理架构，用顺序执行的模块来对网络数据块进行处理。这种简单的架构使得 Pipy 底层简单可靠，同时具备了动态编排流量的能力，兼顾了简单和灵活。通过使用 REUSE_PORT 的机制（主流 Linux 和 BSD 版本都支持该功能），Pipy 可以以多进程模式运行，使得 Pipy 不仅适用于 Sidecar 模式，也适用于大规模的流量处理场景。 在实践中，Pipy 独立部署的时候用作“软负载”，可以在低延迟的情况下，实现媲美硬件的负载均衡吞吐能力，同时具有灵活的扩展性。</description></item><item><title>Open Policy Agent: Top 5 Kubernetes 准入控制策略</title><link>https://atbug.com/open-policy-agent-top-5-kubernetes-admission-control/</link><pubDate>Mon, 12 Jul 2021 08:20:40 +0800</pubDate><guid>https://atbug.com/open-policy-agent-top-5-kubernetes-admission-control/</guid><description>如何使用 Open Policy Agent 实现准入策略控制，可以参考这里
本文翻译自 Open Policy Agent: The Top 5 Kubernetes Admission Control Policies
Kubernetes 开发人员和平台工程师通常承受着非常大的压力，以保持应用程序部署的快速进行，并且总是为了速度和进度而做出妥协。平台团队越来越有责任确保这些妥协（例如管理 Ingress）不会导致客户数据暴露在整个互联网上等后果。
幸运的是，Kubernetes 提供了设置策略的能力，通过检查并防止部署错误将其投入生产，从而避免这些后果。为了确保团队的应用程序不会比信心更重要，以下是现在应该在集群中运行的前五个 Kubernetes 准入控制策略。
1. 可信镜像仓库 此策略很简单，但功能强大：仅允许从受信任的镜像仓库中拉取的容器映像，并且可以选择仅拉取与允许的仓库镜像地址列表匹配的那些镜像。
当然，从互联网（或可信镜像仓库库以外的任何地方）拉取未知镜像会带来风险——例如恶意软件。但是还有其他很好的理由来维护单一的可信来源，例如在企业中实现可支持性。通过确保镜像仅来自受信任的镜像仓库，可以密切控制镜像库存，降低软件熵和蔓延的风险，并提高集群的整体安全性。
相关策略：
禁止所有带有“latest” tag 的镜像 仅允许签名镜像或匹配特定哈希/SHA 的镜像 策略示例：
package kubernetes.validating.images deny[msg] { some i input.</description></item><item><title>Kubernetes 的魔力在于企业标准化，而不是应用程序的可移植性</title><link>https://atbug.com/translation-kubernetes-magic-is-in-enterprise-standardization-not-app-portability/</link><pubDate>Sun, 11 Jul 2021 08:05:42 +0800</pubDate><guid>https://atbug.com/translation-kubernetes-magic-is-in-enterprise-standardization-not-app-portability/</guid><description>笔者：Kubernetes 抽象了资源和工作负载的操作模式，统一了工具集，实现人机接口的标准化。正如类 Docker 工具提供了应用运行时的操作模式；Spring Framework 提供了 Java 应用的开发模式。
Kubernetes 是关于跨云的技能、工具和实践的可移植性。不是工作负载的可移植性。 &amp;ndash; Bilgin Lbryam @bibryam
本文翻译自 Kubernetes magic is in enterprise standardization, not app portability
Kubernetes 不会神奇地使你的应用程序具有可移植性，但它可能会给你带来更好的东西。
云为企业提供了看似无限的选择。然而，根据 Canonical-sponsored 的一项调查，这并不是大多数企业采用 Kubernetes 等云友好技术的原因。相反，Kubernetes 的主要目标是标准化——外观和操作与其他人一样。
可移植性不是目标 我之前已经讨论过这个问题，参考了 Gartner 关于 Kubernetes 和可移植性的指南。许多人认为 Kubernetes（和容器）可以让他们在云之间轻松移植，但事实证明并不是这样的。正如 Gartner 分析师 Marco Meinardi 所写，当被问及公司是否应该采用“Kubernetes 使他们的应用程序可移植&amp;hellip;&amp;hellip;答案是：不。” 再说一次？</description></item><item><title>使用 Open Policy Agent 实现可信镜像仓库检查</title><link>https://atbug.com/image-trusted-repository-with-open-policy-agent/</link><pubDate>Sat, 10 Jul 2021 07:14:47 +0800</pubDate><guid>https://atbug.com/image-trusted-repository-with-open-policy-agent/</guid><description>从互联网（或可信镜像仓库库以外的任何地方）拉取未知镜像会带来风险——例如恶意软件。但是还有其他很好的理由来维护单一的可信来源，例如在企业中实现可支持性。通过确保镜像仅来自受信任的镜像仓库，可以密切控制镜像库存，降低软件熵和蔓延的风险，并提高集群的整体安全性。除此以外，有时还会需要检查镜像的 tag，比如禁止使用 latest 镜像。
这今天我们尝试用“策略即代码”的实现 OPA 来实现功能。
还没开始之前可能有人会问：明明可以实现个 Admission Webhook 就行，为什么还要加上 OPA？
确实可以，但是这样策略和逻辑都会耦合在一起，当策略需要调整的时候需要修改代码重新发布。而 OPA 就是用来做解耦的，其更像是一个策略的执行引擎。
什么是 OPA Open Policy Agent（以下简称 OPA，发音 “oh-pa”）一个开源的通用策略引擎，可以统一整个堆栈的策略执行。OPA 提供了一种高级声明性语言（Rego），可让你将策略指定为代码和简单的 API，以从你的软件中卸载策略决策。你可以使用 OPA 在微服务、Kubernetes、CI/CD 管道、API 网关等中实施策略。
Rego 是一种高级的声明性语言，是专门为 OPA 建立的。更多 OPA 的介绍可以看 Open Policy Agent 官网，不想看英文直接看这里。
现在进入正题。
启动集群 启动 minikube</description></item><item><title>Kubernetes CKA 证书备考笔记</title><link>https://atbug.com/notes-for-cka-preparation/</link><pubDate>Fri, 02 Jul 2021 08:02:15 +0800</pubDate><guid>https://atbug.com/notes-for-cka-preparation/</guid><description>Kubernetes 使用有好几年了，但在今年 5 月才完成 CKA 的考试。虽说用了几年，还是提前刷了部分题熟悉下。
绝大部分题都是有在 minikube 的环境上操作过，只有部分比如升级集群受限于环境问题没有实地操作。
写在最前 保存常用文档进书签，如果有 Alfred 启用浏览器书签 workflow。效果见下图 kubectl 自动补全 echo &amp;quot;source &amp;lt;(kubectl completion bash)&amp;quot; &amp;gt;&amp;gt; ~/.bashrc; source ~/.bashrc 每道题开始前要切换 context 和 namespace，直接复制题目里的命令即可 必要的 alias 善用 --dry-run=client -o yaml 避免手动敲太多 善用 kubectl explain [resource[.field]] 看懂题目最重要，输出正确的结果更重要（重要的事讲三遍） 看懂题目最重要，输出正确的结果更重要（重要的事讲三遍） 看懂题目最重要，输出正确的结果更重要（重要的事讲三遍） 书签地址：K8s-CKA-CAKD-Bookmarks.</description></item><item><title>可编程网关 Pipy 第三弹：事件模型设计</title><link>https://atbug.com/pipy-event-handling-design/</link><pubDate>Sun, 27 Jun 2021 09:38:18 +0800</pubDate><guid>https://atbug.com/pipy-event-handling-design/</guid><description>自从参加了 Flomesh 的 workshop，了解了可编程网关 Pipy。对这个“小东西”充满了好奇，前后写了两篇文章，看了部分源码解开了其部分面纱。但始终未见其全貌，没有触及其核心设计。
不是有句话，“好奇害死猫”。其实应该还有后半句，“满足了就没事”（见维基百科）。
所有就有了今天的这一篇，对前两篇感兴趣的可以跳转翻看。
初探可编程网关 Pipy 可编程网关 Pipy 第二弹：编程实现 Metrics 及源码解读 言归正传。
事件模型 上篇写了 Pipy 基于事件的信息流转，其实还未深入触及其核心的事件模型。既然是事件模型，先看事件。
src/event.hpp:41 中定义了 Pipy 的四种事件：
Data MessageStart MessageEnd SessionEnd 翻看源码可知（必须吐槽文档太少）这几种事件其实是有顺序的：MessageStart -&amp;gt; Data -&amp;gt; MessageEnd -&amp;gt; SessionEnd。
这种面向事件模型，必然有生产者和消费者。又是翻看源码可知，生产者和消费者都是 pipy::Filter。我们在上篇文章中讲过：每个 Pipeline 都有一个过滤器链，类似单向链表的数据结构。
那是不是按照上面说的，事件是从一个 Filter 流向下一个 Filter？也对，也不对。</description></item><item><title>常用的几款工具让 Kubernetes 集群上的工作更容易</title><link>https://atbug.com/tools-accelerate-work-on-kubernetes-cluster/</link><pubDate>Sat, 26 Jun 2021 12:16:28 +0800</pubDate><guid>https://atbug.com/tools-accelerate-work-on-kubernetes-cluster/</guid><description>之前写过一篇 介绍了工具加速云原生 Java 开发。
其实日常工作中在集群上的操作也非常多，今天就来介绍我所使用的工具。
kubectl-alias 使用频率最高的工具，我自己稍微修改了一下，加入了 StatefulSet 的支持。
这个是我的 https://github.com/addozhang/kubectl-aliases，基于 https://github.com/ahmetb/kubectl-aliases。
比如输出某个 pod 的 json，kgpoojson xxx 等同于 kubectl get pod xxx -o json。
结合 jq 使用效果更好 😂。
语法解读 k=kubectl sys=--namespace kube-system commands: g=get d=describe rm=delete a:apply -f ak:apply -k k:kustomize ex: exec -i -t lo: logs -f resources: po=pod, dep=deployment, ing=ingress, svc=service, cm=configmap, sec=secret,ns=namespace, no=node flags: output format: oyaml, ojson, owide all: --all or --all-namespaces depending on the command sl: --show-labels w=-w/--watch value flags (should be at the end): n=-n/--namespace f=-f/--filename l=-l/--selector kubectx + kubens 安装看这里</description></item><item><title>Jenkins 如何与 Kubernetes 集群的 Tekton Pipeline 交互？</title><link>https://atbug.com/jenkins-interact-with-tekton-pipelines-via-plugin/</link><pubDate>Wed, 23 Jun 2021 07:58:45 +0800</pubDate><guid>https://atbug.com/jenkins-interact-with-tekton-pipelines-via-plugin/</guid><description>本文详细介绍了 Jenkins 如何通过 tekton-client-plugin 实现与 Kubernetes 上的 Tekton Pipeline 交互，包括 Kubernetes 上安装 Jenkins、Tekton Pipelines 等。
关于如何使用 Tekton Pipeline 实现 CICD 可以看这篇文章 云原生 CICD: Tekton Pipeline 实战
本文用于构建的项目以及所有 manifest yaml 都在可以在这里下载。
TL;DR 惯例，先上总结。tekton-client-plugin 虽然还是处于初期阶段，但是 其价值非常明显，尤其是对先用使用 Jenkins 作为 CICD 实现的用户来说。从 Jenkins 迁移到云原生的 Tekton 时，可以省掉用户界面的开发成本，而且尽可能少的改变用户习惯 ，依靠版本管理可以控制迁移的节奏。</description></item><item><title>云原生 CICD: Tekton Pipeline 实战</title><link>https://atbug.com/tekton-pipeline-practice/</link><pubDate>Tue, 22 Jun 2021 07:19:33 +0800</pubDate><guid>https://atbug.com/tekton-pipeline-practice/</guid><description>更新历史：
v1：2020.1.21 基于 Tekton Pipline v0.9.0 v2（当前）：2021.6.22 基于 Tekton Pipeline v0.25.0 Tekton 是 Google 开源的 Kubernetes 原生CI/CD 系统, 功能强大扩展性强. 前身是 Knavite 里的 build-pipeline 项目, 后期孵化成独立的项目. 并成为 CDF 下的四个项目之一, 其他三个分别是 Jenkins, Jenkins X, Spinnaker.
为什么说 Tekton 是 Kubernetes 原生的, 以内其基于 Kubernetes 的 CRD 定义了 Pipeline 流水线.</description></item><item><title>源码解析：一文读懂 Kubelet</title><link>https://atbug.com/kubelet-source-code-analysis/</link><pubDate>Tue, 15 Jun 2021 08:25:25 +0800</pubDate><guid>https://atbug.com/kubelet-source-code-analysis/</guid><description>本文主要介绍 kubelet 功能、核心组件，以及启动流程的源码分析，总结了 kubelet 的工作原理。
kubelet 简介 从官方的架构图中很容易就能找到 kubelet
执行 kubelet -h 看到 kubelet 的功能介绍：
kubelet 是每个 Node 节点上都运行的主要“节点代理”。使用如下的一个向 apiserver 注册 Node 节点：主机的 hostname；覆盖 host 的参数；或者云提供商指定的逻辑。 kubelet 基于 PodSpec 工作。PodSpec 是用 YAML 或者 JSON 对象来描述 Pod。Kubelet 接受通过各种机制（主要是 apiserver）提供的一组 PodSpec，并确保里面描述的容器良好运行。 除了由 apiserver 提供 PodSpec，还可以通过以下方式提供：</description></item><item><title>可编程网关 Pipy 第二弹：编程实现 Metrics 及源码解读</title><link>https://atbug.com/programming-archive-metrics-with-pipy/</link><pubDate>Fri, 11 Jun 2021 08:27:36 +0800</pubDate><guid>https://atbug.com/programming-archive-metrics-with-pipy/</guid><description>由于要给团队做一下关于 Flomesh 的分享，准备下材料。
“分享是最好的学习方法。”
上一回初探可编程网关 Pipy，领略了 Pipy 的“风骚”。从 Pipy 的 GUI 交互深入了解了 Pipy 的配置加载流程。
今天看一下 Pipy 如何实现 Metrics 的功能，顺便看下数据如何在多个 Pipeline 中进行流转。
前置 首先，需要对 Pipy 有一定的了解，如果不了解看一下上一篇文章。
其次构建好 Pipy 环境，关于构建还是去看上一篇文章。
Metrics 功能实现 至于 Pipy 实现 Metrics 的方式，源码中就有，位于 test/006-metrics/pipy.js。
代理监听 6080 端口，后端服务在 8080 端口，Metrics 在 9090 端口 共有 5 个 Pipeline：3 个 listen 类型，2 个 Pipeline 类型 7 种过滤器：fork、connect、decodeHttpRequest、onMessageStart、decodeHttpResponse、encodeHttpRespnse、replaceMessage 贴一下源码：</description></item><item><title>Kubernetes 的自动伸缩你用对了吗？</title><link>https://atbug.com/auto-scaling-best-practice-in-kubernetes/</link><pubDate>Wed, 09 Jun 2021 00:34:25 +0800</pubDate><guid>https://atbug.com/auto-scaling-best-practice-in-kubernetes/</guid><description>本文翻译自 learnk8s 的 Architecting Kubernetes clusters — choosing the best autoscaling strategy，有增删部分内容。
TL;DR: 在默认设置下，扩展 Kubernetes 集群中的 pod 和节点可能需要几分钟时间。了解如何调整集群节点的大小、配置水平和集群自动缩放器以及过度配置集群以加快扩展速度。
自动扩展器 在 Kubernetes 中，常说的“自用扩展”有：
HPA：Pod 水平缩放器 VPA：Pod 垂直缩放器 CA：集群自动缩放器 不同类型的自动缩放器，使用的场景不一样。
HPA HPA 定期检查内存和 CPU 等指标，自动调整 Deployment 中的副本数，比如流量变化：
VPA 有些时候无法通过增加 Pod 数来扩容，比如数据库。这时候可以通过 VPA 增加 Pod 的大小，比如调整 Pod 的 CPU 和内存：</description></item><item><title>初探可编程网关 Pipy</title><link>https://atbug.com/glance-at-programmable-gateway-pipy/</link><pubDate>Mon, 31 May 2021 00:45:08 +0800</pubDate><guid>https://atbug.com/glance-at-programmable-gateway-pipy/</guid><description>有幸参加了 Flomesh 组织的workshop，了解了他们的 Pipy 网络代理，以及围绕 Pipy 构建起来的生态。Pipy 在生态中，不止是代理的角色，还是 Flomesh 服务网格​中的数据平面。
整理一下，做个记录，顺便瞄一下 Pipy 的部分源码。
介绍 下面是摘自 Github 上关于 Pipy 的介绍：
Pipy 是一个轻量级、高性能、高稳定、可编程的网络代理。Pipy 核心框架使用 C++ 开发，网络 IO 采用 ASIO 库。 Pipy 的可执行文件仅有 5M 左右，运行期的内存占用 10M 左右，因此 Pipy 非常适合做 Sidecar proxy。
Pipy 内置了自研的 pjs 作为脚本扩展，使得Pipy 可以用 JS 脚本根据特定需求快速定制逻辑与功能。</description></item><item><title>使用 Quarkus 和 MicroProfile 实现微服务特性</title><link>https://atbug.com/microservicilities-quarkus/</link><pubDate>Wed, 26 May 2021 07:37:04 +0800</pubDate><guid>https://atbug.com/microservicilities-quarkus/</guid><description>Quarkus 的文章之前写过三篇了，讲过了 Quarkus 的小而快。
Hello, Quarkus 应&amp;quot;云&amp;quot;而生的 Java 框架 Quarkus：构建本机可执行文件 谁说 Java 不能用来跑 Serverless？ 一直在酝酿写一篇 Quarkus 生态相关的，因为最近一直在忙 Meetup 的事情而搁浅。正好看到了这篇文章，就拿来翻译一下，补全云原生中的“微服务”这一块。
本文译自《Implementing Microservicilities with Quarkus and MicroProfile》 。
为什么要使用微服务特性？ 在微服务架构中，一个应用程序是由几个相互连接的服务组成的，这些服务一起工作来实现所需的业务功能。
因此，典型的企业微服务架构如下所示：
刚开始，使用微服务架构实现应用程序看起来很容易。
但是，因为有了单体架构没有一些新的挑战，因此做起来并不容器
举几个例子，比如容错、服务发现、扩展性、日志记录和跟踪。
为了解决这些挑战，每个微服务都应实现我们在 Red Hat 所说的“微服务特性”。
该术语是指除业务逻辑以外，服务还必须实现来解决的跨领域关注点清单，如下图所示： 可以用任何语言（Java、Go、JavaScript）或任何框架（Spring Boot、Quarkus）实现业务逻辑，但是围绕业务逻辑，应实现以下关注点：
API：可通过一组定义的 API 操作来访问该服务。例如，对于 RESTful Web API，HTTP 用作协议。此外，可以使用诸如 Swagger 之类的工具来记录 API 。 服务发现（Discovery）：服务需要发现其他服务。</description></item><item><title>开箱即用的 Prometheus 告警规则集</title><link>https://atbug.com/introduction-awesome-prometheus-alerts/</link><pubDate>Thu, 13 May 2021 08:01:00 +0800</pubDate><guid>https://atbug.com/introduction-awesome-prometheus-alerts/</guid><description>在配置系统监控的时候，是不是即使绞尽脑汁监控的也还是不够全面，或者不知如何获取想要的指标。
Awesome Prometheus alerts 维护了一套开箱即用的 Prometheus 告警规则集合，有 300 多个告警规则。同时，还是说明如何获取对应的指标。这些规则，对每个 Prometheus 都是通用的。
涉及如主机、硬件、容器等基础资源，到数据库、消息代理、运行时、反向代理、负责均衡器，运行时、服务编排，甚至是网络层面和 Prometheus 自身和集群。
Prometheus 的安装和配置不做赘述，配置可以看这里。下面简单看下几个常用规则
主机和硬件资源 主机和硬件资源的告警依赖 node-exporter 输出的指标。例如：
内存不足 可用内存低于阈值 10% 就会触发告警。
- alert: HostOutOfMemory expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 &amp;lt; 10 for: 2m labels: severity: warning annotations: summary: Host out of memory (instance {{ $labels.</description></item><item><title>Kubernetes 上如何控制容器的启动顺序？</title><link>https://atbug.com/k8s-1.18-container-start-sequence-control/</link><pubDate>Fri, 30 Apr 2021 07:43:54 +0800</pubDate><guid>https://atbug.com/k8s-1.18-container-start-sequence-control/</guid><description>去年写过一篇博客：控制 Pod 内容器的启动顺序，分析了 TektonCD 的容器启动控制的原理。
为什么要做容器启动顺序控制？我们都知道 Pod 中除了 init-container 之外，是允许添加多个容器的。类似 TektonCD 中 task 和 step 的概念就分别与 pod 和 container 对应，而 step 是按照顺序执行的。此外还有服务网格的场景，sidecar 容器需要在服务容器启动之前完成配置的加载，也需要对容器的启动顺序加以控制。否则，服务容器先启动，而 sidecar 还无法提供网络上的支持。
现实 期望 到了这里肯定有同学会问，spec.containers[] 是一个数组，数组是有顺序的。Kubernetes 也确实是按照顺序来创建和启动容器，但是 容器启动成功，并不表示容器可以对外提供服务。
在 Kubernetes 1.18 非正式版中曾在 Lifecycle 层面提供了对 sidecar 类型容器的 支持，但是最终该功能并没有落地。</description></item><item><title>云上细粒度访问管理的参考架构</title><link>https://atbug.com/translation-access-management-reference-architecture/</link><pubDate>Wed, 28 Apr 2021 08:02:11 +0800</pubDate><guid>https://atbug.com/translation-access-management-reference-architecture/</guid><description>本文由 Addo Zhang 翻译自 A Reference Architecture for Fine-Grained Access Management on the Cloud
什么是访问管理？ 访问管理是识别用户或一组用户是否应该能够访问给定资源（例如主机、服务或数据库）的过程。例如，对于开发人员来说是否可以使用 SSH 登录生产应用程序服务器，如果可以，那么可以登录多长时间？如果 SRE 在非支持时间尝试访问数据库，他们这样做？如果数据工程师已转移到其他团队，他们是否应该继续访问 ETL 管道的 S3 存储桶？
现在如何进行访问管理？ 在云上各种基础设施和数据服务激增之前，访问管理是 DevOps 和 Security 团队要解决的相对简单的问题。VPN 和堡垒主机是（现在仍然是）在网络级别封锁所有关键资源的首选机制。用户必须先通过 VPN 服务器进行身份验证，或者登录到堡垒主机，然后才能访问专用网络上的所有资源。
当资源是静态的并且它们的数量相对较小时，此方法效果很好。但是，随着越来越多的资源动态地涌入专用网络的各处，VPN / 堡垒主机解决方案变得站不住脚。
具体来说，在三个方面，VPN 和堡垒主机不足以作为一种有效的访问管理机制。
它们作用于网络层面：用户通过 VPN 进行身份验证并获得对专用网络的访问权限后，他们实际上就可以访问其上运行的所有服务。无法根据用户的身份在基础架构或数据服务的粒度上管理访问。 凭据是攻击的媒介：VPN 和堡垒主机都要求用户记住并存储凭据。过期和轮换凭证作为安全策略非常困难，尤其是在涉及大量用户的情况下，凭证因此成为潜在的攻击媒介。 不能管理第三方 SaaS 工具：SaaS 工具（如 Looker、Tableau 和 Periscope Data）需要直接访问数据端点。因此，使用这些工具访问数据的任何人都无法通过使用了相同的机制和凭据的基础设施进行身份验证。 云上访问管理的新架构 在本文中，我们将定义新的参考架构，为那些正在寻求简化访问管理云资源（从 SSH 主机、数据库、数据仓库到消息管道和云存储终结点）解决方案的云原生企业。</description></item><item><title>Quarkus：谁说 Java 不能用来跑 Serverless？</title><link>https://atbug.com/quarkus-enable-java-running-in-serverless/</link><pubDate>Sat, 24 Apr 2021 09:16:05 +0800</pubDate><guid>https://atbug.com/quarkus-enable-java-running-in-serverless/</guid><description>想到这个标题的时候，我第一时间想到的就是星爷的《唐伯虎点秋香》的这一幕。
当讨论起世界上最好的开发语言是什么的时候，Java 的粉丝们总会遇到这种场景：
吹：“Java 语法简单，容易上手！” 黑：“Java 启动慢，性能差，耗资源！” 吹：“Java 有世界上最多的程序员！” 黑：“Java 启动慢，性能差，耗资源！” 吹：“Java 生态好！” 黑：“Java 启动慢，性能差，耗资源！” 吹：“滚！”
今天我们继续说说 Quarkus，应“云”而生的 Java 框架。今天算是第三篇了，没看过的同学可以回顾一下：
Hello, Quarkus 应&amp;quot;云&amp;quot;而生的 Java 框架 Quarkus：构建本机可执行文件 上一篇的结尾预告：试试 Quarkus 在 ArgoCD 中的应用，看下 Serverless 上的使用体验。不过不想用 ArgoCD 了，因为这 workflow 这种场景实在体现不出 Quarkus 到底有多快。但又想做 Serverless，那就想到了 Knative Serving 了。</description></item><item><title>服务网格平稳落地：Istio 中精准控制 Sidecar 的注入</title><link>https://atbug.com/how-to-control-istio-sidecar-injection/</link><pubDate>Wed, 21 Apr 2021 08:13:04 +0800</pubDate><guid>https://atbug.com/how-to-control-istio-sidecar-injection/</guid><description>为什么 说起服务网格，这幅图大家肯定不会陌生。这就是服务网格的网络，也是网格架构的终极形态。
那在迁移到网格架构之前，我们的系统是什么样的？
我们的系统在演进的过程中，不可避免的会遇到各种 0 到 1 过程中的中间态。比如下面这种，可以比较直观的看出 Istio 或者网格是部分覆盖的。这个过程中，我们需要平滑、可控的推进，才能在保障系统可用性的前提下进行架构的演进。
怎么做 Sidecar 的注入分两种：手动和自动。
手动 手动就是利用 Istio 的 cli 工具 istioctl kube-inject 对资源 yaml 进行修改：
$ istioctl kube-inject -f samples/sleep/sleep.yaml | kubectl apply -f - serviceaccount/sleep created service/sleep created deployment.apps/sleep created 手动的方式比较适合开发阶段使用。</description></item><item><title>应“云”而生的 Java 框架：构建本机可执行文件</title><link>https://atbug.com/quarkus-build-native-executable-file/</link><pubDate>Sat, 17 Apr 2021 09:08:40 +0800</pubDate><guid>https://atbug.com/quarkus-build-native-executable-file/</guid><description>电影《功夫》中，火云邪神有句话：“天下武功无坚不摧，唯快不破。”
在 上一篇文章 中，我们写了第一个 Quarkus 应用，并尝试着构建了 legacy-jar 和 fast-jar。
今天来看一下 Quarkus 构建出来的本机可执行文件到底比 Spring 应用能快多少，生态的成熟度不在这里讨论。
TLDR 先上结论， 与只有一个 Controller 的Spring Web 应用做下对比。
应用启动时间：0.012s vs 2.294s 镜像大小：49MB vs 237 MB Spring 应用镜像使用 openjdk:11.0-jre-slim 作为 base 镜像，大小为 220MB。
docker images REPOSITORY TAG IMAGE ID CREATED SIZE spring/spring-getting-started latest 5f47030c5c3f 6 minutes ago 237MB quarkus/quarkus-getting-started distroless2 fe973c5ac172 24 minutes ago 49MB quarkus/quarkus-getting-started distroless 6fe27dd44e86 31 minutes ago 51MB quarkus/quarkus-getting-started ubi 8f86f5915715 58 minutes ago 132MB Java 应用容器化的困境 云原生世界中，应用容器化是个显著的特点。Java 应用容器化时面临了如下问题：</description></item><item><title>应“云”而生的 Java 框架：Hello, Quarkus</title><link>https://atbug.com/hello-quarkus/</link><pubDate>Mon, 05 Apr 2021 21:08:40 +0800</pubDate><guid>https://atbug.com/hello-quarkus/</guid><description>Wikipedia上有关 Quarkus 的信息还很少，只有一句简单的介绍：
Quarkus 是专为 OpenJDK HotSpot 和 GraalVM 定制的全栈 Kubernetes 原生 Java 应用程序框架。与如 Spring 之类的其他框架相比，它提供了较小的内存占用并缩短了启动时间。它允许结合命令式和非阻塞响应式编程。
从 Quarkus 的官网，可以看到其有几个特性：
容器优先 统一了命令式和响应式编程 开发者友好 最佳品种的库及标准 更多 Quarkus 可以参考官网的介绍及文档。今天主要就是跑一下 Quarkus 的 Hello world。
放一张官网的图：
环境准备 基于 Java 11 的 GraalVM Maven 3.6.2+ 笔者使用的是 macos 10.</description></item><item><title>分布式系统在 Kubernetes 上的进化</title><link>https://atbug.com/translation-distributed-systems-kubernetes/</link><pubDate>Mon, 29 Mar 2021 23:11:25 +0800</pubDate><guid>https://atbug.com/translation-distributed-systems-kubernetes/</guid><description>本文译自 The Evolution of Distributed Systems on Kubernetes
在 3 月份的 QCon 上，我做了一个关于 Kubernetes 的分布式系统进化的演讲。首先，我想先问一个问题，微服务之后是什么？我相信大家都有各自的答案，我也有我的答案。你会在最后发现我的想法是什么。为了达到这个目的，我建议大家看看分布式系统的需求是什么？以及这些需求在过去是如何发展的，从单体应用开始到 Kubernetes，再到最近的 Dapr、Istio、Knative 等项目，它们是如何改变我们做分布式系统的方式。我们将尝试对未来做一些预测。
现代分布式应用 为了给这个话题提供更多的背景信息，我认为的分布式系统是由数百个组件组成的系统。这些组件可以是有状态的、无状态的或者无服务器的。此外，这些组件可以用不同的语言创建，运行在混合环境上，并开发开源技术、开放标准和互操作性。我相信你可以使用闭源软件来构建这样的系统，也可以在 AWS 和其他地方构建。具体到这次演讲，我将关注 Kubernetes 生态系统，以及你如何在 Kubernetes 平台上构建这样一个系统。
我们从分布式系统的需求讲起。我认为是我们要创建一个应用或者服务，并写一些业务逻辑。那从运行时的平台到构建分布式系统，我们还需要什么呢？在底层，最开始是我们要一些生命周期的能力。当你用任一语言开发你的应用时，我们希望有能力把这个应用可靠地打包和部署、回滚、健康检查。并且能够把应用部署到不同的节点上，并实现资源隔离、扩展、配置管理，以及所有这些。这些都是你创建分布式应用所需要的第一点。
第二点是围绕网络。我们有了应用之后，我们希望它能够可靠地连接到其他服务，无论该服务是在集群内部还是在外部。我们希望其具有服务发现、负载均衡的能力。为了不同的发布策略或是其他的一些原因的我们希望有流量转移的能力。然后我们还希望其具有与其他系统进行弹性通信的能力，无论是通过重试、超时还是断路器。要有适当的安全保障，并且要有足够的监控、追踪、可观察性等等。
我们有了网络之后，接下来就是我们希望有能力与不同的 API 和端点交互，即资源绑定&amp;ndash;与其他协议和不同的数据格式交互。甚至能够从一种数据格式转换成另一种数据格式。我还会在这里加入诸如滤光的功能，也就是说，当我们订阅一个主题时，我们也许只对某些事件感兴趣。
你认为最后一类是什么？是状态。当我在说状态和有状态的抽象时，我并不是在谈论实际的状态管理，比如数据库或者文件系统的功能。我要说的更多是有关幕后依赖状态的开发人员抽象。可能，你需要具有工作流管理的能力。也许你想管理运行时间长的进程或者做临时调度或者某些定时任务来定期运行服务。也许你还想进行分布式缓存，具有幂等性或者支持回滚。所有这些都是开发人员级的原语，但在幕后，它们依赖于具有某种状态。你想随意使用这些抽象俩创建完善的分布式系统。
我们将使用这个分布式系统原语的框架来评估它们在 Kubernetes 和其他项目上的变化情况。
单体架构 &amp;ndash; 传统中间件功能 假设我们从单体架构以及如何获得这些能力开始。在那种情况下，首先是当我说单体的时候，在分布式应用的情况下我想到的是 ESB。ESB 是相当强大的，当我们检查我们的需求列表时，我们会说 ESB 对所有有状态的抽象有很好的支持。</description></item><item><title>【译】2021 年及未来的云原生预测</title><link>https://atbug.com/translation-cloud-native-predictions-for-2021-and-beyond/</link><pubDate>Tue, 09 Feb 2021 06:43:54 +0800</pubDate><guid>https://atbug.com/translation-cloud-native-predictions-for-2021-and-beyond/</guid><description>本文译自 Cloud Native Predictions for 2021 and Beyond
原文发布在 Chris Aniszczyk 的个人博客
我希望每个人都有一个美好的假期，因为 2021 年 1 月的前几周一直非常疯狂，从叛乱到新的 COVID 菌株。在云原生国度，CNCF 最近发布了关于我们去年完成的所有工作的年度报告。我建议大家找个机会去看一下这份报告，在疫情大流行的这一年，我们收获颇丰。https://twitter.com/CloudNativeFdn/status/1343914259177222145
作为我工作的一部分，我对云原生趋势有一个独特的观点，送给所有与我合作的会员公司和开发人员，所以我想我会分享我对 2021 年及以后云原生发展的想法。
云原生的 IDE
作为一个在 Eclipse 基金会内部从事开发者工具工作的人，我对最近的技术状态进展感到无比兴奋。未来，开发生命周期（代码、构建、调试）将主要发生在云端，而不是你本地的 Emacs 或 VSCode。你将每一个拉动请求最终得到一个完整的开发环境设置，预先配置并连接到他们自己的部署，以协助你的开发和调试需求。今天这种技术的一个具体例子是通过 GitHub Codespaces 和 GitPod 实现的。虽然 GitHub Codespaces 还处于测试阶段，但今天你可以通过 GitPod 来体验，以 Prometheus 为例。一分钟左右，你就拥有了一个有编辑器和预览环境的完全实时的开发环境。最疯狂的是，这个开发环境（工作空间）是 用代码描述，并且可以像其他代码工件一样，与你团队的其他开发者共享。</description></item><item><title>【译】应用架构：为什么要随着市场演进</title><link>https://atbug.com/translation-application-architecture-why-it-should-evolve-with-the-market/</link><pubDate>Sun, 17 Jan 2021 21:37:23 +0800</pubDate><guid>https://atbug.com/translation-application-architecture-why-it-should-evolve-with-the-market/</guid><description>本文译自 Application architecture: why it should evolve with the market 最初由Mia Platform团队发布在Mia Platform的博客上
如今，IT 挑战在于通过有效选择应用架构来适应市场和业务需求的发展。为了满足业务和客户的需求，IT 部门应能够对技术和方法采取行动以确保软件具有灵活性，并实现产品和服务的持续创新流程，从而做出更快的反应 。
当然，过去的单体应用程序和刚性基础设施无法做到这一点。相反，它可以通过为演化而设计的架构来实现，该架构在需要时易于更新和重构。容器化实践的广泛应用（根据 Gartner，到2022年，大公司的就业人数将从目前的 30％ 增长到 75％），这种情况下采用云原生方法重新设计微服务应用是成功的关键。
如何构建不断发展的应用架构 海外专家称它们为可演进的架构，以将它们与当今阻碍或无助于改变的传统架构区分开。应用架构基于微服务架构风格 ，被设计成在现代虚拟化 IT 和云环境中发挥最佳性能。
基本思想是创建可以轻松“分解”的应用程序，其组件可以在其他上下文或组合中重用，如 Lego 系列。开发一系列微服务，每个微服务都用于执行单个业务功能（根据“单一职责原则”），可以在应用本身的开发和演进中获得相当大的灵活性。实际上，可以根据支持功能的特定生命周期独立开发、更新和测试服务。
此外，谈到部署，微服务应用的架构具有很大的优势：可以根据需要在内部或云中通过使用可用资源来扩展单个微服务。
为此，微服务应用获得基于容器的基础设施的支持，该基础设施通过业务编排系统（通常为 Kubernetes）进行管理，该流程可以自动化并促进公司系统之间以及从这些系统到云提供商服务的软件作业的迁移。
随着业务发展的应用架构的优势 基于微服务的应用架构在开发和部署方面具有更大的自治权。如我们所见，微服务可以在其他应用程序中单独实现、“分解”、更新和重用。因此，通过产品或客户需求的演变，它有降低减少市场所需的每个新产品的设计/开发时间和成本。
此外，通过使用容器化实践，可以简化在本地、云、多云或混合环境的任何环境中应用程序的部署，从而优化成本。
在微服务架构风格的优点中，我们还发现有可能在各种服务之间的对话及其健康状况上获得更大的透明度：更好的可观察性意味着可以轻松解决复杂应用的问题。实际上，管理员可以更快地定位和解决性能和安全性问题，在运维和代码层面实施措施，从而使响应速度与变更的长期有效性保持一致。
通过采用微服务以及新的开发和部署方法，可以创建能够随时间发展的应用架构。除了 IT 团队必须掌握的新技能外，还必须对公司的未来有一个清晰的愿景，以确保所提供的服务对业务发展有用。</description></item><item><title>Envoy listener filter times out 问题</title><link>https://atbug.com/envoy-listener-filter-times-out/</link><pubDate>Wed, 09 Dec 2020 20:00:00 +0800</pubDate><guid>https://atbug.com/envoy-listener-filter-times-out/</guid><description>最近在看 openservicemesh 相关内容，这周更新了 main 分支的代码之后。发现原本 v0.5.0 时可以正常代理的 mysql 流量，在新的 commit 中无法代理了。
开启 envoy 的 filter debug 日志后发现出现了超时。
[2020-12-09 08:54:42.285][15][debug][filter] [source/extensions/filters/listener/original_dst/original_dst.cc:18] original_dst: New connection accepted [2020-12-09 08:54:42.285][15][debug][filter] [source/extensions/filters/listener/http_inspector/http_inspector.cc:38] http inspector: new connection accepted [2020-12-09 08:54:42.285][15][trace][filter] [source/extensions/filters/listener/http_inspector/http_inspector.cc:105] http inspector: recv: 0 [2020-12-09 08:54:57.</description></item><item><title>使用 cLion 阅读 envoy 源码</title><link>https://atbug.com/read-envoy-source-code-in-clion/</link><pubDate>Tue, 08 Dec 2020 20:53:39 +0800</pubDate><guid>https://atbug.com/read-envoy-source-code-in-clion/</guid><description>虽然不写 C++，但是看点代码还是能看懂。Envoy 的功能配置复杂，有时候处理问题还是需要看下源码的。
Vim 或者 Code 就算了，我只是阅读源码需要关联跳转就行。在 Clion 中，代码的关联跳转需要一个CMakeLists.txt 文件。
我将生成的内容挂在了 gist 上了，不想花费时间生成的可以直接复制。
准备环境 1. 安装依赖的工具 brew install coreutils wget cmake libtool go automake ninja clang-format 2. bazel 使用 homebrew 进行安装： brew install bazel 即可
安装问运行编译测试下：bazel build //source/exe:envoy-static，提示版本太高。
现在 homebrew 安装的版本是3.</description></item><item><title>Kubernetes 源码解析 - Informer</title><link>https://atbug.com/kubernetes-source-code-how-informer-work/</link><pubDate>Sun, 16 Aug 2020 23:32:38 +0800</pubDate><guid>https://atbug.com/kubernetes-source-code-how-informer-work/</guid><description>上篇扒了 HPA 的源码，但是没深入细节，今天往细节深入。
开局先祭出一张图：
为什么要有 Informer？ Kubernetes 中的持久化数据保存在 etcd中，各个组件并不会直接访问 etcd，而是通过 api-server暴露的 RESTful 接口对集群进行访问和控制。
资源的控制器（图中右侧灰色的部分）读取数据也并不会直接从 api-server 中获取资源信息（这样会增加 api-server 的压力），而是从其“本地缓存”中读取。这个“本地缓存”只是表象的存在，加上缓存的同步逻辑就是今天要是说的Informer（灰色区域中的第一个蓝色块）所提供的功能。
从图中可以看到 Informer 的几个组件：
Reflector：与 api-server交互，监听资源的变更。 Delta FIFO Queue：增量的 FIFO 队列，保存 Reflector 监听到的资源变更（简单的封装）。 Indexer：Informer 的本地缓存，FIFO 队列中的数据根据不同的变更类型，在该缓存中进行操作。 Local Store： 上篇 提到了水平自动伸缩的控制器HorizontalController，其构造方法就需要提供 Informer。
//pkg/controller/podautoscaler/horizontal.go type HorizontalController struct { scaleNamespacer scaleclient.</description></item><item><title>Kubernetes 源码解析 - HPA 水平自动伸缩如何工作</title><link>https://atbug.com/kubernetes-source-code-how-hpa-work/</link><pubDate>Sat, 15 Aug 2020 02:09:37 +0800</pubDate><guid>https://atbug.com/kubernetes-source-code-how-hpa-work/</guid><description>HPA - Horizontal Pod Autoscaler 的缩写，Pod 水平自动伸缩。通过对 Pod 负载的监控，来自动增加或者减少 Pod 的副本数量。
从字面意思来看，其主要包含了两部分：
监控 Pod 的负载 控制 Pod 的副本数量 那具体是如何实现的呢？以下基于1.17 源码，来分析下 HPA 如何工作。
注意：文章中的代码在源码的基础上进行了精简：删掉了注释、序列化等信息，或保留了部分核心代码，加上新的注释。
资源 HPA 的资源是HorizontalPodAutoscaler，在v1版本中，只支持基于 CPU 指标的计算；在v2beta2版本中加入了基于内存和自定义指标的计算。
v1 //staging/src/k8s.io/api/autoscaling/v1/types.go type HorizontalPodAutoscaler struct { metav1.TypeMeta metav1.ObjectMeta Spec HorizontalPodAutoscalerSpec Status HorizontalPodAutoscalerStatus } type HorizontalPodAutoscalerSpec struct { ScaleTargetRef CrossVersionObjectReference //监控的目标资源 MinReplicas *int32 //最小副本数 MaxReplicas int32 //最大副本数 TargetCPUUtilizationPercentage *int32 //触发调整的CPU 使用率 } v2 //staging/src/k8s.</description></item><item><title>带你了解 Ribbon 负载均衡器的实现</title><link>https://atbug.com/how-loadbalancer-works-in-ribbon/</link><pubDate>Tue, 09 Jun 2020 19:35:53 +0800</pubDate><guid>https://atbug.com/how-loadbalancer-works-in-ribbon/</guid><description>Spring Cloud 中 Ribbon有在 Zuul 和 Feign 中使用，当然也可以通过在RestTemplate的 bean 定义上添加@LoadBalanced注解方式获得一个带有负载均衡更能的RestTemplate。
不过实现的方法都大同小异：对HttpClient进行封装，加上实例的”选择“（这个选择的逻辑就是我们所说的负载均衡）。
要学习某个框架的时候，最简单的方案就是：Running+Debugging。
跑就是了。
debug 不一定是为了 bug
debug 出真知
Debugging = Learning
选用 Ali Spittel 的一条推文：
以 Zuul 路由的线程栈为例 调整下顺序：
RetryableRibbonLoadBalancingHttpClient#execute(RibbonApacheHttpRequest, IClientConfig) RetryableRibbonLoadBalancingHttpClient#executeWithRetry(...) RetryTemplate#execute(RetryCallback&amp;lt;T, E&amp;gt;, RecoveryCallback&amp;lt;T&amp;gt;) RetryTemplate#doExecute(RetryCallback&amp;lt;T, E&amp;gt;, RecoveryCallback&amp;lt;T&amp;gt;, RetryState) RetryTemplate#canRetry(RetryPolicy, RetryContext) InterceptorRetryPolicy#canRetry(RetryContext) AbstractLoadBalancingClient#choose(String serviceId) ZoneAwareLoadBalancer#chooseServer(Object key) //key as serviceId BaseLoadBalancer#chooseServer(Object key) PredicateBasedRule#choose(Object key) AbstractServerPredicate#chooseRoundRobinAfterFiltering(List&amp;lt;Server&amp;gt; servers, Object loadBalancerKey) AbstractServerPredicate#apply(Predicate) 分析 Zuul 收到请求经过一系列 Filter 的处理，来到 RibbonRoutingFilter；将请求封装成 RibbonCommandContext，然后使用 context 构建 RibbonCommand。最终调用RibbonCommand#execute()方法，将请求路由到下游。</description></item><item><title>Eureka 实例注册状态保持 STARTING 的问题排查</title><link>https://atbug.com/troubleshooting-on-eureka-instance-keep-starting/</link><pubDate>Thu, 28 May 2020 22:04:02 +0800</pubDate><guid>https://atbug.com/troubleshooting-on-eureka-instance-keep-starting/</guid><description>这是真实发生在生产环境的 case，实例启动后正常运行，而在注册中心的状态一直保持STARTING，而本地的状态为UP。导致服务的消费方无法发现可用实例。
这种情况的出现概率非常低，运行一年多未发现两个实例同时出现问题的情况，因此多实例运行可以避免。文末有问题的解决方案，不想花时间看分析过程可直接跳到最后。
环境说明：
eureka-client: 1.7.2 spring-boot: 1.5.12.RELEASE spring-cloud: Edgware.SR3
问题重现 借助Btrace重现, java -noverify -cp .:btrace-boot.jar -javaagent:btrace-agent.jar=script=&amp;lt;pre-compiled-btrace-script&amp;gt; &amp;lt;MainClass&amp;gt; &amp;lt;AppArguments&amp;gt;
思路 主线程更新实例本地状态(STARTING-&amp;gt;UP)前, 等待心跳线程完成第一次心跳并尝试注册实例, 获取到当前的状态STARTING. 主线程更新状态后触发
Btrace 脚本
import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.Kind; import com.sun.btrace.annotations.Location; import com.sun.btrace.annotations.OnMethod; import java.util.concurrent.atomic.AtomicBoolean; import static com.sun.btrace.BTraceUtils.currentThread; import static com.</description></item><item><title>Tekton 的工作原理</title><link>https://atbug.com/how-tekton-works/</link><pubDate>Sat, 23 May 2020 22:47:14 +0800</pubDate><guid>https://atbug.com/how-tekton-works/</guid><description>这篇文章是基于 Tekton Pipeline 的最新版本v0.12.1版本。
快速入门请参考：云原生 CICD: Tekton Pipeline 实战 ，实战是基于版本 v0.10.x。
Pipeline CRD 与核心资源的关系 $ k api-resources --api-group=tekton.dev NAME SHORTNAMES APIGROUP NAMESPACED KIND clustertasks tekton.dev false ClusterTask conditions tekton.dev true Condition pipelineresources tekton.dev true PipelineResource pipelineruns pr,prs tekton.dev true PipelineRun pipelines tekton.</description></item><item><title>Java 中的 Mysql 时区问题</title><link>https://atbug.com/mysql-timezone-in-java/</link><pubDate>Thu, 14 May 2020 11:34:24 +0800</pubDate><guid>https://atbug.com/mysql-timezone-in-java/</guid><description>(Photo by Andrea Piacquadio from Pexels)
话说工作十多年，mysql 还真没用几年。起初是外企银行，无法直接接触到 DB；后来一直从事架构方面，也多是解决问题为主。
这次搭建海外机房，围绕时区大家做了一番讨论。不说最终的结果是什么，期间有同事认为 DB 返回的是 UTC 时间。
这里简单做个验证，顺便看下时区的问题到底是如何处理。
环境 openjdk version &amp;ldquo;1.8.0_242&amp;rdquo; mysql-connector-java &amp;ldquo;8.0.20&amp;rdquo; mysql &amp;ldquo;5.7&amp;rdquo; 时区 TZ=Europe/London 本地时区 GMT+8
创建个简单的库test及表user， 表结构如下：
CREATE TABLE `user` ( `name` varchar(50) NOT NULL, `birth_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=latin1 插入一条测试数据：</description></item><item><title>翻译：多运行时微服务架构</title><link>https://atbug.com/translation-multi-runtime-microservices-architecture/</link><pubDate>Wed, 01 Apr 2020 23:18:00 +0800</pubDate><guid>https://atbug.com/translation-multi-runtime-microservices-architecture/</guid><description>这样文章通过Google翻译和人工逐字修改的方式完成的，某些位置也加上自己的理解。如有错误，请指出。
翻译这篇文章的目的其实是为了自己加深对微服务、分布式架构以及多运行时架构的理解。整篇文章从”战略“上分析了微服务”从古至今“解决的问题，以及带来的新问题；进而在“战术”层面，给出了解决这些新问题的手段。
个人见解：架构从来都是解决问题并带来问题， 取舍之道 。
背景知识 微服务的 12 要素：
基准代码：一份基准代码，多份部署 依赖：显式声明依赖关系 配置：在环境中存储配置 后端服务：把后端服务当做附加资源 构建、发布、运行：严格分离构建和运行 进程：以一个或多个无状态进程运行应用 端口绑定：通过端口绑定提供服务 并发：通过进程模型进行扩展 易处理：快速启动和优雅终止可最大化健壮性 开发环境与线上环境等价：尽可能的保持开发、预发布、线上环境相同 日志：把日志当做事件流 管理进程：后台管理任务当做一次性进程运行 原文从此处开始：
创建分布式系统并非易事。围绕“微服务”架构和“ 12要素应用程序”设计出现了最佳实践。这些提供了与交付生命周期，网络，状态管理以及对外部依赖项的绑定有关的准则。 但是，以可扩展和可维护的方式一致地实施这些原则是具有挑战性的。 解决这些原理的以技术为中心的传统方法包括企业服务总线（ESB）和面向消息的中间件（MOM）。虽然这些解决方案提供了良好的功能集，但主要的挑战是整体架构以及业务逻辑和平台之间的紧密技术耦合。 随着云，容器和容器协调器（Kubernetes）的流行，出现了解决这些原理的新解决方案。例如，Knative用于交付，服务网格用于网络，而Camel-K用于绑定和集成。 通过这种方法，业务逻辑（称为“微逻辑”）构成了应用程序的核心，并且可以创建提供强大的现成分布式原语的sidecar“ mecha”组件。 微观组件和机械组件的这种分离可以改善第二天的操作，例如打补丁和升级，并有助于维持业务逻辑内聚单元的长期可维护性。 创建良好的分布式应用程序并非易事：此类系统通常遵循12要素应用程序和微服务原则。它们必须是无状态的，可伸缩的，可配置的，独立发布的，容器化的，可自动化的，并且有时是事件驱动的和无服务器的。创建后，它们应该易于升级，并且长期可以承受。在当今的技术中，要在这些相互竞争的要求之间找到良好的平衡仍然是一项艰巨的努力。
在本文中，我将探讨分布式平台如何发展以实现这种平衡，更重要的是，在分布式系统的演进中还需要发生什么事情，以简化可维护的分布式体系结构的创建。如果您想让我实时谈论这个话题，请加入我的QCon 三月的伦敦。
分布式应用程序需求 在此讨论中，我将把现代分布式应用程序的需求分为四个类别-生命周期，网络，状态，绑定-并简要分析它们在最近几年中的发展情况。
生命周期 Lifecycle 打包 Packaging 健康检查 Healthcheck 部署 Deployment 扩展 Scaling 配置 Configuration 让我们从基础开始。当我们编写一项功能时，编程语言将指示生态系统中的可用库，打包格式和运行时。例如，Java使用.</description></item><item><title>控制 Pod 内容器的启动顺序</title><link>https://atbug.com/control-process-order-of-pod-containers/</link><pubDate>Thu, 12 Mar 2020 22:05:16 +0800</pubDate><guid>https://atbug.com/control-process-order-of-pod-containers/</guid><description>2021.4.30 更新：
最新的方案，请跳转新篇 Kubernetes 上如何控制容器的启动顺序。
背景 众所周知, Kubernetes Pod 内有两种容器: 初始化容器(init container)和应用容器(app container). 其中初始化容器的执行先于应用容器, 并且初始化容器和应用容器的个数分别为 0~n 和 1~n.
初始化容器会按照顺序执行, 顺序执行的前提是初始化容器始终会运行到完成(completed)状态. 而应用容器恰好相反: 启动顺序随机, 并始终保持运行(running)状态.
问题 工作中有个架构的方案使用到了 sidecar 容器: 将基础组件功能从容器转移到 sidecar 容器中, 其中有个功能是从远程配置中心获取配置并保持实时更新. 保证实时更新没有问题, 但是配置文件需要在 app 启动之前完成初始化.
对于同为&amp;quot;应用容器&amp;quot;类型的 sidecar 容器来说, 由于容器启动顺序随机而无法做到这一点.</description></item><item><title>Go Docker 镜像进阶: 精简镜像</title><link>https://atbug.com/build-minimal-docker-image-for-go-app/</link><pubDate>Wed, 11 Mar 2020 23:00:27 +0800</pubDate><guid>https://atbug.com/build-minimal-docker-image-for-go-app/</guid><description>​[图片来自 https://www.facebook.com/sequenceprocess/]
问题: 入门到生产级的差距 昨天的文章《为 Go 应用创建 Docker 镜像》, 算是入门级的, 并不适用于生产级. 为什么?
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE addozhang/golang-hello-world latest 4cce1292a87a 4 seconds ago 813MB 整个镜像的大小有 813MB, 这还只有一个简单的 Hello world. 因为其中包含了 Golang 的编译和运行环境. 但是实际生产环境中, 我们并不需要这么多.
先看结果 精简之后只有 2.</description></item><item><title>为 Go 应用创建 Docker 镜像</title><link>https://atbug.com/build-docker-image-for-go-app/</link><pubDate>Wed, 11 Mar 2020 20:41:58 +0800</pubDate><guid>https://atbug.com/build-docker-image-for-go-app/</guid><description>嗯嗯, 最近开始用 Golang 了.
今天需要为 Go 应用创建对象, 看了下官方博客. 拿 hello world 做个测试.
使用下面的命令创建个新的项目
$ mkdir -p $GOPATH/src/github.com/addozhang/golang-hello-world &amp;amp;&amp;amp; cd &amp;#34;$_&amp;#34; $ go mod init github.com/addozhang/golang-hello-world go: creating new go.mod: module github.com/addozhang/golang-hello-world $ cat &amp;lt;&amp;lt; EOF &amp;gt; main.go package main import &amp;#34;fmt&amp;#34; func main() { fmt.</description></item><item><title>云原生CICD: Tekton Trigger 实战</title><link>https://atbug.com/tekton-trigger-practice/</link><pubDate>Wed, 12 Feb 2020 21:30:03 +0800</pubDate><guid>https://atbug.com/tekton-trigger-practice/</guid><description>Trigger的介绍看 这里.
接上文 Tekton Pipeline 实战 , 我们为某个项目创建了一个Pipeline, 但是执行时通过 PipelineRun 来完成的. 在 PipelineRun 中我们制定了 Pipepline 以及要使用的 PipelineResource. 但是日常的开发中, 我们更多希望在提交了代码之后开始 Pipeline 的执行. 这时我们就要用到 Tekton Trigger 了.
思路是这样: 代码提交后将Push Event发送给Tekton Trigger EventController(以下简称 Controller), 然后 Controller 基于的TriggerBinding的配置从 payload 中提取信息, 装载在&amp;quot;Params&amp;quot;中作为TriggerTemplate的入参. 最后 Controller 创建PipelineRun.</description></item><item><title>Tekton Trigger 介绍</title><link>https://atbug.com/tekton-trigger-glance/</link><pubDate>Wed, 05 Feb 2020 18:03:15 +0800</pubDate><guid>https://atbug.com/tekton-trigger-glance/</guid><description>背景 Tekton 的介绍请参考Tekton Pipeline 实战.
通常, CI/CD 事件应该包含如下信息:
确定事件的类型(比如 GitHub Push, GitLab Issue, Docker Hub Webhook 等) 可从特定管道访问并映射到特定管道 (从事件负载中获取 SHA 信息, 然后在管道中使用) 准确地触发管道 (基于有效负载值触发管道) Tekton API 的设计分离了配置(比如 PipelineRun VS Pipeline), 保证了 step 可以被重用. 但是没有提供动态封装配置的机制来生成资源(尤其是 PipelineRun 和 PipelineResource). Triggers 通过下面的 CRDs 在架构上对 Tekton 进行了扩展:</description></item><item><title>Tekton Dashboard 安装</title><link>https://atbug.com/tekton-dashboard-installation/</link><pubDate>Sat, 01 Feb 2020 12:39:28 +0800</pubDate><guid>https://atbug.com/tekton-dashboard-installation/</guid><description>Tekton 提供了dashboard方便用户管理和查看 Tekton PipelineRun 和 TaskRun 以及创建, 执行和完成过程中涉及的资源. 它还允许按标签过滤 PipelineRun 和 TaskRun.
安装方法 kubectl apply --filename https://github.com/tektoncd/dashboard/releases/download/v0.4.1/dashboard_latest_release.yaml 检查dashboard的运行情况, STATUS为Running的话则说明运行成功.
kubectl get pods --namespace tekton-pipelines 访问 访问Tekton的Dashboard有两种方式, 一种是通过port-forward, 另一种是通过ingress来访问.
port-forward kubectl port-forward svc/tekton-dashboard 9097 ingress 先检查ingress是否开启.
minikube addon list ... - ingress: enabled .</description></item><item><title>Tekton 0.9.0 更新</title><link>https://atbug.com/tekton-0.9.0-release/</link><pubDate>Sun, 19 Jan 2020 14:33:17 +0800</pubDate><guid>https://atbug.com/tekton-0.9.0-release/</guid><description>翻译整理自 What’s New in Tekton 0.9
功能及Bug修复 脚本模式 以前如果要在容器里运行个简单的 bash 脚本, 需要这么写:
- name: hello image: ubuntu command: [&amp;#39;bash&amp;#39;] args: - -c - | set -ex echo &amp;#34;hello&amp;#34; 在 0.9 之后, 可以更加简单, 不需要再写command 和讨厌的-c.
- name: hello image: ubuntu script: | #!</description></item><item><title>Tekton安装及Hello world</title><link>https://atbug.com/tekton-installation-and-sample/</link><pubDate>Fri, 17 Jan 2020 19:17:14 +0800</pubDate><guid>https://atbug.com/tekton-installation-and-sample/</guid><description>安装 kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml 检查安装的tekton相关的CRD:
kubectl api-resources | grep tekton clustertasks tekton.dev false ClusterTask conditions tekton.dev true Condition pipelineresources tekton.dev true PipelineResource pipelineruns pr,prs tekton.dev true PipelineRun pipelines tekton.dev true Pipeline taskruns tr,trs tekton.dev true TaskRun tasks tekton.dev true Task tekton的两个pod:</description></item><item><title>Minikube安装istio</title><link>https://atbug.com/install-istio-on-minikube/</link><pubDate>Fri, 17 Jan 2020 08:02:42 +0800</pubDate><guid>https://atbug.com/install-istio-on-minikube/</guid><description>准备 注意: istioctl的安装要使用安装里的, 不要是用homebrew里的. github issue
curl -L https://istio.io/downloadIstio | sh - cd istio-1.4.2 cp bin/istioctl /usr/local/bin/istioctl 安装前检查 istioctl verify-install 如果检查没问题, 会看到Install Pre-Check passed! The cluster is ready for Istio installation.
安装 istio有5种内建的安装配置1: remote, sds, default, demo, minimal
istioctl profile list minimal: 使用istio的流量管理所需组件的最小化安装 default: 根据IstioControlPlane API的默认设置(建议用于生产部署)启用组件.</description></item><item><title>神秘的 Eureka 自我保护</title><link>https://atbug.com/translation-the-mystery-of-eurekas-self-preservation/</link><pubDate>Sun, 05 Jan 2020 14:14:03 +0800</pubDate><guid>https://atbug.com/translation-the-mystery-of-eurekas-self-preservation/</guid><description>本文翻译自The Mystery of Eureka Self-Preservation
根据CAP定理, Eureka是一个AP系统, 这就导致了在网络分区期间多个注册表中的信息不一致. 自我保护功能则是为了尽可能降低这种不一致.
自我保护的定义 自我保护(self preservation)是Eureka的一项功能, Eureka注册表在未收到实例的心跳情况超过一定阈值时停止驱逐过期的实例.
从一个健康的系统开始 把下面看成一个健康的系统
假设所有的微服务都处于健康的状态并成功注册到Eureka注册表中.
多个注册表间会同步注册表记录, 所有的微服务实例都处于UP状态. 假设实例2从注册中心发现里实例4, 并调用实例4上的服务.
突发网络分区 假设出现了网络分区, 系统变成下面的状态.
由于网络分区, 实例4和5丢失了注册中心的连接, 但是实例2仍然可以连接到实例4. Eureka服务端因为没有收到实例4和5的心跳(超过一定时间后), 将他们驱逐. 然后Eureka服务端意识到突然丢失了超过15%(2/5)的心跳, 因此其进入自我保护模式
从此时开始, Eureka服务端不在驱逐任何实例, 即使实例真正的下线了.
实例3下线, 但其始终存在注册表中.
但此时注册表还会接受新实例的注册.
自我保护的基本原理 自我保护功能在下面两种情况下是合理的:
Eureka服务端因为弱网分区问题没有收到心跳(这并不意味着客户端下线), 但是这种问题可能会很快被修复. 即使Eureka服务端和客户端的连接断开, 客户端间还可以继续保持连接.</description></item><item><title>加速云原生的 Java 开发</title><link>https://atbug.com/speed-up-java-development-on-kubernetes/</link><pubDate>Sat, 21 Dec 2019 20:45:22 +0800</pubDate><guid>https://atbug.com/speed-up-java-development-on-kubernetes/</guid><description>今天来说说日常在Kubernetes开发Java项目遇到的问题.
当我们新建一个项目的时候, 总是面临需要新建manifest, 平时都是copy+paste+modify. 能否以变成的方式来生成?
开发时的步骤也比较繁琐: docker build, docker push, kubectl apple, kubectl delete pod. 对于一个Java应用来说还多了一步编译. 操作一次还ok, 但是一天十几次总会有想吐的感觉. 这些步骤能否简化成一个命令, 甚至修改了代码自动就完成上面一系列的操作?
实现这些我们需要几个工具: dekorate, Jib, Skaffold. 其中Jib也在上一篇文章使用Jib为Java应用构建镜像中介绍过.
dekorate Dekorate is a collection of Java compile-time generators and decorators for Kubernetes/OpenShift manifests. Dekorate是Java编译时生成和装饰Kubernetes/OpenShift的manifests的工具</description></item><item><title>使用 Jib 为 Java 应用构建镜像</title><link>https://atbug.com/build-docker-or-oci-image-with-jib-for-java/</link><pubDate>Mon, 09 Dec 2019 10:05:30 +0800</pubDate><guid>https://atbug.com/build-docker-or-oci-image-with-jib-for-java/</guid><description>Jib是Google Container Tools中的一个工具。
Jib builds optimized Docker and OCI images for your Java applications without a Docker daemon - and without deep mastery of Docker best-practices. It is available as plugins for Maven and Gradle and as a Java library.</description></item><item><title>Spring Cloud Hoxton发布</title><link>https://atbug.com/spring-cloud-hoxton-release/</link><pubDate>Wed, 04 Dec 2019 11:09:07 +0800</pubDate><guid>https://atbug.com/spring-cloud-hoxton-release/</guid><description>原文
Spring Cloud Hoxton.RELEASE基于Spring Boot 2.2.1.RELEASE
文档变化 Hoxton.RELEASE使用了新的首页, 新的样式以及单页面, 多页面和PDF版本.
新的负载均衡器实现 Hoxton.RELEASE是第一个包含阻塞和非阻塞客户端负载均衡器实现的版本, 替代进入维护状态的Netflix Ribbon.
搭配BlockingLoadBalancerClient使用RestTemplate, 需要在classpath中引入org.springframework.cloud:spring-cloud-loadbalancer. 这个依赖同样用于使用了@LoadBalanced WebClient.Builder的响应式应用中. 唯一的区别是Spring Cloud会自动配置ReactorLoadBalancerExchangeFilterFunction实例. 更多内容查看文档. 新的ReactorLoadBalancerExchangeFilterFunction可用于自动装配并自动传递给WebClient.Builder(文档).
Spring Cloud Netflix 增加了新的ReactiveDiscoveryClient, 同时增加了新的Spring Cloud Circuit Breaker API的Hystrix实现. 增加配置项spring.cloud.circuitbreaker.hystrix.enabled来禁用Spring Cloud CircuitBreaker Hystrix的自动配置. Spring Cloud Cloudfoundry 支持新的ReactiveDiscoveryClient
Spring Cloud Bus 文档更新</description></item><item><title>Bose QC35 固件降级</title><link>https://atbug.com/bose-qc35-downgrade/</link><pubDate>Sun, 10 Nov 2019 19:50:27 +0800</pubDate><guid>https://atbug.com/bose-qc35-downgrade/</guid><description>为什么要降级? 既然已经搜到了这里, 相信个中原因也都清楚.
我的QC35一代, 之前升级到了3.0.3固件. 目前已成功降级到了1.0.6, 下面的操作步骤Mac OSX的, 作者也提供了Windows上的操作步骤.
操作步骤 确认已经卸载Bose Updater GitHub上下载作者已经改好的6.0.0.4388版本的Bose Updater 解压后复制到/Applications下 运行Bose Updater确认能否正常运行 退出 (右键&amp;gt;Exit) 检查是否还有Bose Updater的进程 打开https://btu.bose.com/ 页面上选择Launch Bose Updater 看到下面界面(借用作者的图)时, 键盘上依次按: a, d, v, 方向上, 方向下 接下来会看到下面界面(借用作者的图) 可以从下拉列表中选择版本进行升/降级 等待升/降级完成 卸载手机app (我是卸载了, 防止再次手贱) 参考 Reddit GitHub</description></item><item><title>Docker Engine API on Mac Osx</title><link>https://atbug.com/docker-engine-api-on-mac-osx/</link><pubDate>Wed, 06 Nov 2019 20:19:50 +0800</pubDate><guid>https://atbug.com/docker-engine-api-on-mac-osx/</guid><description>根据官方的文档Docker Desktop on Mac vs. Docker Toolbox, Docker Desktop on Mac只提供了UNIX socket/var/run/docker.sock, 并未提供tcp的监听(默认2375端口).
如果使用linux的配置方式在Docker Desktop中配置host, Docker Desktop将无法启动. 需要去~/.docker/daemon.json中删除hosts配置才能正常启动.
通过下面的方式暴露出2375的tcp
docker run --rm -d -v /var/run/docker.sock:/var/run/docker.sock -p 127.0.0.1:2375:2375 bobrik/socat TCP-LISTEN:2375,fork UNIX-CONNECT:/var/run/docker.sock 然后通过docker version查看当前的docker engine的版本, 比如1.40. 查看官方的Engine API文档: https://docs.docker.com/engine/api/v1.40
搜索个镜像测试一下:</description></item><item><title>Spring Boot 2.2.0 发布</title><link>https://atbug.com/spring-boot-2-2-0-release/</link><pubDate>Tue, 22 Oct 2019 09:27:03 +0800</pubDate><guid>https://atbug.com/spring-boot-2-2-0-release/</guid><description>译自: https://spring.io/blog/2019/10/16/spring-boot-2-2-0
组件升级 Spring AMQP 2.2 Spring Batch 4.2 Spring Data Moore Spring Framework 5.2 Spring HATEOAS 1.0 Spring Integration 5.2 Spring Kafka 2.3 Spring Security 5.2 Spring Session Corn 第三方库升级 Elasticsearch 6.7 Flyway 6.0 Jackson 2.10 JUnit 5.5 Micrometer 1.</description></item><item><title>Zipkin dependencies的坑之二: 心跳超时和Executor OOM</title><link>https://atbug.com/zipkin-dependencies-bug-two-timeout-and-oom/</link><pubDate>Sun, 22 Sep 2019 18:27:37 +0800</pubDate><guid>https://atbug.com/zipkin-dependencies-bug-two-timeout-and-oom/</guid><description>上回说为了解决吞吐问题, 将zipkin-dependencies的版本升级到了2.3.0.
好景不长, 从某一天开始作业运行报错:
Issue communicating with driver in heartbeater org.apache.spark.rpc.RpcTimeoutException: Futures timed out after [10000 milliseconds]. This timeout is controlled by spark.executor.heartbeatInterval ... 19/09/18 08:33:20 ERROR Executor: Exception in task 1.0 in stage 1.0 (TID 4) java.lang.OutOfMemoryError: Java heap space .</description></item><item><title>Zipkin dependencies的坑之一: 耗时越来越长</title><link>https://atbug.com/zipkin-dependencies-bug-one/</link><pubDate>Sun, 22 Sep 2019 17:59:56 +0800</pubDate><guid>https://atbug.com/zipkin-dependencies-bug-one/</guid><description>zipkin-dependencies是zipkin调用链的依赖分析工具.
系统上线时使用了当时的最新版本2.0.1, 运行一年之后随着服务的增多, 分析一天的数据耗时越来越多. 从最初的几分钟, 到最慢的几十小时(数据量18m).
最终返现是版本的问题, 升级到&amp;gt;=2.3.0的版本之后吞吐迅速上升.
所以便有了issue: Reminder: do NOT use the version before 2.3.0
但这也引来了另一个坑: 心跳超时和Executor OOM
TL;DR 简单浏览了下zipkin-dependencies的源码, 2.0.1和2.3.2的比较大的差距是依赖的elasticsearch-spark的版本. 前者用的是6.3.2, 后者是7.3.0.
尝试在zipkin-dependencies-2.0.1中使用elasticsearch-spark-7.3.0, 和2.3.2的性能一直.
通过打开log4j debug日志, 发现到elasticsearch-spark两个版本的运行差异:
#7.3.0 19/09/05 18:13:14 INFO DAGScheduler: Submitting 3 missing tasks from ShuffleMapStage 0 (MapPartitionsRDD[1] at groupBy at ElasticsearchDependenciesJob.</description></item><item><title>如何选择Kafka Topic的分区数</title><link>https://atbug.com/how-to-choose-topic-partition-count-number-kafka/</link><pubDate>Fri, 30 Aug 2019 11:10:46 +0800</pubDate><guid>https://atbug.com/how-to-choose-topic-partition-count-number-kafka/</guid><description>在kafka中, topic的分区是并行计算的单元. 在producer端和broker端, 可以同时并发的写数据到不同的分区中. 在consumer端, Kafka总是将某个分区分配个一个consumer线程. 因此同一个消费组内的并行度与分区数息息相关.
Partition分区数的大小, 更多直接影响到消费端的吞吐(一个分区只能同一消费组的一个消费者消费). 分区数小, 消费端的吞吐就低. 但是太大也会有其他的影响
原则:
更多的分区可提高吞吐量 分区数越多打开的文件句柄越多 分区数越多降低可用性 更多的分区增加端到端的延迟 客户端需要更多的内存 归根结底还是得有个度. 如何找出这个度?
有个粗略的计算公式: max(t/p, t/c). t就是所预期吞吐量, p是当前生产端单个分区的吞吐, 那c就是消费端单个分区的吞吐.
比如单个partition的生产端吞吐是200, 消费端是100. 预期的吞吐是500, 那么partition的数量就是5.
单个分区的吞吐通常通过修改配置来提升, 比如生产端的批处理大小, 压缩算法, acknowledgement类型, 副本数等. 而在消费端则更依赖于消息的处理速度.
参考 Confluent博客 Linkedin的benchmark</description></item><item><title>博客最近半年没什么产出</title><link>https://atbug.com/no-output-in-past-half-year/</link><pubDate>Tue, 27 Aug 2019 14:29:12 +0000</pubDate><guid>https://atbug.com/no-output-in-past-half-year/</guid><description>上一篇日志更新还是在去年的12月, 至今有差不多10个月没有更新了.
不是说没有东西可写, 而且想写的东西很多. 工作太忙, 不忙的时候又太懒, 归根结底还是太懒.
过去一年多都是在做基础架构方面的工作, 围绕技术中台展开的. 有很多技术需要去学习, 也有很多问题要处理. 过程中一直有记笔记的习惯, 所以可以写的东西很多. 不过有些属于公司的部分还是不能写的, 必要的职业道德还是要有的.
笔记记录一直在用MWeb, 并使用iCloud同步, 最近几个月也在结合幕布整理思路和工作安排. 好用的软件我也比较喜欢分享, 记得最早在Workpress上的博客就分享了很多自己常用的软件. (有点扯远了~~~)
MWeb没有统计功能, 还有使用的是sqlite. 简单sql查询了下, 从去年这份工作开始有244篇笔记. 今年到现在有109篇. 当然有些笔记的内容比较少, 不得不说这一年多收获甚多.
为什么今天又写了这么一篇, 源于阮一峰的科技爱好者周刊：第 69 期.
刊首语是&amp;quot;一件事&amp;quot;做得好&amp;quot;比较好，还是&amp;quot;做得快&amp;quot;比较好？&amp;quot;, 直接copy他的结论.
我很赞同一篇文章的结论：做得快更好。
做得快不仅可以让你在单位时间内完成更多的工作，而且 因为你工作得很快，所以你会觉得成本低，从而倾向于做更多。
写一篇博客，你可能需要两天。这是很高的时间成本，你觉得太贵了，于是你很少写。但是，做好一件事的唯一方法，就是多做这件事。 做得越快，这件事的时间成本就越低，你会愿意做得更多。
人们总是倾向于，多消费时间成本低的东西。网站很快，就会多访问；搜索很快，就会多搜索；文章很容易读懂，就会多读几篇。做得快的核心，就是要让时间成本降下来，从而多做。</description></item><item><title>Spring Boot源码分析 - Configuration注解</title><link>https://atbug.com/spring-boot-configuration-annotation/</link><pubDate>Mon, 10 Dec 2018 16:24:33 +0000</pubDate><guid>https://atbug.com/spring-boot-configuration-annotation/</guid><description>@Configuration注解 @Configuration注解指示一个类声明一个或多个@Bean方法, 并且可以由Spring容器处理, 以在运行时为这些bean生成bean定义和服务请求.
使用ConfigurationClassParser来对@Configuration标注的类进行解析, 封装成ConfigurationClass实例. 具体的实现通过ConfigurationClassPostProcessor来实现的.
ConfigurationClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor接口, 间接实现了BeanFactorPostProcessor接口.
#postProcessBeanDefinitionRegistry(): 注册所有ConfigurationClass中的BeanDefinition, 包括@Bean注解的方法, @ImporResource引入的资源中定义的bean, 和@Import注解引入的ImportBeanDefinitionRegistrar中注册的BeanDefinition #postProcessBeanFactory(): 在运行时以通过cglig增强的类来替换ConfigurationClass, 为服务bean请求做准备. 增强的实现是通过ConfigurationClassEnhancer完成的. 插入一点, ConfigurationClassEnhancer实现了直接使用bean注册方法来获取bean的操作, 提供了一个BeanMethodInterceptor的内部类来实行.
@Configuration public class Config { @Bean public A a() { ... return a; } @Bean public B b() { b.</description></item><item><title>Nginx实现Elasticsearch的HTTP基本认证</title><link>https://atbug.com/elasticsearch-http-basic-authentication-via-nginx/</link><pubDate>Tue, 06 Nov 2018 09:24:24 +0000</pubDate><guid>https://atbug.com/elasticsearch-http-basic-authentication-via-nginx/</guid><description>Elasticssearch的HTTP基本认证实现有两种方案: x-pack和nginx反向代理. 前者收费, 后者不太适合生产使用. 如果仅仅是开发测试, 第二种完全足够.
创建密码 htpasswd -bc ./passwd [username] [password] Docker compose version: &amp;#39;3&amp;#39; services: elasticsearch: image: elasticsearch:5.5.2 container_name: elasticsearch restart: unless-stopped volumes: - /tmp/elasticsearch:/usr/share/elasticsearch/data nginx: image: nginx:latest container_name: elasticsearch-proxy ports: - 9200:9200 links: - elasticsearch volumes: - .</description></item><item><title>Search Result</title><link>https://atbug.com/search/</link><pubDate>Mon, 24 Sep 2018 11:07:10 +0600</pubDate><guid>https://atbug.com/search/</guid><description/></item><item><title>Alpine容器安装Docker和OpenShift Client Tools</title><link>https://atbug.com/install-docker-and-openshift-client-tools-in-alpine-container/</link><pubDate>Tue, 28 Aug 2018 09:14:12 +0000</pubDate><guid>https://atbug.com/install-docker-and-openshift-client-tools-in-alpine-container/</guid><description>安装Docker echo &amp;#34;http://dl-2.alpinelinux.org/alpine/edge/main&amp;#34; &amp;gt; /etc/apk/repositories echo &amp;#34;http://dl-2.alpinelinux.org/alpine/edge/community&amp;#34; &amp;gt;&amp;gt; /etc/apk/repositories echo &amp;#34;http://dl-2.alpinelinux.org/alpine/edge/testing&amp;#34; &amp;gt;&amp;gt; /etc/apk/repositories apk -U --no-cache \ --allow-untrusted add \ shadow \ docker \ py-pip \ openrc \ &amp;amp;&amp;amp; pip install docker-compose rc-update add docker boot 安装OpenShift Client Tools 需要先安装glibc</description></item><item><title>Zuul网关Ribbon重试</title><link>https://atbug.com/ribbon-retry-in-zuul/</link><pubDate>Thu, 02 Aug 2018 08:55:43 +0000</pubDate><guid>https://atbug.com/ribbon-retry-in-zuul/</guid><description>相关配置 #如果路由转发请求发生超时(连接超时或处理超时), 只要超时时间的设置小于Hystrix的命令超时时间,那么它就会自动发起重试. 默认为false. 或者对指定响应状态码进行重试 zuul.retryable = true zuul.routes.&amp;lt;route&amp;gt;.retryable = false #同一实例上的最大重试次数, 默认值为0. 不包括首次调用 ribbon.MaxAutoRetries=0 #重试其他实例的最大重试次数, 不包括第一次选的实例. 默认为1 ribbon.MaxAutoRetriesNextServer=1 #是否所有操作执行重试, 默认值为false, 只重试`GET`请求 ribbon.OkToRetryOnAllOperations=false #连接超时, 默认2000 ribbon.ConnectTimeout=15000 #响应超时, 默认5000 ribbon.ReadTimeout=15000 #每个host的最大连接数 ribbon.MaxHttpConnectionsPerHost=50 #最大连接数 ribbon.MaxTotalHttpConnections=200 #何种响应状态码才进行重试 ribbon.retryableStatusCodes=404,502 实现 SimpleRouteLocator#getRoute返回的route对象中会带上retryable的设置. PreDecorationFilter在对RequestContext进行装饰的时候会将retryable的设置通过keyFilterConstants.RETRYABLE_KEY注入RequestContext中. RibbonRoutingFilter#buildCommandContext会使用RequestContext的retryable设置构造RibbonCommandContext对象. RibbonCommandFactory使用RibbonCommandContext构建出RibbonCommand对象. RibbonCommand#run中, 当retryable为true时, 会调用IClient的execute方法处理请求.</description></item><item><title>Hystrix工作原理三</title><link>https://atbug.com/hystrix-exception-handling/</link><pubDate>Sun, 24 Jun 2018 16:20:16 +0000</pubDate><guid>https://atbug.com/hystrix-exception-handling/</guid><description>异常处理 Hystrix异常类型 HystrixRuntimeException HystrixBadRequestException HystrixTimeoutException RejectedExecutionException HystrixRuntimeException HystrixCommand失败时抛出, 不会触发fallback.
HystrixBadRequestException 用提供的参数或状态表示错误的异常, 而不是执行失败. 与其他HystrixCommand抛出的异常不同, 这个异常不会触发fallback, 也不会记录进failure的指标, 因而也不会触发断路器,
应该在用户输入引起的错误是抛出, 否则会它与容错和后退行为的目的相悖.
不会触发fallback, 也不会记录到错误的指标中, 也不会触发断路器.
RejectedExecutionException 线程池发生reject时抛出
HystrixTimeoutException 在HystrixCommand.run()或者HystrixObservableCommand.construct()时抛出, 会记录timeout的次数. 如果希望某些类型的失败被记录为timeout, 应该将这些类型的失败包装为HystrixTimeoutException
异常处理 ignoreExceptions
final Func1&amp;lt;Throwable, Observable&amp;lt;R&amp;gt;&amp;gt; handleFallback = new Func1&amp;lt;Throwable, Observable&amp;lt;R&amp;gt;&amp;gt;() { @Override public Observable&amp;lt;R&amp;gt; call(Throwable t) { circuitBreaker.</description></item><item><title>Hystrix工作原理二</title><link>https://atbug.com/hystrix-isolation/</link><pubDate>Sun, 24 Jun 2018 16:18:52 +0000</pubDate><guid>https://atbug.com/hystrix-isolation/</guid><description>隔离策略 线程和线程池 客户端(库, 网络调用等)在各自的线程上运行. 这种做法将他们与调用线程隔开, 因此调用者可以从一个耗时的依赖调用&amp;quot;离开(walk away)&amp;quot;
Hystrix使用单独的, 每个依赖的线程池作为约束任何给定依赖的一种方式, 因此潜在执行的延迟将仅在该池中使可用线程饱和.
如果不试用线程池可以保护你免受故障的影响, 但是这需要客户端可信任地快速失败(网络连接/读取超时, 重试的配置)并始终表现良好.
在Hystrix的设计中, Netflix选择试用线程和线程池来达到隔离的目的, 原因有:
很多应用程序调用了由很多不同的团队开发的许多(有时超过1000)不同的后端服务 每个服务都各自提供了其客户端库 客户端库不断地在更新 客户端库可能被添加使用新的网络调用 客户端库的逻辑中可能包含重试, 数据解析, 缓存(内存或者跨网络)和其他类似的行为 客户端库更类似于一个黑盒, 其实现细节, 网络访问模式, 默认配置等是对使用者不透明的 在实际的生产问题中, 根源经常是 &amp;ldquo;有些东西改变了, 配置应该被修改&amp;rdquo; 或者 &amp;ldquo;客户端库修改了逻辑&amp;rdquo; 即使客户端没有改变, 服务端自身发生了变会员. 这种变化会是客户端设置无效而影响性能特性 传递依赖会引入其他客户端, 这些客户端不是可预期的, 也可能没有被正确地配置 大多数网络访问是同步的 失败和延迟也可能发生在客户端, 不只是网络调用 线程池的优势 该应用程序完全免受失控客户端库的保护.</description></item><item><title>Hystrix工作原理一</title><link>https://atbug.com/how-hystrix-works/</link><pubDate>Mon, 04 Jun 2018 08:47:40 +0000</pubDate><guid>https://atbug.com/how-hystrix-works/</guid><description>运行时的流程图 构建HystrixCommand或者HystrixObservableCommand对象
第一步是构建一个HystrixCommand或HystrixObservableCommand对象来代表对依赖服务所做的请求。 将在请求发生时将需要的任何参数传递给构造函数。
如果依赖的服务预期会返回单一的响应, 构造一个HystrixCommand对象, 例如:
HystrixCommand command = new HystrixCommand(arg1, arg2); 如果依赖的服务预期会返回一个发出响应的Observable对象, 则构造一个HystrixObservableCommand对象, 例如:
HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2); 执行Command
响应是否被缓存?
如果Command的缓存请求被开启, 同时请求的响应在缓存中可用, 缓存的响应被立即以一个Observable的方式返回.
断路器是否开启?
执行Command时, Hystrix会检查断路器(circuti-breaker)是否开始回路(circuit).
如果回路开启, Hystrix将不会执行Command, 而直接去到流程8: Get the Fallback 如果关闭, 则执行流程5检查是否有足够的容量来运行该命令
线程池/队列/限号量是否满?
假如与Command相关的线程池和队列(或者信号量, 不适用隔离线程的话)满了, Hystrix将不会执行Command, 而是直接去到流程8.</description></item><item><title>解决 rsyslogd 资源占用率高问题</title><link>https://atbug.com/rsyslogd-high-cpu-trouble-shooting/</link><pubDate>Fri, 01 Jun 2018 09:32:28 +0000</pubDate><guid>https://atbug.com/rsyslogd-high-cpu-trouble-shooting/</guid><description>rsyslogd资源占用高问题记录 问题: openshift集群安装在esxi的虚拟机上. 各个节点出现问题, 集群响应很慢.
kswapd0进程cpu 90%多. rsyslogd进程内存 90%多. **先上总结: **
system-journal服务监听/dev/logsocket获取日志, 保存在内存中, 并间歇性的写入/var/log/journal目录中.
rsyslog服务启动后监听/run/systemd/journal/syslogsocket获取syslog类型日志, 并写入/var/log/messages文件中. 获取日志时需要记录日志条目的position到/var/lib/rsyslog/imjournal.state文件中.
可能是虚拟机系统安装问题, 导致没有创建/var/lib/rsyslog. rsyslog将异常日志写入/dev/logsocket中.
这样就导致了死循环, rsyslog因为要打开/var/log/messages并写入日志, 消耗cpu, 内存还有磁盘I/O.
诊断步骤: rsyslog 重启rsyslog服务
重启之后内存得到释放, 但是rsyslogd进程cpu跑到90%多, 且内存在持续升高.
检查服务状态发现进程一直在报错:
fopen() failed: &amp;#39;Permission denied&amp;#39;, path: &amp;#39;/imjournal.state.tmp&amp;#39; [try http://www.rsyslog.com/e/2013 ] fopen() failed: &amp;#39;Permission denied&amp;#39;, path: &amp;#39;/imjournal.</description></item><item><title>Kubernetes 中的 Nginx 动态解析</title><link>https://atbug.com/nginx-dynamic-domain-parse-in-kubernetes/</link><pubDate>Wed, 30 May 2018 12:10:32 +0000</pubDate><guid>https://atbug.com/nginx-dynamic-domain-parse-in-kubernetes/</guid><description>背景 Nginx运行在kubernets中, 反向代理service提供服务.
kubernetes版本v1.9.1+a0ce1bc657.
问题: 配置如下:
location ^~/info { proxy_pass: http://serviceName:port; } 删除并重建Service的时候, nginx会出现下面的问题:
connect() failed (113: No route to host) &amp;hellip; upstream: &amp;ldquo;xxxxx&amp;rdquo;
分析 通过google发现, 是nginx的dns解析方案的问题.
nginx官方的说明:
If the domain name can’t be resolved, NGINX fails to start or reload its configuration.</description></item><item><title>Spring Cloud Ribbon 详解</title><link>https://atbug.com/spring-cloud-ribbon-breakdown-1/</link><pubDate>Sat, 05 May 2018 11:18:05 +0000</pubDate><guid>https://atbug.com/spring-cloud-ribbon-breakdown-1/</guid><description>&lt;p>客户端负载均衡, Ribbon的核心概念是命名的客户端.&lt;/p>
&lt;h2 id="使用">使用&lt;/h2>
&lt;h3 id="引入ribbon依赖和配置">引入Ribbon依赖和配置&lt;/h3>
&lt;p>加入&lt;code>spring-cloud-starter-netflix-ribbon&lt;/code>依赖&lt;/p>
&lt;h3 id="代码中使用ribbonclient注解">代码中使用RibbonClient注解&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">@RibbonClient&lt;/span>&lt;span style="color:#f92672">(&lt;/span>name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;foo&amp;#34;&lt;/span>&lt;span style="color:#f92672">,&lt;/span> configuration &lt;span style="color:#f92672">=&lt;/span> FooConfiguration&lt;span style="color:#f92672">.&lt;/span>&lt;span style="color:#a6e22e">class&lt;/span>&lt;span style="color:#f92672">)&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">TestConfiguration&lt;/span> &lt;span style="color:#f92672">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">@Configuration&lt;/span> &lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">FooConfiguration&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> ZonePreferenceServerListFilter &lt;span style="color:#a6e22e">serverListFilter&lt;/span>&lt;span style="color:#f92672">()&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ZonePreferenceServerListFilter filter &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ZonePreferenceServerListFilter&lt;span style="color:#f92672">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filter&lt;span style="color:#f92672">.&lt;/span>&lt;span style="color:#a6e22e">setZone&lt;/span>&lt;span style="color:#f92672">(&lt;/span>&lt;span style="color:#e6db74">&amp;#34;myTestZone&amp;#34;&lt;/span>&lt;span style="color:#f92672">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> filter&lt;span style="color:#f92672">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> IPing &lt;span style="color:#a6e22e">ribbonPing&lt;/span>&lt;span style="color:#f92672">()&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> PingUrl&lt;span style="color:#f92672">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Ribbon客户端的配置, 如果不指定会使用默认的实现:&lt;/p>
&lt;ul>
&lt;li>IClientConfig 客户端相关配置&lt;/li>
&lt;li>IRule 定义负载均衡策略&lt;/li>
&lt;li>IPing 定义如何ping目标服务实例来判断是否存活, ribbon使用单独的线程每隔一段时间(默认10s)对本地缓存的ServerList做一次检查&lt;/li>
&lt;li>ServerList&lt;Server> 定义如何获取服务实例列表. 两种实现基于配置的&lt;code>ConfigurationBasedServerList&lt;/code>和基于Eureka服务发现的&lt;code>DiscoveryEnabledNIWSServerList&lt;/code>&lt;/li>
&lt;li>ServerListFilter&lt;Server> 用来使用期望的特征过滤静态配置动态获得的候选服务实例列表. 若未提供, 默认使用&lt;code>ZoneAffinityServerListFilter&lt;/code>&lt;/li>
&lt;li>ILoadBalancer 定义了软负载均衡器的操作的接口. 一个典型的负载均衡器至少需要一组用来做负载均衡的服务实例, 一个标记某个服务实例不在旋转中的方法, 和对应的方法调用从实例列表中选出某一个服务实例.&lt;/li>
&lt;li>ServerListUpdater DynamicServerListLoadBalancer用来更新实例列表的策略(推&lt;code>EurekaNotificationServerListUpdater&lt;/code>/拉&lt;code>PollingServerListUpdater&lt;/code>, 默认是拉)&lt;/li>
&lt;/ul></description></item><item><title>Jenkins CI/CD (一) 基于角色的授权策略</title><link>https://atbug.com/using-role-based-authorization-strategy-in-jenkins/</link><pubDate>Fri, 20 Apr 2018 12:18:46 +0000</pubDate><guid>https://atbug.com/using-role-based-authorization-strategy-in-jenkins/</guid><description>&lt;p>最近开始客串运维做CI/CD的规划设计, 主要是基于&amp;rsquo;Pipeline as Code in Jenkins&amp;rsquo;. 整理了下思路和技术点, 慢慢的写.&lt;/p>
&lt;p>这一篇是关于基于角色的授权策略, 用的是&lt;code>Role-Based Authorization Strategy Plugin&lt;/code>.&lt;/p>
&lt;p>授权在CI/CD流程中比较常见, 比如我们只让某些特定用户才可以构建Pre-Release的Job. 而更高级的Release发布, 又会需要某些用户的审批才可以进行. 需要授权时, 可能还需要发邮件提醒用户.&lt;/p>
&lt;p>UI上如何使用就不提了, 这里只说Pipeline as Code. 后面的几篇也会是这个背景.&lt;/p>
&lt;p>参考的这篇&lt;a href="https://www.avioconsulting.com/blog/using-role-based-authorization-strategy-jenkins">文章&lt;/a>, 文章里的代码运行失败, 做了修复.&lt;/p>
&lt;h2 id="配置">配置&lt;/h2>
&lt;p>安装完插件, 需要开始&lt;code>基于角色的授权策略&lt;/code>. 同时添加角色和为用户分配角色.&lt;/p>
&lt;h3 id="使用role-based-strategy作为验证方式">使用&lt;code>Role-Based Strategy&lt;/code>作为验证方式&lt;/h3>
&lt;p>&lt;code>Manage Jenkins / Configure Global Security / Configure Global Security&lt;/code>&lt;/p>
&lt;p>&lt;img src="http://7xvxng.com1.z0.glb.clouddn.com/15241955282214.jpg" alt="">&lt;/p></description></item><item><title>KVM 安装手册</title><link>https://atbug.com/kvm-installation-note/</link><pubDate>Thu, 12 Apr 2018 12:45:15 +0000</pubDate><guid>https://atbug.com/kvm-installation-note/</guid><description>&lt;h2 id="添加虚拟机流程">添加虚拟机流程：&lt;/h2>
&lt;pre>&lt;code>1. 配置网络
2. 配置存储池
3. 上传镜像
4. 安装虚拟机，指定配置
&lt;/code>&lt;/pre>
&lt;h3 id="安装kvm虚拟机">安装KVM虚拟机&lt;/h3>
&lt;h4 id="1-关闭防火墙selinux">1. 关闭防火墙，selinux&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># service iptables stop&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># setenforce 0 临时关闭&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># chkconfig NetworkManager off&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2-安装kvm虚拟机">2. 安装kvm虚拟机&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># yum install kvm libvirt libvirt-devel python-virtinst python-virtinst qemu-kvm virt-viewer bridge-utils virt-top libguestfs-tools ca-certificates audit-libs-python device-mapper-libs virt-install&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># 启动服务&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># service libvirtd restart&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>下载virtio-win-1.5.2-1.el6.noarch.rpm 如果不安装window虚拟机或者使用带virtio驱动的镜像可以不用安装
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># rpm -ivh virtio-win-1.5.2-1.el6.noarch.rpm&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="3-libvirt在管理本地或远程hypervisor时的表现形式如下">3. Libvirt在管理本地或远程Hypervisor时的表现形式如下。&lt;/h4>
&lt;p>在libvirt内部管理了五部分：&lt;/p>
&lt;ul>
&lt;li>节点：所谓的节点就是我们的物理服务器，一个服务器代表一个节点，上边存放着Hyper和Domain&lt;/li>
&lt;li>Hypervisor：即VMM，指虚拟机的监控程序，在KVM中是一个加载了kvm.ko的标准Linux系统。&lt;/li>
&lt;li>域（Domain）：指虚拟机，一个域代表一个虚拟机（估计思路来源于Xen的Domain0）&lt;/li>
&lt;li>存储池（Storage Pool）：存储空间，支持多种协议和网络存储。作为虚拟机磁盘的存储源。&lt;/li>
&lt;li>卷组（Volume）：虚拟机磁盘在Host上的表现形式。
上边的五部分，我们必须使用的是前三个，因为很多时候根据业务规则或应用的灵活性并没有使用卷组（其实就是有了编制的虚拟磁盘文件），也就没有必要使用存储池。&lt;/li>
&lt;/ul></description></item><item><title>启用Jenkins CLI</title><link>https://atbug.com/jenkins-cli-enable/</link><pubDate>Mon, 09 Apr 2018 11:16:38 +0000</pubDate><guid>https://atbug.com/jenkins-cli-enable/</guid><description>Jenkins CLI提供了SSH和Client模式.
Docker运行Jenkins
version: &amp;#39;3&amp;#39; services: jenkins: image: jenkins/jenkins:alpine ports: - 8080:8080 - 50000:50000 - 46059:46059 volumes: - &amp;#34;/Users/addo/DevApps/Docker/data/jenkins:/var/jenkins_home&amp;#34; note: 以为是docker运行, ssh端口设置选用了固定端口.
Client 从http://JENKINS_URL/cli页面下载client jar
使用方法:
java -jar jenkins-cli.jar -s http://localhost:8080/ help 构建:
java -jar jenkins-cli.jar -s http://localhost:8080/ build JOB [-c] [-f] [-p] [-r N] [-s] [-v] [-w] Starts a build, and optionally waits for a completion.</description></item><item><title>Jenkins - 解决execute shell中启动的进程被在Job退出时被杀死问题</title><link>https://atbug.com/resolve-process-be-killed-after-jenkins-job-done/</link><pubDate>Thu, 15 Mar 2018 17:00:25 +0000</pubDate><guid>https://atbug.com/resolve-process-be-killed-after-jenkins-job-done/</guid><description>因为ProcessTreeKiller的存在, 构建过程中使用shell启动的进程在Job完成时都会被kill掉.
各种搜索以及ProcessTreeKiller提供的解决方式是修改BUILD_ID和添加 -Dhudson.util.ProcessTree.disable=true都无法解决.
最后参考StackOverflow和Jenkins JIRA, 修改JENKINS_NODE_COOKIE为任何值, 如dontKillMe. 这种方法可以解决, 记录一下. (搜索排名靠前的结果都不对).</description></item><item><title>macOS 安装 minishift</title><link>https://atbug.com/install-minishift-on-mac/</link><pubDate>Fri, 23 Feb 2018 15:32:26 +0000</pubDate><guid>https://atbug.com/install-minishift-on-mac/</guid><description>MacOS环境安装minishift
安装minishift cli brew cask install minishift 使用virtualbox安装 安装的时候可以指定HTTP代理, 拉取墙外镜像时需要; 还可以指定insecure的镜像库.
minishift start --docker-env HTTP_PROXY=&amp;#34;192.168.99.1:1087&amp;#34; --docker-env HTTPS_PROXY=&amp;#34;192.168.99.1:1087&amp;#34; --docker-env NO_PROXY=&amp;#34;192.168.0.0/16,172.30.0.0/16&amp;#34; --insecure-registry=&amp;#34;192.168.1.34&amp;#34; --vm-driver=virtualbox 启动 minishift start --vm-driver=virtualbox 删除 minishift delete 打开Openshift控制面板 minishift dashboard 获取集群ip地址 minishift ip 安装Openshift Cli brew install openshift-cli 可以使用openshift cli进行操作.</description></item><item><title>Spring Cloud Zuul详解</title><link>https://atbug.com/spring-cloud-zuul-breakdown/</link><pubDate>Thu, 22 Feb 2018 17:02:26 +0000</pubDate><guid>https://atbug.com/spring-cloud-zuul-breakdown/</guid><description>&lt;p>Spring Cloud对Netflix Zuul做了封装集成, 使得在Spring Cloud环境中使用Zuul更方便. Netflix Zuul相关分析请看&lt;a href="http://atbug.com/learn-netflix-zuul/">上一篇&lt;/a>.&lt;/p>
&lt;h2 id="实现">实现&lt;/h2>
&lt;p>@EnableZuulProxy 与 @EnableZuulServer
二者的区别在于前者使用了服务发现作为路由寻址, 并使用Ribbon做客户端的负载均衡; 后者没有使用.
Zuul server的路由都通过&lt;code>ZuulProperties&lt;/code>进行配置.&lt;/p>
&lt;h3 id="具体实现">具体实现:&lt;/h3>
&lt;ol>
&lt;li>使用&lt;code>ZuulController&lt;/code>(&lt;code>ServletWrappingController&lt;/code>的子类)封装&lt;code>ZuulServlet&lt;/code>实例, 处理从&lt;code>DispatcherServlet&lt;/code>进来的请求.&lt;/li>
&lt;li>&lt;code>ZuulHandlerMapping&lt;/code>负责注册handler mapping, 将&lt;code>Route&lt;/code>的&lt;code>fullPath&lt;/code>的请求交由&lt;code>ZuulController&lt;/code>处理.&lt;/li>
&lt;li>同时使用&lt;code>ServletRegistrationBean&lt;/code>注册&lt;code>ZuulServlet&lt;/code>, 默认使用&lt;code>/zuul&lt;/code>作为urlMapping. 所有来自以&lt;code>/zuul&lt;/code>开头的path的请求都会直接进入&lt;code>ZuulServlet&lt;/code>, 不会进入&lt;code>DispatcherServlet&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h4 id="使用注解">使用注解&lt;/h4>
&lt;ul>
&lt;li>
&lt;p>&lt;code>@EnableZuulProxy&lt;/code>引入了&lt;code>ZuulProxyMarkerConfiguration&lt;/code>, &lt;code>ZuulProxyMarkerConfiguration&lt;/code>只做了一件事, 实例化了内部类&lt;code>Marker&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ZuulProxyMarkerConfiguration&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> Marker &lt;span style="color:#a6e22e">zuulProxyMarkerBean&lt;/span>&lt;span style="color:#f92672">()&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> Marker&lt;span style="color:#f92672">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Marker&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;code>@EnableZuulServer&lt;/code>引入了&lt;code>ZuulServerMarkerConfiguration&lt;/code>, &lt;code>ZuulServerMarkerConfiguration&lt;/code>也只做了一件事: 实例化了内部类&lt;code>Marker&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">@Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ZuulServerMarkerConfiguration&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">@Bean&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> Marker &lt;span style="color:#a6e22e">zuulServerMarkerBean&lt;/span>&lt;span style="color:#f92672">()&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> Marker&lt;span style="color:#f92672">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Marker&lt;/span> &lt;span style="color:#f92672">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul></description></item><item><title>Spring Cloud - Eureka服务注册</title><link>https://atbug.com/spring-cloud-service-registry-via-eureka/</link><pubDate>Wed, 14 Feb 2018 07:32:43 +0000</pubDate><guid>https://atbug.com/spring-cloud-service-registry-via-eureka/</guid><description>&lt;p>之前分析过&lt;a href="http://atbug.com/spring-cloud-eureka-client-source-code-analysis/">Spring Cloud的Eureka服务发现&lt;/a>, 今天分析一下服务注册.&lt;/p>
&lt;h2 id="配置">配置&lt;/h2>
&lt;h3 id="bootstrapconfiguration">BootstrapConfiguration&lt;/h3>
&lt;h4 id="eurekadiscoveryclientconfigservicebootstrapconfiguration">EurekaDiscoveryClientConfigServiceBootstrapConfiguration&lt;/h4>
&lt;p>spring-cloud-config环境中使用的配置&lt;/p>
&lt;p>引入&lt;code>EurekaDiscoveryClientConfiguration&lt;/code>和&lt;code>EurekaClientAutoConfiguration&lt;/code>&lt;/p>
&lt;h5 id="eurekadiscoveryclientconfiguration">EurekaDiscoveryClientConfiguration&lt;/h5>
&lt;ol>
&lt;li>在spring-cloud中(通过是否存在RefreshScopeRefreshedEvent.class判断), 添加&lt;code>RefreshScopeRefreshedEvent&lt;/code>的listener. 收到事件后重新注册实例.&lt;/li>
&lt;li>在&lt;code>eureka.client.healthcheck.enabled&lt;/code>设置为true时, 注册&lt;code>EurekaHealthCheckHandler&lt;/code>bean. &lt;code>EurekaHealthCheckHandler&lt;/code>负责将应用状态映射为实例状态&lt;code>InstanceStatus&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h5 id="eurekaclientautoconfiguration">EurekaClientAutoConfiguration&lt;/h5>
&lt;p>支持spring-cloud和非spring-cloud环境, 在spring-cloud环境中, 下面两个bean要使用&lt;code>@RefreshScope&lt;/code>标注&lt;/p>
&lt;ol>
&lt;li>实例化&lt;code>EurekaClient&lt;/code>bean, 在spring-cloud中使用实现类&lt;code>CloudEurekaClient&lt;/code>.&lt;/li>
&lt;li>使用&lt;code>EurekaInstanceConfig&lt;/code>实例, 实例化&lt;code>ApplicationInfoManager&lt;/code>bean&lt;/li>
&lt;/ol></description></item><item><title>初识 Netflix Zuul</title><link>https://atbug.com/learn-netflix-zuul/</link><pubDate>Sun, 11 Feb 2018 10:07:18 +0000</pubDate><guid>https://atbug.com/learn-netflix-zuul/</guid><description>&lt;p>嵌入式的zuul代理&lt;/p>
&lt;p>使用了Netfilx OSS的其他组件:&lt;/p>
&lt;ul>
&lt;li>Hystrix 熔断&lt;/li>
&lt;li>Ribbon 负责发送外出请求的客户端, 提供软件负载均衡功能&lt;/li>
&lt;li>Trubine 实时地聚合细粒度的metrics数据&lt;/li>
&lt;li>Archaius 动态配置&lt;/li>
&lt;/ul>
&lt;h2 id="介绍">介绍&lt;/h2>
&lt;p>由于2.0停止开发且会有bug, 故下面的分析基于1.x版本.&lt;/p>
&lt;h3 id="特性">特性&lt;/h3>
&lt;ul>
&lt;li>Authentication 认证&lt;/li>
&lt;li>Insights 洞察&lt;/li>
&lt;li>Stress Testing 压力测试&lt;/li>
&lt;li>Canary Testing 金丝雀测试&lt;/li>
&lt;li>Dynamic Routing 动态路由&lt;/li>
&lt;li>Multi-Region Resiliency 多区域弹性&lt;/li>
&lt;li>Load Shedding 负载脱落&lt;/li>
&lt;li>Security 安全&lt;/li>
&lt;li>Static Response handling 静态响应处理&lt;/li>
&lt;li>Multi-Region Resiliency 主动/主动流量管理&lt;/li>
&lt;/ul></description></item><item><title>ConfigurationProperties到底需不需要getter</title><link>https://atbug.com/configurationproperties-requires-getter-or-not/</link><pubDate>Wed, 07 Feb 2018 15:53:21 +0000</pubDate><guid>https://atbug.com/configurationproperties-requires-getter-or-not/</guid><description>为什么要讨论这个问题, 工作中一个同事写的类使用了ConfigurationProperties, 只提供了标准的setter方法. 属性的访问, 提供了定制的方法. 可以参考EurekaClientConfigBean.
他使用的是spring boot 2.0.0.M5版本, 可以正常获取配置文件中的属性值, 但是在1.5.8.RELEASE获取不到.
看下文档和源码:
Annotation for externalized configuration. Add this to a class definition or a @Bean method in a @Configuration class if you want to bind and validate some external Properties (e.</description></item><item><title>Go In Action 读书笔记 四</title><link>https://atbug.com/go-in-action-four/</link><pubDate>Mon, 01 Jan 2018 12:30:55 +0000</pubDate><guid>https://atbug.com/go-in-action-four/</guid><description>&lt;p>&lt;img src="https://talks.golang.org/2013/go4python/img/fib-go.png" alt="">&lt;/p>
&lt;h2 id="并发模式">并发模式&lt;/h2>
&lt;h3 id="runner">runner&lt;/h3>
&lt;p>runner展示了如何使用通道来监视程序的执行时间, 如果程序执行时间太长, 也可以用终止程序.
这个程序可用作corn作业执行&lt;/p></description></item><item><title>Go In Action 读书笔记 三</title><link>https://atbug.com/go-in-action-three/</link><pubDate>Mon, 01 Jan 2018 12:30:31 +0000</pubDate><guid>https://atbug.com/go-in-action-three/</guid><description>&lt;h2 id="并发">并发&lt;/h2>
&lt;p>Go语言里的并发是指让某个函数可以独立于其他函数运行的能力. 当一个函数创建为goroutine时, Go会将其视为一个独立的工作单元. 这个工作单元会被调度到可用的&lt;strong>逻辑处理器&lt;/strong>上执行.&lt;/p>
&lt;p>Go的运行时调度器可以管理所有创建的goroutine, 并为其分配执行时间.
这个调度器在操作系统之上, 将操作系统的线程与逻辑处理器绑定, 并在逻辑处理器执行goroutine. &lt;strong>调度器可以在任何给定的时间, 全面控制哪个goroutine在哪个逻辑处理器上运行&lt;/strong>.&lt;/p>
&lt;p>Go的并发同步模型来自一个叫做通信顺序进程(Communicating Sequential Processes, &lt;a href="http://www.usingcsp.com">CSP&lt;/a>). CSP是一个消息传递模型, 通过在goroutine之前传递数据来传递消息, 不需要通过加锁实现同步访问. 用于在goroutine间传递消息的数据结构叫做通道(channel).&lt;/p>
&lt;h3 id="并发与并行">并发与并行&lt;/h3>
&lt;p>操作系统的线程(thread)和进程(process).&lt;/p>
&lt;p>进程类似应用程序在运行中需要用到和维护的各种资源的容器.
资源包括但不限于: 内存(来自文件系统的代码和数据), 句柄(文件, 设备, 操作系统), 线程.&lt;/p></description></item><item><title>Go In Action 读书笔记 二</title><link>https://atbug.com/go-in-action-two/</link><pubDate>Mon, 01 Jan 2018 12:28:04 +0000</pubDate><guid>https://atbug.com/go-in-action-two/</guid><description>&lt;h2 id="go语言的类型系统">Go语言的类型系统&lt;/h2>
&lt;p>Go语言是静态类型的变成语言. 编译的时候需要确定类型.&lt;/p>
&lt;h3 id="用户定义的类型">用户定义的类型&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">user&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">name&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">email&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">ext&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">privileged&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>使用&lt;/strong>
零值和&lt;strong>结构字面量&lt;/strong>初始化&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//引用类型, 各个字段初始化为对应的零值
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">bill&lt;/span> &lt;span style="color:#a6e22e">user&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">#&lt;/span>{ &lt;span style="color:#ae81ff">0&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">//创建并初始化, 使用结构字面量
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">lisa&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">user&lt;/span>{ &lt;span style="color:#75715e">//{Lisa lisa@email.com 123 true}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">name&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Lisa&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">email&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;lisa@email.com&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">ext&lt;/span>: &lt;span style="color:#ae81ff">123&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">privileged&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>结构字面量的赋值方式:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>不同行声明每一个字段和对应的值, 字段名和字段以&lt;code>:&lt;/code>分隔, 末尾以&lt;code>,&lt;/code>结尾&lt;/li>
&lt;li>不适用字段名, 只声明对应的值. 写在一行里, 以&lt;code>,&lt;/code>分隔, 结尾不需要&lt;code>,&lt;/code>. &lt;strong>要保证顺序&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">lisa&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> {&lt;span style="color:#e6db74">&amp;#34;Lisa&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;lisa@email.com&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">123&lt;/span>, &lt;span style="color:#66d9ef">true&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Go In Action 读书笔记 一</title><link>https://atbug.com/go-in-action-one/</link><pubDate>Mon, 01 Jan 2018 12:27:10 +0000</pubDate><guid>https://atbug.com/go-in-action-one/</guid><description>&lt;p>&lt;img src="http://7xvxng.com1.z0.glb.clouddn.com/15142714785285.jpg" alt="架构流程图">&lt;/p>
&lt;h2 id="关键字">关键字&lt;/h2>
&lt;h3 id="var">var&lt;/h3>
&lt;p>变量使用&lt;code>var&lt;/code>声明, 如果变量不是定义在任何一个函数作用域内, 这个变量就是包级变量.&lt;/p>
&lt;blockquote>
&lt;p>Go语言中, 所有变量都被初始化为其&lt;strong>零值&lt;/strong>. 对于数值类型, 其零值是&lt;strong>0&lt;/strong>; 对于字符串类型, 其零值是&lt;strong>空字符串&amp;quot;&amp;quot;&lt;/strong>; 对于布尔类型, 其零值是&lt;strong>false&lt;/strong>. 对于引用类型来说, 底层数据结构会被初始化对应的零值. 但是被生命被起零值的引用类型的变量, 会返回&lt;strong>nil&lt;/strong>作为其值.&lt;/p>
&lt;/blockquote>
&lt;h3 id="const">const&lt;/h3>
&lt;p>定义常量&lt;/p>
&lt;h3 id="interface">interface&lt;/h3>
&lt;p>声明接口&lt;/p>
&lt;h3 id="func">func&lt;/h3>
&lt;p>声明函数&lt;/p>
&lt;h3 id="defer">defer&lt;/h3>
&lt;p>安排后面的函数调用在当前函数返回时才执行.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">file&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span> = &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">open&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;filePath&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">defer&lt;/span> &lt;span style="color:#a6e22e">file&lt;/span>.close()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">#&lt;/span> &lt;span style="color:#a6e22e">more&lt;/span> &lt;span style="color:#a6e22e">file&lt;/span> &lt;span style="color:#a6e22e">operation&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>自定义GOPATH下安装godep失败</title><link>https://atbug.com/install-godep-issue-in-custom-gopath/</link><pubDate>Fri, 22 Dec 2017 13:02:38 +0000</pubDate><guid>https://atbug.com/install-godep-issue-in-custom-gopath/</guid><description>我的环境变量是这样的:
export GOROOT=/usr/local/go export GOPATH=/Users/addo/Workspaces/go_w export GOBIN=$GOROOT/bin export PATH=$PATH:$GOBIN 使用下面的命令安装报错:
go get -v github.com/tools/godep
github.com/tools/godep (download) github.com/tools/godep/vendor/github.com/pmezard/go-difflib/difflib github.com/tools/godep/vendor/github.com/kr/fs github.com/tools/godep/vendor/github.com/kr/text github.com/tools/godep/vendor/golang.org/x/tools/go/vcs github.com/tools/godep/vendor/github.com/kr/pretty github.com/tools/godep go install github.com/tools/godep: open /usr/local/go/bin/godep: permission denied
默认是安装到$GOBIN目录下, 权限不够.
使用:
sudo go get -v github.com/tools/godep
sudo go get -v github.</description></item><item><title>SpringBoot源码 - 启动</title><link>https://atbug.com/glance-over-spring-boot-source/</link><pubDate>Fri, 08 Dec 2017 17:48:43 +0000</pubDate><guid>https://atbug.com/glance-over-spring-boot-source/</guid><description>SpringBoot Application启动部分的源码阅读.
SpringApplication 常用的SpringApplication.run(Class, Args)启动Spring应用, 创建或者更新ApplicationContext
静态方法run 使用source类实例化一个SpringApplication实例, 并调用实例方法run.
public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); } 初始化initialize 实例化的时候首先通过尝试加载javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext推断当前是否是web环境.
然后从spring.factories获取ApplicationContextInitializer的实现类.
从spring.factories获取ApplicationListener的实现类
推断出应用的启动类(包含main方法的类): 检查线程栈中元素的方法名是否是main
private Class&amp;lt;?&amp;gt; deduceMainApplicationClass() { try { //获取线程栈数据 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if (&amp;#34;main&amp;#34;.</description></item><item><title>Java序列化工具性能对比</title><link>https://atbug.com/java-serval-serializer-benchmark/</link><pubDate>Sat, 02 Dec 2017 07:35:43 +0000</pubDate><guid>https://atbug.com/java-serval-serializer-benchmark/</guid><description>最近在调整系统的性能, 系统中正使用Jackson作为序列化工具. 做了下与fastJson, Avro, ProtoStuff的序列化吞吐对比.
由于只是做横向对比, 没有优化系统或者JVM任何参数. 服务器一般都用Linux, 在Docker里做了Linux系统的测试.
Mac:
Benchmark Mode Cnt Score Error Units JMHTest.avroSerializer thrpt 2 3124799.325 ops/s JMHTest.fastJsonSerializer thrpt 2 3122720.917 ops/s JMHTest.jacksonSerializer thrpt 2 2373347.208 ops/s JMHTest.protostuffSerializer thrpt 2 4196009.673 ops/s Docker:
Benchmark Mode Cnt Score Error Units JMHTest.</description></item><item><title>Kafka的消息可靠传递</title><link>https://atbug.com/kafka-reliable-data-delivery/</link><pubDate>Sat, 18 Nov 2017 14:01:46 +0000</pubDate><guid>https://atbug.com/kafka-reliable-data-delivery/</guid><description>Kafka提供的基础保障可以用来构建可靠的系统, 却无法保证完全可靠. 需要在可靠性和吞吐之间做取舍.
Kafka在分区上提供了消息的顺序保证. 生产的消息在写入到所有的同步分区上后被认为是已提交 (不需要刷到硬盘). 生产者可以选择在消息提交完成后接收broker的确认, 是写入leader之后, 或者所有的副本 只要有一个副本存在, 提交的消息就不会丢失 消费者只能读取到已提交的消息 复制 Kafka的复制机制保证每个分区有多个副本, 每个副本可以作为leader或者follower的角色存在. 为了保证副本的同步, 需要做到:
保持到zk的连接会话: 每隔6s向zk发送心跳, 时间可配置 每隔10s向leader拉取消息, 时间可配置 从leader拉取最近10s的写入的消息. 保持不间断的从leader获取消息是不够的, 必须保证几乎没有延迟 Broker配置 复制因子 default.replication.factor broker级别的副本数设置, 通过这个配置来控制自动创建的topic的副本数. 为N的时候, 可以容忍失去N-1个副本, 保证topic的可读写.
脏副本的leader选举 unclean.leader.election.enable 0.11.0.0之前的版本, 默认为true; 之后的版本默认为false. 这个设置控制不同步的副本能否参与leader的选举. 如果设置为true, 当没有同步副本可用的时候, 不同步的副本会成为leader, 意味着有数据丢失.</description></item><item><title>Spring Cloud - Eureka Client源码分析</title><link>https://atbug.com/spring-cloud-eureka-client-source-code-analysis/</link><pubDate>Sat, 14 Oct 2017 22:04:59 +0000</pubDate><guid>https://atbug.com/spring-cloud-eureka-client-source-code-analysis/</guid><description>准备做个Spring Cloud源码分析系列, 作为Spring Cloud的源码分析笔记.
这一篇是Eureka的客户端.
客户端 两种方式, 最终的实现基本一样.
显示指定服务发现的实现类型 使用@EnableEurekaClient注解显示的指定使用Eureka作为服务发现的实现, 并实例化EurekaClient实例. 实际上使用的是@EnableDiscoveryClient注解.
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @EnableDiscoveryClient public @interface EnableEurekaClient { } 动态配置实现 使用@EnableDiscoveryClient注解来配置服务发现的实现.
源码分析 EnableDiscoveryClient
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(EnableDiscoveryClientImportSelector.class) public @interface EnableDiscoveryClient { } EnableDiscoveryClient注解的作用主要是用来引入EnableDiscoveryClientImportSelector
EnableDiscoveryClientImportSelector
@Order(Ordered.LOWEST_PRECEDENCE - 100) public class EnableDiscoveryClientImportSelector extends SpringFactoryImportSelector&amp;lt;EnableDiscoveryClient&amp;gt; { @Override protected boolean isEnabled() { return new RelaxedPropertyResolver(getEnvironment()).</description></item><item><title>Raft算法学习</title><link>https://atbug.com/learning-raft/</link><pubDate>Sat, 14 Oct 2017 05:57:34 +0000</pubDate><guid>https://atbug.com/learning-raft/</guid><description>Raft 强一致性算法
名词 复制状态机 复制状态机是通过复制日志来实现的, 按照日志中的命令的顺序来执行这些命令. 相同的状态机执行相同的日志命令, 获得相同的执行结果.
任期号 (currentTerm) 每个成员都会保存一个任期号, 称为服务器最后知道的任期号.
投票的候选人id (votedFor) 当前任期内, 投票的候选人id, 即响应投票请求(见下文)返回true时的候选人id.
已被提交的最大日志条目的索引值 (commitIndex) 每个成员都会持有已被提交的最大日志条目的索引值
被状态机执行的最⼤日志条⽬的索引值 (lastApplied) 每个成员都会持有被状态机执行的最⼤日志条⽬的索引值
请求 日志复制请求 (AppendEntries RPC) 由领导人发送给其他服务器, 也用作heartbeat
请求内容
term 领导人的任期号 leaderId 领导人的id prevLogIndex 已经被状态机执行的最大索引值, 即最新日志之前的日志的索引值. preLogTerm 最新日志之前的日志的领导人的任期号 entries[] 需要被复制的日志条目 leaderCommit 领导人提交的日志条目索引值 响应内容</description></item><item><title>Kafka发送不同确认方式的性能差异</title><link>https://atbug.com/kafka-producer-acknowledge-benchmark/</link><pubDate>Tue, 10 Oct 2017 11:49:58 +0000</pubDate><guid>https://atbug.com/kafka-producer-acknowledge-benchmark/</guid><description>背景 Kafka的性能众所周知，Producer支持acknowledge模式。即Kafka会想Producer返回消息发送的结果。但是在Java Client中，acknowledge的确认有两种：同步和异步。 同步是通过调用future.get()实现的；异步则是通过提供callback方法来实现。写了个简单的程序测试一下单线程中吞吐差异能有多大。注意这里只考虑横向对比。
发送端单线程 Kafka为单集群节点 topic的分区数为1 key长度1 payload长度100 测试工具 JMeter Kafka Meter future.get() + batch size =1 future.get() + batch size = 16K callback + batch size = 16k callback + batch size = 1</description></item><item><title>Kafka消息消费一致性</title><link>https://atbug.com/kafka-consumer-consistency/</link><pubDate>Tue, 26 Sep 2017 19:13:48 +0000</pubDate><guid>https://atbug.com/kafka-consumer-consistency/</guid><description>Kafka消费端的offset主要由consumer来控制, Kafka降每个consumer所监听的tocpic的partition的offset保存在__consumer_offsets主题中. consumer需要将处理完成的消息的offset提交到服务端, 主要有ConsumerCoordinator完成的.
每次从kafka拉取数据之前, 假如是异步提交offset, 会先调用已经完成的offset commit的callBack, 然后检查ConsumerCoordinator的连接状态. 如果设置了自动提交offset, 会继续上次从服务端获取的数据的offset异步提交到服务端. 这里需要注意的是会有几种情况出现:
消息处理耗时较多, 假如处理单条消息的耗时为t, 拉取的消息个数为n. t * n &amp;gt; auto_commit_interval_ms, 会导致没有处理完的消息的offset被commit到服务端. 假如此时消费端挂掉, 没有处理完的数据将会丢失. 假如消息处理完成, offset还未commit到服务端的时候消费端挂掉, 已经处理完的消息会被再次消费. 下面配置影响着数据一致性和性能, 因此需要结合业务场景合理配置一下参数, 进行取舍.
enable.auto.commit 默认为true
auto.commit.interval.ms 默认为5000 ms (5s)
max.poll.records 默认为500
fetch.max.bytes 默认为52428800 bytes (50Mib).</description></item><item><title>Kafka 恰好一次发送和事务消费示例</title><link>https://atbug.com/kafka-exactly-once-delivery-and-transactional-messaging-example/</link><pubDate>Fri, 22 Sep 2017 18:03:43 +0000</pubDate><guid>https://atbug.com/kafka-exactly-once-delivery-and-transactional-messaging-example/</guid><description>核心思想 生产端一致性: 开启幂等和事务, 包含重试, 发送确认, 同一个连接的最大未确认请求数. 消费端一致性: 通过设置读已提交的数据和同时处理完成每一条消息之后手动提交offset. 生产端 public class ProducerTest { public static void main(String[] args) throws InterruptedException, ExecutionException { Properties props = new Properties(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, &amp;#34;192.168.31.186:9092&amp;#34;); props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, &amp;#34;my-transactional-id&amp;#34;); props.put(ProducerConfig.ACKS_CONFIG, &amp;#34;all&amp;#34;); props.put(ProducerConfig.RETRIES_CONFIG, &amp;#34;3&amp;#34;); props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, &amp;#34;1&amp;#34;); Producer&amp;lt;String, String&amp;gt; producer = new KafkaProducer&amp;lt;&amp;gt;(props, new StringSerializer(), new StringSerializer()); producer.</description></item><item><title>恰好一次发送和事务消息(译)</title><link>https://atbug.com/kafka-exactly-once-delivery-and-transactional-messaging/</link><pubDate>Tue, 19 Sep 2017 19:13:26 +0000</pubDate><guid>https://atbug.com/kafka-exactly-once-delivery-and-transactional-messaging/</guid><description>Kafka提供“至少一次”交付语义, 这意味着发送的消息可以传送一次或多次. 人们真正想要的是“一次”语义,因为重复的消息没有被传递。
普遍地发声重复消息的情况有两种:
如果客户端尝试向集群发送消息并获取网络错误, 则重试可能会导致重复. 如果在发送消息之前发生网络错误, 则不会发生重复. 但是, 如果在将消息附加到日志之后发生网络错误, 但在将响应发送给发件人之前, 发件人将不知道发生了什么. 唯一的选择是重试和冒险重复或放弃并声明消息丢失。 如果客户端尝试向集群发送消息并获取网络错误, 则重试可能会导致重复. 如果在发送消息之前发生网络错误, 则不会发生重复. 但是, 如果在将消息附加到日志之后发生网络错误, 但在将响应发送给发件人之前, 发件人将不知道发生了什么. 唯一的选择是重试和冒险重复或放弃并声明消息丢失。 第二种情况可以通过使用Kafka提供的偏移量由消费者处理. 他们可以将偏移量与其输出进行存储, 然后确保新消费者始终从最后存储的偏移量中提取. 或者, 他们可以使用偏移量作为一种关键字, 并使用它来对其输出的任何最终目标系统进行重复数据删除。
Producer API改动 KafkaProducer.java
public interface Producer&amp;lt;K,V&amp;gt; extends Closeable { /** * Needs to be called before any of the other transaction methods.</description></item><item><title>Kafka Producer配置解读</title><link>https://atbug.com/kafka-producer-config/</link><pubDate>Tue, 19 Sep 2017 15:38:03 +0000</pubDate><guid>https://atbug.com/kafka-producer-config/</guid><description>按照重要性分类, 基于版本0.11.0.0
高 bootstrap.servers 一组host和port用于初始化连接. 不管这里配置了多少台server, 都只是用作发现整个集群全部server信息. 这个配置不需要包含集群所有的机器信息. 但是最好多于一个, 以防服务器挂掉.
key.serializer 用来序列化key的Serializer接口的实现类.
value.serializer 用来序列化value的Serializer接口的实现类
acks producer希望leader返回的用于确认请求完成的确认数量. 可选值 all, -1, 0 1. 默认值为1
acks=0 不需要等待服务器的确认. 这是retries设置无效. 响应里来自服务端的offset总是-1. producer只管发不管发送成功与否。延迟低，容易丢失数据。 acks=1 表示leader写入成功（但是并没有刷新到磁盘）后即向producer响应。延迟中等，一旦leader副本挂了，就会丢失数据。 acks=all等待数据完成副本的复制, 等同于-1. 假如需要保证消息不丢失, 需要使用该设置. 同时需要设置unclean.leader.election.enable为true, 保证当ISR列表为空时, 选择其他存活的副本作为新的leader. buffer.memory producer可以使用的最大内存来缓存等待发送到server端的消息. 如果消息速度大于producer交付到server端的阻塞时间max.block.ms, 将会抛出异常. 默认值33554432 byte (32m).</description></item><item><title>JSON Patch</title><link>https://atbug.com/json-patch/</link><pubDate>Sun, 27 Aug 2017 14:41:44 +0000</pubDate><guid>https://atbug.com/json-patch/</guid><description>JSON Path是在使用Kubernetes API的过程中首次使用的. 使用API做扩缩容的时候, 发送整个Deployment的全文不是个明智的做法, 虽然可行. 因此便使用了JSON Patch.
JsonObject item = new JsonObject(); item.add(&amp;#34;op&amp;#34;, new JsonPrimitive(&amp;#34;replace&amp;#34;)); item.add(&amp;#34;path&amp;#34;, new JsonPrimitive(&amp;#34;/spec/replicas&amp;#34;)); item.add(&amp;#34;value&amp;#34;, new JsonPrimitive(instances)); JsonArray body = new JsonArray(); body.add(item); appsV1beta1Api.patchNamespacedScaleScale(id, namespace, body, null); fabric8s提供的kubernetes-client中使用的zjsonpatch则封装了JSON Patch操作. 例如在做扩缩容的时候或者当前的deployment, 修改replicas的值. 然后比较对象的不同(JsonDiff.asJson(sourceJsonNode, targetJsonNode)).
下面的内容部分翻译自JSON PATH, 有兴趣的可以跳转看原文.</description></item><item><title>如何在Openshift中使用hostPath</title><link>https://atbug.com/how-to-use-hostpath-in-openshift/</link><pubDate>Wed, 23 Aug 2017 19:29:51 +0000</pubDate><guid>https://atbug.com/how-to-use-hostpath-in-openshift/</guid><description>使用openshift搭建的k8s的api创建Deployment，在启动的时候报下面的错误：
Invalid value: &amp;ldquo;hostPath&amp;rdquo;: hostPath volumes are not allowed to be used]
解决方案：
一个方案是将user加入privileged scc中，另一个方案就是：
oc edit scc restricted #添加下面这行 allowHostDirVolumePlugin: true</description></item><item><title>Kubernetes — 持久卷</title><link>https://atbug.com/kubernetes-persistent-volumes/</link><pubDate>Sun, 20 Aug 2017 22:25:40 +0000</pubDate><guid>https://atbug.com/kubernetes-persistent-volumes/</guid><description>Persistent Volume 译自Persistent Volumes
介绍 管理存储是管理计算的独特问题。 PersistentVolume子系统为用户和管理员提供了一个API，其中提供了如何从如何使用存储提供存储的详细信息。为此，我们介绍两种新的API资源：PersistentVolume和PersistentVolumeClaim。
PersistentVolume（PV）是由管理员配置的集群中的一段存储。它是集群中的一种资源就像一个节点是一个集群的资源。 PV是类似Volumes的卷插件，但是具有独立于使用PV的任何单个pod的生命周期。该API对象捕获存储的实现细节，即NFS，iSCSI或云提供商特定的存储系统。
PersistentVolumeClaim（PVC）是用户存储的请求。它类似于pod。 Pod消耗节点资源，PVC消耗PV资源。Pods可以请求特定级别的资源（CPU和内存）。声明可以请求特定的大小和访问模式（例如，一次读写或者多次只读）。
虽然PersistentVolumeClaims允许用户使用抽象存储资源，但是常见的是，用户需要具有不同属性（如性能）的PersistentVolumes，用于不同的问题。 集群管理员需要能够提供多种彼此不同的PersistentVolumes，而不仅仅是大小和访问模式，而不会使用户了解这些卷的实现细节。 对于这些需求，有一个StorageClass资源。
StorageClass为管理员提供了一种描述他们提供的存储的“类”的方法。 不同的类可能映射到服务质量级别，或备份策略，或者由群集管理员确定的任意策略。 Kubernetes本身对于什么类别代表是不言而喻的。 这个概念有时在其他存储系统中称为“配置文件”。
请参阅详细演练与工作示例。
存储和声明的生命周期 PVs是集群中的资源；PVCs是对这种资源的声明，同时也扮演者对资源声明的检查。PVs和PVCs之前的交互遵循生命周期：供应、绑定、使用中、重新申请。
集群管理员创建多个PV。它们携带可供集群用户使用的真实存储的详细信息。它们存在于Kubernetes API中，可用于消费。
供应(Provisioning) PVs会以两种方式供应：静态和动态。
静态 集群管理员创建多个PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中，可被使用。
动态 当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时，集群可能会尝试为PVC指定动态配置卷。 此配置基于StorageClasses：PVC必须指定一个类，并且管理员必须已创建并配置该类才能进行动态配置。 要求该类的声明有效地为自己禁用动态配置。
绑定(Binding) 当用户创建、或已经创建了一个PersistenVolumenClaim并指定大小和访问类型。Master中的控制循环会检测新的PVC，找到一个匹配的PV（如果可能的话），并将它们绑定在一起。如果一个PV被动态地供应某个PVC，循环将总是把这个PV和该PVC绑定。否则，用户总是至少得到他们要求的内容，但是卷可能超出了要求。一旦绑定，PersistentVolumeClaim绑定是排他的，不管用于绑定它们的模式。
如果匹配的卷不存在，请求将无限期地保持。 随着匹配卷变得可用，请求将被绑定。 例如，提供许多50Gi PV的集群将不匹配要求100Gi的PVC。 当集群中添加100Gi PV时，可以绑定PVC。</description></item><item><title>Kubernetes学习 — Macos安装Kubernetes</title><link>https://atbug.com/install-kubernetes-on-macos/</link><pubDate>Thu, 17 Aug 2017 09:44:17 +0000</pubDate><guid>https://atbug.com/install-kubernetes-on-macos/</guid><description>Kubernetes 安装 macos 检查环境 sysctl -a | grep machdep.cpu.features | grep VMX 安装VirtualBox http://download.virtualbox.org/virtualbox/5.1.26/Oracle_VM_VirtualBox_Extension_Pack-5.1.26-117224.vbox-extpack 安装minikube curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.21.0/minikube-darwin-amd64 &amp;amp;&amp;amp; chmod +x minikube &amp;amp;&amp;amp; sudo mv minikube /usr/local/bin/ 创建集群 默认使用virtualbox。
主机的ip是192.168.31.186， 1087是proxy的端口。需要将ss的http代理监听地址从127.0.0.1改为主机的ip。
#启动 minikube start #使用私有库 minikube start --insecure-registry=&amp;#34;192.168.31.34&amp;#34; #使用proxy，用于获取镜像 minikube start --docker-env HTTP_PROXY=&amp;#34;192.</description></item><item><title>暴力停止ExecutorService的线程</title><link>https://atbug.com/stop-a-thread-of-executor-service/</link><pubDate>Wed, 19 Jul 2017 22:25:19 +0000</pubDate><guid>https://atbug.com/stop-a-thread-of-executor-service/</guid><description>停止，stop，这里说的是真的停止。如何优雅的结束，这里就不提了。
这里要用Thread.stop()。众所周知，stop()方法在JDK中是废弃的。
该方法天生是不安全的。使用thread.stop()停止一个线程，导致释放（解锁）所有该线程已经锁定的监视器（因沿堆栈向上传播的未检查异常ThreadDeath而解锁）。如果之前受这些监视器保护的任何对象处于不一致状态，则不一致状态的对象（受损对象）将对其他线程可见，这可能导致任意的行为。
有时候我们会有这种需求，不需要考虑线程执行到哪一步。一般这种情况是外部执行stop，比如执行业务的线程因为各种原因假死或者耗时较长，由于设计问题又无法响应优雅的停止指令。
现在大家在项目中都很少直接使用线程，而是通过concurrent包中的类来实现多线程，例如ExecutorService的各种实现类。
一个简单的停止线程的例子：
public class ExecutorServiceTest { public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); final AtomicReference&amp;lt;Thread&amp;gt; t = new AtomicReference&amp;lt;&amp;gt;(); Future&amp;lt;?&amp;gt; firstFuture = executor.submit(new Runnable() { public void run() { Thread currentThread = Thread.</description></item><item><title>私有构造函数捕获模式</title><link>https://atbug.com/private-constructor-capture-idiom/</link><pubDate>Wed, 24 May 2017 06:50:44 +0000</pubDate><guid>https://atbug.com/private-constructor-capture-idiom/</guid><description>《Java并发编程实践》的注解中有提到这一概念。
The private constructor exists to avoid the race condition that would occur if the copy constructor were implemented as this (p.x, p.y); this is an example of the private constructor capture idiom (Bloch and Gafter, 2005).
结合原文代码：
@ThreadSafe public class SafePoint{ @GuardedBy(&amp;#34;this&amp;#34;) private int x,y; private SafePoint (int [] a) { this (a[0], a[1]); } public SafePoint(SafePoint p) { this (p.</description></item><item><title>Docker 快速构建 Cassandra 和 Java 操作</title><link>https://atbug.com/java-operate-cassandra-deployed-in-docker/</link><pubDate>Thu, 18 May 2017 23:33:24 +0000</pubDate><guid>https://atbug.com/java-operate-cassandra-deployed-in-docker/</guid><description>搭建Cassandra 使用docker创建Cassandra，方便快捷
docker pull cassandra:latest docker run -d --name cassandra -p 9042:9042 cassandra docker exec -it cassandra bash 创建keyspace、table #cqlsh&amp;gt; #create keyspace CREATE KEYSPACE contacts WITH REPLICATION = { &amp;#39;class&amp;#39; : &amp;#39;SimpleStrategy&amp;#39;, &amp;#39;replication_factor&amp;#39; : 1 }; #use USE contacts; #create table CREATE TABLE contact ( id UUID, email TEXT PRIMARY KEY ); 查看表数据 cqlsh:contacts&amp;gt; SELECT * FROM contact; email | id -------+---- (0 rows) Java客户端 引入依赖 &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;com.</description></item><item><title>从零开始用 docker 运行 spring boot 应用</title><link>https://atbug.com/run-spring-boot-app-in-docker/</link><pubDate>Thu, 20 Apr 2017 21:58:42 +0000</pubDate><guid>https://atbug.com/run-spring-boot-app-in-docker/</guid><description>假设已经安装好Docker
Springboot应用 pom添加依赖和构建插件 &amp;lt;parent&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt; &amp;lt;version&amp;gt;1.5.3.RELEASE&amp;lt;/version&amp;gt; &amp;lt;/parent&amp;gt; &amp;lt;dependencies&amp;gt; &amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt; &amp;lt;/dependency&amp;gt; &amp;lt;/dependencies&amp;gt; &amp;lt;build&amp;gt; &amp;lt;plugins&amp;gt; &amp;lt;plugin&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt; &amp;lt;/plugin&amp;gt; &amp;lt;/plugins&amp;gt; &amp;lt;/build&amp;gt; 应用代码 package com.atbug.spring.boot.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Created by addo on 2017/5/15.</description></item><item><title>Jasig CAS Web and Proxy flow</title><link>https://atbug.com/jasig-cas-web-and-proxy-flow/</link><pubDate>Tue, 18 Apr 2017 10:36:16 +0000</pubDate><guid>https://atbug.com/jasig-cas-web-and-proxy-flow/</guid><description>最近因为需求在看CAS相关的只是，由于需要后端调用，用到proxy（代理）模式。整理了下web flow和proxy web flow的流程。
Web Flow Proxy Web Flow</description></item><item><title>MetaspaceSize的坑</title><link>https://atbug.com/java8-metaspace-size-issue/</link><pubDate>Thu, 13 Apr 2017 11:55:14 +0000</pubDate><guid>https://atbug.com/java8-metaspace-size-issue/</guid><description>这几天生产上有台机器的Metaspace一直在告警，Metaspace使用达到了97%。使用-XX:MetaspaceSize=512m，告警也还在在持续，查看MC只有81536.0，显然这个参数没起作用。
也有人遇到类似的问题，并在openjdk上提过类似的bug，其实是一个注释的bug，最终在JDK-8151845中修复了。
Class metadata is deallocated when the corresponding Java class is unloaded. Java classes are unloaded as a result of garbage collection, and garbage collections may be induced in order to unload classes and deallocate class metadata. When the space committed for class metadata reaches a certain level (a high-water mark), a garbage collection is induced.</description></item><item><title>一个Tomcat类加载问题</title><link>https://atbug.com/one-tomcat-class-load-issue/</link><pubDate>Wed, 12 Apr 2017 10:40:01 +0000</pubDate><guid>https://atbug.com/one-tomcat-class-load-issue/</guid><description>背景 一个Tomcat实例中运行了三个应用，其中一个对接了Apereo的CAS系统。现在要求另外两个系统也对接CAS系统，问题就出现了：
应用启动后打开其中两个应用的任何一个，登录完成后系统都没有问题。唯独首选打开第三个，其他两个报错ClassNotFoundException: org.apache.xerces.parsers.SAXParser。
发现这个类来自xerces:xercesImpl:jar:2.6.2，使用mvn dependency:tree发现是被xom:xom:1.1简洁引用。
分析 CAS client jar中使用XMLReaderFactory创建XMLReader，首次创建会从classpath中查找META-INF/services/org.xml.sax.driver文件，这个文件里的内容是一个类的全名。比如xercesImpl中该文件的内容是org.apache.xerces.parsers.SAXParser。
找到之后会将类名保存在XMLReaderFactory的静态变量_clsFromJar，并标记不会再查找org.xml.sax.driver文件。找不到的话则使用com.sun.org.apache.xerces.internal.parsers.SAXParser类。
然后再使用当前线程的ContextClassLoader对类进行加载，这里的的ContextClassLoader是一个WebAppClassLoader的实例。
同时XMLReaderFactory类是被BootStrapClassLoader加载的，为三个应用共享。
Tomcat类记载机制 Tomcat中有四个位置可以存放Java类库：/commons、/server、/shared和各Web应用的WEB-INF/lib目录。
/commons目录中的类库可以被Tomcat和所有Web应用使用 /server目录中的类库只能被Tomcat使用 /shared目录中的可以被所有Web应用的使用，但是对Tomcat不可见 各Web应用的WEB-INF/lib目录中的类库则只能被该的应用使用
Tomcat的使用CommonClassLoader、CatalinaClassLoader、SharedClassLoader、WebAPPClassLoader加载对应目录中的类库。
Bootstrap、Extension、Application是虚拟机使用的系统类加载器。
类的加载使用双亲委派机制(Parent-Delegation)。
Bootstrap | Extension | Application | System | Common / \ Catalina Shared / \ WebApp1 ... WebApp2 | | Jasper Jasper 解决方案 在另外两个应用中添加xerces:xercesImpl:jar:2.</description></item><item><title>Scala笔记：用函数字面量块调用高阶函数</title><link>https://atbug.com/call-high-order-function-in-function-literal/</link><pubDate>Tue, 11 Apr 2017 10:15:15 +0000</pubDate><guid>https://atbug.com/call-high-order-function-in-function-literal/</guid><description>这里会用到几个概念高阶函数、函数字面量、参数组
高阶函数 high-order function 函数的一种，简单来说它包含了一个函数类型的参数或者返回值。
所谓的高阶是跟一阶函数相比，深入一下：
一个或多个参数是函数，并返回一个值。 返回一个函数，但没有参数是函数。 上述两者叠加：一个或多个参数是函数，并返回一个函数。 示例：
def stringSafeOp(s: String, f: String =&amp;gt; String) = { if ( s != null) f(s) else s } //stringSafeOp: (s: String, f: String =&amp;gt; String)String def reverse(s: String) = s.reverse //reverse: (s: String)String stringSafeOp(&amp;#34;Ready&amp;#34;, reverse) //res86: String = ydaeR 函数字面量 function literal，其他名字：匿名函数、Lambda表达式等。 函数字面量可以存储在函数值和变量中，或者也可以定义为高阶函数调用的一部分。在任何接受函数类型的地方都可以使用函数字面量。</description></item><item><title>GreenPlum JDBC和C3P0数据源</title><link>https://atbug.com/greenplum-jdbc-and-c3p0-datasource/</link><pubDate>Mon, 10 Apr 2017 08:29:00 +0000</pubDate><guid>https://atbug.com/greenplum-jdbc-and-c3p0-datasource/</guid><description>在网上搜索GreenPlum（GPDB）的数据源配置的时候，发现搜索结果都是用postgresql的配置。
import com.mchange.v2.c3p0.DataSources; import javax.sql.DataSource; import java.sql.*; import java.util.Properties; /** * Created by addo on 2017/4/10. */ public class JDBCTest { private static String POSTGRESQL_URL = &amp;#34;jdbc:postgresql://192.168.56.101:5432/example&amp;#34;; private static String POSTGRESQL_USERNAME = &amp;#34;dbuser&amp;#34;; private static String POSTGRESQL_PASSWORD = &amp;#34;password&amp;#34;; private static String GPDB_URL = &amp;#34;jdbc:pivotal:greenplum://192.</description></item><item><title>Scala笔记：def VS val</title><link>https://atbug.com/def-vs-val-in-scala/</link><pubDate>Sun, 09 Apr 2017 08:24:40 +0000</pubDate><guid>https://atbug.com/def-vs-val-in-scala/</guid><description>先说原理： val修饰的在定义的时候执行
def修饰的在调用的时候执行
直观的例子： //注释的行为REPL输出 def test: () =&amp;gt; Int = { println(&amp;#34;def called&amp;#34;) val r = util.Random.nextInt () =&amp;gt; r } //test: () =&amp;gt; Int test() //def called //res82: Int = -950077410 test() //def called //res83: Int = 1027028032 val test: () =&amp;gt; Int = { println(&amp;#34;def called&amp;#34;) val r = util.</description></item><item><title>Centos 编译安装 Redis</title><link>https://atbug.com/install-redis-on-centos/</link><pubDate>Fri, 07 Apr 2017 16:48:46 +0000</pubDate><guid>https://atbug.com/install-redis-on-centos/</guid><description>版本 Centos7
Redis3.2.8
编译安装 wget http://download.redis.io/releases/redis-3.2.8.tar.gz tar -zxvf redis-3.2.8.tar.gz cd redis-3.2.8 sudo make test sudo make install 启动 redis-server 问题 /bin/sh: cc: command not found
**原因：**Centos安装时选择的类型是Infrastructure，没有c++的编译工具。
解决：sudo yum -y install gcc gcc-c++ libstdc++-devel
malloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory</description></item><item><title>Centos 上安装 Postgresql</title><link>https://atbug.com/install-postgresql-on-centos/</link><pubDate>Thu, 06 Apr 2017 22:54:17 +0000</pubDate><guid>https://atbug.com/install-postgresql-on-centos/</guid><description>版本 Centos7
Postgresql9.2
Enable ssh service sshd start
Open firewall for 22 firewall-cmd —state
firewall-cmd —list-all
firewall-cmd —permanent —zone=public —add-port=22/tcp
firewall-cmd —reload
Install Postgresql yum install postgres
su postgres
postgres —version
默认会创建postgres:postgres用户和组
切换用户 su - postgres
初始化数据库 通过指定数据文件目录初始化db
initdb -D /var/lib/pgsql/data</description></item><item><title>Key长度对Redis性能影响</title><link>https://atbug.com/redis-performance-key-length/</link><pubDate>Thu, 16 Mar 2017 10:37:03 +0000</pubDate><guid>https://atbug.com/redis-performance-key-length/</guid><description>最近Redis的使用中用的到key可能比较长，但是Redis的官方文档没提到key长度对性能的影响，故简单做了个测试。
环境 Redis和测试程序都是运行在本地，不看单次的性能，只看不同的长度堆读写性能的影响。
测试方法 使用长度分别为10, 100, 500, 1000, 2500, 5000, 7500, 10,000, and 20,000的key，value长度1000，读写1000次。
结果 从结果来看随着长度的增加，读写的耗时都随之增加。
长度为10：写平均耗时0.053ms，读0.040ms 长度为20000：写平均耗时0.352ms，读0.084ms 测试代码 源码
/** * Created by addo on 2017/3/16. */ public class RedisTest { private static String[] keys = new String[1000]; private static String randomString(int length) { Random random = new Random(); char[] chars = &amp;#34;0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#34;.</description></item><item><title>遍历 Collection 时删除元素</title><link>https://atbug.com/remove-element-while-looping-collection/</link><pubDate>Sun, 05 Mar 2017 22:04:58 +0000</pubDate><guid>https://atbug.com/remove-element-while-looping-collection/</guid><description>其实标题我想用《为什么foreach边循环边移除元素要用Iterator？》可是太长。
不用Iterator，用Collection.remove()，会报ConcurrentModificationException错误。
for(Integer i : list) { list.remove(i); //Throw ConcurrentModificationException } 其实使用foreach的时候，会自动生成一个Iterator来遍历list。不只是remove，使用add、clear等方法一样会出错。
拿ArrayList来说，它有一个私有的Iterator接口的内部类Itr：
private class Itr implements Iterator&amp;lt;E&amp;gt; { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; //sevrval methods } 使用Iterator来遍历ArrayList实际上是通过两个指针来遍历ArrayList底层的数组：cursor是下一个返回的元素在数组中的下标；lastRet是上一个元素的下标。还有一个重要的expectedModCount使用的是ArrayList的modCount的（modCount具体是什么意思下文会提到）。</description></item><item><title>Java Volatile关键字</title><link>https://atbug.com/deep-in-java-volatile-keywork/</link><pubDate>Thu, 02 Mar 2017 08:30:29 +0000</pubDate><guid>https://atbug.com/deep-in-java-volatile-keywork/</guid><description>volatile通过保证对变量的读或写都是直接从内存中读取或直接写入内存中，保证了可见性；但是volatile并不足以保证线程安全，因为无法保证原子性，如count++操作：
将值从内存读入寄存器中 进行加1操作，内存保存到寄存器中 结果从寄存器flush到内存中 借用一张图来看：
不是volatile的变量的指令执行顺序是1-&amp;gt;2-&amp;gt;3；而声明为volatile的变量，顺序是1-&amp;gt;23。从这里看，volatile保证了一个线程修改了volatile修饰的变量，变化会马上体现在内存中。线程间看到的值是一样的。
上面说了无法保证原子性是指：多核cpu，线程A执行了指令1，线程B也执行了指令1。A进行了加1操作，结果写入寄存器同时flush到内存；随后B也执行了同样的操作。count本来应该的结果是加2，但是却只加了1。原因就是我们通常所指的读和写不是原子操作。我们最希望看到的是123同时执行，手段就是sychronized或者java.util.concurrent包中的原子数据类型。
简单拿AtomicInteger来看，其中的一个int类型的value字段声明为volatile，保证了123同时执行。
参考：Java Volatile</description></item><item><title>Haproxy虚拟主机SSL</title><link>https://atbug.com/haproxy-multi-host-with-ssl/</link><pubDate>Mon, 27 Feb 2017 19:31:53 +0000</pubDate><guid>https://atbug.com/haproxy-multi-host-with-ssl/</guid><description>Haproxy为多个域名配置SSL
生成自签名证书 sudo mkdir /etc/ssl/atbug.com sudo openssl genrsa -out /etc/ssl/atbug.com/atbug.com.key 1024 sudo openssl req -new -key /etc/ssl/atbug.com/atbug.com.key -out /etc/ssl/atbug.com/atbug.com.csr sudo openssl x509 -req -days 365 -in /etc/ssl/atbug.com/atbug.com.csr -singkey /etc/ssl/atbug.com/atbug.com.key -out /etc/ssl/atbug.com/atbug.com.crt sudo openssl x509 -req -days 365 -in /etc/ssl/atbug.com/atbug.com.csr -signkey /etc/ssl/atbug.</description></item><item><title>mybatis报错“Result Maps collection already contains value for ***”</title><link>https://atbug.com/duplicate-resultmap-in-mybatis-mapper/</link><pubDate>Wed, 22 Feb 2017 14:12:18 +0000</pubDate><guid>https://atbug.com/duplicate-resultmap-in-mybatis-mapper/</guid><description>这是工作中遇到的一个问题：测试环境部署出错，报了下面的问题。
Caused by: java.lang.IllegalArgumentException: Result Maps collection already contains value for xxx.xxx.xxxRepository.BaseResultMap at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:802) at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:774) at org.apache.ibatis.session.Configuration.addResultMap(Configuration.java:556) at org.apache.ibatis.builder.MapperBuilderAssistant.addResultMap(MapperBuilderAssistant.java:217) at org.apache.ibatis.builder.ResultMapResolver.resolve(ResultMapResolver.java:47) at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:285) at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:252) at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElements(XMLMapperBuilder.java:244) at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:116) 检查了对应的mapper文件和java文件，已经8个多月没有修改过了。也检查了内容，没有发现重复的BaseResultMap；select中也resultMap的引用也都正确。
其实到最后发现跟代码一丁点关系都没有，是部署的时候没有删除旧版本的代码导致两个不同版本的jar同时存在，相应的mapper文件也有两个。
看了下源码，mybatis在创建SessionFactoryBean解析xml时候，会把xml中的resultMap放入到一个HashMap的子类StrictMap中，key是mapper的namespace与resultmap的id拼接成的。
StrictMap在put元素的时候，会检查map中是否已存在key。
public void addResultMap(ResultMap rm) { resultMaps.put(rm.getId(), rm); checkLocallyForDiscriminatedNestedResultMaps(rm); checkGloballyForDiscriminatedNestedResultMaps(rm); }</description></item><item><title>消费时offset被重置导致重复消费</title><link>https://atbug.com/offset-be-reset-when-consuming/</link><pubDate>Mon, 20 Feb 2017 13:23:49 +0000</pubDate><guid>https://atbug.com/offset-be-reset-when-consuming/</guid><description>这是实际使用时遇到的问题：kafka api的版本是0.10，发现有重复消费问题；检查log后发现在commit offset的时候发生超时。
Auto offset commit failed for group test: Commit cannot be completed since the group has already rebalanced and assigned the partitions to another member. This means that the time between subsequent calls to poll() was longer than the configured session.</description></item><item><title>TheadPoolExecutor源码分析</title><link>https://atbug.com/threadpoolexecutor-sourcecode-analysis/</link><pubDate>Mon, 20 Feb 2017 09:56:07 +0000</pubDate><guid>https://atbug.com/threadpoolexecutor-sourcecode-analysis/</guid><description>TheadPoolExecutor源码分析 ThreadPoolExecutor是多线程中经常用到的类，其使用一个线程池执行提交的任务。
实现 没有特殊需求的情况下，通常都是用Executors类的静态方法如newCachedThreadPoll来初始化ThreadPoolExecutor实例：
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue&amp;lt;Runnable&amp;gt;()); } 从Executors的方法实现中看出，BlockingQueue使用的SynchronousQueue，底层使用了栈的实现。值得注意的是，这个SynchronousQueue是没有容量限制的，Executors也将maximumPoolSize设为Integer.MAX_VALUE。
ThreadPoolExecutor的构造方法：
按照javadoc的解释：
corePoolSize是池中闲置的最小线程数 maximumPoolSize是池中允许的最大线程数 keepAliveTime是线程数大于最小线程数时，过量闲置线程的最大存活时间 unit是上面存活时间的单位 workQueue是用来暂时保存运行前的任务 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue&amp;lt;Runnable&amp;gt; workQueue) public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.</description></item><item><title>Kafka Java生产者模型</title><link>https://atbug.com/kafka-java-producer-model/</link><pubDate>Wed, 04 Jan 2017 16:33:02 +0000</pubDate><guid>https://atbug.com/kafka-java-producer-model/</guid><description>Producer初始化 初始化KafkaProducer实例，同时通过Config数据初始化MetaData、NetWorkClient、Accumulator和Sender线程。启动Sender线程。
MetaData信息 记录Cluster的相关信息，第一次链接使用Config设置，之后会从远端poll信息回来，比如host.name等信息。
Accumulator实例 Accumulator持有一个Map实例，key为TopicPartition（封装了topic和partition信息）对象，Value为RecordBatch的Deque集合。
NetworkClient实例 通过MetaData信息初始化NetworkClient实例，NetworkClient使用NIO模型。
Sender线程 sender持有NetworkClient和Accumulator实例，在Producer实例初始化完成之后，持续地将Accumulator中的Batch数据drain到一个List中，调用NetworkClient进行发送。
发送 调用Producer实例进行消息发送，首先将消息序列化之后追加到Accumulator的Deque的最后一个batch中，之后唤醒sender-&amp;gt;client-&amp;gt;Selector进行消息发送。</description></item><item><title>Redis清理缓存</title><link>https://atbug.com/clean-speicified-keys-in-redis/</link><pubDate>Tue, 13 Dec 2016 16:54:41 +0000</pubDate><guid>https://atbug.com/clean-speicified-keys-in-redis/</guid><description>最近有个需求需要主动的去清理部分缓存，考虑的原子性的问题，用Lua脚本进行实现。
Lua脚本
local count = 0 for _,k in ipairs(redis.call(&amp;#39;KEYS&amp;#39;, ARGV[1])) do redis.call(&amp;#39;DEL&amp;#39;, k) count = count + 1 end return count shell运行
redis-cli --eval file.lua ,[KEY PATTERN] #sample: 清理所有key以Test开头的记录 redis-cli --eval clear.lua , Test* Java
Jedis jedis = new Jedis(&amp;#34;127.</description></item><item><title>Flume - FileChannel （一）</title><link>https://atbug.com/flume-filechannel-overview/</link><pubDate>Wed, 23 Nov 2016 09:23:57 +0000</pubDate><guid>https://atbug.com/flume-filechannel-overview/</guid><description>概述 当使用Flume的时候，每个流程都包含了输入源、通道和输出。一个典型的例子是一个web服务器将事件通过RPC（搬入AvroSource）写入输入源中，输入源将其写入MemoryChannel，最后HDFS Sink消费事件将其写入HDFS中。
MemeoryChannel提供了高吞吐量但是在系统崩溃或者断电时会丢失数据。因此需要开发一个可持久话通道。FileChannel是在FLUME-1085里实现的。目标是提供一个高可用高吞吐量的通道。FileChannle保证了在失误提交之后，在崩溃或者断电后不丢失数据。
需要注意的是FileChannel自己不做任何的数据复制，因此它只是和基本的磁盘一样高可用。使用FileChannle的用户需要购买配置更多的硬盘。硬盘最好是RAID、SAN或者类似的。
很多需要通过损失少量的数据（每隔几秒将内存数据fsync到硬盘）换取高吞吐量。Flume团队决定使用另一种方式实现FileChannel。Flue是一个事务型的系统，在一次存或取的事务中可以操作多个事件。通过改变批量大小来控制吞吐量。使用大的批量，Flume可以以比较高的吞吐量传送数据，同时不丢失数据。批量的大小完全由客户端控制。使用RDBMS的用户对这种方式会比较熟悉。
一个Flume事务由存或取组成，但不能同时做两种操作，同样提交和回滚也是一样。每个事务实现了存和取的方法。数据源将事件存入通道，输出从通道中将事件取出。
设计 FileChannel在WAL（预写式日志）的基础上添加了一个内存队列。每个事务都被写成一个基于事务类型（存或取）的WAL，内存队列也相应的被更新。每次是事务提交，正确的文件被fsync保证数据被真正地保存到磁盘上，同时该事件的指针也被保存到了内存队列中。这个队列提供的功能跟其他队列没有区别：管理那些还没有被输出消费的事件。在取的过程中，指针被从队列中删除。事件直接从WAL中读取。得益于当前大容量的RAM，从操作系统的文件缓存中读取很常见。
在系统崩溃之后，WAL可以被重现到队列中保持原来的状态，没有被提交的事务会丢失。重现WAL是耗时的，因此队列也被周期性地写到磁盘上。写队列到磁盘被称作checkpoint。崩溃后，从磁盘读取队列。只有队列保存到磁盘之后提交的事务被重现，这样可以显著的减少需要读取的WAL的数量。
例如，如下有两个事件的通道：
WAL包含了三个重要的元素：事务id、序列号和事件数据。每个事务都有一个唯一的事务id，每个事件都有一个唯一的序列号。事务id只被用来标识事务中的一组事件，序列号在重演日志的时候被用到。上面的例子中，事务id是1，序列号是1、2、3。
当队列被保存到硬盘后 &amp;ndash; 一次checkpoint &amp;ndash; 序列号自动增加并同样被保存。在重启时，队列最先被从硬盘上加载，所有序列号大于队列的WAL项被重现。在checkpoint操作时，channle被锁住以保证没有存取操作改变它的状态。如果允许修改，会导致保存到硬盘上的队列快照不一致。
上面例子中的队列，checkpoint发送在事务1提交之后，因此事件a、b的指针和序列号4被保存到硬盘。
之后，事件a在事务2中被从队列中取出：
如果这时系统崩溃，队列的checkpoint从硬盘中加载。注意这个checkpoint发生在事务2之前，事件a、b的指针存在队列中。因此WAL中序列号大于4的已提交的事务被重现，事件a指针被从队列中删除。
上面的设计有两点没提到。checkpoint时发生的存和取操作会丢失。假设checkpoint在取事件a之后发生：
如果这时系统崩溃，根据上面的设计，事件b指针保存在队列中，所有序列号大于5的WAL项被重现：事务2的回滚被重现。但是事务2的取操作不会被重现。因此事件a指针不会被放回队列因而导致数据丢失。存的场景也类似。因此在队列checkpoint的时候，进行中的事务操作也会被重现，这样这种情况能被正确处理。
实现 FileChannel被保存在flume项目的flume-file-channel模块中，他的java包名是org.apache.flume.channel.file。上面提到队列被叫做FlumeEventQueue，WAL被叫做 Log。队列是一个环形数组，使用Memory Mapped File。WAL是一组以LogFile或其子类序列化的文件。
总结 FileChannle在硬件、软件和系统故障下的持久化并同时保证高吞吐量。如果这亮点都看中的话，FileChannel是推荐使用的通道。
原文</description></item><item><title>探索Rabbitmq的Java客户端</title><link>https://atbug.com/deep-in-rabbitmq-java-client/</link><pubDate>Sun, 09 Oct 2016 09:20:07 +0000</pubDate><guid>https://atbug.com/deep-in-rabbitmq-java-client/</guid><description>AMQPConnection 实例初始化 创建Connection时会通过FrameHandlerFacotry创建一个SocketFrameHandler，SocketFrameHandler对Socket进行了封装。
public AMQConnection(ConnectionParams params, FrameHandler frameHandler) { checkPreconditions(); this.username = params.getUsername(); this.password = params.getPassword(); this._frameHandler = frameHandler; this._virtualHost = params.getVirtualHost(); this._exceptionHandler = params.getExceptionHandler(); this._clientProperties = new HashMap&amp;lt;String, Object&amp;gt;(params.getClientProperties()); this.requestedFrameMax = params.getRequestedFrameMax(); this.requestedChannelMax = params.getRequestedChannelMax(); this.requestedHeartbeat = params.getRequestedHeartbeat(); this.shutdownTimeout = params.</description></item><item><title>Git回车换行</title><link>https://atbug.com/crlf-in-git/</link><pubDate>Wed, 14 Sep 2016 09:16:10 +0000</pubDate><guid>https://atbug.com/crlf-in-git/</guid><description>最近又个项目，checkout之后，没做任何改动前git status发现已经有modified了，通过git diff发现有两种改动：
- warning: CRLF will be replaced by LF in **
- 删除并添加的同样的行
使用git diff -w却没有改动；使用git diff –ws-error-highlight=new,old发现行尾有**^M**
我本人用的是Linux，其他同事有用Windows，问题就出在平台上。
Windows用CR LF来定义换行，Linux用LF。CR全称是Carriage Return ,或者表示为\r, 意思是回车。 LF全称是Line Feed，它才是真正意义上的换行表示符。
git config中关于CRLF有两个设定：core.autocrlf和core.safecrlf。
一、AutoCRLF
#提交时转换为LF，检出时转换为CRLF
git config –global core.autocrlf true
#提交时转换为LF，检出时不转换
git config –global core.</description></item><item><title>深入剖析 HashSet 和 HashMap 实现</title><link>https://atbug.com/deep-in-implementation-of-hashset/</link><pubDate>Mon, 11 Jul 2016 14:57:16 +0000</pubDate><guid>https://atbug.com/deep-in-implementation-of-hashset/</guid><description>HashSet是一个包含非重复元素的集合，如何实现的，要从底层实现代码看起。
背景 首先非重复元素如何定义，看Set的描述：
More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
Set不会找到两个元素，并且两个元素满足e1.equals(e2)为true；并且最多只有一个null元素。
如果没有重写equals方法，查看Object类中equal方法的实现，==比较的其实是两个对象在内存中的地址。
public boolean equals(Object obj) { return (this == obj); } 说起equals方法，就不得不说hashCode方法了。Java中对于hashCode有个常规协定
The general contract of hashCode is:</description></item><item><title>多线程下的单例模式+反汇编</title><link>https://atbug.com/singleton-in-multi-threads-programming/</link><pubDate>Wed, 06 Jul 2016 16:57:09 +0000</pubDate><guid>https://atbug.com/singleton-in-multi-threads-programming/</guid><description>多线程下的单例模式的实现，顺便做了反汇编。
public class MySingleton { private static MySingleton INSTANCE; private MySingleton() { } public static MySingleton getInstance() { if (INSTANCE == null) { synchronized (MySingleton.class) { INSTANCE = new MySingleton(); } } return INSTANCE; } } Compiled from &amp;#34;MySingleton.java&amp;#34; public class MySingleton { public static MySingleton getInstance(); Code: 0: getstatic #2 // Field INSTANCE:LMySingleton; //+获得类的指定域，并压入栈顶 3: ifnonnull 32 //+不为null时跳转到行号32 6: ldc_w #3 // class MySingleton //+常量值从常量池中推送至栈顶（宽索引），推送的为地址 9: dup //+复制栈顶数值，并且复制值进栈 10: astore_0 //+将栈顶数值（objectref）存入当前 frame的局部变量数组中指定下标(index）处的变量中，栈顶数值出栈。这里存的是MySingleton类定义的地址 11: monitorenter //+获得对象锁即MySingleton地址 12: new #3 // class MySingleton //+创建一个对象，并且其引用进栈 15: dup //+复制栈顶数值，并且复制值进栈 16: invokespecial #4 // Method &amp;#34;&amp;lt;init&amp;gt;&amp;#34;:()V //+调用超类构造方法、实例初始化方法、私有方法 19: putstatic #2 // Field INSTANCE:LMySingleton; //+为指定的类的静态域赋值 22: aload_0 //+当前frame的局部变量数组中下标为 index的引用型局部变量进栈，这里是MySingleton类定义的地址 23: monitorexit //+释放对象锁 24: goto 32 //+跳转到行号32 27: astore_1 //+将栈顶数值（objectref）存入当前 frame的局部变量数组中指定下标(index）处的变量中，栈顶数值出栈。 28: aload_0 //+当前frame的局部变量数组中下标为 0的引用型局部变量进栈 29: monitorexit //+//+释放对象锁 30: aload_1 //+当前frame的局部变量数组中下标为 1的引用型局部变量进栈 31: athrow //+将栈顶的数值作为异常或错误抛出 32: getstatic #2 // Field INSTANCE:LMySingleton; //+获得类的指定域，并压入栈顶 35: areturn //+从方法中返回一个对象的引用 Exception table: from to target type 12 24 27 any 27 30 27 any }</description></item><item><title>使用Kryo替换spring amqp的Java序列化</title><link>https://atbug.com/use-kryo-in-spring-amqp-serialization/</link><pubDate>Wed, 29 Jun 2016 05:29:14 +0000</pubDate><guid>https://atbug.com/use-kryo-in-spring-amqp-serialization/</guid><description>spring amqp的原生并没有对Kryo加以支持，Kryo的优点就不多说了。
git地址：https://github.com/addozhang/spring-kryo-messaeg-converter
public class KryoMessageConverter extends AbstractMessageConverter { public static final String CONTENT_TYPE = &amp;#34;application/x-kryo&amp;#34;; public static final String DEFAULT_CHARSET = &amp;#34;UTF-8&amp;#34;; private String defaultCharset = DEFAULT_CHARSET; private KryoFactory kryoFactory = new DefaultKryoFactory(); /** * Crate a message from the payload object and message properties provided.</description></item><item><title>Rabbitmq延迟队列实现</title><link>https://atbug.com/rabbitmq-delay-queue-implementation/</link><pubDate>Wed, 30 Mar 2016 14:27:02 +0000</pubDate><guid>https://atbug.com/rabbitmq-delay-queue-implementation/</guid><description>工作中很多场景需要用到定时任务、延迟任务，常用的方法用crontab job、Spring的Quartz，然后扫描整张数据库表，判断哪些数据需要处理。控制的粒度没办法做到特定数据上。 后来就想到了Rabbitmq，Rabbitmq本来不没有延迟队列的功能，但是有个[Dead Letter Exchange](https://www.rabbitmq.com/dlx.html)功能。 DLX是指队列中的消息在下面几种情况下会变为死信（dead letter），然后会被发布到另一个exchange中。 在requeue=false的情况系，消息被client reject 消息过期 队列长度超过限制 有了DLX，就可以将需要延迟的操作设置下次执行时间（如消息的TTL时间）放入一个存储队列中，消息过期后会经由DLX进入监听的队列中。有消费方进行相关的操作，结束或者再次进入存储队列中。 Spring AMQP实现 Configuration: &amp;lt;rabbit:connection-factory id="rabbitMQConnectionFactory" requested-heartbeat="" host="${rabbit.host}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}" publisher-confirms="true" channel-cache-size="10"/&amp;gt; &amp;lt;rabbit:admin connection-factory="rabbitMQConnectionFactory"/&amp;gt; &amp;lt;!--声明延时队列--&amp;gt; &amp;lt;rabbit:queue id="delayQueue" name="${rabbit.tracking.no.pre.track.delay.queue}"&amp;gt; &amp;lt;rabbit:queue-arguments&amp;gt; &amp;lt;entry key="x-dead-letter-exchange" value="tracking_dead_exchange"/&amp;gt; &amp;lt;/rabbit:queue-arguments&amp;gt; &amp;lt;/rabbit:queue&amp;gt; &amp;lt;!--声明监听队列--&amp;gt; &amp;lt;rabbit:queue id="preTrackingQueue" name="${rabbit.tracking.no.pre.track.queue}"/&amp;gt; &amp;lt;!</description></item><item><title>关于SLF4J</title><link>https://atbug.com/about-slf4j/</link><pubDate>Sat, 18 Apr 2015 11:16:26 +0000</pubDate><guid>https://atbug.com/about-slf4j/</guid><description>Spring的功能越来越强大，同时也越来越臃肿。比如想快速搭建一个基于Spring的项目，解决依赖问题非常耗时。Spring的项目模板的出现就解决了这个问题，通过这个描述文件，可以快速的找到你所需要的模板。
第一次认识SLF4J就是在这些项目模板里，它的全称是Simple Logging Facade for Java。从字面上可以看出它只是一个Facade，不提供具体的日志解决方案，只服务于各个日志系统。简单说有了它，我们就可以随意的更换日志系统（如java.util.logging、logback、log4j）。比如在开发的时候使用logback，部署的时候可以切换到log4j；如果关闭所有的log，切换到NOP就可以了。只需要更改依赖，提供日志配置文件，免去了修改代码的麻烦。
首先看如何使用：
[java] import org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class HelloWorld { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info(&amp;quot;Hello World&amp;quot;); } } [/java]
SLF4J封装了使用起来和其他日志系统一样简单。上面提到过SLF4J不提供具体的日志解决方案，所以使用的时候除了要引用SLF4J包，还要引用具体的日志解决方案包（log4j、logging&amp;ndash;JDK提供、logback），还有所对应的binding包（slf4j-log4j_、slf4j-jdk14、logback-classic_）。
以log4j为例，我们看SLF4J的实现方式。
SLF4J类在初始化的时候会尝试从ClassLoader中org/slf4j/impl/StaticLoggerBinder.class。这个类比较特殊，每个binding包里都有。不同binding包里的StaticLoggerBinder类会去初始化一个相应的实例，如slf4j-log4j里：
[java] /**
截取的部分代码 */ private StaticLoggerBinder() { loggerFactory = new Log4jLoggerFactory(); } [/java] 而Log4jLoggerAdapter实现了SLF4J的Logger接口，使用了Adapter模式对Log4j的Logger进行了封装并暴露了Logger的接口，Log4jLoggerFactory持有了Log4jLoggerAdapter的实例。</description></item><item><title>张晓辉</title><link>https://atbug.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://atbug.com/about/</guid><description>英文名 Addo。
资深程序员，LF APAC 开源布道师，CNCF Ambassador，云原生社区管委会成员，公众号“云原生指北”作者，微软 Azure MVP。
曾任职于汇丰软件、唯品会、数人云、小鹏汽车，有多年的微服务和基础架构实践经验，主要工作涉及微服务、容器、Kubernetes、DevOps 等。
这里所有的文章都会同步发送到公众号：云原生指北。</description></item></channel></rss>