<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on Wenhan blog</title>
    <link>https://wenhan.blog/zh/posts/</link>
    <description>Recent content in Posts on Wenhan blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-CN</language>
    <lastBuildDate>Thu, 01 May 2025 23:49:19 +0900</lastBuildDate><atom:link href="https://wenhan.blog/zh/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>用 Konnect &#43; AWS ECS 构建 API Gateway 环境</title>
      <link>https://wenhan.blog/zh/posts/20250501_konnect_ecs_gui/</link>
      <pubDate>Thu, 01 May 2025 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20250501_konnect_ecs_gui/</guid>
      <description>本文介绍如何以 Kong Konnect 作为 Data Plane，利用 AWS ECS（Elastic Container Service）Fargate 部署微服务，构建 API Gateway 环境。
准备工作 AWS 账号 Kong Konnect 账号 搭建步骤 Konnect 侧操作 登录 Konnect，依次进入 Gateway manager -&amp;gt; Data Plane Nodes -&amp;gt; Configure data plane，点击 Generate Certificate 生成 docker run 命令。请复制该命令中的参数，后续会用到。
证书生成后，Konnect 侧操作完成。证书和 key 默认带有换行，为了作为 ECS 环境变量设置，可用如下命令转为一行：
1 awk &amp;#39;NF {sub(/\r/, &amp;#34;&amp;#34;); printf &amp;#34;%s\\n&amp;#34;,$0;}&amp;#39; tls.key AWS ECS 侧操作 容器定义 进入 AWS ECS -&amp;gt; Task definitions -&amp;gt; Create new task definition with JSON，进行容器定义。</description>
    </item>
    
    <item>
      <title>用 Kong Serverless 功能让请求处理更智能</title>
      <link>https://wenhan.blog/zh/posts/20250415_kong_serverless/</link>
      <pubDate>Tue, 15 Apr 2025 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20250415_kong_serverless/</guid>
      <description>Pre-function 插件解读与实用示例 Kong Gateway 是功能强大的 API 网关，但你知道它可以直接内嵌轻量级 serverless 函数吗？
本文将用 Lua 脚本演示一个实用场景：从请求 Body 中提取 UUID，写入 Header 并输出到日志。
什么是 Pre-function 插件？ 如官方文档所述，pre-function 插件允许你在API 请求发送前用 Lua 处理请求，非常灵活。
常见用途包括：
条件过滤 Header/Body 定制 轻量日志或监控 简单的维护拦截 适合&amp;quot;无需专门开发插件、只想做点小处理&amp;quot;的场景。
实用案例 本例用 serverless 功能实现：从请求体提取 UUID，写入 Header 并记录日志。
处理流程 从请求体提取 UUID（JSON 解析） 若有 UUID，则写入 x-uuid 头 提取结果写入日志（用 File Log 插件） 前置准备：创建 Service 和 Route 1 2 3 4 5 6 7 8 # 创建服务 curl -i -X POST http://localhost:8001/services \ --data name=mock-service \ --data url=http://httpbin.</description>
    </item>
    
    <item>
      <title>Kong Gateway 管理 GraphQL API 的接入与治理</title>
      <link>https://wenhan.blog/zh/posts/20250325_kong_graphql_mngm/</link>
      <pubDate>Tue, 25 Mar 2025 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20250325_kong_graphql_mngm/</guid>
      <description>GraphQL 简介回顾 GraphQL 是 Facebook 开发的一种 API 查询语言，相比传统 REST API，能更灵活地获取数据。
GraphQL 的特点 单一端点：REST API 需要多个端点，GraphQL 只需一个端点（如 /graphql），可发送不同查询。 只获取所需数据：客户端可指定所需字段，避免无用数据传输。 类型系统：API 的 schema 以类型定义，数据结构清晰。 例如，向 https://countries.trevorblades.com/ 这个 GraphQL API 发送如下查询，可获取日本的国家信息：
1 2 3 4 5 6 7 { country(code: &amp;#34;JP&amp;#34;) { name capital currency } } 用 curl 执行该查询：
1 2 3 curl -X POST https://countries.trevorblades.com/ \ -H &amp;#34;Content-Type: application/json&amp;#34; \ -d &amp;#39;{&amp;#34;query&amp;#34;: &amp;#34;{ country(code: \&amp;#34;JP\&amp;#34;) { name capital currency } }&amp;#34;}&amp;#39; 响应：</description>
    </item>
    
    <item>
      <title>Kong Gateway 入门 - mTLS 支持</title>
      <link>https://wenhan.blog/zh/posts/20241224_kong_101_mtls/</link>
      <pubDate>Tue, 24 Dec 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241224_kong_101_mtls/</guid>
      <description>API 通信的安全性对于系统可靠性至关重要。本文将详细介绍如何使用 Kong API Gateway 配置 mTLS（双向 TLS/相互 TLS）。重点讲解&amp;quot;Client -&amp;gt; Kong&amp;quot;和&amp;quot;Kong -&amp;gt; Upstream API&amp;quot;这两条通信路径的 mTLS 配置方法。
mTLS 基础知识 mTLS 是 TLS（传输层安全协议）的扩展，通信双方（客户端和服务器）都会用证书进行相互认证。普通 TLS 只验证服务器，mTLS 还增加了如下流程：
服务器认证：客户端确认服务器持有可信证书。 客户端认证：服务器确认客户端持有可信证书。 这种双向认证实现了更安全的通信。
用 Kong API Gateway 实现 mTLS 通信 通过 Kong API Gateway，可以轻松实现如下两条通信路径的 mTLS：
客户端 → Kong API Gateway Kong API Gateway → 上游 API Client-&amp;gt;Kong 的 mTLS 配置 要在客户端到 Kong API Gateway 的通信中实现 mTLS，需使用 Kong 的&amp;quot;mTLS 插件&amp;quot;。主要步骤如下：
管理员在 Kong 中设置客户端认证用的 CA 证书 客户端请求时向 Kong 提交自己的证书 Kong 验证客户端证书，允许安全通信 注册 CA 证书 在 Kong 中注册 CA 证书，用于验证客户端证书</description>
    </item>
    
    <item>
      <title>如何用 Kong Gateway 灵活定制请求与响应</title>
      <link>https://wenhan.blog/zh/posts/20241223_kong_101_req_res_trans/</link>
      <pubDate>Mon, 23 Dec 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241223_kong_101_req_res_trans/</guid>
      <description>前言 在现代应用开发中，API 是系统与服务集成的关键基础。然而，不同系统间常常会遇到 API 要求不一致、数据格式不同、安全要求不满足等问题。此时，利用 API 网关灵活地修改请求和响应，是最有效的解决方式。
像 Kong Gateway 这样的 API 网关不仅仅是 API 管理工具。它在流量控制过程中，提供了强大的请求与响应内容定制能力。例如，可以实现如下操作：
Header 头部 可以为客户端请求头添加认证信息或自定义值，也可以编辑响应头，加入缓存控制或安全相关信息。
Body 转换 可以加工请求体，使其符合上游服务的格式要求，或过滤响应体内容，去除不需要的数据。
状态码变更 可根据 API 行为修改状态码，灵活控制错误处理或重定向。
数据格式转换 如果老系统要求 XML，新系统用 JSON，可以在 API 网关动态转换数据格式，实现兼容。
加密与解密 根据安全要求，对请求和响应数据进行加密或解密，提升通信安全性。
这些变更都无需改动 API 客户端或后端。API 网关在系统边界操作，既能灵活定制，又能最大限度降低对其他系统的影响。
如何用 Kong 修改请求与响应 本文将详细介绍如何用 Kong Gateway 实现上述变更。
Request transformer / Response transformer 插件简介 Kong Gateway 提供的 Request Transformer 和 Response Transformer 插件，是便捷定制请求与响应内容的利器。通过这些插件，可以灵活处理请求/响应头和 Body，满足系统集成和特定 API 需求。
主要功能 Request Transformer 插件主要功能：
添加、删除、覆盖请求头 添加、删除、覆盖查询参数 添加或删除请求体字段 Response Transformer 插件主要功能：</description>
    </item>
    
    <item>
      <title>使用 Kong 进行 JWT 验证的终极指南</title>
      <link>https://wenhan.blog/zh/posts/20241216_kong_jwt_ultra/</link>
      <pubDate>Mon, 16 Dec 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241216_kong_jwt_ultra/</guid>
      <description>（翻译自 https://tech.aufomm.com/the-ultimate-guide-of-using-jwt-with-kong/）
:::note 本文也许还称不上&amp;quot;终极&amp;quot;指南，但目标是彻底讲解 Kong 官方可用的 JWT 验证插件。读完后，你应该能全面理解各种选项，并能为自己的场景选择最合适的插件。 :::
背景 JSON Web Token（JWT）是 Web 开发中不可或缺的要素，是系统间安全传递重要信息的手段。为了保证其可靠性，JWT 验证是必不可少的步骤。同时，Kong 这样的 API 网关，尤其在 OAuth 2.0 和 OIDC 标准普及的今天，在企业 API 架构中扮演着关键角色。 本文将详细讲解 Kong 中用于 JWT 验证的工具，并探讨这一流程在现代 Web 开发中的作用。
什么是 JWT？ :::note 如果你已经了解 JWT，可以跳过插件选择细节，直接阅读后续章节。 :::
JSON Web Token（JWT）有两种格式：JSON Web Signature（JWS）和 JSON Web Encryption（JWE）。本文主要聚焦于 JWS 的验证。如需了解 JWE 验证，请参见这里。
根据 RFC7515，JSON Web Signature（JWS）是一种基于 JSON 的数据结构，用于表达被数字签名或消息认证码（MAC）保护的内容。简单来说，JWS 就像一封&amp;quot;带签名的信&amp;quot;，里面包含重要信息。要确保其可信性，必须验证签名。
算法 JWS 所用的加密算法及其标识符定义在 JSON Web Algorithms (JWA) JWA 规范中，并在 IANA 注册表中列出。
推荐优先使用非对称算法（如 RS256、ES256），不推荐使用对称算法（如 HS256）。</description>
    </item>
    
    <item>
      <title>Kong gateway 入门 - 利用缓存提升性能</title>
      <link>https://wenhan.blog/zh/posts/20241206_kong_101_proxy_caching/</link>
      <pubDate>Fri, 06 Dec 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241206_kong_101_proxy_caching/</guid>
      <description>前言 在 API 网关中使用缓存的主要目的如下：
响应速度快 降低后端负载 节省成本 上方（第一次请求）流程：
客户端向 Kong Gateway 发送请求 Kong Gateway 将请求转发到后端（上游服务） Kong Gateway 收到后端响应，并将其作为响应缓存保存 将响应返回给 Consumer 下方（第二次请求）流程：
客户端再次发送相同请求 Kong Gateway 检查缓存，直接返回已保存的响应 此时无需访问后端，响应速度大幅提升 只要 TTL（缓存有效期）未过期，该缓存即可被使用 Kong 的缓存功能通过插件实现。只需启用插件并进行必要配置，即可轻松使用缓存。你可以精细控制缓存的有效期和目标数据，满足不同场景需求。此外，缓存本身也可以外部存储，即使在大规模系统中也能高效运行，并与 Kong 的高可扩展性结合。
缓存的基本概念 缓存是一种临时存储频繁访问数据、以实现高速响应的机制。大部分缓存数据存储在内存（RAM）中，响应极快。缓存包含以下三要素：
缓存键：用于标识存储数据的唯一标识符 缓存数据：实际存储的响应数据 TTL（Time-To-Live）：数据的有效期 Kong Gateway 提供的缓存功能 Kong 提供了两种缓存插件：
Proxy Caching Proxy Caching Advanced
Proxy Caching Proxy Caching 插件会缓存 HTTP 响应，对后续相同请求直接返回缓存数据，从而最小化对后端服务的访问，提高 API 性能。
首先，创建测试用 Service 和 Route。
1 2 3 4 5 6 7 curl -i -X POST http://localhost:8001/services/ \ --data name=uuid_service \ --data url=http://httpbin.</description>
    </item>
    
    <item>
      <title>Kong API Gateway 入门 - Kong gateway 入门 - 流量限制的实现方法</title>
      <link>https://wenhan.blog/zh/posts/20241203_kong_101_ratelimiting/</link>
      <pubDate>Tue, 03 Dec 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241203_kong_101_ratelimiting/</guid>
      <description>流量限制（Rate Limiting）的重要性 在当今的 API 经济中，流量限制（Rate Limiting）在系统稳定性、安全性和成本效率方面起着至关重要的作用。下面详细说明其重要性。
服务器负载管理 API 可能会收到不可预测数量的请求。例如，突然爆火的应用或意外的流量激增（即&amp;quot;流量高峰&amp;quot;）可能导致服务器过载。通过设置流量限制，可以控制最大请求量，确保服务器维持良好性能。
公平性保障 流量限制是保障所有用户公平访问 API 的重要手段。它防止某些用户或应用独占资源，保护其他用户的体验。
提升安全性 流量限制对于防御恶意请求、DoS（拒绝服务）攻击和机器人滥用非常有效。通过检测并限制异常请求模式，可以保护 API 及整个系统。
成本管理 许多云服务商和基础设施服务会按请求数或数据流量计费。若允许无限制请求，可能导致意外的高额费用。通过设置流量限制，可以更好地预测成本，避免不必要的支出。
Kong 的流量限制基本机制 Kong Gateway 是一个开源 API 网关，便于管理和保护 API。企业和开发者常用它提升 API 的安全性、性能和可靠性。Kong Gateway 的流量限制功能用于控制 API 端点的流量，通过插件形式提供，配置简单灵活。
Kong 的流量限制插件工作原理如下：
请求计数 插件会统计请求数，在指定时间窗口（秒、分、时、天等）内超出设定阈值时进行限制。 超限时的处理 超出阈值的请求会被拒绝，返回 HTTP 状态码（通常为 429 Too Many Requests），也可自定义错误信息。 存储方式 计数信息可存储在本地内存或 Redis 等外部存储中，从而保证分布式环境下的一致性。 灵活的应用范围 按 Service：为特定 API 设置限制 按 Consumer：为不同用户或应用设置不同限制 按 Route：仅为特定 API 端点设置限制 基本配置：Kong 流量限制插件 Kong 提供两种流量限制插件，均可按时间单位限制请求数。例如，可轻松设置每分钟最多 3 次请求。
[OSS] https://docs.konghq.com/hub/kong-inc/rate-limiting/ [Enterprise] https://docs.konghq.com/hub/kong-inc/rate-limiting-advanced/ 插件配置 本文以 OSS 版为例。首先创建测试用 Service 和 Route。</description>
    </item>
    
    <item>
      <title>Kong API Gateway 入门 - 理解 Service 和 Route：从基础到进阶</title>
      <link>https://wenhan.blog/zh/posts/20241201_kong_101_service_route/</link>
      <pubDate>Sun, 01 Dec 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241201_kong_101_service_route/</guid>
      <description>在使用 Kong Gateway 时，&amp;ldquo;Service（服务）&amp;ldquo;和&amp;quot;Route（路由）&amp;ldquo;扮演着至关重要的角色。Service 定义了 API 请求的转发目标，而 Route 则规定了将请求映射到 Service 的规则。
什么是 Service？ Service 是对 Kong Gateway 代理的外部 API 或微服务的信息定义。具体来说，你可以配置请求转发目标的 URL、主机名、端口、协议等。
例如，如果你想用 Kong 代理后端的&amp;quot;用户 API&amp;rdquo;，就需要将该 API 注册为 Service。Kong 会接收客户端请求，并将其转发到已注册的 Service。创建 Service 时主要配置项如下：
name：服务名称，作为标识用的标签 host：请求转发目标主机名（如 example.com） port：目标端口（默认 80 或 443） protocol：使用的协议（http 或 https） path：转发到的具体路径（如 /api/v1/users） 什么是 Route？ Service 不能单独工作，需与 Route 配合使用。Route 是将客户端请求映射到特定 Service 的配置。Route 可指定如下条件：
Path：客户端访问的 URL 路径 Method：HTTP 方法（如 GET、POST） Host：请求的 host 头 Header：特定 HTTP 头的值 通过 Route 配置，Kong 能判断哪些请求转发到哪个 Service。</description>
    </item>
    
    <item>
      <title>在 RKE2 上安装 Kong API Gateway</title>
      <link>https://wenhan.blog/zh/posts/20241029_konggw_rke2/</link>
      <pubDate>Tue, 29 Oct 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241029_konggw_rke2/</guid>
      <description>本文记录了在 RKE2 上安装 Kong 的步骤。
RKE2 安装 RKE2 Server 1 2 3 4 5 6 7 8 9 10 root@ip-10-0-25-27:~# curl -sfL https://get.rke2.io | sh - [INFO] finding release for channel stable [INFO] using v1.30.5+rke2r1 as release [INFO] downloading checksums at https://github.com/rancher/rke2/releases/download/v1.30.5+rke2r1/sha256sum-amd64.txt [INFO] downloading tarball at https://github.com/rancher/rke2/releases/download/v1.30.5+rke2r1/rke2.linux-amd64.tar.gz [INFO] verifying tarball [INFO] unpacking tarball file to /usr/local root@ip-10-0-25-27:~# systemctl enable rke2-server.service --now Created symlink /etc/systemd/system/multi-user.target.wants/rke2-server.service → /usr/local/lib/systemd/system/rke2-server.service. root@ip-10-0-25-27:~# 后续处理
1 2 3 4 5 6 7 # RKE2 Agent 使用的 Token 文件 cat /var/lib/rancher/rke2/server/node-token # 一同安装的 kubectl 路径 cp /var/lib/rancher/rke2/bin/kubectl /usr/local/bin/ # kubeconfig 文件路径 mkdir .</description>
    </item>
    
    <item>
      <title>K3s &#43; Kong Mesh 部署实践</title>
      <link>https://wenhan.blog/zh/posts/20241023_k3s_kong_mesh/</link>
      <pubDate>Wed, 23 Oct 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241023_k3s_kong_mesh/</guid>
      <description>Kong Mesh 是一个简化服务网格管理的平台，可以安全高效地管理微服务间的通信。它提供安全性、可观测性、流量控制等功能，并通过 Sidecar 架构代理各服务间的通信。支持 Kubernetes 和虚拟机，能够在不同环境下实现统一的网络管理。这样开发者可以专注于应用本身，降低运维复杂度。
本文记录 Kong Mesh 的部署方法及其策略用法。
K8s 环境准备 标准的 k8s 环境都可以，这里以 k3s 为例。
1 2 export INSTALL_K3S_EXEC=&amp;#34;--tls-san &amp;lt;ip address&amp;gt; --write-kubeconfig ~/.kube/config --write-kubeconfig-mode 644&amp;#34; curl -sfL https://get.k3s.io | sh - 如果在 sh 前加上 INSTALL_K3S_CHANNEL=v1.24.4+k3s1，可以指定安装版本。省略则安装最新版。
1 2 3 kubectl get node NAME STATUS ROLES AGE VERSION wenhan-demo Ready control-plane,master 5m56s v1.30.4+k3s1 Kong Mesh 部署 本次以最简单的 Single Zone 架构为例。
Kumactl kumactl 是 Kong Mesh/Kuma 的管理 CLI 工具，可用于服务网格的配置和资源操作。通过 CLI 命令可以部署网格、设置策略、监控资源。 按如下步骤安装 kumactl 并加入 PATH。省略 VERSION 则安装最新版。</description>
    </item>
    
    <item>
      <title>使用 Terraform 配置 Kong Gateway</title>
      <link>https://wenhan.blog/zh/posts/20241008_terraform_deploy/</link>
      <pubDate>Tue, 08 Oct 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20241008_terraform_deploy/</guid>
      <description>有一篇关于使用 Terraform 构建 Konnect 环境 的文章，介绍了如何用 Terraform 配置 Konnect 环境。本文不涉及 Konnect，而是记录如何用 Terraform 配置本地（On-prem）Kong Gateway。
本次使用的 Terraform provider 如下：
https://github.com/Kong/terraform-provider-kong-gateway
https://registry.terraform.io/providers/Kong/kong-gateway/latest/docs
Kong 和 Terraform 的安装过程略。
最低所需文件 指定 provider 及 Kong Gateway Admin API 地址 1 2 3 4 5 6 7 8 9 10 11 terraform { required_providers { kong-gateway = { source = &amp;#34;kong/kong-gateway&amp;#34; } } } provider &amp;#34;kong-gateway&amp;#34; { server_url = &amp;#34;http://localhost:8001&amp;#34; } Service 定义 1 2 3 4 5 6 7 resource &amp;#34;kong-gateway_service&amp;#34; &amp;#34;httpbin&amp;#34; { name = &amp;#34;HTTPBin&amp;#34; protocol = &amp;#34;http&amp;#34; host = &amp;#34;httpbin.</description>
    </item>
    
    <item>
      <title>在 AWS ECS Fargate 环境部署 Kong Gateway</title>
      <link>https://wenhan.blog/zh/posts/20240919_konggw_ee_ecs/</link>
      <pubDate>Thu, 19 Sep 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20240919_konggw_ee_ecs/</guid>
      <description>AWS Elastic Container Service (ECS) 概述 AWS 的 Elastic Container Service (ECS) 是一项全托管的容器编排服务，可以轻松运行、停止和管理容器化应用。ECS 具有以下主要特点：
利用 AWS 基础设施实现高可用性和可扩展性 支持 Docker 容器 与其他 AWS 服务集成 灵活的调度选项 Kong Gateway 是高性能的开源 API 网关，提供微服务管理、安全、监控等多种功能。本文介绍如何在 ECS 环境中安装 Kong Gateway。整体安装流程如下图所示：
数据库准备 本次部署使用与 PostgreSQL 兼容的 Aurora DB。创建 Aurora DB 时可以指定数据库名，建议一开始就为 Kong 指定好。
ecs-cli 命令准备 接下来准备命令。可以预先定义访问权限和集群信息。由于本次创建的是 FARGATE 类型，需将 default-launch-type 设为 FARGATE。
1 2 3 4 5 6 7 8 9 10 11 12 &amp;gt; ecs-cli configure profile \ --profile-name kong-profile \ --access-key xxxxxxxx \ --secret-key xxxxxxxx INFO[0000] Saved ECS CLI profile configuration kong-profile.</description>
    </item>
    
    <item>
      <title>Kong Gateway 的审计日志（Audit Log）</title>
      <link>https://wenhan.blog/zh/posts/20240501_kong_gw_audit_log/</link>
      <pubDate>Wed, 01 May 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20240501_kong_gw_audit_log/</guid>
      <description>概要 Kong 的审计日志（Audit Log）通过记录 Kong 执行的各类操作，为系统状态和安全性提供洞察。这使得系统变更、问题追踪以及安全事件检测成为可能。
Kong 的审计日志通常为 JSON 格式，记录的信息包括：
请求和响应数据 对路由和服务的访问尝试 用户或客户端的认证信息 API Key 的使用情况 错误或警告信息 系统事件和配置变更 https://docs.konghq.com/gateway/latest/kong-enterprise/audit-log/
功能启用 Kong Audit Log 是 Enterprise 版本的功能，默认关闭。可通过修改 audit_log 参数来启用或禁用。
1 2 audit_log = on ## 启用审计日志 audit_log = off ## 关闭审计日志 使用方法 例如，执行如下访问：
1 curl -i -X GET http://localhost:8001/status 该访问会被记录到审计日志中，可通过如下请求获取：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 $ curl -i -X GET http://localhost:8001/audit/requests HTTP/1.</description>
    </item>
    
    <item>
      <title>用 decK CLI 配置 Kong API Gateway</title>
      <link>https://wenhan.blog/zh/posts/20240404_deck_apiops_basic/</link>
      <pubDate>Thu, 04 Apr 2024 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20240404_deck_apiops_basic/</guid>
      <description>Deck CLI decK 是为 API 生命周期自动化（APIOps）开发的命令行工具。开发和运维团队可以用它从开发到部署全流程管理 API，确保 API 集成的一致性、可靠性和速度。
decK 命令具备以下功能和特点：
导出（备份） 将现有 Kong 配置导出为 YAML 格式的配置文件
导入（还原） 使用导出的或手写的配置文件应用到 Kong 配置
差异和同步 decK 可以比较文件中的配置和 Kong 数据库中的配置，并进行同步，也可以检测配置差异
反向同步 如果 Kong 数据库中有而文件中没有的配置，也可以反向同步到文件
校验 校验配置文件的语法错误
重置 删除 Kong 数据库中的所有实体
并发操作 对 Kong Admin API 的调用会并发执行，利用多线程加速同步过程
Kong 认证 通过 --headers Kong-Admin-Token:test 这种方式在 HTTP 头中加入认证信息，可访问需要认证的 Kong
多文件管理 Kong 配置 可以根据实体间共享的一组标签，将 Kong 配置拆分为多个文件管理
配置管理自动化 decK 设计为 CI 流水线的一部分，不仅能推送配置到 Kong，还能检测配置漂移
下面按子命令举例说明
事前准备 以下 Kong 配置文件为基础示例。包含一个 Service 和一个 Route。</description>
    </item>
    
    <item>
      <title>Kong OpenID Connect 插件的其他使用案例</title>
      <link>https://wenhan.blog/zh/posts/20240123_kong-oidc-plugin-extra-use-cases/</link>
      <pubDate>Tue, 23 Jan 2024 22:55:37 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20240123_kong-oidc-plugin-extra-use-cases/</guid>
      <description>（翻译自 https://tech.aufomm.com/kong-oidc-plugin-extra-use-cases/ ）
Kong 的 OIDC 插件非常强大且复杂（有近 200 个参数……），如果你知道需要怎样的参数组合，可以实现更多功能。本文介绍一些实际使用场景，帮助你更好地用好这个插件。
注：我使用的是 Kong Gateway (Enterprise) 2.4.1.1 版本。
前置条件
Kong Gateway (Enterprise) 已有 OIDC 服务器（本文以 Keycloak 为例）。如果不会用 Keycloak，可以参考我之前的文章。 从 IDP Token 提取数据并添加到 Header 如果你想把 IDP token 里的值映射到上游 header，可以用 config.upstream_headers_names 和 config.upstream_headers_claims。
比如 JWT token 的 payload 如下：
1 2 3 4 5 { &amp;#34;payload&amp;#34;: { &amp;#34;kong-test&amp;#34;: &amp;#34;to-upstream&amp;#34; } } 目标是把 to-upstream 这个值映射到 header kong-test，并传递给上游服务器。OIDC 插件可以这样配置（以 password flow 为例）：
1 2 3 4 5 6 7 8 9 10 curl --request POST \ --url http://kong.</description>
    </item>
    
    <item>
      <title>Kong Gateway 的 JWT 插件使用实践</title>
      <link>https://wenhan.blog/zh/posts/20230522_kong_jwt_plugin/</link>
      <pubDate>Mon, 22 May 2023 00:33:31 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20230522_kong_jwt_plugin/</guid>
      <description>（翻译自 https://tech.aufomm.com/how-to-use-jwt-plugin/ ）
Kong 有很多认证插件，本文介绍 JWT 插件的用法。
使用示例 创建 Service 1 2 3 4 curl -X POST http://localhost:8001/services \ -H &amp;#34;Content-Type: application/json&amp;#34; \ -H &amp;#34;Accept: application/json, */*&amp;#34; \ -d &amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;jwt-service&amp;#34;,&amp;#34;url&amp;#34;:&amp;#34;https://httpbin.org/anything&amp;#34;}&amp;#39; 创建 Route 1 2 3 4 curl -X POST http://localhost:8001/services/jwt-service/routes \ -H &amp;#34;Content-Type: application/json&amp;#34; \ -H &amp;#34;Accept: application/json, */*&amp;#34; \ -d &amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;jwt-route&amp;#34;,&amp;#34;paths&amp;#34;:[&amp;#34;/jwt&amp;#34;]}&amp;#39; 用 curl &#39;http://localhost:8000/jwt&#39; -i 访问路由，应该返回 HTTP/1.1 200 OK。
启用 JWT 插件 :::note 该插件可以按 Service 或全局启用。 :::
1 2 3 4 curl --request POST \ --url http://localhost:8001/plugins \ --header &amp;#39;Content-Type: application/x-www-form-urlencoded&amp;#39; \ --data name=jwt 再次访问上面的路由，应该返回 HTTP/1.</description>
    </item>
    
    <item>
      <title>用 Mozila SOPS&#43;age 实现 Kong Gateway 安全部署</title>
      <link>https://wenhan.blog/zh/posts/20230308_using-sops-and-age-deploy-konggw/</link>
      <pubDate>Wed, 08 Mar 2023 16:41:18 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20230308_using-sops-and-age-deploy-konggw/</guid>
      <description>背景 在部署 Kong Gateway 时，常常有一些不希望以明文保存的数据库连接信息等敏感数据。为了解决这个问题，Kong Secret Manager 被开发出来，结合 AWS Secrets Manager 等第三方服务可以很好地解决。但有些环境无法连接外部安全服务，这时就无法使用这些功能。这里介绍如何利用 Mozila 开发的 SOPS 加密工具和 Github Action 的 CI/CD workflow，将配置文件平时加密存储，仅在部署时解密并安装 Kong GW。
事前准备 在构建 CI/CD workflow 之前，先在本地环境试试加密和解密。需要安装以下工具：
age sops SOPS 是非常流行的加解密工具，支持 PGP、age、Google Cloud KMS、Azure Key Vault、Hashicorp Vault 等。本文因尽量不依赖云服务，选择了 age。
工具安装好后，试试 age-keygen --help：
1 2 3 4 5 6 7 8 $ age-keygen --help Usage: age-keygen [-o OUTPUT] age-keygen -y [-o OUTPUT] [INPUT] Options: -o, --output OUTPUT Write the result to the file at path OUTPUT.</description>
    </item>
    
    <item>
      <title>结合 HashiCorp Vault 部署 Kong Gateway</title>
      <link>https://wenhan.blog/zh/posts/20230223_vault_konggwdeploy_via_sm/</link>
      <pubDate>Thu, 23 Feb 2023 00:34:38 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20230223_vault_konggwdeploy_via_sm/</guid>
      <description>背景 从 Kong Gateway 3.0 开始，Secrets Management（密钥管理）已正式 GA。Kong Gateway 运行时依赖许多密钥，从数据库密码到插件用的 API Key。过去可以用 RBAC 限制 Admin API 和 Kong Manager 对敏感信息的访问，但如果能让密钥不以明文显示进行管理就更好了，这正是 Secrets Management 能实现的。
支持的 Vault 目前支持以下四种 Vault：
AWS Secrets Manager GCP Secrets Manager HashiCorp Vault 环境变量 Kong 对上述系统做了抽象，实际使用时只需切换 Vault 关键字（hcv、aws、gcp 或 env）即可。例如，要访问 HashiCorp Vault 中 Postgres Secret 的 password 字段，可以这样引用：
{vault://hcv/postgres/password}
AWS Secrets Manager 的情况：
{vault://aws/postgres/password}
环境变量的情况：
1 2 export POSTGRES=&amp;#39;{&amp;#34;username&amp;#34;:&amp;#34;user&amp;#34;, &amp;#34;password&amp;#34;:&amp;#34;pass&amp;#34;}&amp;#39; {vault://env/postgres/password} 演示 下面实际用 Secrets Management 参照 Vault 的密钥并部署 Kong。</description>
    </item>
    
    <item>
      <title>用 Kong Gateway 实现！请求失败时通过 Slack Webhook 通知</title>
      <link>https://wenhan.blog/zh/posts/20230220_exit_slack/</link>
      <pubDate>Mon, 20 Feb 2023 14:21:23 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20230220_exit_slack/</guid>
      <description>背景 使用 Kong Gateway 访问 API 时，如果请求失败，通常希望能收到通知。常规做法是用日志类插件保存日志，再用第三方产品（如 ELK）设置告警和通知。但引入其他产品部署麻烦，还涉及授权和配置，最好能直接在 Kong 内部实现。本文记录如何用 Exit Transformer 插件，在请求出错时通过 Lua 脚本调用 Slack Webhook 实现通知。该插件允许用 Lua 函数自定义和转换 Kong 的响应消息，包括修改消息、状态码、Header，甚至完全自定义响应结构。
插件试用 先试试官方文档的例子。
创建 Service 和 Route 1 2 http :8001/services name=example.com host=mockbin.org http -f :8001/services/example.com/routes hosts=example.com 添加 key-auth 插件制造失败 1 http :8001/services/example.com/plugins name=key-auth 编写 Lua 脚本 下面的代码会添加 x-some-header 头，并在 message 末尾加上 , arr。保存为 transform.lua。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -- transform.</description>
    </item>
    
    <item>
      <title>试用 Kong Gateway 的 Plugin Ordering 功能</title>
      <link>https://wenhan.blog/zh/posts/20230127_plugin_ordering/</link>
      <pubDate>Fri, 27 Jan 2023 11:42:28 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20230127_plugin_ordering/</guid>
      <description>在做 Kong Gateway 的 PoC 时遇到了一个问题。 PoC 的需求如下：
用 Key-auth 插件保护整个 Gateway 用 Request-transformer 插件将所需的 API Key 添加到 Header 以通过认证 配置好两个插件后，即使在请求中添加了 API key，认证依然失败。
1 2 3 4 5 6 7 8 9 10 11 # key-auth is enabled ❯ http --header localhost:8000/demo | head -1 HTTP/1.1 401 Unauthorized # 创建 request-transformer-adv 插件后依然 401。 ❯ curl -X POST http://localhost:8001/services/testsvc/plugins \ --data &amp;#34;name=request-transformer-advanced&amp;#34; \ --data &amp;#34;config.add.headers=apikey:wenhandemo&amp;#34; ... ❯ http --header localhost:8000/demo | head -1 HTTP/1.</description>
    </item>
    
    <item>
      <title>Kong Gateway 的 Webhook/Event Hook 试用</title>
      <link>https://wenhan.blog/zh/posts/20221122_event-hooks/</link>
      <pubDate>Tue, 22 Nov 2022 22:57:09 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20221122_event-hooks/</guid>
      <description>通过 Event Hooks 功能，可以在 Kong Gateway 中发生特定事件时收到通知。如果你想监控新管理员或服务的创建，或插件限流等事件，这会非常有用。
这是什么功能？ Event Hooks 有四种类型：
webhook：发送定义格式的 POST 请求 webhook-custom：发送自定义 HTTP 请求 log：将事件内容记录到 Kong Gateway 的错误日志中 lambda：触发预定义的 Lua 函数 本文将演示 webhook-custom 和 log 的用法。
查看可用事件 Kong Gateway 的 admin API 提供了 /event-hooks/sources 端点，可以查看可用的 source、event 及参数。数据较多，这里只摘录部分：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... &amp;#34;rate-limiting-advanced&amp;#34;: { &amp;#34;rate-limit-exceeded&amp;#34;: { &amp;#34;fields&amp;#34;: [ &amp;#34;consumer&amp;#34;, &amp;#34;ip&amp;#34;, &amp;#34;service&amp;#34;, &amp;#34;rate&amp;#34;, &amp;#34;limit&amp;#34;, &amp;#34;window&amp;#34; ], &amp;#34;unique&amp;#34;: [ &amp;#34;consumer&amp;#34;, &amp;#34;ip&amp;#34;, &amp;#34;service&amp;#34; ], &amp;#34;description&amp;#34;: &amp;#34;Run an event when a rate limit has been exceeded&amp;#34; } }, .</description>
    </item>
    
    <item>
      <title>Kong Manager 主机名部署配置</title>
      <link>https://wenhan.blog/zh/posts/20220906_gettingkongmanagertowork/</link>
      <pubDate>Wed, 07 Sep 2022 00:40:42 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20220906_gettingkongmanagertowork/</guid>
      <description>NOTE: 翻译自 https://svenwal.de/blog/20210316_kong_manager_install/
TL;DR：如果 Kong Manager 无法正常工作，请检查 KONG_ADMIN_API_URI 和 KONG_ADMIN_GUI_URL 的设置。
Kong Manager 简介 自 2021 年 2 月起，Kong Manager（长期以来是 Kong Enterprise 的功能）已成为 Kong 免费版的一部分。
我长期使用 Kong Manager，也帮助过许多用户正确配置它，这里想分享一些典型的配置问题。
Kong Manager 易于使用 Kong Manager 开箱即用。在本地机器上安装并启用 Kong（免费或企业版）后，可以直接通过 http://localhost:8002 访问 Kong Manager。
常见问题与修复 实际部署时，通常不是在本地机器上运行 Kong Manager，而是安装在服务器上，并通过合适的 DNS 入口访问。同时，你希望用不同的主机名而不是像 8002 这样的端口。
假设 Kong 安装在 LoadBalancer/Ingress/&amp;hellip; 后面，Kong Manager 通过 https://kong-manager.my-company.example.com 这样的主机名暴露。当你访问时，Kong Manager 页面能打开，但默认工作区消失了，也没有新建工作区的按钮。发生了什么？
Kong 的一切都是基于 API 的，Kong Manager 的整个 UI 其实是运行在本地浏览器中的 Web 应用。如果你没有修改配置，它会默认调用 Admin-API 地址（http://localhost:8001）。</description>
    </item>
    
    <item>
      <title>试用 Kong Gateway 的 Route By Header 插件</title>
      <link>https://wenhan.blog/zh/posts/20220802_using-kong-gw-route-by-header-plugin/</link>
      <pubDate>Wed, 03 Aug 2022 01:19:02 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20220802_using-kong-gw-route-by-header-plugin/</guid>
      <description>介绍 在 Kong Gateway 中，可以通过 Route 定义子路径，从而决定访问哪个 Service。例如，URL 末尾的子路径为 pathA 时，流量会被路由到 serviceA，最终访问 endpointA。
1 http://mykong.org:8000/pathA =&amp;gt; serviceA =&amp;gt; endpointA 但有时我们希望根据请求更动态地决定转发目标，而不仅仅依赖子路径。这个插件允许你根据预定义的请求头和目标规则，动态决定转发目的地。规则有两个参数：condition（要匹配的头和取值）和 upstream_name（目标 Upstream 对象）。
1 {&amp;#34;rules&amp;#34;:[{&amp;#34;condition&amp;#34;: {&amp;#34;location&amp;#34;:&amp;#34;us-east&amp;#34;}, &amp;#34;upstream_name&amp;#34;: &amp;#34;east.doamin.com&amp;#34;}]} 本文记录了该插件的实现方法。 注意：该插件只能通过 Admin API 管理，Kong Manager 无法操作。
https://docs.konghq.com/hub/kong-inc/route-by-header/
演示 下面实际体验一下该插件。首先创建一个用于演示的 service 和 route。service 的 endpoint 是 http://httpbin.org/anything，当没有命中任何插件规则时会访问它。
1 2 3 4 5 6 7 8 9 10 curl -i -X POST http://localhost:8001/services \ --data protocol=http \ --data host=httpbin.org \ --date path=/anything --data name=demo curl -i -X POST http://localhost:8001/routes \ --data &amp;#34;paths[]=/&amp;#34; \ --data service.</description>
    </item>
    
    <item>
      <title>试用 Kong 的 Canary Release 插件</title>
      <link>https://wenhan.blog/zh/posts/20220531_using-kong-canary-release-plugin/</link>
      <pubDate>Tue, 31 May 2022 13:58:39 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20220531_using-kong-canary-release-plugin/</guid>
      <description>介绍 通过 Kong Gateway 的插件，可以非常方便地实现金丝雀发布。金丝雀发布不仅支持简单的百分比流量分配，还可以实现流量逐步扩大、白名单和黑名单等多种模式。
本文记录了金丝雀发布的操作方法。
https://docs.konghq.com/hub/kong-inc/canary/
创建 Service 和 Route 本次演示中，当前版本指向 http://httpbin.org/xml，新版本指向 http://httpbin.org/json。因此，访问当前版本时会返回 XML 响应，访问新版本时会返回 JSON 响应。
1 2 3 4 5 6 7 ❯ http POST localhost:8001/services \ name=canary-api-service \ url=http://httpbin.org/xml ❯ http -f POST localhost:8001/services/canary-api-service/routes \ name=canary-api-route \ paths=/api/canary 验证效果，应该能收到 XML 响应：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ❯ http GET localhost:8000/api/canary HTTP/1.</description>
    </item>
    
    <item>
      <title>试用 Kong 的 Mocking 插件</title>
      <link>https://wenhan.blog/zh/posts/20220517_using-kong-mocking-plugin/</link>
      <pubDate>Tue, 17 May 2022 13:58:39 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20220517_using-kong-mocking-plugin/</guid>
      <description>介绍 Mocking 插件为开发中的 API 提供模拟端点，便于测试。它基于 Open API（OAS）规范，对发送到 API 的请求返回模拟响应。为了测试的灵活性，Mocking 插件可以很方便地启用或禁用。 https://docs.konghq.com/hub/kong-inc/mocking/
注意：该插件只能返回 200、201 和 204 响应。它是为测试正常流程而设计，无法返回 200 以外的状态码。
本文将以 Docker 容器方式搭建 db-less 的 Kong Gateway 环境，并在其上试用 Mocking 插件。
部署 Kong Gateway 之前 由于是 db-less 环境，无法通过 Admin API 或 GUI 修改配置（因为没有存储位置）。因此，启动容器时需要提前准备好名为 Declarative Configuration 的配置文件。
https://docs.konghq.com/gateway/2.8.x/reference/db-less-and-declarative-config/#main
本次的目标是试用 Mocking，所以也要在该文件中写好 Mocking 的相关配置。正如 Mocking 插件介绍页面 所述，配置本身非常简单，关键参数有 api_specification 和 api_specification_filename：
api_specification_filename：指定保存规范内容的文件名。 api_specification：直接将规范内容作为参数写入。 如果用 api_specification_filename，需要提前将文件上传到 Dev Portal，并且只写文件名，不带路径。示例见这里。但本次我们用 db-less 环境，无法使用 Dev Portal，因此不能用 api_specification_filename。
所以我们直接在 api_specification 里写入 API 规范的示例内容，然后启动容器。规范示例可参考 Mocking 插件介绍页面。</description>
    </item>
    
    <item>
      <title>外部访问 K3s 集群的方法</title>
      <link>https://wenhan.blog/zh/posts/20220303_access-k3s-from-outside/</link>
      <pubDate>Thu, 03 Mar 2022 23:49:19 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20220303_access-k3s-from-outside/</guid>
      <description>从外部访问 k3s 集群 当你用默认设置创建 k3s 集群时，只能在本节点内部访问。 如果将 /etc/rancher/k3s/k3s.yaml kubeconfig 文件拷贝到其他主机并导入，尝试访问时会遇到如下报错：
1 2 ❯ kubectl get node Unable to connect to the server: x509: certificate is valid for 10.0.140.68, 10.43.0.1, 127.0.0.1, not xxx.xxx.xxx.xxx 要让 k3s 集群支持外部访问，可以在创建集群时加上如下参数：
1 --tls-san value (listener) Add additional hostname or IP as a Subject Alternative Name in the TLS cert 该参数详情见 https://rancher.com/docs/k3s/latest/en/installation/install-options/#registration-options-for-the-k3s-server
安装命令示例：
1 curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC=&amp;#34;--tls-san &amp;lt;your node public ip address&amp;gt;&amp;#34; sh - 然后将 /etc/rancher/k3s/k3s.</description>
    </item>
    
    <item>
      <title>--node-cidr-mask-size 错误的原因与修复</title>
      <link>https://wenhan.blog/zh/posts/20220126_--node-cidr-mask-size_error/</link>
      <pubDate>Wed, 26 Jan 2022 11:33:00 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20220126_--node-cidr-mask-size_error/</guid>
      <description>在调整 rke 集群的 CIDR 时，遇到了如下报错，导致集群创建失败：
1 {&amp;#34;log&amp;#34;:&amp;#34;F1203 01:36:22.168496 1 node_ipam_controller.go:115] Controller: Invalid --cluster-cidr, mask size of cluster CIDR must be less than or equal to --node-cidr-mask-size configured for CIDR family\n&amp;#34;,&amp;#34;stream&amp;#34;:&amp;#34;stderr&amp;#34;,&amp;#34;time&amp;#34;:&amp;#34;2021-12-03T01:36:22.16859524Z&amp;#34;} cluster.yaml 相关配置如下：
1 2 3 4 5 6 services: kube-controller: cluster_cidr: 10.42.0.0/25 service_cluster_ip_range: 10.43.0.0/25 kube-api: service_cluster_ip_range: 10.43.0.0/25 问题出在 CIDR 的掩码大小。该掩码由 --node-cidr-mask-size 参数设置，必须大于（即 IP 范围更小）集群 CIDR 的掩码。默认值为 24，而上述配置为 25，比默认值大（IP 范围溢出），因此报错。
可以通过如下方式在 extra_args 中修改 --node-cidr-mask-size：
1 2 3 4 5 6 services: kube-controller: cluster_cidr: 10.</description>
    </item>
    
    <item>
      <title>使用 Rancher 的 Continuous Delivery 功能轻松实现 GitOps</title>
      <link>https://wenhan.blog/zh/posts/20211111_fleet-demo/</link>
      <pubDate>Thu, 11 Nov 2021 16:18:38 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20211111_fleet-demo/</guid>
      <description>本文将搭建一个单节点 Rancher server 和两个 k3s 集群环境，并通过 Rancher 的 Continuous Delivery 功能，用 GitOps 操作这两个 k3s 集群。
Step 1: 部署 Rancher Server 首先在 rancher 节点上执行如下 docker 命令，搭建单节点 Rancher server。
1 2 3 4 sudo docker run -d --restart=unless-stopped \ -p 80:80 -p 443:443 \ --privileged \ rancher/rancher:v2.5.10 Rancher server 大约 1 分钟后启动，使用浏览器访问 rancher 节点的 IP 地址即可进入 Rancher UI。
本例中 Rancher 使用自签名证书，浏览器会弹出证书警告，可以直接跳过。有些浏览器没有跳过按钮，此时点击错误页面任意位置并输入 thisisunsafe，即可跳过警告并接受证书。
首次访问时需要设置初始密码，按页面提示操作即可。
Step 2: 部署 k3s Kubernetes 集群 接下来部署 k3s 集群。方法很简单，只需在 k3s-1 和 k3s-2 节点分别执行如下命令。</description>
    </item>
    
    <item>
      <title>Kubernetes证书过期测试</title>
      <link>https://wenhan.blog/zh/posts/20210623_modify-kubeadm-cert-expired-period-test/</link>
      <pubDate>Wed, 23 Jun 2021 17:34:31 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20210623_modify-kubeadm-cert-expired-period-test/</guid>
      <description>由于工作需要，需要验证Kubernetes集群证书过期后的续期流程。 因为之前没有做过，特此记录。
修改并编译 kubeadm 源码 在构建环境中安装 go 和 git，并将 go 的 bin 路径加入 PATH。
下载 Kubernetes 源码，这里以 v1.18.18 为例。
git clone -b v1.18.18 https://github.com/kubernetes/kubernetes
修改以下文件，将证书有效期设置为 10 分钟：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index b56891ca908.</description>
    </item>
    
    <item>
      <title>Rancher 从零学习</title>
      <link>https://wenhan.blog/zh/posts/20200609_rancher-learning-from-zero/</link>
      <pubDate>Tue, 09 Jun 2020 16:12:08 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20200609_rancher-learning-from-zero/</guid>
      <description>介绍 Rancher 由 rancher-server、rancher-agent 以及一个或多个 kubernetes cluster 组成。其中，rancher-agent 运行在被管理的 kubernetes 上，与 rancher-server 通信并发送集群信息。
rancher-server 提供用于管理 kubernetes 的 WebUI 和 API。rancher-server 只能通过 HTTPS 访问。
安装 单节点 单节点有两种搭建方式：
直接用 docker 运行 rancher-server 用 rke 在单节点上启用所有 role rke 的方法后面会介绍，这里先用 docker 的方法。
在要运行 rancher-server 的节点上输入如下命令：
1 2 3 docker run -d --restart=unless-stopped \ -p 80:80 -p 443:443 \ rancher/rancher:latest 这样就启动了单节点的 rancher-server，可通过 http://&amp;lt;IP Address&amp;gt; 访问。
多节点 用 rke 构建 HA 环境的 rancher-server。
rke (rancher k8s engine) 是用于搭建 kubernetes 集群的命令行工具，环境准备好后只需一条命令即可搭建集群。</description>
    </item>
    
    <item>
      <title>自建 NAS 环境项目</title>
      <link>https://wenhan.blog/zh/posts/20200405_self-nas-environment-project/</link>
      <pubDate>Sun, 05 Apr 2020 15:22:58 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20200405_self-nas-environment-project/</guid>
      <description>本文是我用 Ubuntu 20.04 搭建私人 NAS 站点的笔记。NAS 的主要用途是存储照片。 其他服务方面，使用 webmin 通过 WebUI 管理主机，Emby/Jellyfin 用于多媒体。这两个服务都运行在 docker 中，所以我也用 portainer 来管理。
概览 用 Samba 共享存储，iPhone 上传照片到 NAS 用 PhotoSync。用 smartctl 检查磁盘健康，如果有异常则邮件通知。
文件共享（Samba） 安装 Samba：
1 sudo apt-get install samba smbfs 编辑 /etc/samba/smb.conf 配置文件，如有需要可修改工作组。
1 2 # Change this to the workgroup/NT-domain name your Samba server will part of workgroup = WORKGROUP 然后设置共享文件夹，在文件末尾添加如下内容：
1 2 3 4 5 6 7 [share] comment = Share directory for my self-nas path = /share read only = no guest only = no guest ok = no share modes = yes 重启 smbd 服务并确认其运行状态。</description>
    </item>
    
    <item>
      <title>Multipass 启动因网络超时失败</title>
      <link>https://wenhan.blog/zh/posts/20200331_multipass-launch-failed-by-network-timeout/</link>
      <pubDate>Tue, 31 Mar 2020 10:04:31 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20200331_multipass-launch-failed-by-network-timeout/</guid>
      <description>Multipass 是一个非常实用的工具，可以用来创建 Ubuntu 虚拟机实例。 它提供了一个 CLI 来启动和管理 Linux 实例。云镜像的下载也是自动完成的，几分钟内就能启动一个 VM。
https://multipass.run/
但在我的情况下，我尝试用 multipass 1.1.0 快速启动实例时，遇到了 Network timeout 错误。
1 2 3 4 5 6 $ multipass version multipass 1.1.0 multipassd 1.1.0 $ multipass launch launch failed: failed to download from &amp;#39;http://cloud-images.ubuntu.com/releases/server/releases/bionic/release-20200317/ubuntu-18.04-server-cloudimg-amd64.img&amp;#39;: Network timeout 我用 wget 检查了这个链接，没有问题，所以我猜测镜像下载时间太长，超过了 multipass 的某个超时设置。 能否手动下载镜像并用它来启动实例？当然可以。 你可以先下载镜像，然后将镜像路径作为参数传递。
1 2 $ multipass launch file:///home/wshi/Downloads/ubuntu-18.04-server-cloudimg-amd64.img Launched: lucrative-eelpout 关于在日本的下载速度还有一点补充。 http://cloud-images.ubuntu.com 在我这里速度很慢，所以我换用了富山大学的镜像 http://ubuntutym2.u-toyama.ac.jp/cloud-images/releases/。 比官方源快了10倍，希望对你有帮助。</description>
    </item>
    
    <item>
      <title>从 Hexo 迁移到 Hugo</title>
      <link>https://wenhan.blog/zh/posts/20200330_moving-from-hexo-to-hugo/</link>
      <pubDate>Mon, 30 Mar 2020 14:07:21 +0900</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20200330_moving-from-hexo-to-hugo/</guid>
      <description>从 Hexo 迁移到 Hugo 之前一直用 Hexo 写博客，后来在学习 Golang 的时候顺便把博客迁移到了 Hugo。 原因有很多，主要有以下几点：
Hexo 依赖多个模块，有时候安装会出错。 相比之下，Hugo 只需要一个二进制文件就够了。 生成 HTML 文件的速度，Hugo 明显比 Hexo 快很多。 关于 Hugo 的安装和使用方法这里就不赘述了，这篇文章主要整理一下这次博客迁移过程中遇到的问题。
标签的写法 Hexo 下可以用如下格式写标签，但在 Hugo 下不行：
1 tag: Python 统一用下面这种格式就没问题了：
1 2 tags: - Python 博客 md 文件的保存位置 这个因主题而异。我用的是 Even 主题，所以是在 post 目录下。
生成的博客页面与 Github Action 的集成 Hexo 可以用 deploy 子命令直接推送到 Github，但 Hugo 不行。 虽然有 Hugo deploy 命令，但主要是面向 AWS、GCE、Azure 等云平台： https://gohugo.io/hosting-and-deployment/hugo-deploy/
所以需要手动将生成的文件推送到 Github Pages 仓库。 生成的博客页面都在 public 目录下。 如果用 Github Action，可以实现全自动化：写好新文章并 push 后，上述流程都能自动完成。 具体做法会在下一篇文章中总结。</description>
    </item>
    
    <item>
      <title>ASRock Fatal1ty B450 Gaming-ITX &#34;断电恢复&#34;功能无效</title>
      <link>https://wenhan.blog/zh/posts/20191001_asrock-fatal1ty-b450-gaming-itx-restore-on-ac-power-loss-not-working/</link>
      <pubDate>Tue, 01 Oct 2019 13:13:11 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20191001_asrock-fatal1ty-b450-gaming-itx-restore-on-ac-power-loss-not-working/</guid>
      <description>我有一块 ASRock Fatal1ty B450 Gaming-ITX，BIOS 版本为 3.40。 为了实现远程开机，我在 BIOS 设置中启用了&amp;quot;Restore on AC/Power Loss（断电恢复）&amp;ldquo;选项，但发现并没有生效。
后来我查到下面的帖子，把 BIOS 固件升级到 3.53 后，一切就正常了！
v3.53 可以在下面的链接找到：
http://forum.asrock.com/forum_posts.asp?TID=12059&amp;amp;title=fatal1ty-b450-gamingitx-restore-on-ac-power-loss</description>
    </item>
    
    <item>
      <title>在 Linux 下用 nmcli 命令连接 Wi-Fi</title>
      <link>https://wenhan.blog/zh/posts/20190817_access-wifi-point-via-command-line-cli/</link>
      <pubDate>Sat, 17 Aug 2019 00:55:53 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20190817_access-wifi-point-via-command-line-cli/</guid>
      <description>这里用 nmcli 来完成这个任务。
首先需要安装 network-manager 包，并启动 Daemon。
1 2 sudo apt install network-manager sudo systemctl start NetworkManager 然后用下面的命令查看网络接口状态：
1 2 3 4 5 6 7 $ nmcli dev status DEVICE TYPE STATE CONNECTION wlp2s0 wifi connected xibuka-wifi-5G enp0s31f6 ethernet connected netplan-enp0s31f6 p2p-dev-wlp2s0 wifi-p2p disconnected -- eth0 ethernet unavailable -- lo loopback unmanaged -- 接下来查看可用的 Wi-Fi 热点。
1 2 3 4 5 6 7 8 9 10 $ nmcli dev wifi list SSID MODE CHAN RATE SIGNAL BARS SECURITY aterm-55ebc5-g Infra 11 405 Mbit/s 77 ▂▄▆_ WPA1 WPA2 xibuka-wifi-2G Infra 11 405 Mbit/s 77 ▂▄▆_ WPA1 WPA2 xibuka-wifi-5G Infra 36 405 Mbit/s 63 ▂▄▆_ WPA1 WPA2 aterm-55ebc5-a Infra 36 405 Mbit/s 54 ▂▄__ WPA1 WPA2 HG8045-0D9B-bg Infra 8 195 Mbit/s 49 ▂▄__ WPA1 WPA2 HG8045-0D9B-a Infra 44 405 Mbit/s 35 ▂▄__ WPA1 WPA2 HG8045-A39A-a Infra 108 405 Mbit/s 35 ▂▄__ WPA1 WPA2 MNG6300-F560-G Infra 11 130 Mbit/s 30 ▂___ WPA1 WPA2 如果没有看到你想用的 SSID，可以用下面的命令重新扫描：</description>
    </item>
    
    <item>
      <title>bash 脚本开头常用选项</title>
      <link>https://wenhan.blog/zh/posts/20190625_bash-head-options/</link>
      <pubDate>Tue, 25 Jun 2019 00:12:59 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20190625_bash-head-options/</guid>
      <description>命令失败时立即退出脚本
1 2 set -o errexit set -e 引用未定义变量时输出错误并立即退出脚本
1 2 set -o nounset set -u 即使管道中的某个命令失败也让脚本退出
1 set -o pipefail </description>
    </item>
    
    <item>
      <title>在 ContainerDays Tokyo 介绍 microk8s</title>
      <link>https://wenhan.blog/zh/posts/20181207_presentasion-about-microk8s-at-containerdaystokyo/</link>
      <pubDate>Fri, 07 Dec 2018 00:33:49 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20181207_presentasion-about-microk8s-at-containerdaystokyo/</guid>
      <description>我在 ContainerDays Tokyo 做了关于 microk8s 和 snap 的演讲。 日文版的演示文稿可在下方查看：
speakerdeck.com
欢迎查阅！</description>
    </item>
    
    <item>
      <title>OpenStack 常用命令</title>
      <link>https://wenhan.blog/zh/posts/20180927_openstack-frequently-used-command/</link>
      <pubDate>Thu, 27 Sep 2018 22:09:02 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180927_openstack-frequently-used-command/</guid>
      <description>OpenStack 常用命令 OpenStack Compute - Nova 列出实例 nova list openstack server list 列出/查看 flavor nova flavor-list nova flavor-show &amp;lt;name or ID&amp;gt; openstack flavor list openstack flavor show &amp;lt;name or ID&amp;gt; 创建 flavor openstack flavor create --ram &amp;lt;ram&amp;gt; --vcpus &amp;lt;cpu number&amp;gt; --disk &amp;lt;size&amp;gt; --id &amp;lt;id&amp;gt; &amp;lt;name&amp;gt; nova flavor-create &amp;lt;name&amp;gt; &amp;lt;id&amp;gt; &amp;lt;ram&amp;gt; &amp;lt;disk&amp;gt; &amp;lt;vcpus&amp;gt; 启动实例 nova boot &amp;lt;name&amp;gt; --image &amp;lt;image&amp;gt; --flavor &amp;lt;flavor&amp;gt; openstack server create --flavor &amp;lt;flavor&amp;gt; --image &amp;lt;image&amp;gt; &amp;lt;name&amp;gt; 指定网络启动实例 openstack server create --flavor &amp;lt;flavor&amp;gt; --image &amp;lt;image&amp;gt; &amp;lt;name&amp;gt; net-id=&amp;lt;network&amp;gt; 指定密钥对启动实例 nova boot &amp;lt;name&amp;gt; --image &amp;lt;image&amp;gt; --flavor &amp;lt;flavor&amp;gt; --key-name &amp;lt;key-pair name&amp;gt; openstack server create --flavor &amp;lt;flavor&amp;gt; --image &amp;lt;image&amp;gt; &amp;lt;name&amp;gt; 通过路由器访问实例 ip netns list sudo ip netns exec &amp;lt;qrouter-id&amp;gt; ssh -i &amp;lt;key&amp;gt; user@ip 使用自定义端口启动实例 nova boot --image &amp;lt;image&amp;gt; --flavor &amp;lt;flavor&amp;gt; --nic port-id=&amp;lt;port-id&amp;gt; &amp;lt;instance name&amp;gt; 删除实例 nova delete &amp;lt;ID&amp;gt; openstack server delete &amp;lt;ID or name&amp;gt; Openstack Network - Neutron 列出网络 openstack network list 列出子网 openstack subnet list --long 创建网络 openstack network create &amp;lt;net name&amp;gt; 创建子网 openstack subnet create &amp;lt;subnet name&amp;gt; --network &amp;lt;net name&amp;gt; --subnet-range &amp;lt;ip address&amp;gt;/&amp;lt;prefix&amp;gt; --gateway &amp;lt;gw ip&amp;gt; --allocation-pool start=IP_ADDR,end=IP_ADDR 例如： openstack subnet create practicesubnet --network practice --subnet-range 10.</description>
    </item>
    
    <item>
      <title>Certified Kubernetes Administrator (CKA) 学习笔记</title>
      <link>https://wenhan.blog/zh/posts/20180829_certified-kubernetes-administrator-cka-learning-note/</link>
      <pubDate>Wed, 29 Aug 2018 11:40:56 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180829_certified-kubernetes-administrator-cka-learning-note/</guid>
      <description>安装 安装 docker.io 1 apt install docker.io 如果你用其他 cgroup driver 安装了 docker，需要确保 docker 和 Kubernetes 使用相同的 cgroup driver。
1 2 3 4 5 cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /etc/docker/daemon.json { &amp;#34;exec-opts&amp;#34;: [&amp;#34;native.cgroupdriver=systemd&amp;#34;] } EOF 向系统添加 apt key 和源 1 2 3 4 5 root@kube-master:~# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - OK root@kube-master:~# cat &amp;lt;&amp;lt;EOF &amp;gt;&amp;gt; /etc/apt/sources.list.d/kubernetes.list &amp;gt; deb http://apt.kubernetes.io/ kubernetes-xenial main &amp;gt; EOF 然后安装 kubernetes 相关包
1 2 root@kube-master:~# apt update -y root@kube-master:~# apt install -y kubelet kubeadm kubectl 使用 kubeadm 进行设置和配置 执行 kubeadm init 时必须选择 CNI，这里选择 flannel，所以要加 --pod-network-cidr 选项。</description>
    </item>
    
    <item>
      <title>[Juju] 使用 juju 在 LXD 中部署 Minecraft 服务器</title>
      <link>https://wenhan.blog/zh/posts/20180628_juju-use-juju-to-deploy-minecraft-server-in-lxd/</link>
      <pubDate>Thu, 28 Jun 2018 17:01:36 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180628_juju-use-juju-to-deploy-minecraft-server-in-lxd/</guid>
      <description>Juju 是一个支持多种云服务商（如 AWS、Azure、Google Cloud Platform、MAAS 和 LXD）的部署工具。本文将重点介绍如何使用 Juju 和 LXD 构建 OpenStack 测试环境。
安装 LXD 安装 LXD 非常简单，只需运行以下命令：
1 sudo apt-install lxd 如果找不到 lxd 包，请运行以下命令添加 PPA（Personal Package Archive），然后重新执行安装命令。
1 2 3 sudo apt-add-repository ppa:ubuntu-lxc/stable sudo apt update sudo apt dist-upgrade 配置 LXD 运行以下命令，按步骤配置 lxd 设置：
1 2 3 4 5 6 7 8 $ sudo lxd init Do you want to configure a new storage pool (yes/no) [default=yes]? Name of the storage backend to use (dir or zfs) [default=dir]: Would you like LXD to be available over the network (yes/no) [default=no]?</description>
    </item>
    
    <item>
      <title>Thinkpad X1C 6代在 Ubuntu 18.04 下的挂起（休眠）问题</title>
      <link>https://wenhan.blog/zh/posts/20180611_suspend-issue-on-thinkpad-x1c-6th-with-ubuntu-18-04/</link>
      <pubDate>Mon, 11 Jun 2018 15:38:10 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180611_suspend-issue-on-thinkpad-x1c-6th-with-ubuntu-18-04/</guid>
      <description>默认不支持 S3 挂起 在 Thinkpad X1 Carbon 上使用 Ubuntu 18.04 时，挂起（休眠）会有问题。合上盖子后挂起无法正常工作，笔记本依然持续耗电并发热。
根本原因是第六代 X1 Carbon 支持 S0i3（也称为 Windows Modern Standby），但不支持 S3 睡眠状态。
S0i3 睡眠支持 经过一些研究，可以通过以下方法规避：
添加如下内核参数以启用 S0i3 睡眠支持 这会禁用通过合盖唤醒/恢复
1 acpi.ec_no_wakeup=1 在 BIOS 设置中启用 Thunderbolt BIOS Assist Mode 路径为 Config -&amp;gt; Thunderbolt BIOS Assist Mode - 设为 &amp;quot;Enabled&amp;quot;
禁用 SD 卡读卡器
关于此问题的更多细节可参考：
X1 Carbon Gen 6 cannot enter deep sleep (S3 state aka Suspend-to-RAM) on Linux Suspend issues X1 Carbon 6th gen S0i3 sleep broken</description>
    </item>
    
    <item>
      <title>错误：容器创建失败：加载 raw.lxc 失败</title>
      <link>https://wenhan.blog/zh/posts/20180604_error-failed-container-creation-failed-to-load-raw-lxc/</link>
      <pubDate>Mon, 04 Jun 2018 16:19:39 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180604_error-failed-container-creation-failed-to-load-raw-lxc/</guid>
      <description>我参考了以下链接尝试安装和测试 MaaS：
https://docs.maas.io/2.1/en/installconfig-lxd-install
在文档的 profile 编辑部分：
执行 lxc profile edit maas
将 config 后的 {} 替换为如下内容（不包括 config:）：
1 2 3 4 5 6 config: raw.lxc: |- lxc.cgroup.devices.allow = c 10:237 rwm lxc.apparmor.profile = unconfined lxc.cgroup.devices.allow = b 7:* rwm security.privileged: &amp;#34;true&amp;#34; 在 launch 步骤遇到如下报错：
1 2 3 $ lxc launch -p maas ubuntu:16.04 xenial-maas Creating xenial-maas Error: Failed container creation: Failed to load raw.lxc 这是因为我用的是 LXD 3.0，上述配置键已经过时。 根据这个评论，lxd 2.1 之后 lxc.</description>
    </item>
    
    <item>
      <title>GlusterFS 中服务端修复与客户端修复的区别</title>
      <link>https://wenhan.blog/zh/posts/20180326_the-different-between-server-side-heal-and-client-side-heal-in-glusterfs/</link>
      <pubDate>Mon, 26 Mar 2018 09:56:21 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180326_the-different-between-server-side-heal-and-client-side-heal-in-glusterfs/</guid>
      <description>在 GlusterFS 中有两种修复（heal）机制：服务端修复和客户端修复。
服务端修复由所有 gluster 服务器节点上的 self-heal 守护进程自动执行。它会从 brick 路径下的 .glusterfs 目录中遍历文件和目录信息进行修复，因此可以从服务器端保持文件数据和元数据的一致性。
客户端修复则不同，当客户端从挂载路径访问文件时（即文件描述符上的操作），会针对该特定文件触发修复。这意味着每次文件访问操作都会多经过一组函数调用来检查和修复文件，可能会带来一定的性能影响。客户端修复默认是开启的，可以通过以下命令关闭：
文件数据
1 gluster volume set &amp;lt;volume name&amp;gt; cluster.data-self-heal off 目录项数据（目录内容/条目）
1 gluster volume set &amp;lt;volume name&amp;gt; cluster.entry-self-heal off 元数据
1 gluster volume set &amp;lt;volume name&amp;gt; cluster.metadata-self-heal off 需要注意的是，关闭客户端修复并不意味着会影响数据的完整性和一致性。对于文件读取，会评估副本的 pending xattr，只会从正确的副本读取。对于文件写入，新数据会写入所有副本 brick，gluster 节点上的 self-heal 守护进程会在需要时自动修复。</description>
    </item>
    
    <item>
      <title>yum update失败及修复方法</title>
      <link>https://wenhan.blog/zh/posts/20180218_failed-at-yum-update-and-how-to-fix-it/</link>
      <pubDate>Sun, 18 Feb 2018 16:46:33 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180218_failed-at-yum-update-and-how-to-fix-it/</guid>
      <description>在我的 centOS 7 上运行 yum update 时遇到了错误，命令无法更新系统！
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # yum update Loaded plugins: fastestmirror Determining fastest mirrors * base: centos.gbeservers.com * epel: linux.mirrors.es.net * extras: linux.mirrors.es.net * ius: hkg.mirror.rackspace.com * updates: mirror.atlantic.net File &amp;#34;/usr/libexec/urlgrabber-ext-down&amp;#34;, line 28 except OSError, e: ^ SyntaxError: invalid syntax File &amp;#34;/usr/libexec/urlgrabber-ext-down&amp;#34;, line 28 except OSError, e: ^ SyntaxError: invalid syntax File &amp;#34;/usr/libexec/urlgrabber-ext-down&amp;#34;, line 28 except OSError, e: ^ SyntaxError: invalid syntax File &amp;#34;/usr/libexec/urlgrabber-ext-down&amp;#34;, line 28 except OSError, e: ^ SyntaxError: invalid syntax File &amp;#34;/usr/libexec/urlgrabber-ext-down&amp;#34;, line 28 except OSError, e: ^ SyntaxError: invalid syntax Exiting on user cancel 看起来系统有点问题。查了一下，发现根本原因是 python 版本不匹配。&amp;lsquo;yum&amp;rsquo; 命令需要 /usr/bin/python 这个符号链接指向 python2.</description>
    </item>
    
    <item>
      <title>Docker基础入门</title>
      <link>https://wenhan.blog/zh/posts/20180218_docker-basic-foundation/</link>
      <pubDate>Sun, 18 Feb 2018 16:35:32 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180218_docker-basic-foundation/</guid>
      <description>以 CentOS 7、Docker 1.12.6 为基础说明。
安装 Docker 在 Linux 系统上安装 Docker 的前置条件：
Docker 只能安装在 64 位操作系统上。 建议内核版本高于 3.10。 需要启用 cgroup 和 namespace。 现在在 CentOS 或 Fedora 上安装 Docker 更加简单，可以用如下 yum 命令搜索：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ yum search docker | grep ^docker docker-client.x86_64 : Client side files for Docker docker-client-latest.x86_64 : Client side files for Docker docker-common.</description>
    </item>
    
    <item>
      <title>如何在OpenShift的容器/Pod内禁用IPv6</title>
      <link>https://wenhan.blog/zh/posts/20180201_how-to-disable-ipv6-inside-a-container-pod-in-openshift/</link>
      <pubDate>Thu, 01 Feb 2018 15:46:56 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20180201_how-to-disable-ipv6-inside-a-container-pod-in-openshift/</guid>
      <description>虽然OpenShift中的容器/Pod通过IPv4协议传输数据，通常无需关心IPv6的设置，但有时我们希望只在容器内禁用IPv6，而不影响其他容器/Pod或主机系统。
下面是容器中输出的IPv6信息示例：
1 2 3 4 5 6 7 8 9 10 11 12 13 [root@ocp37 ~]# oc exec django-ex-4-6gmsj -- ip a 1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 3: eth0@if45: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP link/ether 0a:58:0a:80:00:24 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.</description>
    </item>
    
    <item>
      <title>Gluster 文件名与 GFID 互转</title>
      <link>https://wenhan.blog/zh/posts/20171211_gluster-filename-and-gfid/</link>
      <pubDate>Mon, 11 Dec 2017 15:41:23 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20171211_gluster-filename-and-gfid/</guid>
      <description>创建测试文件 挂载 gluster 卷并创建一个文件
1 2 3 4 [root@client-1 ~]# mount -t glusterfs gluster-node-1:/vol /mnt [root@client-1 ~]# mkdir -p /mnt/hoge/hello-gluster/ [root@client-1 ~]# touch /mnt/hoge/hello-gluster/file [root@client-1 ~]# umount /mnt/ 获取 Gluster 卷内文件的 GFID 从 brick 目录获取 登录 gluster 节点，在 brick 目录中定位文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@gluster-node-1 ~]# gluster volume info vol Volume Name: vol Type: Replicate Volume ID: 4ac36bcc-7127-48c4-ac21-421850d8bc47 Status: Started Snapshot Count: 0 Number of Bricks: 1 x 3 = 3 Transport-type: tcp,rdma Bricks: Brick1: gluster-node-1:/gluster/brick-vol &amp;lt;-- 确认 brick 路径 Brick2: gluster-node-2:/gluster/brick-vol Brick3: gluster-node-3:/gluster/brick-vol Options Reconfigured: performance.</description>
    </item>
    
    <item>
      <title>AWK - 时间函数</title>
      <link>https://wenhan.blog/zh/posts/20171124_awk-time-functions/</link>
      <pubDate>Fri, 24 Nov 2017 08:34:34 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20171124_awk-time-functions/</guid>
      <description>除了 date，awk 还内置了如下时间函数，可以帮助你解决时间转换问题。
systime() 返回当前时间（自 Epoch 1970-01-01 00:00:00 起的秒数）。
1 2 3 4 $ awk &amp;#39;BEGIN { print &amp;#34;Number of seconds since the Epoch = &amp;#34; systime() }&amp;#39; Number of seconds since the Epoch = 1511480989 mktime(YYYY MM DD HH MM SS) 将日期字符串 &amp;ldquo;YYYY MM DD HH MM SS&amp;rdquo; 转换为自 Epoch 起的秒数。输出格式与 systime 相同。
1 2 3 4 $ awk &amp;#39;BEGIN { print &amp;#34;Number of seconds since the Epoch = &amp;#34; mktime(&amp;#34;2017 11 24 08 52 10&amp;#34;) }&amp;#39; Number of seconds since the Epoch = 1511481130 strftime() 根据指定格式格式化时间戳。</description>
    </item>
    
    <item>
      <title>如何判断磁盘是SSD还是HDD</title>
      <link>https://wenhan.blog/zh/posts/20171102_how-to-check-if-a-disk-is-ssd-or-hdd/</link>
      <pubDate>Thu, 02 Nov 2017 08:27:14 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20171102_how-to-check-if-a-disk-is-ssd-or-hdd/</guid>
      <description>Linux会自动检测SSD。 自内核2.6.29版本起，可以用如下命令检查/dev/sda：
1 # cat /sys/block/sda/queue/rotational 返回值为0表示/dev/sda是SSD，1表示是HDD。 注意，如果你的磁盘是由硬件RAID创建的，这个命令可能无效。
另一种方法是使用util-linux包中的lsblk命令：
1 2 3 4 # lsblk -d -o name,rota NAME ROTA sda 0 sdb 1 ROTA表示是否为旋转设备，1为HDD，0为SSD。 这个命令显示的信息和/sys/block/.../queue/rotational一致。
参考：How to know if a disk is an SSD or an HDD</description>
    </item>
    
    <item>
      <title>如何对ps命令输出进行排序</title>
      <link>https://wenhan.blog/zh/posts/20170919_how-to-sort-ps-command-output/</link>
      <pubDate>Tue, 19 Sep 2017 14:11:37 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170919_how-to-sort-ps-command-output/</guid>
      <description>ps命令有一个--sort选项，可以帮助你对进程进行排序。
1 2 3 --sort spec 指定排序顺序。排序语法为 [+|-]key[,[+|-]key[,...]]。key请参考STANDARD FORMAT SPECIFIERS部分。&amp;#34;+&amp;#34;可省略，默认是升序（数值或字典序）。等同于k。例如：ps jax --sort=uid,-ppid,+pid 按内存排序ps输出 内存使用从高到低 最大值在命令输出顶部
1 ps aux --sort -rss 内存使用从低到高 最大值在命令输出底部
1 ps aux --sort rss 按CPU使用率排序ps输出 CPU使用率从高到低 最大值在命令输出顶部
1 ps aux --sort -pcpu CPU使用率从低到高 最大值在命令输出底部
1 ps aux --sort rss 其他排序关键字 请查阅ps命令的man手册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 STANDARD FORMAT SPECIFIERS 这里列出了可用于控制输出格式（如-o选项）或用GNU风格--sort选项排序的关键字。 例如：ps -eo pid,user,args --sort user 该版本ps会尽量识别其他实现中常用的关键字。 args, cmd, comm, command, fname, ucmd, ucomm, lstart, bsdstart, start等关键字可能包含空格。 有些关键字可能无法用于排序。 CODE HEADER 说明 %cpu %CPU 进程的CPU利用率（##.</description>
    </item>
    
    <item>
      <title>CentOS7/RHEL7 上 systemd 入门指南</title>
      <link>https://wenhan.blog/zh/posts/20170911_beginner-guide-of-systemd-on-centos7-rhel7/</link>
      <pubDate>Mon, 11 Sep 2017 10:19:16 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170911_beginner-guide-of-systemd-on-centos7-rhel7/</guid>
      <description>systemd 简介 在 CentOS 7 和 RHEL 7 之前，系统控制器一直是 System V。 系统控制器可以管理所有进程、服务和启动任务。 System V 由于使用脚本管理任务，存在性能问题，只能串行启动任务，导致系统启动变慢。
从 CentOS 7 开始，systemd 成为新的系统控制器。最大变化是可以并行启动任务，从而提升了启动速度。而且 systemd 的 PID 是 1，负责管理系统中的所有进程！
本文只介绍 systemd 的&amp;quot;服务&amp;quot;、&amp;ldquo;启动任务&amp;quot;和&amp;quot;日志管理&amp;quot;相关内容。
查看系统服务状态 查看系统中所有服务 1 systemctl list-unit-files --type=service 可用 PageUp 和 PageDown 上下翻页，按 q 退出。
查看系统中所有正在运行的服务 1 systemctl list-units --type=service 如果服务名前有一个大圆点（●），表示该服务存在问题。
查看开机自启的所有服务 1 systemctl list-unit-files --type=service | grep enabled 查看某个服务的详细信息 1 systemctl status &amp;lt;服务名&amp;gt; 输出内容包括活动状态、PID、服务路径和最近 10 条日志。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 systemctl status rsyslog ● rsyslog.</description>
    </item>
    
    <item>
      <title>在 docker 容器中 systemctl 命令返回 &#39;Failed to connect to bus: No such file or directory&#39; 的解决方法</title>
      <link>https://wenhan.blog/zh/posts/20170905_systemctl-command-return-failed-to-connect-to-bus-no-such-file-or-directory-in-a-docker-container/</link>
      <pubDate>Tue, 05 Sep 2017 10:30:55 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170905_systemctl-command-return-failed-to-connect-to-bus-no-such-file-or-directory-in-a-docker-container/</guid>
      <description>为了在 docker 容器中启用并启动 cron 任务和 httpd 服务，我尝试使用 systemctl 命令，但遇到了如下错误：
1 2 3 4 [root@5f1f0a5cde43 app]# systemctl status crond Failed to connect to bus: No such file or directory [root@5f1f0a5cde43 app]# systemctl status httpd Failed to connect to bus: No such file or directory 解决方法是在 docker run 命令中添加 --privileged 参数：
1 2 3 4 5 6 7 # docker images REPOSITORY TAG IMAGE ID CREATED SIZE freshcase v0.5 55acda97e409 4 minutes ago 707 MB # docker run --privileged -d freshcase # docker container list CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 81fbd5eb01cb freshcase:v0.</description>
    </item>
    
    <item>
      <title>如何在 CentOS 7 或 RHEL 7 中更改时区</title>
      <link>https://wenhan.blog/zh/posts/20170810_how-to-change-timezone-in-centos-7-or-rhel-7/</link>
      <pubDate>Thu, 10 Aug 2017 14:18:08 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170810_how-to-change-timezone-in-centos-7-or-rhel-7/</guid>
      <description>查看当前时区状态 1 2 3 4 5 6 7 8 9 [root@rhel7 ~]# timedatectl Local time: Thu 2017-08-10 05:19:53 UTC Universal time: Thu 2017-08-10 05:19:53 UTC RTC time: Thu 2017-08-10 05:19:52 Time zone: UTC (UTC, +0000) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: n/a 列出可用时区 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@rhel7 ~]# timedatectl list-timezones Asia/Aden Asia/Almaty Asia/Amman Asia/Anadyr Asia/Aqtau Asia/Aqtobe Asia/Ashgabat .</description>
    </item>
    
    <item>
      <title>用 Python 实现桌面通知</title>
      <link>https://wenhan.blog/zh/posts/20170728_desktop-notifier-by-python/</link>
      <pubDate>Fri, 28 Jul 2017 16:37:40 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170728_desktop-notifier-by-python/</guid>
      <description>本文介绍如何使用 Python 发送桌面通知。
安装依赖 我们需要用 pip 安装 notify2。
1 2 3 4 5 $ pip install notify2 Collecting notify2 Downloading notify2-0.3.1-py2.py3-none-any.whl Installing collected packages: notify2 Successfully installed notify2-0.3.1 编码实现 首先需要导入 notify2。
1 import notify2 然后需要初始化 d-bus 连接。 D-Bus 是一个消息总线系统，是应用程序之间通信的简单方式。
1 2 # 初始化 d-bus 连接 notify2.init(&amp;#34;hello&amp;#34;) 接下来需要创建一个 Notification 对象。 最简单的方式如下：
1 n = notify2.Notification(None) 你也可以为通知添加一个图标。
1 n = notify2.Notification(None, icon = &amp;#34;/home/wenshi/Pictures/me.jpg&amp;#34;) 然后，设置通知的紧急级别。
1 n.set_urgency(notify2.URGENCY_NORMAL) 其他可用的选项有：
1 2 3 notify2.URGENCY_LOW notify2.</description>
    </item>
    
    <item>
      <title>在CentOS、Fedora或RHEL中通过yum安装Chrome浏览器</title>
      <link>https://wenhan.blog/zh/posts/20170714_install-chrome-browser-via-yum-in-centos-or-fedora-or-rhel/</link>
      <pubDate>Fri, 14 Jul 2017 09:29:58 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170714_install-chrome-browser-via-yum-in-centos-or-fedora-or-rhel/</guid>
      <description>创建google yum源 创建文件 /etc/yum.repos.d/google-chrome.repo，并写入如下内容：
1 2 3 4 5 6 [google-chrome] name=google-chrome baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch enabled=1 gpgcheck=1 gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub 检查google源 运行如下命令检查仓库是否可用。
1 yum info google-chrome-stable 在输出中你可以看到google-chrome-stable包的最新版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ yum info google-chrome-stable Fedora 26 - x86_64 - Updates 6.3 MB/s | 4.1 MB 00:00 Fedora 26 - x86_64 7.5 MB/s | 53 MB 00:07 google-chrome 73 kB/s | 3.</description>
    </item>
    
    <item>
      <title>OpenShift 使用 HTPasswd 配置认证和用户代理</title>
      <link>https://wenhan.blog/zh/posts/20170712_openshift-configure-authetication-and-user-agent-using-htpasswd/</link>
      <pubDate>Wed, 12 Jul 2017 14:43:47 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170712_openshift-configure-authetication-and-user-agent-using-htpasswd/</guid>
      <description>使用 Ansible 方法安装 OpenShift 时，默认的身份提供者（identity provider）为 Deny all，即拒绝所有用户访问。要允许用户访问，必须选择其他身份提供者并配置 master 配置文件。默认的 master 配置文件 路径为 /etc/origin/master/master-config.yaml。 OpenShift 提供多种身份提供者用于用户认证管理。本例使用 HTPasswd。更多信息见：Configuring Authentication and User Agent
安装软件包 htpasswd 工具包含在 httpd-tools 包中。
1 yum install httpd-tools 配置 master 配置文件 1 2 3 4 5 6 7 8 9 10 11 oauthConfig: ... identityProviders: - name: my_htpasswd_provider challenge: true login: true mappingMethod: claim provider: apiVersion: v1 kind: HTPasswdPasswordIdentityProvider file: /path/to/users.htpasswd 需要重启 atomic-openshift-master 服务使配置生效。
1 systemctl restart atomic-openshift-master 设置用户名和密码 HTPasswd 使用一个平面文件管理用户名和密码（密码为哈希值）。运行如下命令创建文件并为 username 设置密码。</description>
    </item>
    
    <item>
      <title>[OpenShift]快速在多节点安装 OpenShift</title>
      <link>https://wenhan.blog/zh/posts/20170707_openshift-use-ansible-to-install-openshift-to-multi-nodes/</link>
      <pubDate>Fri, 07 Jul 2017 15:32:15 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170707_openshift-use-ansible-to-install-openshift-to-multi-nodes/</guid>
      <description>本文介绍如何使用 ansible 驱动的 atomic-openshift-installer 快速命令，在多节点上安装 OpenShift。
主机准备 我使用如下虚拟机作为 OpenShift 节点：
类型 CPU 内存 硬盘 主机名 操作系统 Master 1 2 GB 20 GB master.example.com RHEL 7 node 1 2 GB 20 GB node1.example.com RHEL 7 node 1 2 GB 20 GB node2.example.com RHEL 7 主机注册 注意：如果你使用的不是 RHEL，请直接跳到&amp;quot;## 安装必要软件包&amp;quot;部分安装相关包。如果有包无法安装，可以尝试添加仓库或手动下载 rpm 文件。
每台主机都需要通过 RHSM（Red Hat Subscription Manager）注册，以获取所需软件包。
在每台主机上注册 RHSM：
1 # subscription-manager register --username=&amp;lt;user_name&amp;gt; --password=&amp;lt;password&amp;gt; 列出可用的 OpenShift 订阅：
1 # subscription-manager list --available --matches &amp;#39;*OpenShift*&amp;#39; 找到 OpenShift Container Platform 订阅的 pool ID 并绑定：</description>
    </item>
    
    <item>
      <title>如何在VIM中复制文本到系统剪贴板</title>
      <link>https://wenhan.blog/zh/posts/20170704_how-to-copy-paste-text-to-from-the-system-clipboard-in-vim/</link>
      <pubDate>Tue, 04 Jul 2017 11:44:42 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170704_how-to-copy-paste-text-to-from-the-system-clipboard-in-vim/</guid>
      <description>确认你的VIM已启用+clipboard功能 在.vimrc中添加&amp;quot;set clipboard=unnamedplus&amp;quot; 检查+clipboard是否启用 1 2 3 $ vim --version | grep clipboard +clipboard +job +path_extra +user_commands +eval +mouse_dec +statusline +xterm_clipboard 如果显示&amp;rsquo;-clipboard&amp;rsquo;，你需要带此功能重新编译VIM。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ git clone https://github.com/vim/vim.git $ cd vim/src/ $ ./configure --with-features=huge \ --enable-multibyte \ --enable-rubyinterp=yes \ --enable-pythoninterp=yes \ --with-python-config-dir=/usr/lib64/python2.7/config \ --enable-perlinterp=yes \ --enable-luainterp=yes \ --prefix=/usr/local/ \ --enable-fail-if-missing \ --enable-gui=no \ --enable-tclinterp=yes \ --enable-cscope=yes \ --enable-gpm \ --enable-cscope \ --enable-fontset \ --with-x \ --with-compiledby=koturn $ make -j5 # 编译后可在.</description>
    </item>
    
    <item>
      <title>如何在注销后让进程继续运行</title>
      <link>https://wenhan.blog/zh/posts/20170629_how-to-keep-process-script-running-after-end-the-ssh-session/</link>
      <pubDate>Thu, 29 Jun 2017 14:31:14 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170629_how-to-keep-process-script-running-after-end-the-ssh-session/</guid>
      <description>tmux 是一个开源软件，可以帮你实现这个需求。tmux 还有很多其他功能，这里只介绍如何让进程在 ssh 断开后依然运行。步骤如下：
在 shell 中输入 tmux 启动一个 tmux 会话。 在 tmux 会话中启动你的进程或脚本。 按下 Ctrl+b 然后按 d，即可离开（detach）tmux 会话。 现在你可以安全地注销或断开 ssh 连接，你的进程/脚本依然在 tmux 会话中运行。想要查看进程状态时，重新登录后输入 tmux attach 即可重新连接会话。
当前运行的会话可以用 tmux list-sessions 查看。
tmux 还有更多功能，详细用法请参考 man tmux。</description>
    </item>
    
    <item>
      <title>[VIM] 利用内置拼写检查功能</title>
      <link>https://wenhan.blog/zh/posts/20170626_vim-use-built-in-spell-check/</link>
      <pubDate>Mon, 26 Jun 2017 16:37:57 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170626_vim-use-built-in-spell-check/</guid>
      <description>从第7版开始，VIM 内置了拼写检查功能，但默认是关闭的。
启用/禁用 可以使用 :set spell 和 :set nospell 来启用或禁用拼写检查。拼写检查不仅支持英语，也支持其他语言。用 :echo &amp;amp;spelllang 可以确认当前目标语言。用 :set spelllang=en_GB.UTF-8 可以切换目标语言，也可以用 set spelllang=en_us,nl,medical 设置多个语言。
拼写检查 用 ]s 跳转到下一个拼写错误，用 [s 跳转到上一个拼写错误。
修正拼写错误 用 z= 列出拼写错误的建议，输入数字选择。
对于特殊单词，可以用 zg 添加到用户词典，用 zw 删除。
总结 command action :set spell 启用拼写检查 :set nospell 禁用拼写检查 ]s 跳到下一个错误 [s 跳到上一个错误 z= 列出建议 zg 添加单词 zw 删除单词 </description>
    </item>
    
    <item>
      <title>[OpenShift]安装 OpenShift 并创建第一个项目 Hello-OpenShift</title>
      <link>https://wenhan.blog/zh/posts/20170626_openshift-install-openshift-and-create-first-project-hello-openshift/</link>
      <pubDate>Mon, 26 Jun 2017 13:40:40 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170626_openshift-install-openshift-and-create-first-project-hello-openshift/</guid>
      <description>安装 Linux 系统并确认网络设置 你需要创建一台 Linux 系统的机器来安装 OpenShift。最低要求如下：
CPU 内存 硬盘 网络 x86_64 1核 2 GB 20 GB IPv4 首先安装 CentOS 7.3，选择最小化安装。安装完成后，检查你的 IP 地址，确保有可用的 IP。如下例 eth0 的 IP 地址为 &amp;ldquo;192.168.122.45&amp;rdquo;。
1 2 [root@openshift ~]# ip a ...（省略：命令输出为英文原文）... OpenShift 需要主机名来提供服务。用 &amp;ldquo;hostnamectl set-hostname&amp;rdquo; 设置主机名，并用 &amp;ldquo;hostname&amp;rdquo; 确认。如下例主机名为 &amp;ldquo;openshift.example.com&amp;rdquo;。
1 2 3 [root@openshift ~]# hostnamectl set-hostname openshift.example.com [root@openshift ~]# hostname openshift.example.com 安装 docker Openshift 使用 docker 作为容器引擎。用如下命令安装 docker。
1 [root@openshift ~]# yum install -y docker 安装完成后，启动并设置 docker 服务开机自启。</description>
    </item>
    
    <item>
      <title>如何在Linux下清理内存缓存、buffer和swap</title>
      <link>https://wenhan.blog/zh/posts/20170616_how-to-clear-memory-cache-buffer-and-swap-on-linux/</link>
      <pubDate>Fri, 16 Jun 2017 12:05:35 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170616_how-to-clear-memory-cache-buffer-and-swap-on-linux/</guid>
      <description>如何在Linux下清理缓存 只清理 PageCache 1 sync ; echo 1 &amp;gt; /proc/sys/vm/drop_caches 清理 Dentry 和 inode（元数据） 1 sync ; echo 2 &amp;gt; /proc/sys/vm/drop_caches 清理所有缓存（包括 PageCache、Dentry 和 inode） 1 sync ; echo 3 &amp;gt; /proc/sys/vm/drop_caches 如何在Linux下清理swap空间 1 swapoff -a &amp;amp;&amp;amp; swapon -a 注意：如果你的RAM本来就很少，这个操作可能会导致系统不稳定。 swap空间中的数据会被强制移到内存，如果内存不足，可能会导致OOM（内存溢出）。</description>
    </item>
    
    <item>
      <title>GlusterFS 克隆节点探测失败</title>
      <link>https://wenhan.blog/zh/posts/20170615_glusterfs-failed-to-probe-a-cloned-peer/</link>
      <pubDate>Thu, 15 Jun 2017 11:43:46 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170615_glusterfs-failed-to-probe-a-cloned-peer/</guid>
      <description>如果你想节省每次搭建 gluster 系统的时间，可以在一台虚拟机上配置好所有内容，然后克隆它。 但这样做后，在其他节点上探测 peer 时可能会遇到问题。 当你尝试运行如下命令时，可能会遇到如下报错：
1 2 [root@node1 ~]# gluster peer probe node2 peer probe: failed: Peer uuid (host node2) is same as local uuid 这是因为 glusterfs-server 包首次安装时，会在 /var/lib/glusterd/glusterd.info 生成一个节点 UUID 文件。 所以如果你克隆了已安装 glusterfs 的虚拟机，所有虚拟机上都会有相同的 glusterd.info 和 UUID。
解决方法如下：
在所有节点上停止 glusterd 服务 删除 /var/lib/glusterd/glusterd.info 在所有节点上启动 glusterd 服务 再次运行 peer probe 命令 第3步后，系统会生成带有新 UUID 的 glusterd.info 文件，问题即可彻底解决。</description>
    </item>
    
    <item>
      <title>文件和目录的特殊权限</title>
      <link>https://wenhan.blog/zh/posts/20170427_linux-special-permissions-for-file-and-dir/</link>
      <pubDate>Thu, 27 Apr 2017 23:59:20 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170427_linux-special-permissions-for-file-and-dir/</guid>
      <description>特殊权限说明 文件和文件夹有三种特殊权限：setuid、setgid 和 sticky bit。 setuid 和 setgid 权限表示命令以文件所有者或组的身份执行，而不是以运行命令的用户或组的身份执行。 sticky bit 权限对文件删除有特殊限制：只有文件所有者和 root 可以删除目录中的文件。
特殊权限 对文件的影响 对目录的影响 u+s (suid) 文件以所有者身份执行，而不是以运行者身份执行。 无影响 g+s (sgid) 文件以所属组身份执行。 目录中新建文件的所属组与目录的组相同。 o+t (sticky) 无影响 具有写权限的用户只能删除自己拥有的文件，不能删除或修改他人拥有的文件。 设置特殊权限 符号方式：setuid = u+s；setgid = g+s；sticky = o+t 数字方式：setuid = 4； setgid = 2； sticky = 1
示例
1 2 chmod g+s file # 给文件添加 setgid 位 chmod 1755 file # 给文件添加 sticky 位 </description>
    </item>
    
    <item>
      <title>Linux 用户密码有效期管理</title>
      <link>https://wenhan.blog/zh/posts/20170427_linux-password-aging/</link>
      <pubDate>Thu, 27 Apr 2017 23:15:00 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170427_linux-password-aging/</guid>
      <description>本文介绍如何管理 Linux 用户密码的有效期（aging）。
last change (-d)：密码最后一次修改的天数 min days (-m)：密码可更改的最小天数 max days (-M)：密码必须更改的最大天数 warn days (-W)：密码即将过期时提前警告的天数 password expiration date：密码过期日期 inactive days (-l)：密码过期后账户保留的天数 inactive date：账户变为非活动状态的日期 上述信息可以通过 chage 命令进行修改。
1 chage -m 0 -M 60 -W 7 -l 30 chage 命令的其他用法：
下次登录时强制用户修改密码 1 chage -d 0 username 显示当前设置 1 chage -l username 指定日期让账户过期 1 chage -E YYYY-MM-DD username 与用户账户相关的其他内容：
锁定和解锁账户 1 2 usermod -L username usermod -U username </description>
    </item>
    
    <item>
      <title>软链接与硬链接的区别</title>
      <link>https://wenhan.blog/zh/posts/20170427_soft-link-and-hard-link/</link>
      <pubDate>Thu, 27 Apr 2017 22:14:48 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170427_soft-link-and-hard-link/</guid>
      <description>记录一下软链接和硬链接的区别。 如果以后想起其他点会再补充。
项目 软链接 硬链接 大小 4 字节 显示上与原文件相同 inode 与原文件不同 inode 与原文件相同 inode 限制 - 必须在同一文件系统下 如下可以看到链接的大小和 inode 编号的区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 wenhanMBP: /tmp/link → ls -li total 1024 12807105 -rw-r--r-- 1 shiwenhan wheel 524288 4 27 22:18 file wenhanMBP: /tmp/link → ln file hardlink-to-file wenhanMBP: /tmp/link → ls -li total 2048 12807105 -rw-r--r-- 2 shiwenhan wheel 524288 4 27 22:18 file 12807105 -rw-r--r-- 2 shiwenhan wheel 524288 4 27 22:18 hardlink-to-file wenhanMBP: /tmp/link → ln -s file softlink-to-file wenhanMBP: /tmp/link → ls -lih total 2056 12807105 -rw-r--r-- 2 shiwenhan wheel 512K 4 27 22:18 file 12807105 -rw-r--r-- 2 shiwenhan wheel 512K 4 27 22:18 hardlink-to-file 12807448 lrwxr-xr-x 1 shiwenhan wheel 4B 4 27 22:27 softlink-to-file -&amp;gt; file </description>
    </item>
    
    <item>
      <title>修复 hexo 报错 &#39;./build/Release/DTraceProviderBindings&#39;</title>
      <link>https://wenhan.blog/zh/posts/20170421_fix-hexo-error-output-build-release-dtraceproviderbindings/</link>
      <pubDate>Fri, 21 Apr 2017 00:56:19 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170421_fix-hexo-error-output-build-release-dtraceproviderbindings/</guid>
      <description>每次运行 hexo 命令时都会出现一个烦人的报错：
1 Error: Cannot find module &amp;#39;./build/Release/DTraceProviderBindings&amp;#39; 这只是一个追踪错误，不会影响实际操作，但实在太吵了，我很想去掉它。
参考以下链接： https://github.com/hexojs/hexo/issues/1922 https://github.com/yarnpkg/yarn/issues/1915
发现根本原因是 dtrace-provider 包。 而我根本用不到它，所以只想卸载掉。
1 npm uninstall dtrace-provider -g 但由于这个包和 hexo 有关联，实际上不会被移除…… 你可以用下面的命令看到它依然存在：
1 npm list | grep dtrace 那就清理环境，把 hexo-cli 和 dtrace-provider 都卸载掉。
注意：必须用 sudo 执行命令 1 2 sudo npm uninstall hexo-cli -g sudo npm uninstall dtrace-provider -g 然后用 &amp;ndash;no-optional 选项重新安装 hexo-cli，确认 dtrace-provider 没有被装上。
注意：必须用 sudo 执行命令 1 sudo npm install hexo-cli --no-optional -g 终于，世界又安静了:)</description>
    </item>
    
    <item>
      <title>Python每日获取天气情报并通过邮件通知</title>
      <link>https://wenhan.blog/zh/posts/20170421_get-weather-info-and-send-by-gmail-python/</link>
      <pubDate>Fri, 21 Apr 2017 00:16:17 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170421_get-weather-info-and-send-by-gmail-python/</guid>
      <description>因为没看天气预报，被大雨浇了几次之后，我打算为我这样的懒人写一个小程序。 很简单，算是Python的一个小练习，用Python实现一个邮件提醒每日天气。
这段程序一共分两步
通过网站获取天气信息 将天气信息通过邮件发送到指定信箱 获取天气信息 这里我用了常用的requests和BeautifulSoup4来获取网页并抽取信息。 网站用tenki.jp搜索天气，然后通过id来抽取搜索界面的天气信息框框。 搜索界面如下，可以看到用BS4抽取id为&amp;rsquo;map_world_point_wrap&amp;rsquo;的&amp;lt;div&amp;gt;以下的HTML就好了。 我们准备直接发送HTML代码，这样看起来更好看一些。
代码如下 注：soup.find之后需要将内容转换为string格式，不然python2环境下无法发送邮件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #!/root/.pyenv/shims/python #-*- coding: UTF-8 -*- import sys import time import requests from bs4 import BeautifulSoup #Some User Agents hds=[{&amp;#39;User-Agent&amp;#39;:&amp;#39;Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6&amp;#39;}] def weather_notice(): url=&amp;#39;http://www.</description>
    </item>
    
    <item>
      <title>在 Mac 上安装支持 python 的 vim8</title>
      <link>https://wenhan.blog/zh/posts/20170415_install-vim-8-with-python/</link>
      <pubDate>Sat, 15 Apr 2017 00:50:08 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170415_install-vim-8-with-python/</guid>
      <description>今天配置了我的 Mac Pro 开发环境。 发现用 brew 安装带 python 支持的 VIM8 非常简单。 （比 cent 7 简单多了）
如果你有 brew，只需运行：
1 brew install --with-python vim 如果想同时支持 python3 和 python2，运行：
1 brew install --with-python --with-python3 vim 补充：在某些新版系统上，python2 需要用下面的命令：
1 brew install --with-python@2 你可能需要先删除旧的 vim 可执行文件：
1 rm -f /usr/local/bin/vim 然后重新链接 vim：
1 brew link --overwrite vim 搞定！尽情享受你的 vim 吧</description>
    </item>
    
    <item>
      <title>无法以 root 用户 ssh 到服务器</title>
      <link>https://wenhan.blog/zh/posts/20170216_cannot-ssh-to-server-by-root-user/</link>
      <pubDate>Thu, 16 Feb 2017 16:36:12 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20170216_cannot-ssh-to-server-by-root-user/</guid>
      <description>这是一个明明知道 root 密码却无法以 root 用户 ssh 登录服务器的解决方法。
实际上，sshd 的配置文件中有一个参数可以控制是否允许 root 登录。 在 Linux 环境下，编辑配置文件 /etc/ssh/sshd_config：
1 PermitRootLogin no 将其修改为：
1 PermitRootLogin yes 然后重启 sshd 服务，就可以用 root 登录了。
1 systemctl restart sshd 另外，还有如下参数可以关闭自动登录：
1 PubkeyAuthentication no 也可以针对特定 IP 进行单独设置：
1 2 Match Address 172.25.0.11 PubkeyAuthentication no </description>
    </item>
    
    <item>
      <title>用 Python 编写爬虫</title>
      <link>https://wenhan.blog/zh/posts/20161201_crawling-webpage-use-python-requests/</link>
      <pubDate>Thu, 01 Dec 2016 00:54:09 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161201_crawling-webpage-use-python-requests/</guid>
      <description>使用 Python 的 Requests 库可以提取并保存网页内容。 然后，利用 BeautifulSoup 库可以抽取你想要的信息。
什么是 Requests？ Requests 是 Python 的 HTTP 库，比 urllib2 好用太多了。 官网介绍：
Requests is an Apache2 Licensed HTTP library, written in Python, for human beings.
正如介绍所说，它让代码更易于人类阅读和编写。
什么是 BeautifulSoup？ BeautifulSoup 是一个可以在 Python 中使用的 HTML 和 XML 解析器。
安装 1 2 pip install requests pip install BeautifulSoup 用法 1 2 import requests from bs4 import BeautifulSoup requests 库为每种 HTTP 方法都提供了一一对应的方法。
1 2 3 4 5 requests.</description>
    </item>
    
    <item>
      <title>使用Vundle轻松管理Vim插件</title>
      <link>https://wenhan.blog/zh/posts/20161121_manage-vim-plugins-with-vundle/</link>
      <pubDate>Mon, 21 Nov 2016 00:37:36 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161121_manage-vim-plugins-with-vundle/</guid>
      <description>Vundle 是一个流行的 Vim 插件管理工具，本文记录其使用方法。
官网：[https://github.com/VundleVim/Vundle.vim]
使用 Vundle 的优点
可以通过 .vimrc 安装/更新/删除插件 只需写插件名即可自动查找 安装 只需执行以下命令复制文件即可完成安装：
1 git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim 配置 将以下内容添加到 .vimrc 文件的顶部。 部分行仅为说明用途，实际使用时请根据需要注释掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 set nocompatible &amp;#34; be iMproved, required filetype off &amp;#34; required &amp;#34; set the runtime path to include Vundle and initialize set rtp+=~/.</description>
    </item>
    
    <item>
      <title>用 Python 调用 Google Maps API</title>
      <link>https://wenhan.blog/zh/posts/20161112_google-maps-api-with-python/</link>
      <pubDate>Sat, 12 Nov 2016 01:22:46 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161112_google-maps-api-with-python/</guid>
      <description>用 Python 调用 Google Maps API 做了下路线查询。
用到的模块是 googlemaps/google-maps-services-python
环境搭建直接用如下命令，顺便也可以装下 ipython。
1 2 pip install googlemaps pip install ipython 另外需要申请 Google 的 API Key，在 Google APIs 申请并启用即可。
到这里没问题的话，马上试用一下。 以&amp;quot;户冢站&amp;quot;到&amp;quot;踊场站&amp;quot;的驾车路线为例获取路线信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 $ ipython Python 3.</description>
    </item>
    
    <item>
      <title>iPhone上显示Google地图的注意事项</title>
      <link>https://wenhan.blog/zh/posts/20161101_googlemap-on-ios/</link>
      <pubDate>Tue, 01 Nov 2016 01:16:21 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161101_googlemap-on-ios/</guid>
      <description>记录一下在iPhone上显示Google地图时遇到的坑。
参考了Google Maps API &amp;gt; iOS向け &amp;gt; Maps SDK for iOS，想在屏幕上显示Google Map，结果页面一片空白，什么都没有。
控制台里出现了如下错误信息：
1 ClientParametersRequest failed, 0 attempts remaining (0 vs 6). Error Domain=com.google.HTTPStatus Code=400 在网上查了一下，发现了这个网站。 我自己的情况是第三条，API Key 变成了&amp;quot;无效&amp;quot;。
这个错误大多是API Key设置有误导致的。请检查以下几点：
生成API Key时，建议设置Xcode工程的bundle ID。虽然Google说这个参数&amp;quot;可选&amp;quot;，但最好还是设置。 如果更改了App的Bundle ID，请务必重新生成API Key。 在Google Developers控制台确认&amp;quot;Google Maps SDK for iOS&amp;quot;已启用。默认是未启用的。（我这次就是卡在这里） 有时间的话，准备把API Key启用界面的截图也上传一下。</description>
    </item>
    
    <item>
      <title>用 Python 检查回文</title>
      <link>https://wenhan.blog/zh/posts/20161029_python-reverse-str-to-check-palindrome/</link>
      <pubDate>Sat, 29 Oct 2016 01:21:18 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161029_python-reverse-str-to-check-palindrome/</guid>
      <description>又做了一道题。Palindrome chain length
这是一个关于回文的计算题。 回文是指将字符串从中间分开，左右两边反转后对称的字符串。 比如，&amp;ldquo;あいういあ&amp;quot;是回文，&amp;ldquo;あいうえお&amp;quot;不是回文。 数字同理，5、44、171、4884 是回文，43、194、4773 不是回文。
这道题要求对给定的数字，按照特定的计算方法，算出需要几次操作才能变成回文数。 这里的特定计算方法是&amp;quot;将数字各位反转后与原数字相加&amp;rdquo;。
比如，给定 87，经过 4 次计算变成回文数，所以返回值为 4。
1 2 3 4 87 + 78 = 165; 165 + 561 = 726; 726 + 627 = 1353; 1353 + 3531 = 4884; 难点在于如何反转数字。 查了一下，发现其实很简单，一行就能搞定。Python 反转字符串
1 2 str(n) # 把数字转成字符串 str(n)[::-1] # 把数字转成字符串后反转 这种写法就是&amp;quot;从字符串末尾往前依次取到开头&amp;rdquo;。
针对这道题，我的解答如下：
1 2 3 4 5 6 def palindrome_chain_length(n): cnt=0 while str(n) != str(n)[::-1] : cnt += 1 n += int(str(n)[::-1]) return cnt 真方便啊~ 这样今天又能睡个好觉了~</description>
    </item>
    
    <item>
      <title>Python 的输出格式化</title>
      <link>https://wenhan.blog/zh/posts/20161029_python-print-format/</link>
      <pubDate>Sat, 29 Oct 2016 00:28:06 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161029_python-print-format/</guid>
      <description>今天又练习了一道 CodeWar 的题目：RGB To Hex Conversion
简单来说，就是把 rgb 数字转换成十六进制输出。
1 2 3 4 rgb(255, 255, 255) # returns FFFFFF rgb(255, 255, 300) # returns FFFFFF rgb(0,0,0) # returns 000000 rgb(148, 0, 211) # returns 9400D3 直接用 hex 转换会多出&amp;quot;0x&amp;quot;，而且&amp;quot;00&amp;quot;会变成&amp;quot;0&amp;quot;。 想着有没有类似 sprintf 的函数，结果找到了这个网站： 像 C 的 sprintf 一样的字符串格式化
没想到只要指定格式，数字就能直接转换！还能指定位数。
我的解答如下：
1 2 3 4 5 def limit(a): return min(max(a, 0), 255) def rgb(r, g, b): return &amp;#34;%02X%02X%02X&amp;#34; % (limit(r), limit(g), limit(b),) 真方便啊~</description>
    </item>
    
    <item>
      <title>安装 Hexo</title>
      <link>https://wenhan.blog/zh/posts/20161024_install-of-hexo/</link>
      <pubDate>Mon, 24 Oct 2016 23:15:49 +0000</pubDate>
      
      <guid>https://wenhan.blog/zh/posts/20161024_install-of-hexo/</guid>
      <description>参考了这里的 URL，设置了 Hexo 博客。 https://liginc.co.jp/web/programming/server/104594
安装过程中出现了如下错误：
1 2 % hexo deploy ERROR Deployer not found: github 参考 https://github.com/hexojs/hexo/issues/1040 解决了。
1 % npm install hexo-deployer-git --save _config.yml 的 type 也要改成 git
1 2 deploy: type: git 还得学习 Markdown 语法……要做的事情真多~</description>
    </item>
    
  </channel>
</rss>
